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_DATA = 8,
36 MONO_PROFILER_FILE_BLOCK_KIND_HEAP_SUMMARY = 9
37 } MonoProfilerFileBlockKind;
39 #define MONO_PROFILER_LOADED_EVENT_MODULE 1
40 #define MONO_PROFILER_LOADED_EVENT_ASSEMBLY 2
41 #define MONO_PROFILER_LOADED_EVENT_APPDOMAIN 4
42 #define MONO_PROFILER_LOADED_EVENT_SUCCESS 8
43 #define MONO_PROFILER_LOADED_EVENT_FAILURE 16
46 MONO_PROFILER_EVENT_DATA_TYPE_OTHER = 0,
47 MONO_PROFILER_EVENT_DATA_TYPE_METHOD = 1,
48 MONO_PROFILER_EVENT_DATA_TYPE_CLASS = 2
49 } MonoProfilerEventDataType;
51 typedef struct _ProfilerEventData {
56 unsigned int data_type:2;
59 unsigned int value:26;
62 #define EVENT_VALUE_BITS (26)
63 #define MAX_EVENT_VALUE ((1<<EVENT_VALUE_BITS)-1)
66 MONO_PROFILER_EVENT_METHOD_JIT = 0,
67 MONO_PROFILER_EVENT_METHOD_FREED = 1,
68 MONO_PROFILER_EVENT_METHOD_CALL = 2
69 } MonoProfilerMethodEvents;
71 MONO_PROFILER_EVENT_CLASS_LOAD = 0,
72 MONO_PROFILER_EVENT_CLASS_UNLOAD = 1,
73 MONO_PROFILER_EVENT_CLASS_EXCEPTION = 2,
74 MONO_PROFILER_EVENT_CLASS_ALLOCATION = 3
75 } MonoProfilerClassEvents;
77 MONO_PROFILER_EVENT_RESULT_SUCCESS = 0,
78 MONO_PROFILER_EVENT_RESULT_FAILURE = 4
79 } MonoProfilerEventResult;
80 #define MONO_PROFILER_EVENT_RESULT_MASK MONO_PROFILER_EVENT_RESULT_FAILURE
82 MONO_PROFILER_EVENT_THREAD = 1,
83 MONO_PROFILER_EVENT_GC_COLLECTION = 2,
84 MONO_PROFILER_EVENT_GC_MARK = 3,
85 MONO_PROFILER_EVENT_GC_SWEEP = 4,
86 MONO_PROFILER_EVENT_GC_RESIZE = 5,
87 MONO_PROFILER_EVENT_GC_STOP_WORLD = 6,
88 MONO_PROFILER_EVENT_GC_START_WORLD = 7
91 MONO_PROFILER_EVENT_KIND_START = 0,
92 MONO_PROFILER_EVENT_KIND_END = 1
93 } MonoProfilerEventKind;
95 #define MONO_PROFILER_GET_CURRENT_TIME(t) {\
96 struct timeval current_time;\
97 gettimeofday (¤t_time, NULL);\
98 (t) = (((guint64)current_time.tv_sec) * 1000000) + current_time.tv_usec;\
101 static gboolean use_fast_timer = FALSE;
103 #if (defined(__i386__) || defined(__x86_64__)) && ! defined(PLATFORM_WIN32)
105 #if defined(__i386__)
106 static const guchar cpuid_impl [] = {
107 0x55, /* push %ebp */
108 0x89, 0xe5, /* mov %esp,%ebp */
109 0x53, /* push %ebx */
110 0x8b, 0x45, 0x08, /* mov 0x8(%ebp),%eax */
111 0x0f, 0xa2, /* cpuid */
112 0x50, /* push %eax */
113 0x8b, 0x45, 0x10, /* mov 0x10(%ebp),%eax */
114 0x89, 0x18, /* mov %ebx,(%eax) */
115 0x8b, 0x45, 0x14, /* mov 0x14(%ebp),%eax */
116 0x89, 0x08, /* mov %ecx,(%eax) */
117 0x8b, 0x45, 0x18, /* mov 0x18(%ebp),%eax */
118 0x89, 0x10, /* mov %edx,(%eax) */
120 0x8b, 0x55, 0x0c, /* mov 0xc(%ebp),%edx */
121 0x89, 0x02, /* mov %eax,(%edx) */
127 typedef void (*CpuidFunc) (int id, int* p_eax, int* p_ebx, int* p_ecx, int* p_edx);
130 cpuid (int id, int* p_eax, int* p_ebx, int* p_ecx, int* p_edx) {
133 __asm__ __volatile__ (
136 "movl %%eax, %%edx\n"
137 "xorl $0x200000, %%eax\n"
142 "xorl %%edx, %%eax\n"
143 "andl $0x200000, %%eax\n"
165 CpuidFunc func = (CpuidFunc) cpuid_impl;
166 func (id, p_eax, p_ebx, p_ecx, p_edx);
168 * We use this approach because of issues with gcc and pic code, see:
169 * http://gcc.gnu.org/cgi-bin/gnatsweb.pl?cmd=view%20audit-trail&database=gcc&pr=7329
170 __asm__ __volatile__ ("cpuid"
171 : "=a" (*p_eax), "=b" (*p_ebx), "=c" (*p_ecx), "=d" (*p_edx)
179 static void detect_fast_timer (void) {
180 int p_eax, p_ebx, p_ecx, p_edx;
182 if (cpuid (0x1, &p_eax, &p_ebx, &p_ecx, &p_edx)) {
184 use_fast_timer = TRUE;
186 use_fast_timer = FALSE;
189 use_fast_timer = FALSE;
194 #if defined(__x86_64__)
195 static void detect_fast_timer (void) {
197 guint32 eax,ebx,ecx,edx;
198 __asm__ __volatile__ ("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(op));
200 use_fast_timer = TRUE;
202 use_fast_timer = FALSE;
207 static __inline__ guint64 rdtsc(void) {
209 __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
210 return ((guint64) lo) | (((guint64) hi) << 32);
212 #define MONO_PROFILER_GET_CURRENT_COUNTER(c) {\
213 if (use_fast_timer) {\
216 MONO_PROFILER_GET_CURRENT_TIME ((c));\
220 static detect_fast_timer (void) {
221 use_fast_timer = FALSE;
223 #define MONO_PROFILER_GET_CURRENT_COUNTER(c) MONO_PROFILER_GET_CURRENT_TIME ((c))
227 #define CLASS_LAYOUT_PACKED_BITMAP_SIZE 64
228 #define CLASS_LAYOUT_NOT_INITIALIZED (0xFFFF)
231 HEAP_CODE_OBJECT = 1,
232 HEAP_CODE_FREE_OBJECT_CLASS = 2,
234 } HeapProfilerJobValueCode;
235 typedef struct _MonoProfilerClassData {
244 } MonoProfilerClassData;
246 typedef struct _MonoProfilerMethodData {
249 } MonoProfilerMethodData;
251 typedef struct _ClassIdMappingElement {
255 struct _ClassIdMappingElement *next_unwritten;
256 MonoProfilerClassData data;
257 } ClassIdMappingElement;
259 typedef struct _MethodIdMappingElement {
263 struct _MethodIdMappingElement *next_unwritten;
264 MonoProfilerMethodData data;
265 } MethodIdMappingElement;
267 typedef struct _ClassIdMapping {
269 ClassIdMappingElement *unwritten;
273 typedef struct _MethodIdMapping {
275 MethodIdMappingElement *unwritten;
279 typedef struct _LoadedElement {
281 guint64 load_start_counter;
282 guint64 load_end_counter;
283 guint64 unload_start_counter;
284 guint64 unload_end_counter;
288 guint8 unload_written;
291 #define PROFILER_HEAP_SHOT_OBJECT_BUFFER_SIZE 1024
292 #define PROFILER_HEAP_SHOT_HEAP_BUFFER_SIZE 4096
293 #define PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE 4096
295 typedef struct _ProfilerHeapShotObjectBuffer {
296 struct _ProfilerHeapShotObjectBuffer *next;
297 MonoObject **next_free_slot;
299 MonoObject **first_unprocessed_slot;
300 MonoObject *buffer [PROFILER_HEAP_SHOT_OBJECT_BUFFER_SIZE];
301 } ProfilerHeapShotObjectBuffer;
303 typedef struct _ProfilerHeapShotHeapBuffer {
304 struct _ProfilerHeapShotHeapBuffer *next;
305 struct _ProfilerHeapShotHeapBuffer *previous;
306 MonoObject **start_slot;
307 MonoObject **end_slot;
308 MonoObject *buffer [PROFILER_HEAP_SHOT_HEAP_BUFFER_SIZE];
309 } ProfilerHeapShotHeapBuffer;
311 typedef struct _ProfilerHeapShotHeapBuffers {
312 ProfilerHeapShotHeapBuffer *buffers;
313 ProfilerHeapShotHeapBuffer *last;
314 ProfilerHeapShotHeapBuffer *current;
315 MonoObject **first_free_slot;
316 } ProfilerHeapShotHeapBuffers;
319 typedef struct _ProfilerHeapShotWriteBuffer {
320 struct _ProfilerHeapShotWriteBuffer *next;
321 gpointer buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE];
322 } ProfilerHeapShotWriteBuffer;
324 typedef struct _ProfilerHeapShotClassSummary {
333 } ProfilerHeapShotClassSummary;
335 typedef struct _ProfilerHeapShotCollectionSummary {
336 ProfilerHeapShotClassSummary *per_class_data;
338 } ProfilerHeapShotCollectionSummary;
340 typedef struct _ProfilerHeapShotWriteJob {
341 struct _ProfilerHeapShotWriteJob *next;
342 struct _ProfilerHeapShotWriteJob *next_unwritten;
346 ProfilerHeapShotWriteBuffer *buffers;
347 ProfilerHeapShotWriteBuffer **last_next;
348 guint32 full_buffers;
349 gboolean heap_shot_was_signalled;
350 guint64 start_counter;
355 ProfilerHeapShotCollectionSummary summary;
356 gboolean dump_heap_data;
357 } ProfilerHeapShotWriteJob;
359 typedef struct _ProfilerPerThreadData {
360 ProfilerEventData *events;
361 ProfilerEventData *next_free_event;
362 ProfilerEventData *end_event;
363 ProfilerEventData *first_unwritten_event;
364 ProfilerEventData *first_unmapped_event;
365 guint64 start_event_counter;
366 guint64 last_event_counter;
368 ProfilerHeapShotObjectBuffer *heap_shot_object_buffers;
369 struct _ProfilerPerThreadData* next;
370 } ProfilerPerThreadData;
372 typedef struct _ProfilerStatisticalHit {
375 } ProfilerStatisticalHit;
377 typedef struct _ProfilerStatisticalData {
378 ProfilerStatisticalHit *hits;
381 int first_unwritten_index;
382 } ProfilerStatisticalData;
384 typedef struct _ProfilerUnmanagedSymbol {
389 } ProfilerUnmanagedSymbol;
391 struct _ProfilerExecutableFile;
393 typedef struct _ProfilerExecutableMemoryRegionData {
401 struct _ProfilerExecutableFile *file;
402 guint32 symbols_count;
403 guint32 symbols_capacity;
404 ProfilerUnmanagedSymbol *symbols;
405 } ProfilerExecutableMemoryRegionData;
407 typedef struct _ProfilerExecutableMemoryRegions {
408 ProfilerExecutableMemoryRegionData **regions;
409 guint32 regions_capacity;
410 guint32 regions_count;
412 guint32 next_unmanaged_function_id;
413 } ProfilerExecutableMemoryRegions;
415 /* Start of ELF definitions */
417 typedef guint16 ElfHalf;
418 typedef guint32 ElfWord;
419 typedef gsize ElfAddr;
420 typedef gsize ElfOff;
423 unsigned char e_ident[EI_NIDENT];
429 ElfOff e_shoff; // Section header table
431 ElfHalf e_ehsize; // Header size
434 ElfHalf e_shentsize; // Section header entry size
435 ElfHalf e_shnum; // Section header entries number
436 ElfHalf e_shstrndx; // String table index
439 #if (SIZEOF_VOID_P == 4)
444 ElfAddr sh_addr; // Address in memory
445 ElfOff sh_offset; // Offset in file
449 ElfWord sh_addralign;
456 unsigned char st_info; // Use ELF32_ST_TYPE to get symbol type
457 unsigned char st_other;
458 ElfHalf st_shndx; // Or one of SHN_ABS, SHN_COMMON or SHN_UNDEF.
460 #elif (SIZEOF_VOID_P == 8)
465 ElfAddr sh_addr; // Address in memory
466 ElfOff sh_offset; // Offset in file
475 unsigned char st_info; // Use ELF_ST_TYPE to get symbol type
476 unsigned char st_other;
477 ElfHalf st_shndx; // Or one of SHN_ABS, SHN_COMMON or SHN_UNDEF.
482 #error Bad size of void pointer
486 #define ELF_ST_BIND(i) ((i)>>4)
487 #define ELF_ST_TYPE(i) ((i)&0xf)
500 ELF_FILE_TYPE_NONE = 0,
501 ELF_FILE_TYPE_REL = 1,
502 ELF_FILE_TYPE_EXEC = 2,
503 ELF_FILE_TYPE_DYN = 3,
504 ELF_FILE_TYPE_CORE = 4
521 ELF_SHT_PROGBITS = 1,
545 ELF_SHF_EXECINSTR = 4,
548 #define ELF_SHN_UNDEF 0
549 #define ELF_SHN_LORESERVE 0xff00
550 #define ELF_SHN_LOPROC 0xff00
551 #define ELF_SHN_HIPROC 0xff1f
552 #define ELF_SHN_ABS 0xfff1
553 #define ELF_SHN_COMMON 0xfff2
554 #define ELF_SHN_HIRESERVE 0xffff
555 /* End of ELF definitions */
557 typedef struct _ProfilerExecutableFileSectionRegion {
558 ProfilerExecutableMemoryRegionData *region;
559 guint8 *section_address;
560 gsize section_offset;
561 } ProfilerExecutableFileSectionRegion;
563 typedef struct _ProfilerExecutableFile {
564 guint32 reference_count;
566 /* Used for mmap and munmap */
573 guint8 *symbols_start;
574 guint32 symbols_count;
576 const char *symbols_string_table;
577 const char *main_string_table;
579 ProfilerExecutableFileSectionRegion *section_regions;
581 struct _ProfilerExecutableFile *next_new_file;
582 } ProfilerExecutableFile;
584 typedef struct _ProfilerExecutableFiles {
586 ProfilerExecutableFile *new_files;
587 } ProfilerExecutableFiles;
590 #define CLEANUP_WRITER_THREAD() do {profiler->writer_thread_terminated = TRUE;} while (0)
591 #define CHECK_WRITER_THREAD() (! profiler->writer_thread_terminated)
593 #ifndef PLATFORM_WIN32
594 #include <sys/types.h>
595 #include <sys/time.h>
596 #include <sys/stat.h>
600 #include <semaphore.h>
602 #include <sys/mman.h>
603 #include <sys/types.h>
604 #include <sys/stat.h>
608 #define MUTEX_TYPE pthread_mutex_t
609 #define INITIALIZE_PROFILER_MUTEX() pthread_mutex_init (&(profiler->mutex), NULL)
610 #define DELETE_PROFILER_MUTEX() pthread_mutex_destroy (&(profiler->mutex))
611 #define LOCK_PROFILER() do {/*LOG_WRITER_THREAD ("LOCK_PROFILER");*/ pthread_mutex_lock (&(profiler->mutex));} while (0)
612 #define UNLOCK_PROFILER() do {/*LOG_WRITER_THREAD ("UNLOCK_PROFILER");*/ pthread_mutex_unlock (&(profiler->mutex));} while (0)
614 #define THREAD_TYPE pthread_t
615 #define CREATE_WRITER_THREAD(f) pthread_create (&(profiler->data_writer_thread), NULL, ((void*(*)(void*))f), NULL)
616 #define EXIT_THREAD() pthread_exit (NULL);
617 #define WAIT_WRITER_THREAD() do {\
618 if (CHECK_WRITER_THREAD ()) {\
619 pthread_join (profiler->data_writer_thread, NULL);\
622 #define CURRENT_THREAD_ID() (gsize) pthread_self ()
624 #ifndef HAVE_KW_THREAD
625 static pthread_key_t pthread_profiler_key;
626 static pthread_once_t profiler_pthread_once = PTHREAD_ONCE_INIT;
628 make_pthread_profiler_key (void) {
629 (void) pthread_key_create (&pthread_profiler_key, NULL);
631 #define LOOKUP_PROFILER_THREAD_DATA() ((ProfilerPerThreadData*) pthread_getspecific (pthread_profiler_key))
632 #define SET_PROFILER_THREAD_DATA(x) (void) pthread_setspecific (pthread_profiler_key, (x))
633 #define ALLOCATE_PROFILER_THREAD_DATA() (void) pthread_once (&profiler_pthread_once, make_pthread_profiler_key)
634 #define FREE_PROFILER_THREAD_DATA() (void) pthread_key_delete (pthread_profiler_key)
637 #define EVENT_TYPE sem_t
638 #define WRITER_EVENT_INIT() do {\
639 sem_init (&(profiler->enable_data_writer_event), 0, 0);\
640 sem_init (&(profiler->wake_data_writer_event), 0, 0);\
641 sem_init (&(profiler->done_data_writer_event), 0, 0);\
643 #define WRITER_EVENT_DESTROY() do {\
644 sem_destroy (&(profiler->enable_data_writer_event));\
645 sem_destroy (&(profiler->wake_data_writer_event));\
646 sem_destroy (&(profiler->done_data_writer_event));\
648 #define WRITER_EVENT_WAIT() (void) sem_wait (&(profiler->wake_data_writer_event))
649 #define WRITER_EVENT_RAISE() (void) sem_post (&(profiler->wake_data_writer_event))
650 #define WRITER_EVENT_ENABLE_WAIT() (void) sem_wait (&(profiler->enable_data_writer_event))
651 #define WRITER_EVENT_ENABLE_RAISE() (void) sem_post (&(profiler->enable_data_writer_event))
652 #define WRITER_EVENT_DONE_WAIT() do {\
653 if (CHECK_WRITER_THREAD ()) {\
654 (void) sem_wait (&(profiler->done_data_writer_event));\
657 #define WRITER_EVENT_DONE_RAISE() (void) sem_post (&(profiler->done_data_writer_event))
660 #define FILE_HANDLE_TYPE FILE*
661 #define OPEN_FILE() profiler->file = fopen (profiler->file_name, "wb");
662 #define WRITE_BUFFER(b,s) fwrite ((b), 1, (s), profiler->file)
663 #define FLUSH_FILE() fflush (profiler->file)
664 #define CLOSE_FILE() fclose (profiler->file);
666 #define FILE_HANDLE_TYPE int
667 #define OPEN_FILE() profiler->file = open (profiler->file_name, O_WRONLY|O_CREAT|O_TRUNC, 0664);
668 #define WRITE_BUFFER(b,s) write (profiler->file, (b), (s))
670 #define CLOSE_FILE() close (profiler->file);
677 #define MUTEX_TYPE CRITICAL_SECTION
678 #define INITIALIZE_PROFILER_MUTEX() InitializeCriticalSection (&(profiler->mutex))
679 #define DELETE_PROFILER_MUTEX() DeleteCriticalSection (&(profiler->mutex))
680 #define LOCK_PROFILER() EnterCriticalSection (&(profiler->mutex))
681 #define UNLOCK_PROFILER() LeaveCriticalSection (&(profiler->mutex))
683 #define THREAD_TYPE HANDLE
684 #define CREATE_WRITER_THREAD(f) CreateThread (NULL, (1*1024*1024), (f), NULL, 0, NULL);
685 #define EXIT_THREAD() ExitThread (0);
686 #define WAIT_WRITER_THREAD() do {\
687 if (CHECK_WRITER_THREAD ()) {\
688 WaitForSingleObject (profiler->data_writer_thread, INFINITE);\
691 #define CURRENT_THREAD_ID() (gsize) GetCurrentThreadId ()
693 #ifndef HAVE_KW_THREAD
694 static guint32 profiler_thread_id = -1;
695 #define LOOKUP_PROFILER_THREAD_DATA() ((ProfilerPerThreadData*)TlsGetValue (profiler_thread_id))
696 #define SET_PROFILER_THREAD_DATA(x) TlsSetValue (profiler_thread_id, (x));
697 #define ALLOCATE_PROFILER_THREAD_DATA() profiler_thread_id = TlsAlloc ()
698 #define FREE_PROFILER_THREAD_DATA() TlsFree (profiler_thread_id)
701 #define EVENT_TYPE HANDLE
702 #define WRITER_EVENT_INIT() (void) do {\
703 profiler->enable_data_writer_event = CreateEvent (NULL, FALSE, FALSE, NULL);\
704 profiler->wake_data_writer_event = CreateEvent (NULL, FALSE, FALSE, NULL);\
705 profiler->done_data_writer_event = CreateEvent (NULL, FALSE, FALSE, NULL);\
707 #define WRITER_EVENT_DESTROY() CloseHandle (profiler->statistical_data_writer_event)
708 #define WRITER_EVENT_INIT() (void) do {\
709 CloseHandle (profiler->enable_data_writer_event);\
710 CloseHandle (profiler->wake_data_writer_event);\
711 CloseHandle (profiler->done_data_writer_event);\
713 #define WRITER_EVENT_WAIT() WaitForSingleObject (profiler->wake_data_writer_event, INFINITE)
714 #define WRITER_EVENT_RAISE() SetEvent (profiler->wake_data_writer_event)
715 #define WRITER_EVENT_ENABLE_WAIT() WaitForSingleObject (profiler->enable_data_writer_event, INFINITE)
716 #define WRITER_EVENT_ENABLE_RAISE() SetEvent (profiler->enable_data_writer_event)
717 #define WRITER_EVENT_DONE_WAIT() do {\
718 if (CHECK_WRITER_THREAD ()) {\
719 WaitForSingleObject (profiler->done_data_writer_event, INFINITE);\
722 #define WRITER_EVENT_DONE_RAISE() SetEvent (profiler->done_data_writer_event)
724 #define FILE_HANDLE_TYPE FILE*
725 #define OPEN_FILE() profiler->file = fopen (profiler->file_name, "wb");
726 #define WRITE_BUFFER(b,s) fwrite ((b), 1, (s), profiler->file)
727 #define FLUSH_FILE() fflush (profiler->file)
728 #define CLOSE_FILE() fclose (profiler->file);
732 #ifdef HAVE_KW_THREAD
733 static __thread ProfilerPerThreadData * tls_profiler_per_thread_data;
734 #define LOOKUP_PROFILER_THREAD_DATA() ((ProfilerPerThreadData*) tls_profiler_per_thread_data)
735 #define SET_PROFILER_THREAD_DATA(x) tls_profiler_per_thread_data = (x)
736 #define ALLOCATE_PROFILER_THREAD_DATA() /* nop */
737 #define FREE_PROFILER_THREAD_DATA() /* nop */
740 #define GET_PROFILER_THREAD_DATA(data) do {\
741 ProfilerPerThreadData *_result = LOOKUP_PROFILER_THREAD_DATA ();\
743 _result = profiler_per_thread_data_new (profiler->per_thread_buffer_size);\
745 _result->next = profiler->per_thread_data;\
746 profiler->per_thread_data = _result;\
748 SET_PROFILER_THREAD_DATA (_result);\
753 #define PROFILER_FILE_WRITE_BUFFER_SIZE (profiler->write_buffer_size)
754 typedef struct _ProfilerFileWriteBuffer {
755 struct _ProfilerFileWriteBuffer *next;
757 } ProfilerFileWriteBuffer;
759 #define CHECK_PROFILER_ENABLED() do {\
760 if (! profiler->profiler_enabled)\
763 struct _MonoProfiler {
766 MonoProfileFlags flags;
767 gboolean profiler_enabled;
769 char *file_name_suffix;
770 FILE_HANDLE_TYPE file;
773 guint64 start_counter;
777 guint64 last_header_counter;
779 MethodIdMapping *methods;
780 ClassIdMapping *classes;
782 GHashTable *loaded_assemblies;
783 GHashTable *loaded_modules;
784 GHashTable *loaded_appdomains;
786 guint32 per_thread_buffer_size;
787 guint32 statistical_buffer_size;
788 ProfilerPerThreadData* per_thread_data;
789 ProfilerStatisticalData *statistical_data;
790 ProfilerStatisticalData *statistical_data_ready;
791 ProfilerStatisticalData *statistical_data_second_buffer;
792 int statistical_call_chain_depth;
794 THREAD_TYPE data_writer_thread;
795 EVENT_TYPE enable_data_writer_event;
796 EVENT_TYPE wake_data_writer_event;
797 EVENT_TYPE done_data_writer_event;
798 gboolean terminate_writer_thread;
799 gboolean writer_thread_terminated;
800 gboolean detach_writer_thread;
801 gboolean writer_thread_enabled;
802 gboolean writer_thread_flush_everything;
804 ProfilerFileWriteBuffer *write_buffers;
805 ProfilerFileWriteBuffer *current_write_buffer;
806 int write_buffer_size;
807 int current_write_position;
808 int full_write_buffers;
810 ProfilerHeapShotWriteJob *heap_shot_write_jobs;
811 ProfilerHeapShotHeapBuffers heap;
813 char *heap_shot_command_file_name;
814 int dump_next_heap_snapshots;
815 guint64 heap_shot_command_file_access_time;
816 gboolean heap_shot_was_signalled;
817 guint32 garbage_collection_counter;
819 ProfilerExecutableMemoryRegions *executable_regions;
820 ProfilerExecutableFiles executable_files;
827 gboolean unreachable_objects;
828 gboolean collection_summary;
832 static MonoProfiler *profiler;
834 #ifndef PLATFORM_WIN32
837 #ifdef MONO_ARCH_USE_SIGACTION
838 #define SIG_HANDLER_SIGNATURE(ftn) ftn (int _dummy, siginfo_t *info, void *context)
839 #elif defined(__sparc__)
840 #define SIG_HANDLER_SIGNATURE(ftn) ftn (int _dummy, void *sigctx)
842 #define SIG_HANDLER_SIGNATURE(ftn) ftn (int _dummy)
846 SIG_HANDLER_SIGNATURE (gc_request_handler) {
847 profiler->heap_shot_was_signalled = TRUE;
848 WRITER_EVENT_RAISE ();
852 add_gc_request_handler (int signal_number)
856 #ifdef MONO_ARCH_USE_SIGACTION
857 sa.sa_sigaction = gc_request_handler;
858 sigemptyset (&sa.sa_mask);
859 sa.sa_flags = SA_SIGINFO;
861 sa.sa_handler = gc_request_handler;
862 sigemptyset (&sa.sa_mask);
866 g_assert (sigaction (signal_number, &sa, NULL) != -1);
870 SIG_HANDLER_SIGNATURE (toggle_handler) {
871 if (profiler->profiler_enabled) {
872 profiler->profiler_enabled = FALSE;
874 profiler->profiler_enabled = TRUE;
879 add_toggle_handler (int signal_number)
883 #ifdef MONO_ARCH_USE_SIGACTION
884 sa.sa_sigaction = toggle_handler;
885 sigemptyset (&sa.sa_mask);
886 sa.sa_flags = SA_SIGINFO;
888 sa.sa_handler = toggle_handler;
889 sigemptyset (&sa.sa_mask);
893 g_assert (sigaction (signal_number, &sa, NULL) != -1);
899 #define DEBUG_LOAD_EVENTS 0
900 #define DEBUG_MAPPING_EVENTS 0
901 #define DEBUG_LOGGING_PROFILER 0
902 #define DEBUG_HEAP_PROFILER 0
903 #define DEBUG_CLASS_BITMAPS 0
904 #define DEBUG_STATISTICAL_PROFILER 0
905 #define DEBUG_WRITER_THREAD 0
906 #define DEBUG_FILE_WRITES 0
907 #if (DEBUG_LOGGING_PROFILER || DEBUG_STATISTICAL_PROFILER || DEBUG_HEAP_PROFILER || DEBUG_WRITER_THREAD || DEBUG_FILE_WRITES)
908 #define LOG_WRITER_THREAD(m) printf ("WRITER-THREAD-LOG %s\n", m)
910 #define LOG_WRITER_THREAD(m)
913 #if DEBUG_LOGGING_PROFILER
914 static int event_counter = 0;
915 #define EVENT_MARK() printf ("[EVENT:%d]", ++ event_counter)
919 static ClassIdMappingElement*
920 class_id_mapping_element_get (MonoClass *klass) {
921 return g_hash_table_lookup (profiler->classes->table, (gconstpointer) klass);
924 static MethodIdMappingElement*
925 method_id_mapping_element_get (MonoMethod *method) {
926 return g_hash_table_lookup (profiler->methods->table, (gconstpointer) method);
929 #define BITS_TO_BYTES(v) do {\
935 static ClassIdMappingElement*
936 class_id_mapping_element_new (MonoClass *klass) {
937 ClassIdMappingElement *result = g_new (ClassIdMappingElement, 1);
939 result->name = g_strdup_printf ("%s.%s", mono_class_get_namespace (klass), mono_class_get_name (klass));
940 result->klass = klass;
941 result->next_unwritten = profiler->classes->unwritten;
942 profiler->classes->unwritten = result;
943 result->id = profiler->classes->next_id;
944 profiler->classes->next_id ++;
946 result->data.bitmap.compact = 0;
947 result->data.layout.slots = CLASS_LAYOUT_NOT_INITIALIZED;
948 result->data.layout.references = CLASS_LAYOUT_NOT_INITIALIZED;
950 g_hash_table_insert (profiler->classes->table, klass, result);
952 #if (DEBUG_MAPPING_EVENTS)
953 printf ("Created new CLASS mapping element \"%s\" (%p)[%d]\n", result->name, klass, result->id);
959 class_id_mapping_element_build_layout_bitmap (MonoClass *klass, ClassIdMappingElement *klass_id) {
960 MonoClass *parent_class = mono_class_get_parent (klass);
961 int number_of_reference_fields = 0;
962 int max_offset_of_reference_fields = 0;
963 ClassIdMappingElement *parent_id;
965 MonoClassField *field;
967 #if (DEBUG_CLASS_BITMAPS)
968 printf ("class_id_mapping_element_build_layout_bitmap: building layout for class %s.%s: ", mono_class_get_namespace (klass), mono_class_get_name (klass));
971 if (parent_class != NULL) {
972 parent_id = class_id_mapping_element_get (parent_class);
973 g_assert (parent_id != NULL);
975 if (parent_id->data.layout.slots == CLASS_LAYOUT_NOT_INITIALIZED) {
976 #if (DEBUG_CLASS_BITMAPS)
977 printf ("[recursively building bitmap for father class]\n");
979 class_id_mapping_element_build_layout_bitmap (parent_class, parent_id);
986 while ((field = mono_class_get_fields (klass, &iter)) != NULL) {
987 MonoType* field_type = mono_field_get_type (field);
988 // For now, skip static fields
989 if (mono_field_get_flags (field) & 0x0010 /*FIELD_ATTRIBUTE_STATIC*/)
992 if (MONO_TYPE_IS_REFERENCE (field_type)) {
993 int field_offset = mono_field_get_offset (field) - sizeof (MonoObject);
994 if (field_offset > max_offset_of_reference_fields) {
995 max_offset_of_reference_fields = field_offset;
997 number_of_reference_fields ++;
999 MonoClass *field_class = mono_class_from_mono_type (field_type);
1000 if (field_class && mono_class_is_valuetype (field_class)) {
1001 ClassIdMappingElement *field_id = class_id_mapping_element_get (field_class);
1002 g_assert (field_id != NULL);
1004 if (field_id->data.layout.slots == CLASS_LAYOUT_NOT_INITIALIZED) {
1005 if (field_id != klass_id) {
1006 #if (DEBUG_CLASS_BITMAPS)
1007 printf ("[recursively building bitmap for field %s]\n", mono_field_get_name (field));
1009 class_id_mapping_element_build_layout_bitmap (field_class, field_id);
1011 #if (DEBUG_CLASS_BITMAPS)
1012 printf ("[breaking recursive bitmap build for field %s]", mono_field_get_name (field));
1015 klass_id->data.bitmap.compact = 0;
1016 klass_id->data.layout.slots = 0;
1017 klass_id->data.layout.references = 0;
1021 if (field_id->data.layout.references > 0) {
1022 int field_offset = mono_field_get_offset (field) - sizeof (MonoObject);
1023 int max_offset_reference_in_field = (field_id->data.layout.slots - 1) * sizeof (gpointer);
1025 if ((field_offset + max_offset_reference_in_field) > max_offset_of_reference_fields) {
1026 max_offset_of_reference_fields = field_offset + max_offset_reference_in_field;
1029 number_of_reference_fields += field_id->data.layout.references;
1035 #if (DEBUG_CLASS_BITMAPS)
1036 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);
1038 if ((number_of_reference_fields == 0) && ((parent_id == NULL) || (parent_id->data.layout.references == 0))) {
1039 #if (DEBUG_CLASS_BITMAPS)
1040 printf ("[no references at all]");
1042 klass_id->data.bitmap.compact = 0;
1043 klass_id->data.layout.slots = 0;
1044 klass_id->data.layout.references = 0;
1046 if ((parent_id != NULL) && (parent_id->data.layout.references > 0)) {
1047 #if (DEBUG_CLASS_BITMAPS)
1048 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);
1050 klass_id->data.layout.slots = parent_id->data.layout.slots;
1051 klass_id->data.layout.references = parent_id->data.layout.references;
1053 #if (DEBUG_CLASS_BITMAPS)
1054 printf ("[no references from parent]");
1056 klass_id->data.layout.slots = 0;
1057 klass_id->data.layout.references = 0;
1060 if (number_of_reference_fields > 0) {
1061 klass_id->data.layout.slots += ((max_offset_of_reference_fields / sizeof (gpointer)) + 1);
1062 klass_id->data.layout.references += number_of_reference_fields;
1063 #if (DEBUG_CLASS_BITMAPS)
1064 printf ("[adding data, going to %d references in %d slots]", klass_id->data.layout.references, klass_id->data.layout.slots);
1068 if (klass_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
1069 #if (DEBUG_CLASS_BITMAPS)
1070 printf ("[zeroing bitmap]");
1072 klass_id->data.bitmap.compact = 0;
1073 if ((parent_id != NULL) && (parent_id->data.layout.references > 0)) {
1074 #if (DEBUG_CLASS_BITMAPS)
1075 printf ("[copying compact father bitmap]");
1077 klass_id->data.bitmap.compact = parent_id->data.bitmap.compact;
1080 int size_of_bitmap = klass_id->data.layout.slots;
1081 BITS_TO_BYTES (size_of_bitmap);
1082 #if (DEBUG_CLASS_BITMAPS)
1083 printf ("[allocating %d bytes for bitmap]", size_of_bitmap);
1085 klass_id->data.bitmap.extended = g_malloc0 (size_of_bitmap);
1086 if ((parent_id != NULL) && (parent_id->data.layout.references > 0)) {
1087 int size_of_father_bitmap = parent_id->data.layout.slots;
1088 if (size_of_father_bitmap <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
1090 #if (DEBUG_CLASS_BITMAPS)
1091 printf ("[copying %d bits from father bitmap]", size_of_father_bitmap);
1093 for (father_slot = 0; father_slot < size_of_father_bitmap; father_slot ++) {
1094 if (parent_id->data.bitmap.compact & (((guint64)1) << father_slot)) {
1095 klass_id->data.bitmap.extended [father_slot >> 3] |= (1 << (father_slot & 7));
1099 BITS_TO_BYTES (size_of_father_bitmap);
1100 #if (DEBUG_CLASS_BITMAPS)
1101 printf ("[copying %d bytes from father bitmap]", size_of_father_bitmap);
1103 memcpy (klass_id->data.bitmap.extended, parent_id->data.bitmap.extended, size_of_father_bitmap);
1109 #if (DEBUG_CLASS_BITMAPS)
1110 printf ("[starting filling iteration]\n");
1113 while ((field = mono_class_get_fields (klass, &iter)) != NULL) {
1114 MonoType* field_type = mono_field_get_type (field);
1115 // For now, skip static fields
1116 if (mono_field_get_flags (field) & 0x0010 /*FIELD_ATTRIBUTE_STATIC*/)
1119 #if (DEBUG_CLASS_BITMAPS)
1120 printf ("[Working on field %s]", mono_field_get_name (field));
1122 if (MONO_TYPE_IS_REFERENCE (field_type)) {
1123 int field_offset = mono_field_get_offset (field) - sizeof (MonoObject);
1125 g_assert ((field_offset % sizeof (gpointer)) == 0);
1126 field_slot = field_offset / sizeof (gpointer);
1127 if (klass_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
1128 klass_id->data.bitmap.compact |= (((guint64)1) << field_slot);
1130 klass_id->data.bitmap.extended [field_slot >> 3] |= (1 << (field_slot & 7));
1132 #if (DEBUG_CLASS_BITMAPS)
1133 printf ("[reference at offset %d, slot %d]", field_offset, field_slot);
1136 MonoClass *field_class = mono_class_from_mono_type (field_type);
1137 if (field_class && mono_class_is_valuetype (field_class)) {
1138 ClassIdMappingElement *field_id = class_id_mapping_element_get (field_class);
1142 g_assert (field_id != NULL);
1143 field_offset = mono_field_get_offset (field) - sizeof (MonoObject);
1144 g_assert ((field_id->data.layout.references == 0) || ((field_offset % sizeof (gpointer)) == 0));
1145 field_slot = field_offset / sizeof (gpointer);
1146 #if (DEBUG_CLASS_BITMAPS)
1147 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);
1150 if (field_id->data.layout.references > 0) {
1152 if (field_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
1153 for (sub_field_slot = 0; sub_field_slot < field_id->data.layout.slots; sub_field_slot ++) {
1154 if (field_id->data.bitmap.compact & (((guint64)1) << sub_field_slot)) {
1155 int actual_slot = field_slot + sub_field_slot;
1156 if (klass_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
1157 klass_id->data.bitmap.compact |= (((guint64)1) << actual_slot);
1159 klass_id->data.bitmap.extended [actual_slot >> 3] |= (1 << (actual_slot & 7));
1164 for (sub_field_slot = 0; sub_field_slot < field_id->data.layout.slots; sub_field_slot ++) {
1165 if (field_id->data.bitmap.extended [sub_field_slot >> 3] & (1 << (sub_field_slot & 7))) {
1166 int actual_slot = field_slot + sub_field_slot;
1167 if (klass_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
1168 klass_id->data.bitmap.compact |= (((guint64)1) << actual_slot);
1170 klass_id->data.bitmap.extended [actual_slot >> 3] |= (1 << (actual_slot & 7));
1179 #if (DEBUG_CLASS_BITMAPS)
1182 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);
1183 for (slot = 0; slot < klass_id->data.layout.slots; slot ++) {
1184 if (klass_id->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
1185 if (klass_id->data.bitmap.compact & (((guint64)1) << slot)) {
1191 if (klass_id->data.bitmap.extended [slot >> 3] & (1 << (slot & 7))) {
1205 static MethodIdMappingElement*
1206 method_id_mapping_element_new (MonoMethod *method) {
1207 MethodIdMappingElement *result = g_new (MethodIdMappingElement, 1);
1208 char *signature = mono_signature_get_desc (mono_method_signature (method), TRUE);
1210 result->name = g_strdup_printf ("%s (%s)", mono_method_get_name (method), signature);
1212 result->method = method;
1213 result->next_unwritten = profiler->methods->unwritten;
1214 profiler->methods->unwritten = result;
1215 result->id = profiler->methods->next_id;
1216 profiler->methods->next_id ++;
1217 g_hash_table_insert (profiler->methods->table, method, result);
1219 result->data.code_start = NULL;
1220 result->data.code_size = 0;
1222 #if (DEBUG_MAPPING_EVENTS)
1223 printf ("Created new METHOD mapping element \"%s\" (%p)[%d]\n", result->name, method, result->id);
1230 method_id_mapping_element_destroy (gpointer element) {
1231 MethodIdMappingElement *e = (MethodIdMappingElement*) element;
1238 class_id_mapping_element_destroy (gpointer element) {
1239 ClassIdMappingElement *e = (ClassIdMappingElement*) element;
1242 if ((e->data.layout.slots != CLASS_LAYOUT_NOT_INITIALIZED) && (e->data.layout.slots > CLASS_LAYOUT_PACKED_BITMAP_SIZE))
1243 g_free (e->data.bitmap.extended);
1247 static MethodIdMapping*
1248 method_id_mapping_new (void) {
1249 MethodIdMapping *result = g_new (MethodIdMapping, 1);
1250 //result->table = g_hash_table_new_full (mono_aligned_addr_hash, NULL, NULL, method_id_mapping_element_destroy);
1251 result->table = g_hash_table_new_full (g_direct_hash, NULL, NULL, method_id_mapping_element_destroy);
1252 result->unwritten = NULL;
1253 result->next_id = 1;
1257 static ClassIdMapping*
1258 class_id_mapping_new (void) {
1259 ClassIdMapping *result = g_new (ClassIdMapping, 1);
1260 //result->table = g_hash_table_new_full (mono_aligned_addr_hash, NULL, NULL, class_id_mapping_element_destroy);
1261 result->table = g_hash_table_new_full (g_direct_hash, NULL, NULL, class_id_mapping_element_destroy);
1262 result->unwritten = NULL;
1263 result->next_id = 1;
1268 method_id_mapping_destroy (MethodIdMapping *map) {
1269 g_hash_table_destroy (map->table);
1274 class_id_mapping_destroy (ClassIdMapping *map) {
1275 g_hash_table_destroy (map->table);
1279 #if (DEBUG_LOAD_EVENTS)
1281 print_load_event (const char *event_name, GHashTable *table, gpointer item, LoadedElement *element);
1284 static LoadedElement*
1285 loaded_element_load_start (GHashTable *table, gpointer item) {
1286 LoadedElement *element = g_new0 (LoadedElement, 1);
1287 #if (DEBUG_LOAD_EVENTS)
1288 print_load_event ("LOAD START", table, item, element);
1290 MONO_PROFILER_GET_CURRENT_COUNTER (element->load_start_counter);
1291 g_hash_table_insert (table, item, element);
1295 static LoadedElement*
1296 loaded_element_load_end (GHashTable *table, gpointer item, char *name) {
1297 LoadedElement *element = g_hash_table_lookup (table, item);
1298 #if (DEBUG_LOAD_EVENTS)
1299 print_load_event ("LOAD END", table, item, element);
1301 g_assert (element != NULL);
1302 MONO_PROFILER_GET_CURRENT_COUNTER (element->load_end_counter);
1303 element->name = name;
1304 element->loaded = TRUE;
1308 static LoadedElement*
1309 loaded_element_unload_start (GHashTable *table, gpointer item) {
1310 LoadedElement *element = g_hash_table_lookup (table, item);
1311 #if (DEBUG_LOAD_EVENTS)
1312 print_load_event ("UNLOAD START", table, item, element);
1314 g_assert (element != NULL);
1315 MONO_PROFILER_GET_CURRENT_COUNTER (element->unload_start_counter);
1319 static LoadedElement*
1320 loaded_element_unload_end (GHashTable *table, gpointer item) {
1321 LoadedElement *element = g_hash_table_lookup (table, item);
1322 #if (DEBUG_LOAD_EVENTS)
1323 print_load_event ("UNLOAD END", table, item, element);
1325 g_assert (element != NULL);
1326 MONO_PROFILER_GET_CURRENT_COUNTER (element->unload_end_counter);
1327 element->unloaded = TRUE;
1333 loaded_element_destroy (gpointer element) {
1334 if (((LoadedElement*)element)->name)
1335 g_free (((LoadedElement*)element)->name);
1339 #if (DEBUG_LOAD_EVENTS)
1341 print_load_event (const char *event_name, GHashTable *table, gpointer item, LoadedElement *element) {
1342 const char* item_name;
1345 if (table == profiler->loaded_assemblies) {
1346 //item_info = g_strdup_printf("ASSEMBLY %p (dynamic %d)", item, mono_image_is_dynamic (mono_assembly_get_image((MonoAssembly*)item)));
1347 item_info = g_strdup_printf("ASSEMBLY %p", item);
1348 } else if (table == profiler->loaded_modules) {
1349 //item_info = g_strdup_printf("MODULE %p (dynamic %d)", item, mono_image_is_dynamic ((MonoImage*)item));
1350 item_info = g_strdup_printf("MODULE %p", item);
1351 } else if (table == profiler->loaded_appdomains) {
1352 item_info = g_strdup_printf("APPDOMAIN %p (id %d)", item, mono_domain_get_id ((MonoDomain*)item));
1355 g_assert_not_reached ();
1358 if (element != NULL) {
1359 item_name = element->name;
1361 item_name = "<NULL>";
1364 printf ("%s EVENT for %s (%s)\n", event_name, item_info, item_name);
1370 profiler_heap_shot_object_buffers_destroy (ProfilerHeapShotObjectBuffer *buffer) {
1371 while (buffer != NULL) {
1372 ProfilerHeapShotObjectBuffer *next = buffer->next;
1373 #if DEBUG_HEAP_PROFILER
1374 printf ("profiler_heap_shot_object_buffers_destroy: destroyed buffer %p (%p-%p)\n", buffer, & (buffer->buffer [0]), buffer->end);
1381 static ProfilerHeapShotObjectBuffer*
1382 profiler_heap_shot_object_buffer_new (ProfilerPerThreadData *data) {
1383 ProfilerHeapShotObjectBuffer *buffer;
1384 ProfilerHeapShotObjectBuffer *result = g_new (ProfilerHeapShotObjectBuffer, 1);
1385 result->next_free_slot = & (result->buffer [0]);
1386 result->end = & (result->buffer [PROFILER_HEAP_SHOT_OBJECT_BUFFER_SIZE]);
1387 result->first_unprocessed_slot = & (result->buffer [0]);
1388 result->next = data->heap_shot_object_buffers;
1389 data->heap_shot_object_buffers = result;
1390 #if DEBUG_HEAP_PROFILER
1391 printf ("profiler_heap_shot_object_buffer_new: created buffer %p (%p-%p)\n", result, result->next_free_slot, result->end);
1393 for (buffer = result; buffer != NULL; buffer = buffer->next) {
1394 ProfilerHeapShotObjectBuffer *last = buffer->next;
1395 if ((last != NULL) && (last->first_unprocessed_slot == last->end)) {
1396 buffer->next = NULL;
1397 profiler_heap_shot_object_buffers_destroy (last);
1404 static ProfilerHeapShotWriteJob*
1405 profiler_heap_shot_write_job_new (gboolean heap_shot_was_signalled, gboolean dump_heap_data, guint32 collection) {
1406 ProfilerHeapShotWriteJob *job = g_new (ProfilerHeapShotWriteJob, 1);
1408 job->next_unwritten = NULL;
1410 if (profiler->action_flags.unreachable_objects || dump_heap_data) {
1411 job->buffers = g_new (ProfilerHeapShotWriteBuffer, 1);
1412 job->buffers->next = NULL;
1413 job->last_next = & (job->buffers->next);
1414 job->start = & (job->buffers->buffer [0]);
1415 job->cursor = job->start;
1416 job->end = & (job->buffers->buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE]);
1418 job->buffers = NULL;
1419 job->buffers->next = NULL;
1420 job->last_next = NULL;
1425 job->full_buffers = 0;
1427 if (profiler->action_flags.collection_summary) {
1428 job->summary.capacity = profiler->classes->next_id;
1429 job->summary.per_class_data = g_new0 (ProfilerHeapShotClassSummary, job->summary.capacity);
1431 job->summary.capacity = 0;
1432 job->summary.per_class_data = NULL;
1435 job->heap_shot_was_signalled = heap_shot_was_signalled;
1436 job->collection = collection;
1437 job->dump_heap_data = dump_heap_data;
1438 #if DEBUG_HEAP_PROFILER
1439 printf ("profiler_heap_shot_write_job_new: created job %p with buffer %p(%p-%p) (collection %d, dump %d)\n", job, job->buffers, job->start, job->end, collection, dump_heap_data);
1445 profiler_heap_shot_write_job_add_buffer (ProfilerHeapShotWriteJob *job, gpointer value) {
1446 ProfilerHeapShotWriteBuffer *buffer = g_new (ProfilerHeapShotWriteBuffer, 1);
1447 buffer->next = NULL;
1448 *(job->last_next) = buffer;
1449 job->last_next = & (buffer->next);
1450 job->full_buffers ++;
1451 buffer->buffer [0] = value;
1452 job->start = & (buffer->buffer [0]);
1453 job->cursor = & (buffer->buffer [1]);
1454 job->end = & (buffer->buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE]);
1455 #if DEBUG_HEAP_PROFILER
1456 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);
1458 ProfilerHeapShotWriteBuffer *current_buffer;
1459 for (current_buffer = job->buffers; current_buffer != NULL; current_buffer = current_buffer->next) {
1460 printf ("profiler_heap_shot_write_job_add_buffer: now job %p has buffer %p\n", job, current_buffer);
1467 profiler_heap_shot_write_job_free_buffers (ProfilerHeapShotWriteJob *job) {
1468 ProfilerHeapShotWriteBuffer *buffer = job->buffers;
1470 while (buffer != NULL) {
1471 ProfilerHeapShotWriteBuffer *next = buffer->next;
1472 #if DEBUG_HEAP_PROFILER
1473 printf ("profiler_heap_shot_write_job_free_buffers: in job %p, freeing buffer %p\n", job, buffer);
1479 job->buffers = NULL;
1481 if (job->summary.per_class_data != NULL) {
1482 g_free (job->summary.per_class_data);
1483 job->summary.per_class_data = NULL;
1485 job->summary.capacity = 0;
1489 profiler_heap_shot_write_block (ProfilerHeapShotWriteJob *job);
1492 profiler_process_heap_shot_write_jobs (void) {
1493 gboolean done = FALSE;
1496 ProfilerHeapShotWriteJob *current_job = profiler->heap_shot_write_jobs;
1497 ProfilerHeapShotWriteJob *previous_job = NULL;
1498 ProfilerHeapShotWriteJob *next_job;
1501 while (current_job != NULL) {
1502 next_job = current_job->next_unwritten;
1504 if (next_job != NULL) {
1505 if (current_job->buffers != NULL) {
1508 if (next_job->buffers == NULL) {
1509 current_job->next_unwritten = NULL;
1513 if (current_job->buffers != NULL) {
1514 LOG_WRITER_THREAD ("profiler_process_heap_shot_write_jobs: writing...");
1515 profiler_heap_shot_write_block (current_job);
1516 LOG_WRITER_THREAD ("profiler_process_heap_shot_write_jobs: done");
1517 if (previous_job != NULL) {
1518 previous_job->next_unwritten = NULL;
1523 previous_job = current_job;
1524 current_job = next_job;
1530 profiler_free_heap_shot_write_jobs (void) {
1531 ProfilerHeapShotWriteJob *current_job = profiler->heap_shot_write_jobs;
1532 ProfilerHeapShotWriteJob *next_job;
1534 if (current_job != NULL) {
1535 while (current_job->next_unwritten != NULL) {
1536 #if DEBUG_HEAP_PROFILER
1537 printf ("profiler_free_heap_shot_write_jobs: job %p must not be freed\n", current_job);
1539 current_job = current_job->next_unwritten;
1542 next_job = current_job->next;
1543 current_job->next = NULL;
1544 current_job = next_job;
1546 while (current_job != NULL) {
1547 #if DEBUG_HEAP_PROFILER
1548 printf ("profiler_free_heap_shot_write_jobs: job %p will be freed\n", current_job);
1550 next_job = current_job->next;
1551 profiler_heap_shot_write_job_free_buffers (current_job);
1552 g_free (current_job);
1553 current_job = next_job;
1559 profiler_destroy_heap_shot_write_jobs (void) {
1560 ProfilerHeapShotWriteJob *current_job = profiler->heap_shot_write_jobs;
1561 ProfilerHeapShotWriteJob *next_job;
1563 while (current_job != NULL) {
1564 next_job = current_job->next;
1565 profiler_heap_shot_write_job_free_buffers (current_job);
1566 g_free (current_job);
1567 current_job = next_job;
1572 profiler_add_heap_shot_write_job (ProfilerHeapShotWriteJob *job) {
1573 job->next = profiler->heap_shot_write_jobs;
1574 job->next_unwritten = job->next;
1575 profiler->heap_shot_write_jobs = job;
1576 #if DEBUG_HEAP_PROFILER
1577 printf ("profiler_add_heap_shot_write_job: added job %p\n", job);
1581 #if DEBUG_HEAP_PROFILER
1582 #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)
1583 #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)
1585 #define STORE_ALLOCATED_OBJECT_MESSAGE1(d,o)
1586 #define STORE_ALLOCATED_OBJECT_MESSAGE2(d,o)
1588 #define STORE_ALLOCATED_OBJECT(d,o) do {\
1589 if ((d)->heap_shot_object_buffers->next_free_slot < (d)->heap_shot_object_buffers->end) {\
1590 STORE_ALLOCATED_OBJECT_MESSAGE1 ((d), (o));\
1591 *((d)->heap_shot_object_buffers->next_free_slot) = (o);\
1592 (d)->heap_shot_object_buffers->next_free_slot ++;\
1594 ProfilerHeapShotObjectBuffer *buffer = profiler_heap_shot_object_buffer_new (d);\
1595 STORE_ALLOCATED_OBJECT_MESSAGE2 ((d), (o));\
1596 *((buffer)->next_free_slot) = (o);\
1597 (buffer)->next_free_slot ++;\
1601 static ProfilerPerThreadData*
1602 profiler_per_thread_data_new (guint32 buffer_size)
1604 ProfilerPerThreadData *data = g_new (ProfilerPerThreadData, 1);
1606 data->events = g_new0 (ProfilerEventData, buffer_size);
1607 data->next_free_event = data->events;
1608 data->end_event = data->events + (buffer_size - 1);
1609 data->first_unwritten_event = data->events;
1610 data->first_unmapped_event = data->events;
1611 MONO_PROFILER_GET_CURRENT_COUNTER (data->start_event_counter);
1612 data->last_event_counter = data->start_event_counter;
1613 data->thread_id = CURRENT_THREAD_ID ();
1614 data->heap_shot_object_buffers = NULL;
1615 if ((profiler->action_flags.unreachable_objects == TRUE) ||
1616 (profiler->action_flags.heap_shot == TRUE) ||
1617 (profiler->action_flags.collection_summary == TRUE)) {
1618 profiler_heap_shot_object_buffer_new (data);
1624 profiler_per_thread_data_destroy (ProfilerPerThreadData *data) {
1625 g_free (data->events);
1626 profiler_heap_shot_object_buffers_destroy (data->heap_shot_object_buffers);
1630 static ProfilerStatisticalData*
1631 profiler_statistical_data_new (MonoProfiler *profiler) {
1632 int buffer_size = profiler->statistical_buffer_size * (profiler->statistical_call_chain_depth + 1);
1633 ProfilerStatisticalData *data = g_new (ProfilerStatisticalData, 1);
1635 data->hits = g_new0 (ProfilerStatisticalHit, buffer_size);
1636 data->next_free_index = 0;
1637 data->end_index = profiler->statistical_buffer_size;
1638 data->first_unwritten_index = 0;
1644 profiler_statistical_data_destroy (ProfilerStatisticalData *data) {
1645 g_free (data->hits);
1650 profiler_add_write_buffer (void) {
1651 if (profiler->current_write_buffer->next == NULL) {
1652 profiler->current_write_buffer->next = g_malloc (sizeof (ProfilerFileWriteBuffer) + PROFILER_FILE_WRITE_BUFFER_SIZE);
1653 profiler->current_write_buffer->next->next = NULL;
1655 //printf ("Added next buffer %p, to buffer %p\n", profiler->current_write_buffer->next, profiler->current_write_buffer);
1658 profiler->current_write_buffer = profiler->current_write_buffer->next;
1659 profiler->current_write_position = 0;
1660 profiler->full_write_buffers ++;
1664 profiler_free_write_buffers (void) {
1665 ProfilerFileWriteBuffer *current_buffer = profiler->write_buffers;
1666 while (current_buffer != NULL) {
1667 ProfilerFileWriteBuffer *next_buffer = current_buffer->next;
1669 //printf ("Freeing write buffer %p, next is %p\n", current_buffer, next_buffer);
1671 g_free (current_buffer);
1672 current_buffer = next_buffer;
1676 #define WRITE_BYTE(b) do {\
1677 if (profiler->current_write_position >= PROFILER_FILE_WRITE_BUFFER_SIZE) {\
1678 profiler_add_write_buffer ();\
1680 profiler->current_write_buffer->buffer [profiler->current_write_position] = (b);\
1681 profiler->current_write_position ++;\
1686 write_current_block (guint16 code) {
1687 guint32 size = (profiler->full_write_buffers * PROFILER_FILE_WRITE_BUFFER_SIZE) + profiler->current_write_position;
1688 ProfilerFileWriteBuffer *current_buffer = profiler->write_buffers;
1689 guint64 current_counter;
1690 guint32 counter_delta;
1693 MONO_PROFILER_GET_CURRENT_COUNTER (current_counter);
1694 if (profiler->last_header_counter != 0) {
1695 counter_delta = current_counter - profiler->last_header_counter;
1699 profiler->last_header_counter = current_counter;
1701 header [0] = code & 0xff;
1702 header [1] = (code >> 8) & 0xff;
1703 header [2] = size & 0xff;
1704 header [3] = (size >> 8) & 0xff;
1705 header [4] = (size >> 16) & 0xff;
1706 header [5] = (size >> 24) & 0xff;
1707 header [6] = counter_delta & 0xff;
1708 header [7] = (counter_delta >> 8) & 0xff;
1709 header [8] = (counter_delta >> 16) & 0xff;
1710 header [9] = (counter_delta >> 24) & 0xff;
1712 #if (DEBUG_FILE_WRITES)
1713 printf ("write_current_block: writing header (code %d)\n", code);
1715 WRITE_BUFFER (& (header [0]), 10);
1717 while ((current_buffer != NULL) && (profiler->full_write_buffers > 0)) {
1718 #if (DEBUG_FILE_WRITES)
1719 printf ("write_current_block: writing buffer (size %d)\n", PROFILER_FILE_WRITE_BUFFER_SIZE);
1721 WRITE_BUFFER (& (current_buffer->buffer [0]), PROFILER_FILE_WRITE_BUFFER_SIZE);
1722 profiler->full_write_buffers --;
1723 current_buffer = current_buffer->next;
1725 if (profiler->current_write_position > 0) {
1726 #if (DEBUG_FILE_WRITES)
1727 printf ("write_current_block: writing last buffer (size %d)\n", profiler->current_write_position);
1729 WRITE_BUFFER (& (current_buffer->buffer [0]), profiler->current_write_position);
1732 #if (DEBUG_FILE_WRITES)
1733 printf ("write_current_block: buffers flushed\n");
1736 profiler->current_write_buffer = profiler->write_buffers;
1737 profiler->current_write_position = 0;
1738 profiler->full_write_buffers = 0;
1742 #define SEVEN_BITS_MASK (0x7f)
1743 #define EIGHT_BIT_MASK (0x80)
1746 write_uint32 (guint32 value) {
1747 while (value > SEVEN_BITS_MASK) {
1748 WRITE_BYTE (value & SEVEN_BITS_MASK);
1751 WRITE_BYTE (value | EIGHT_BIT_MASK);
1754 write_uint64 (guint64 value) {
1755 while (value > SEVEN_BITS_MASK) {
1756 WRITE_BYTE (value & SEVEN_BITS_MASK);
1759 WRITE_BYTE (value | EIGHT_BIT_MASK);
1762 write_string (const char *string) {
1763 while (*string != 0) {
1764 WRITE_BYTE (*string);
1770 #if DEBUG_HEAP_PROFILER
1771 #define WRITE_HEAP_SHOT_JOB_VALUE_MESSAGE(v,c) printf ("WRITE_HEAP_SHOT_JOB_VALUE: writing value %p at cursor %p\n", (v), (c))
1773 #define WRITE_HEAP_SHOT_JOB_VALUE_MESSAGE(v,c)
1775 #define WRITE_HEAP_SHOT_JOB_VALUE(j,v) do {\
1776 if ((j)->cursor < (j)->end) {\
1777 WRITE_HEAP_SHOT_JOB_VALUE_MESSAGE ((v), ((j)->cursor));\
1778 *((j)->cursor) = (v);\
1781 profiler_heap_shot_write_job_add_buffer (j, v);\
1786 #undef GUINT_TO_POINTER
1787 #undef GPOINTER_TO_UINT
1788 #if (SIZEOF_VOID_P == 4)
1789 #define GUINT_TO_POINTER(u) ((void*)(guint32)(u))
1790 #define GPOINTER_TO_UINT(p) ((guint32)(void*)(p))
1791 #elif (SIZEOF_VOID_P == 8)
1792 #define GUINT_TO_POINTER(u) ((void*)(guint64)(u))
1793 #define GPOINTER_TO_UINT(p) ((guint64)(void*)(p))
1795 #error Bad size of void pointer
1798 #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)))
1800 #if DEBUG_HEAP_PROFILER
1801 #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)
1803 #define UPDATE_JOB_BUFFER_CURSOR_MESSAGE()
1805 #define UPDATE_JOB_BUFFER_CURSOR() do {\
1807 if (cursor >= end) {\
1808 buffer = buffer->next;\
1809 if (buffer != NULL) {\
1810 cursor = & (buffer->buffer [0]);\
1811 if (buffer->next != NULL) {\
1812 end = & (buffer->buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE]);\
1820 UPDATE_JOB_BUFFER_CURSOR_MESSAGE ();\
1824 profiler_heap_shot_write_data_block (ProfilerHeapShotWriteJob *job) {
1825 ProfilerHeapShotWriteBuffer *buffer;
1828 guint64 start_counter;
1830 guint64 end_counter;
1833 write_uint64 (job->start_counter);
1834 write_uint64 (job->start_time);
1835 write_uint64 (job->end_counter);
1836 write_uint64 (job->end_time);
1837 write_uint32 (job->collection);
1838 MONO_PROFILER_GET_CURRENT_COUNTER (start_counter);
1839 MONO_PROFILER_GET_CURRENT_TIME (start_time);
1840 write_uint64 (start_counter);
1841 write_uint64 (start_time);
1842 #if DEBUG_HEAP_PROFILER
1843 printf ("profiler_heap_shot_write_data_block: start writing job %p (start %p, end %p)...\n", job, & (job->buffers->buffer [0]), job->cursor);
1845 buffer = job->buffers;
1846 cursor = & (buffer->buffer [0]);
1847 if (buffer->next != NULL) {
1848 end = & (buffer->buffer [PROFILER_HEAP_SHOT_WRITE_BUFFER_SIZE]);
1852 if (cursor >= end) {
1855 #if DEBUG_HEAP_PROFILER
1856 printf ("profiler_heap_shot_write_data_block: in job %p, starting at buffer %p and cursor %p\n", job, buffer, cursor);
1858 while (cursor != NULL) {
1859 gpointer value = *cursor;
1860 HeapProfilerJobValueCode code = GPOINTER_TO_UINT (value) & HEAP_CODE_MASK;
1861 #if DEBUG_HEAP_PROFILER
1862 printf ("profiler_heap_shot_write_data_block: got value %p and code %d\n", value, code);
1865 UPDATE_JOB_BUFFER_CURSOR ();
1866 if (code == HEAP_CODE_FREE_OBJECT_CLASS) {
1867 MonoClass *klass = GUINT_TO_POINTER (GPOINTER_TO_UINT (value) & (~ (guint64) HEAP_CODE_MASK));
1868 //MonoClass *klass = GUINT_TO_POINTER (GPOINTER_TO_UINT (value) % 4);
1869 ClassIdMappingElement *class_id;
1872 class_id = class_id_mapping_element_get (klass);
1873 if (class_id == NULL) {
1874 printf ("profiler_heap_shot_write_data_block: unknown class %p", klass);
1876 g_assert (class_id != NULL);
1877 write_uint32 ((class_id->id << 2) | HEAP_CODE_FREE_OBJECT_CLASS);
1879 size = GPOINTER_TO_UINT (*cursor);
1880 UPDATE_JOB_BUFFER_CURSOR ();
1881 write_uint32 (size);
1882 #if DEBUG_HEAP_PROFILER
1883 printf ("profiler_heap_shot_write_data_block: wrote unreachable object of class %p (id %d, size %d)\n", klass, class_id->id, size);
1885 } else if (code == HEAP_CODE_OBJECT) {
1886 MonoObject *object = GUINT_TO_POINTER (GPOINTER_TO_UINT (value) & (~ (guint64) HEAP_CODE_MASK));
1887 MonoClass *klass = mono_object_get_class (object);
1888 ClassIdMappingElement *class_id = class_id_mapping_element_get (klass);
1889 guint32 size = mono_object_get_size (object);
1890 guint32 references = GPOINTER_TO_UINT (*cursor);
1891 UPDATE_JOB_BUFFER_CURSOR ();
1893 if (class_id == NULL) {
1894 printf ("profiler_heap_shot_write_data_block: unknown class %p", klass);
1896 g_assert (class_id != NULL);
1898 write_uint64 (GPOINTER_TO_UINT (value));
1899 write_uint32 (class_id->id);
1900 write_uint32 (size);
1901 write_uint32 (references);
1902 #if DEBUG_HEAP_PROFILER
1903 printf ("profiler_heap_shot_write_data_block: writing object %p (references %d)\n", value, references);
1906 while (references > 0) {
1907 gpointer reference = *cursor;
1908 write_uint64 (GPOINTER_TO_UINT (reference));
1909 UPDATE_JOB_BUFFER_CURSOR ();
1911 #if DEBUG_HEAP_PROFILER
1912 printf ("profiler_heap_shot_write_data_block: inside object %p, wrote reference %p)\n", value, reference);
1916 #if DEBUG_HEAP_PROFILER
1917 printf ("profiler_heap_shot_write_data_block: unknown code %d in value %p\n", code, value);
1919 g_assert_not_reached ();
1924 MONO_PROFILER_GET_CURRENT_COUNTER (end_counter);
1925 MONO_PROFILER_GET_CURRENT_TIME (end_time);
1926 write_uint64 (end_counter);
1927 write_uint64 (end_time);
1929 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_HEAP_DATA);
1930 #if DEBUG_HEAP_PROFILER
1931 printf ("profiler_heap_shot_write_data_block: writing job %p done.\n", job);
1935 profiler_heap_shot_write_summary_block (ProfilerHeapShotWriteJob *job) {
1936 guint64 start_counter;
1938 guint64 end_counter;
1942 #if DEBUG_HEAP_PROFILER
1943 printf ("profiler_heap_shot_write_summary_block: start writing job %p...\n", job);
1945 MONO_PROFILER_GET_CURRENT_COUNTER (start_counter);
1946 MONO_PROFILER_GET_CURRENT_TIME (start_time);
1947 write_uint64 (start_counter);
1948 write_uint64 (start_time);
1950 write_uint32 (job->collection);
1952 for (id = 0; id < job->summary.capacity; id ++) {
1953 if ((job->summary.per_class_data [id].reachable.instances > 0) || (job->summary.per_class_data [id].unreachable.instances > 0)) {
1955 write_uint32 (job->summary.per_class_data [id].reachable.instances);
1956 write_uint32 (job->summary.per_class_data [id].reachable.bytes);
1957 write_uint32 (job->summary.per_class_data [id].unreachable.instances);
1958 write_uint32 (job->summary.per_class_data [id].unreachable.bytes);
1963 MONO_PROFILER_GET_CURRENT_COUNTER (end_counter);
1964 MONO_PROFILER_GET_CURRENT_TIME (end_time);
1965 write_uint64 (end_counter);
1966 write_uint64 (end_time);
1968 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_HEAP_SUMMARY);
1969 #if DEBUG_HEAP_PROFILER
1970 printf ("profiler_heap_shot_write_summary_block: writing job %p done.\n", job);
1975 profiler_heap_shot_write_block (ProfilerHeapShotWriteJob *job) {
1976 #if DEBUG_HEAP_PROFILER
1977 printf ("profiler_heap_shot_write_block: working on job %p...\n", job);
1980 if (profiler->action_flags.collection_summary == TRUE) {
1981 profiler_heap_shot_write_summary_block (job);
1984 if ((profiler->action_flags.unreachable_objects == TRUE) || (profiler->action_flags.heap_shot == TRUE)) {
1985 profiler_heap_shot_write_data_block (job);
1988 profiler_heap_shot_write_job_free_buffers (job);
1989 #if DEBUG_HEAP_PROFILER
1990 printf ("profiler_heap_shot_write_block: work on job %p done.\n", job);
1995 write_element_load_block (LoadedElement *element, guint8 kind, gsize thread_id) {
1997 write_uint64 (element->load_start_counter);
1998 write_uint64 (element->load_end_counter);
1999 write_uint64 (thread_id);
2000 write_string (element->name);
2001 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_LOADED);
2002 element->load_written = TRUE;
2006 write_element_unload_block (LoadedElement *element, guint8 kind, gsize thread_id) {
2008 write_uint64 (element->unload_start_counter);
2009 write_uint64 (element->unload_end_counter);
2010 write_uint64 (thread_id);
2011 write_string (element->name);
2012 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_UNLOADED);
2013 element->unload_written = TRUE;
2017 write_clock_data (void) {
2021 MONO_PROFILER_GET_CURRENT_COUNTER (counter);
2022 MONO_PROFILER_GET_CURRENT_TIME (time);
2024 write_uint64 (counter);
2025 write_uint64 (time);
2029 write_mapping_block (gsize thread_id) {
2030 ClassIdMappingElement *current_class;
2031 MethodIdMappingElement *current_method;
2033 if ((profiler->classes->unwritten == NULL) && (profiler->methods->unwritten == NULL))
2036 #if (DEBUG_MAPPING_EVENTS || DEBUG_FILE_WRITES)
2037 printf ("[write_mapping_block][TID %ld] START\n", thread_id);
2040 write_clock_data ();
2041 write_uint64 (thread_id);
2043 for (current_class = profiler->classes->unwritten; current_class != NULL; current_class = current_class->next_unwritten) {
2044 write_uint32 (current_class->id);
2045 write_string (current_class->name);
2046 #if (DEBUG_MAPPING_EVENTS)
2047 printf ("mapping CLASS (%d => %s)\n", current_class->id, current_class->name);
2049 g_free (current_class->name);
2050 current_class->name = NULL;
2053 profiler->classes->unwritten = NULL;
2055 for (current_method = profiler->methods->unwritten; current_method != NULL; current_method = current_method->next_unwritten) {
2056 MonoMethod *method = current_method->method;
2057 MonoClass *klass = mono_method_get_class (method);
2058 ClassIdMappingElement *class_element = class_id_mapping_element_get (klass);
2059 g_assert (class_element != NULL);
2060 write_uint32 (current_method->id);
2061 write_uint32 (class_element->id);
2062 write_string (current_method->name);
2063 #if (DEBUG_MAPPING_EVENTS)
2064 printf ("mapping METHOD ([%d]%d => %s)\n", class_element?class_element->id:1, current_method->id, current_method->name);
2066 g_free (current_method->name);
2067 current_method->name = NULL;
2070 profiler->methods->unwritten = NULL;
2072 write_clock_data ();
2073 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_MAPPING);
2075 #if (DEBUG_MAPPING_EVENTS || DEBUG_FILE_WRITES)
2076 printf ("[write_mapping_block][TID %ld] END\n", thread_id);
2081 MONO_PROFILER_PACKED_EVENT_CODE_METHOD_ENTER = 1,
2082 MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EXIT_IMPLICIT = 2,
2083 MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EXIT_EXPLICIT = 3,
2084 MONO_PROFILER_PACKED_EVENT_CODE_CLASS_ALLOCATION = 4,
2085 MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EVENT = 5,
2086 MONO_PROFILER_PACKED_EVENT_CODE_CLASS_EVENT = 6,
2087 MONO_PROFILER_PACKED_EVENT_CODE_OTHER_EVENT = 7
2088 } MonoProfilerPackedEventCode;
2089 #define MONO_PROFILER_PACKED_EVENT_CODE_BITS 3
2090 #define MONO_PROFILER_PACKED_EVENT_DATA_BITS (8-MONO_PROFILER_PACKED_EVENT_CODE_BITS)
2091 #define MONO_PROFILER_PACKED_EVENT_DATA_MASK ((1<<MONO_PROFILER_PACKED_EVENT_DATA_BITS)-1)
2093 #define MONO_PROFILER_EVENT_MAKE_PACKED_CODE(result,data,base) do {\
2094 result = ((base)|((data & MONO_PROFILER_PACKED_EVENT_DATA_MASK) << MONO_PROFILER_PACKED_EVENT_CODE_BITS));\
2095 data >>= MONO_PROFILER_PACKED_EVENT_DATA_BITS;\
2097 #define MONO_PROFILER_EVENT_MAKE_FULL_CODE(result,code,kind,base) do {\
2098 result = ((base)|((((kind)<<4) | (code)) << MONO_PROFILER_PACKED_EVENT_CODE_BITS));\
2101 static ProfilerEventData*
2102 write_event (ProfilerEventData *event) {
2103 ProfilerEventData *next = event + 1;
2104 gboolean write_event_value = TRUE;
2107 guint64 event_value;
2109 event_value = event->value;
2110 if (event_value == MAX_EVENT_VALUE) {
2111 event_value = *((guint64*)next);
2115 if (event->data_type == MONO_PROFILER_EVENT_DATA_TYPE_METHOD) {
2116 MethodIdMappingElement *element = method_id_mapping_element_get (event->data.address);
2117 g_assert (element != NULL);
2118 event_data = element->id;
2120 if (event->code == MONO_PROFILER_EVENT_METHOD_CALL) {
2121 if (event->kind == MONO_PROFILER_EVENT_KIND_START) {
2122 MONO_PROFILER_EVENT_MAKE_PACKED_CODE (event_code, event_data, MONO_PROFILER_PACKED_EVENT_CODE_METHOD_ENTER);
2124 MONO_PROFILER_EVENT_MAKE_PACKED_CODE (event_code, event_data, MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EXIT_EXPLICIT);
2127 MONO_PROFILER_EVENT_MAKE_FULL_CODE (event_code, event->code, event->kind, MONO_PROFILER_PACKED_EVENT_CODE_METHOD_EVENT);
2129 } else if (event->data_type == MONO_PROFILER_EVENT_DATA_TYPE_CLASS) {
2130 ClassIdMappingElement *element = class_id_mapping_element_get (event->data.address);
2131 g_assert (element != NULL);
2132 event_data = element->id;
2134 if (event->code == MONO_PROFILER_EVENT_CLASS_ALLOCATION) {
2135 MONO_PROFILER_EVENT_MAKE_PACKED_CODE (event_code, event_data, MONO_PROFILER_PACKED_EVENT_CODE_CLASS_ALLOCATION);
2137 MONO_PROFILER_EVENT_MAKE_FULL_CODE (event_code, event->code, event->kind, MONO_PROFILER_PACKED_EVENT_CODE_CLASS_EVENT);
2140 event_data = event->data.number;
2141 MONO_PROFILER_EVENT_MAKE_FULL_CODE (event_code, event->code, event->kind, MONO_PROFILER_PACKED_EVENT_CODE_OTHER_EVENT);
2144 #if (DEBUG_LOGGING_PROFILER)
2146 printf ("writing EVENT[%p] data_type:%d, kind:%d, code:%d (%d:%ld:%ld)\n", event,
2147 event->data_type, event->kind, event->code,
2148 event_code, event_data, event_value);
2151 WRITE_BYTE (event_code);
2152 write_uint64 (event_data);
2153 if (write_event_value) {
2154 write_uint64 (event_value);
2161 write_thread_data_block (ProfilerPerThreadData *data) {
2162 ProfilerEventData *start = data->first_unwritten_event;
2163 ProfilerEventData *end = data->first_unmapped_event;
2167 #if (DEBUG_FILE_WRITES)
2168 printf ("write_thread_data_block: preparing buffer for thread %ld\n", (guint64) data->thread_id);
2170 write_clock_data ();
2171 write_uint64 (data->thread_id);
2173 write_uint64 (data->start_event_counter);
2175 while (start < end) {
2176 start = write_event (start);
2179 data->first_unwritten_event = end;
2181 write_clock_data ();
2182 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_EVENTS);
2183 #if (DEBUG_FILE_WRITES)
2184 printf ("write_thread_data_block: buffer for thread %ld written\n", (guint64) data->thread_id);
2188 static ProfilerExecutableMemoryRegionData*
2189 profiler_executable_memory_region_new (gpointer *start, gpointer *end, guint32 file_offset, char *file_name, guint32 id) {
2190 ProfilerExecutableMemoryRegionData *result = g_new (ProfilerExecutableMemoryRegionData, 1);
2191 result->start = start;
2193 result->file_offset = file_offset;
2194 result->file_name = g_strdup (file_name);
2196 result->is_new = TRUE;
2198 result->file = NULL;
2199 result->symbols_capacity = id;
2200 result->symbols_count = id;
2201 result->symbols = NULL;
2207 executable_file_close (ProfilerExecutableMemoryRegionData *region);
2210 profiler_executable_memory_region_destroy (ProfilerExecutableMemoryRegionData *data) {
2211 if (data->file_name != NULL) {
2212 g_free (data->file_name);
2214 if (data->symbols != NULL) {
2215 g_free (data->symbols);
2217 if (data->file != NULL) {
2218 executable_file_close (data);
2223 static ProfilerExecutableMemoryRegions*
2224 profiler_executable_memory_regions_new (int next_id, int next_unmanaged_function_id) {
2225 ProfilerExecutableMemoryRegions *result = g_new (ProfilerExecutableMemoryRegions, 1);
2226 result->regions = g_new0 (ProfilerExecutableMemoryRegionData*, 32);
2227 result->regions_capacity = 32;
2228 result->regions_count = 0;
2229 result->next_id = next_id;
2230 result->next_unmanaged_function_id = next_unmanaged_function_id;
2235 profiler_executable_memory_regions_destroy (ProfilerExecutableMemoryRegions *regions) {
2238 for (i = 0; i < regions->regions_count; i++) {
2239 profiler_executable_memory_region_destroy (regions->regions [i]);
2241 g_free (regions->regions);
2245 static ProfilerExecutableMemoryRegionData*
2246 find_address_region (ProfilerExecutableMemoryRegions *regions, gpointer address) {
2248 int high_index = regions->regions_count;
2249 int middle_index = 0;
2250 ProfilerExecutableMemoryRegionData *middle_region = regions->regions [0];
2252 if ((regions->regions_count == 0) || (regions->regions [low_index]->start > address) || (regions->regions [high_index - 1]->end < address)) {
2256 //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);
2258 while (low_index != high_index) {
2259 middle_index = low_index + ((high_index - low_index) / 2);
2260 middle_region = regions->regions [middle_index];
2262 //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);
2264 if (middle_region->start > address) {
2265 if (middle_index > 0) {
2266 high_index = middle_index;
2270 } else if (middle_region->end < address) {
2271 if (middle_index < regions->regions_count - 1) {
2272 low_index = middle_index + 1;
2277 return middle_region;
2281 if ((middle_region == NULL) || (middle_region->start > address) || (middle_region->end < address)) {
2284 return middle_region;
2289 append_region (ProfilerExecutableMemoryRegions *regions, gpointer *start, gpointer *end, guint32 file_offset, char *file_name) {
2290 if (regions->regions_count >= regions->regions_capacity) {
2291 ProfilerExecutableMemoryRegionData **new_regions = g_new0 (ProfilerExecutableMemoryRegionData*, regions->regions_capacity * 2);
2292 memcpy (new_regions, regions->regions, regions->regions_capacity * sizeof (ProfilerExecutableMemoryRegionData*));
2293 g_free (regions->regions);
2294 regions->regions = new_regions;
2295 regions->regions_capacity = regions->regions_capacity * 2;
2297 regions->regions [regions->regions_count] = profiler_executable_memory_region_new (start, end, file_offset, file_name, regions->next_id);
2298 regions->regions_count ++;
2299 regions->next_id ++;
2303 restore_old_regions (ProfilerExecutableMemoryRegions *old_regions, ProfilerExecutableMemoryRegions *new_regions) {
2307 for (old_i = 0; old_i < old_regions->regions_count; old_i++) {
2308 ProfilerExecutableMemoryRegionData *old_region = old_regions->regions [old_i];
2309 for (new_i = 0; new_i < new_regions->regions_count; new_i++) {
2310 ProfilerExecutableMemoryRegionData *new_region = new_regions->regions [new_i];
2311 if ((old_region->start == new_region->start) &&
2312 (old_region->end == new_region->end) &&
2313 (old_region->file_offset == new_region->file_offset) &&
2314 ! strcmp (old_region->file_name, new_region->file_name)) {
2315 new_regions->regions [new_i] = old_region;
2316 old_regions->regions [old_i] = new_region;
2318 // FIXME (sanity check)
2319 g_assert (new_region->is_new && ! old_region->is_new);
2326 compare_regions (const void *a1, const void *a2) {
2327 ProfilerExecutableMemoryRegionData *r1 = * (ProfilerExecutableMemoryRegionData**) a1;
2328 ProfilerExecutableMemoryRegionData *r2 = * (ProfilerExecutableMemoryRegionData**) a2;
2329 return (r1->start < r2->start)? -1 : ((r1->start > r2->start)? 1 : 0);
2333 sort_regions (ProfilerExecutableMemoryRegions *regions) {
2334 qsort (regions->regions, regions->regions_count, sizeof (ProfilerExecutableMemoryRegionData *), compare_regions);
2338 executable_file_add_region_reference (ProfilerExecutableFile *file, ProfilerExecutableMemoryRegionData *region) {
2339 guint8 *section_headers = file->data + file->header->e_shoff;
2342 for (section_index = 1; section_index < file->header->e_shnum; section_index ++) {
2343 ElfSection *section_header = (ElfSection*) (section_headers + (file->header->e_shentsize * section_index));
2345 if ((section_header->sh_addr != 0) && (section_header->sh_flags & ELF_SHF_EXECINSTR) &&
2346 (region->file_offset <= section_header->sh_offset) && (region->file_offset + (((guint8*)region->end)-((guint8*)region->start)) >= (section_header->sh_offset + section_header->sh_size))) {
2347 ProfilerExecutableFileSectionRegion *section_region = & (file->section_regions [section_index]);
2348 section_region->region = region;
2349 section_region->section_address = (gpointer) section_header->sh_addr;
2350 section_region->section_offset = section_header->sh_offset;
2355 static ProfilerExecutableFile*
2356 executable_file_open (ProfilerExecutableMemoryRegionData *region) {
2357 ProfilerExecutableFiles *files = & (profiler->executable_files);
2358 ProfilerExecutableFile *file = (ProfilerExecutableFile*) g_hash_table_lookup (files->table, region->file_name);
2360 guint16 test = 0x0102;
2361 struct stat stat_buffer;
2362 int symtab_index = 0;
2363 int strtab_index = 0;
2364 int dynsym_index = 0;
2365 int dynstr_index = 0;
2367 guint8 *section_headers;
2371 file = g_new0 (ProfilerExecutableFile, 1);
2372 region->file = file;
2373 file->reference_count ++;
2375 file->fd = open (region->file_name, O_RDONLY);
2376 if (file->fd == -1) {
2377 //g_warning ("Cannot open file '%s': '%s'", region->file_name, strerror (errno));
2380 if (fstat (file->fd, &stat_buffer) != 0) {
2381 //g_warning ("Cannot stat file '%s': '%s'", region->file_name, strerror (errno));
2384 size_t region_length = ((guint8*)region->end) - ((guint8*)region->start);
2385 file->length = stat_buffer.st_size;
2387 if (file->length == region_length) {
2388 file->data = region->start;
2392 file->data = mmap (NULL, file->length, PROT_READ, MAP_PRIVATE, file->fd, 0);
2394 if (file->data == MAP_FAILED) {
2396 //g_warning ("Cannot map file '%s': '%s'", region->file_name, strerror (errno));
2404 header = (ElfHeader*) file->data;
2406 if ((header->e_ident [EI_MAG0] != 0x7f) || (header->e_ident [EI_MAG1] != 'E') ||
2407 (header->e_ident [EI_MAG2] != 'L') || (header->e_ident [EI_MAG3] != 'F')) {
2411 if (sizeof (gsize) == 4) {
2412 if (header->e_ident [EI_CLASS] != ELF_CLASS_32) {
2413 g_warning ("Class is not ELF_CLASS_32 with gsize size %d", (int) sizeof (gsize));
2416 } else if (sizeof (gsize) == 8) {
2417 if (header->e_ident [EI_CLASS] != ELF_CLASS_64) {
2418 g_warning ("Class is not ELF_CLASS_64 with gsize size %d", (int) sizeof (gsize));
2422 g_warning ("Absurd gsize size %d", (int) sizeof (gsize));
2426 if ((*(guint8*)(&test)) == 0x01) {
2427 if (header->e_ident [EI_DATA] != ELF_DATA_MSB) {
2428 g_warning ("Data is not ELF_DATA_MSB with first test byte 0x01");
2431 } else if ((*(guint8*)(&test)) == 0x02) {
2432 if (header->e_ident [EI_DATA] != ELF_DATA_LSB) {
2433 g_warning ("Data is not ELF_DATA_LSB with first test byte 0x02");
2437 g_warning ("Absurd test byte value");
2441 /* OK, this is a usable elf file... */
2442 file->header = header;
2443 section_headers = file->data + header->e_shoff;
2444 file->main_string_table = ((const char*) file->data) + (((ElfSection*) (section_headers + (header->e_shentsize * header->e_shstrndx)))->sh_offset);
2446 for (section_index = 0; section_index < header->e_shnum; section_index ++) {
2447 ElfSection *section_header = (ElfSection*) (section_headers + (header->e_shentsize * section_index));
2449 if (section_header->sh_type == ELF_SHT_SYMTAB) {
2450 symtab_index = section_index;
2451 } else if (section_header->sh_type == ELF_SHT_DYNSYM) {
2452 dynsym_index = section_index;
2453 } else if (section_header->sh_type == ELF_SHT_STRTAB) {
2454 if (! strcmp (file->main_string_table + section_header->sh_name, ".strtab")) {
2455 strtab_index = section_index;
2456 } else if (! strcmp (file->main_string_table + section_header->sh_name, ".dynstr")) {
2457 dynstr_index = section_index;
2462 if ((symtab_index != 0) && (strtab_index != 0)) {
2463 section_index = symtab_index;
2464 strings_index = strtab_index;
2465 } else if ((dynsym_index != 0) && (dynstr_index != 0)) {
2466 section_index = dynsym_index;
2467 strings_index = dynstr_index;
2473 if (section_index != 0) {
2474 ElfSection *section_header = (ElfSection*) (section_headers + (header->e_shentsize * section_index));
2475 file->symbol_size = section_header->sh_entsize;
2476 file->symbols_count = (guint32) (section_header->sh_size / section_header->sh_entsize);
2477 file->symbols_start = file->data + section_header->sh_offset;
2478 file->symbols_string_table = ((const char*) file->data) + (((ElfSection*) (section_headers + (header->e_shentsize * strings_index)))->sh_offset);
2481 file->section_regions = g_new0 (ProfilerExecutableFileSectionRegion, file->header->e_shnum);
2483 region->file = file;
2484 file->reference_count ++;
2487 if (file->header != NULL) {
2488 executable_file_add_region_reference (file, region);
2491 if (file->next_new_file == NULL) {
2492 file->next_new_file = files->new_files;
2493 files->new_files = file;
2499 executable_file_free (ProfilerExecutableFile* file) {
2500 if (file->fd != -1) {
2501 if (close (file->fd) != 0) {
2502 g_warning ("Cannot close file: '%s'", strerror (errno));
2504 if (file->data != NULL) {
2505 if (munmap (file->data, file->length) != 0) {
2506 g_warning ("Cannot unmap file: '%s'", strerror (errno));
2510 if (file->section_regions != NULL) {
2511 g_free (file->section_regions);
2517 executable_file_close (ProfilerExecutableMemoryRegionData *region) {
2518 region->file->reference_count --;
2520 if (region->file->reference_count <= 0) {
2521 ProfilerExecutableFiles *files = & (profiler->executable_files);
2522 g_hash_table_remove (files->table, region->file_name);
2523 executable_file_free (region->file);
2524 region->file = NULL;
2529 executable_file_count_symbols (ProfilerExecutableFile *file) {
2532 for (symbol_index = 0; symbol_index < file->symbols_count; symbol_index ++) {
2533 ElfSymbol *symbol = (ElfSymbol*) (file->symbols_start + (symbol_index * file->symbol_size));
2535 if ((ELF_ST_TYPE (symbol->st_info) == ELF_STT_FUNC) &&
2536 (symbol->st_shndx > 0) &&
2537 (symbol->st_shndx < file->header->e_shnum)) {
2538 int symbol_section_index = symbol->st_shndx;
2539 ProfilerExecutableMemoryRegionData *region = file->section_regions [symbol_section_index].region;
2540 if ((region != NULL) && (region->symbols == NULL)) {
2541 region->symbols_count ++;
2548 executable_memory_regions_prepare_symbol_tables (ProfilerExecutableMemoryRegions *regions) {
2550 for (i = 0; i < regions->regions_count; i++) {
2551 ProfilerExecutableMemoryRegionData *region = regions->regions [i];
2552 if ((region->symbols_count > 0) && (region->symbols == NULL)) {
2553 region->symbols = g_new (ProfilerUnmanagedSymbol, region->symbols_count);
2554 region->symbols_capacity = region->symbols_count;
2555 region->symbols_count = 0;
2561 executable_region_symbol_get_name (ProfilerExecutableMemoryRegionData *region, ProfilerUnmanagedSymbol *symbol) {
2562 ElfSymbol *elf_symbol = (ElfSymbol*) (region->file->symbols_start + (symbol->index * region->file->symbol_size));
2563 return region->file->symbols_string_table + elf_symbol->st_name;
2567 executable_file_build_symbol_tables (ProfilerExecutableFile *file) {
2570 for (symbol_index = 0; symbol_index < file->symbols_count; symbol_index ++) {
2571 ElfSymbol *symbol = (ElfSymbol*) (file->symbols_start + (symbol_index * file->symbol_size));
2573 if ((ELF_ST_TYPE (symbol->st_info) == ELF_STT_FUNC) &&
2574 (symbol->st_shndx > 0) &&
2575 (symbol->st_shndx < file->header->e_shnum)) {
2576 int symbol_section_index = symbol->st_shndx;
2577 ProfilerExecutableFileSectionRegion *section_region = & (file->section_regions [symbol_section_index]);
2578 ProfilerExecutableMemoryRegionData *region = section_region->region;
2580 if (region != NULL) {
2581 ProfilerUnmanagedSymbol *new_symbol = & (region->symbols [region->symbols_count]);
2582 region->symbols_count ++;
2585 new_symbol->index = symbol_index;
2586 new_symbol->size = symbol->st_size;
2587 new_symbol->offset = (((guint8*) symbol->st_value) - section_region->section_address) - (region->file_offset - section_region->section_offset);
2594 compare_region_symbols (const void *p1, const void *p2) {
2595 const ProfilerUnmanagedSymbol *s1 = p1;
2596 const ProfilerUnmanagedSymbol *s2 = p2;
2597 return (s1->offset < s2->offset)? -1 : ((s1->offset > s2->offset)? 1 : 0);
2601 executable_memory_regions_sort_symbol_tables (ProfilerExecutableMemoryRegions *regions) {
2603 for (i = 0; i < regions->regions_count; i++) {
2604 ProfilerExecutableMemoryRegionData *region = regions->regions [i];
2605 if ((region->is_new) && (region->symbols != NULL)) {
2606 qsort (region->symbols, region->symbols_count, sizeof (ProfilerUnmanagedSymbol), compare_region_symbols);
2612 build_symbol_tables (ProfilerExecutableMemoryRegions *regions, ProfilerExecutableFiles *files) {
2614 ProfilerExecutableFile *file;
2616 for (i = 0; i < regions->regions_count; i++) {
2617 ProfilerExecutableMemoryRegionData *region = regions->regions [i];
2618 if ((region->is_new) && (region->file == NULL)) {
2619 executable_file_open (region);
2623 for (file = files->new_files; file != NULL; file = file->next_new_file) {
2624 executable_file_count_symbols (file);
2627 executable_memory_regions_prepare_symbol_tables (regions);
2629 for (file = files->new_files; file != NULL; file = file->next_new_file) {
2630 executable_file_build_symbol_tables (file);
2633 executable_memory_regions_sort_symbol_tables (regions);
2635 file = files->new_files;
2636 while (file != NULL) {
2637 ProfilerExecutableFile *next_file = file->next_new_file;
2638 file->next_new_file = NULL;
2641 files->new_files = NULL;
2644 static ProfilerUnmanagedSymbol*
2645 executable_memory_region_find_symbol (ProfilerExecutableMemoryRegionData *region, guint32 offset) {
2646 if (region->symbols_count > 0) {
2647 ProfilerUnmanagedSymbol *low = region->symbols;
2648 ProfilerUnmanagedSymbol *high = region->symbols + (region->symbols_count - 1);
2649 int step = region->symbols_count >> 1;
2650 ProfilerUnmanagedSymbol *current = region->symbols + step;
2653 step = (high - low) >> 1;
2655 if (offset < current->offset) {
2657 current = high - step;
2658 } else if (offset >= current->offset) {
2659 if (offset >= (current->offset + current->size)) {
2661 current = low + step;
2668 if ((offset >= current->offset) && (offset < (current->offset + current->size))) {
2678 //FIXME: make also Win32 and BSD variants
2679 #define MAPS_BUFFER_SIZE 4096
2682 update_regions_buffer (int fd, char *buffer) {
2683 ssize_t result = read (fd, buffer, MAPS_BUFFER_SIZE);
2685 if (result == MAPS_BUFFER_SIZE) {
2687 } else if (result >= 0) {
2688 *(buffer + result) = 0;
2696 #define GOTO_NEXT_CHAR(c,b,fd) do {\
2698 if (((c) - (b) >= MAPS_BUFFER_SIZE) || ((*(c) == 0) && ((c) != (b)))) {\
2699 update_regions_buffer ((fd), (b));\
2704 static int hex_digit_value (char c) {
2705 if ((c >= '0') && (c <= '9')) {
2707 } else if ((c >= 'a') && (c <= 'f')) {
2708 return c - 'a' + 10;
2709 } else if ((c >= 'A') && (c <= 'F')) {
2710 return c - 'A' + 10;
2732 MAP_LINE_PARSER_STATE_INVALID,
2733 MAP_LINE_PARSER_STATE_START_ADDRESS,
2734 MAP_LINE_PARSER_STATE_END_ADDRESS,
2735 MAP_LINE_PARSER_STATE_PERMISSIONS,
2736 MAP_LINE_PARSER_STATE_OFFSET,
2737 MAP_LINE_PARSER_STATE_DEVICE,
2738 MAP_LINE_PARSER_STATE_INODE,
2739 MAP_LINE_PARSER_STATE_BLANK_BEFORE_FILENAME,
2740 MAP_LINE_PARSER_STATE_FILENAME,
2741 MAP_LINE_PARSER_STATE_DONE
2742 } MapLineParserState;
2744 const char *map_line_parser_state [] = {
2752 "BLANK_BEFORE_FILENAME",
2758 parse_map_line (ProfilerExecutableMemoryRegions *regions, int fd, char *buffer, char *current) {
2759 MapLineParserState state = MAP_LINE_PARSER_STATE_START_ADDRESS;
2760 gsize start_address = 0;
2761 gsize end_address = 0;
2763 char *start_filename = NULL;
2764 char *end_filename = NULL;
2765 gboolean is_executable = FALSE;
2766 gboolean done = FALSE;
2772 case MAP_LINE_PARSER_STATE_START_ADDRESS:
2774 start_address <<= 4;
2775 start_address |= hex_digit_value (c);
2776 } else if (c == '-') {
2777 state = MAP_LINE_PARSER_STATE_END_ADDRESS;
2779 state = MAP_LINE_PARSER_STATE_INVALID;
2782 case MAP_LINE_PARSER_STATE_END_ADDRESS:
2785 end_address |= hex_digit_value (c);
2786 } else if (isblank (c)) {
2787 state = MAP_LINE_PARSER_STATE_PERMISSIONS;
2789 state = MAP_LINE_PARSER_STATE_INVALID;
2792 case MAP_LINE_PARSER_STATE_PERMISSIONS:
2794 is_executable = TRUE;
2795 } else if (isblank (c)) {
2796 state = MAP_LINE_PARSER_STATE_OFFSET;
2797 } else if ((c != '-') && ! isalpha (c)) {
2798 state = MAP_LINE_PARSER_STATE_INVALID;
2801 case MAP_LINE_PARSER_STATE_OFFSET:
2804 offset |= hex_digit_value (c);
2805 } else if (isblank (c)) {
2806 state = MAP_LINE_PARSER_STATE_DEVICE;
2808 state = MAP_LINE_PARSER_STATE_INVALID;
2811 case MAP_LINE_PARSER_STATE_DEVICE:
2813 state = MAP_LINE_PARSER_STATE_INODE;
2814 } else if ((c != ':') && ! isxdigit (c)) {
2815 state = MAP_LINE_PARSER_STATE_INVALID;
2818 case MAP_LINE_PARSER_STATE_INODE:
2820 state = MAP_LINE_PARSER_STATE_BLANK_BEFORE_FILENAME;
2821 } else if (! isdigit (c)) {
2822 state = MAP_LINE_PARSER_STATE_INVALID;
2825 case MAP_LINE_PARSER_STATE_BLANK_BEFORE_FILENAME:
2826 if ((c == '/') || (c == '[')) {
2827 state = MAP_LINE_PARSER_STATE_FILENAME;
2828 start_filename = current;
2829 } else if (! isblank (c)) {
2830 state = MAP_LINE_PARSER_STATE_INVALID;
2833 case MAP_LINE_PARSER_STATE_FILENAME:
2835 state = MAP_LINE_PARSER_STATE_DONE;
2837 end_filename = current;
2840 case MAP_LINE_PARSER_STATE_DONE:
2841 if (done && is_executable) {
2843 append_region (regions, (gpointer) start_address, (gpointer) end_address, offset, start_filename);
2846 case MAP_LINE_PARSER_STATE_INVALID:
2848 state = MAP_LINE_PARSER_STATE_DONE;
2855 } else if (c == '\n') {
2856 state = MAP_LINE_PARSER_STATE_DONE;
2859 GOTO_NEXT_CHAR(current, buffer, fd);
2865 scan_process_regions (ProfilerExecutableMemoryRegions *regions) {
2870 fd = open ("/proc/self/maps", O_RDONLY);
2875 buffer = malloc (MAPS_BUFFER_SIZE);
2876 update_regions_buffer (fd, buffer);
2878 while (current != NULL) {
2879 current = parse_map_line (regions, fd, buffer, current);
2890 MONO_PROFILER_STATISTICAL_CODE_END = 0,
2891 MONO_PROFILER_STATISTICAL_CODE_METHOD = 1,
2892 MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_ID = 2,
2893 MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_NEW_ID = 3,
2894 MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_OFFSET_IN_REGION = 4,
2895 MONO_PROFILER_STATISTICAL_CODE_CALL_CHAIN = 5,
2896 MONO_PROFILER_STATISTICAL_CODE_REGIONS = 7
2897 } MonoProfilerStatisticalCode;
2900 refresh_memory_regions (void) {
2901 ProfilerExecutableMemoryRegions *old_regions = profiler->executable_regions;
2902 ProfilerExecutableMemoryRegions *new_regions = profiler_executable_memory_regions_new (old_regions->next_id, old_regions->next_unmanaged_function_id);
2905 LOG_WRITER_THREAD ("Refreshing memory regions...");
2906 scan_process_regions (new_regions);
2907 restore_old_regions (old_regions, new_regions);
2908 sort_regions (new_regions);
2909 LOG_WRITER_THREAD ("Refreshed memory regions.");
2911 LOG_WRITER_THREAD ("Building symbol tables...");
2912 build_symbol_tables (new_regions, & (profiler->executable_files));
2914 printf ("Symbol tables done!\n");
2915 printf ("Region summary...\n");
2916 for (i = 0; i < new_regions->regions_count; i++) {
2917 ProfilerExecutableMemoryRegionData *region = new_regions->regions [i];
2918 printf ("Region %d[%d][NEW:%d] (%p-%p) at %d in file %s\n", i, region->id, region->is_new,
2919 region->start, region->end, region->file_offset, region->file_name);
2921 printf ("New symbol tables dump...\n");
2922 for (i = 0; i < new_regions->regions_count; i++) {
2923 ProfilerExecutableMemoryRegionData *region = new_regions->regions [i];
2925 if (region->is_new) {
2928 printf ("Region %d[%d][NEW:%d] (%p-%p) at %d in file %s\n", i, region->id, region->is_new,
2929 region->start, region->end, region->file_offset, region->file_name);
2930 for (symbol_index = 0; symbol_index < region->symbols_count; symbol_index ++) {
2931 ProfilerUnmanagedSymbol *symbol = & (region->symbols [symbol_index]);
2932 printf (" [%d] Symbol %s (offset %d, size %d)\n", symbol_index,
2933 executable_region_symbol_get_name (region, symbol),
2934 symbol->offset, symbol->size);
2939 LOG_WRITER_THREAD ("Built symbol tables.");
2941 // This marks the region "sub-block"
2942 write_uint32 (MONO_PROFILER_STATISTICAL_CODE_REGIONS);
2944 // First write the "removed" regions
2945 for (i = 0; i < old_regions->regions_count; i++) {
2946 ProfilerExecutableMemoryRegionData *region = old_regions->regions [i];
2947 if (! region->is_new) {
2948 #if DEBUG_STATISTICAL_PROFILER
2949 printf ("[refresh_memory_regions] Invalidated region %d\n", region->id);
2951 write_uint32 (region->id);
2956 // Then write the new ones
2957 for (i = 0; i < new_regions->regions_count; i++) {
2958 ProfilerExecutableMemoryRegionData *region = new_regions->regions [i];
2959 if (region->is_new) {
2960 region->is_new = FALSE;
2962 #if DEBUG_STATISTICAL_PROFILER
2963 printf ("[refresh_memory_regions] Wrote region %d (%p-%p[%d] '%s')\n", region->id, region->start, region->end, region->file_offset, region->file_name);
2965 write_uint32 (region->id);
2966 write_uint64 (GPOINTER_TO_UINT (region->start));
2967 write_uint32 (GPOINTER_TO_UINT (region->end) - GPOINTER_TO_UINT (region->start));
2968 write_uint32 (region->file_offset);
2969 write_string (region->file_name);
2974 // Finally, free the old ones, and replace them
2975 profiler_executable_memory_regions_destroy (old_regions);
2976 profiler->executable_regions = new_regions;
2980 write_statistical_hit (MonoDomain *domain, gpointer address, gboolean regions_refreshed) {
2981 MonoJitInfo *ji = (domain != NULL) ? mono_jit_info_table_find (domain, (char*) address) : NULL;
2984 MonoMethod *method = mono_jit_info_get_method (ji);
2985 MethodIdMappingElement *element = method_id_mapping_element_get (method);
2987 if (element != NULL) {
2988 #if DEBUG_STATISTICAL_PROFILER
2989 printf ("[write_statistical_hit] Wrote method %d\n", element->id);
2991 write_uint32 ((element->id << 3) | MONO_PROFILER_STATISTICAL_CODE_METHOD);
2993 #if DEBUG_STATISTICAL_PROFILER
2994 printf ("[write_statistical_hit] Wrote unknown method %p\n", method);
2996 write_uint32 (MONO_PROFILER_STATISTICAL_CODE_METHOD);
2999 ProfilerExecutableMemoryRegionData *region = find_address_region (profiler->executable_regions, address);
3001 if (region == NULL && ! regions_refreshed) {
3002 #if DEBUG_STATISTICAL_PROFILER
3003 printf ("[write_statistical_hit] Cannot find region for address %p, refreshing...\n", address);
3005 refresh_memory_regions ();
3006 regions_refreshed = TRUE;
3007 region = find_address_region (profiler->executable_regions, address);
3010 if (region != NULL) {
3011 guint32 offset = ((guint8*)address) - ((guint8*)region->start);
3012 ProfilerUnmanagedSymbol *symbol = executable_memory_region_find_symbol (region, offset);
3014 if (symbol != NULL) {
3015 if (symbol->id > 0) {
3016 #if DEBUG_STATISTICAL_PROFILER
3017 printf ("[write_statistical_hit] Wrote unmanaged symbol %d\n", symbol->id);
3019 write_uint32 ((symbol->id << 3) | MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_ID);
3021 ProfilerExecutableMemoryRegions *regions = profiler->executable_regions;
3022 const char *symbol_name = executable_region_symbol_get_name (region, symbol);
3023 symbol->id = regions->next_unmanaged_function_id;
3024 regions->next_unmanaged_function_id ++;
3025 #if DEBUG_STATISTICAL_PROFILER
3026 printf ("[write_statistical_hit] Wrote new unmanaged symbol in region %d[%d]\n", region->id, offset);
3028 write_uint32 ((region->id << 3) | MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_NEW_ID);
3029 write_uint32 (symbol->id);
3030 write_string (symbol_name);
3033 #if DEBUG_STATISTICAL_PROFILER
3034 printf ("[write_statistical_hit] Wrote unknown unmanaged hit in region %d[%d] (address %p)\n", region->id, offset, address);
3036 write_uint32 ((region->id << 3) | MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_OFFSET_IN_REGION);
3037 write_uint32 (offset);
3040 #if DEBUG_STATISTICAL_PROFILER
3041 printf ("[write_statistical_hit] Wrote unknown unmanaged hit %p\n", address);
3043 write_uint32 (MONO_PROFILER_STATISTICAL_CODE_UNMANAGED_FUNCTION_OFFSET_IN_REGION);
3044 write_uint64 (GPOINTER_TO_UINT (address));
3048 return regions_refreshed;
3052 flush_all_mappings (void);
3055 write_statistical_data_block (ProfilerStatisticalData *data) {
3056 int start_index = data->first_unwritten_index;
3057 int end_index = data->next_free_index;
3058 gboolean regions_refreshed = FALSE;
3059 int call_chain_depth = profiler->statistical_call_chain_depth;
3062 if (end_index > data->end_index)
3063 end_index = data->end_index;
3065 if (start_index == end_index)
3068 data->first_unwritten_index = end_index;
3070 write_clock_data ();
3072 #if DEBUG_STATISTICAL_PROFILER
3073 printf ("[write_statistical_data_block] Starting loop at index %d\n", start_index);
3076 for (index = start_index; index < end_index; index ++) {
3077 int base_index = index * (call_chain_depth + 1);
3078 ProfilerStatisticalHit hit = data->hits [base_index];
3081 regions_refreshed = write_statistical_hit (hit.domain, hit.address, regions_refreshed);
3084 for (callers_count = 0; callers_count < call_chain_depth; callers_count ++) {
3085 hit = data->hits [base_index + callers_count];
3086 if (hit.address == NULL) {
3091 if (callers_count > 0) {
3092 write_uint32 ((callers_count << 3) | MONO_PROFILER_STATISTICAL_CODE_CALL_CHAIN);
3094 for (callers_count = 0; callers_count < call_chain_depth; callers_count ++) {
3095 hit = data->hits [base_index + callers_count];
3096 if (hit.address != NULL) {
3097 regions_refreshed = write_statistical_hit (hit.domain, hit.address, regions_refreshed);
3104 write_uint32 (MONO_PROFILER_STATISTICAL_CODE_END);
3106 #if DEBUG_STATISTICAL_PROFILER
3107 printf ("[write_statistical_data_block] Ending loop at index %d\n", end_index);
3109 write_clock_data ();
3111 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_STATISTICAL);
3115 write_intro_block (void) {
3117 write_string ("mono");
3118 write_uint32 (profiler->flags);
3119 write_uint64 (profiler->start_counter);
3120 write_uint64 (profiler->start_time);
3121 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_INTRO);
3125 write_end_block (void) {
3127 write_uint64 (profiler->end_counter);
3128 write_uint64 (profiler->end_time);
3129 write_current_block (MONO_PROFILER_FILE_BLOCK_KIND_END);
3133 update_mapping (ProfilerPerThreadData *data) {
3134 ProfilerEventData *start = data->first_unmapped_event;
3135 ProfilerEventData *end = data->next_free_event;
3136 data->first_unmapped_event = end;
3138 #if (DEBUG_LOGGING_PROFILER)
3139 printf ("[update_mapping][TID %ld] START\n", data->thread_id);
3141 while (start < end) {
3142 #if DEBUG_LOGGING_PROFILER
3143 printf ("Examining event %p[TID %ld] looking for a new mapping...\n", start, data->thread_id);
3145 if (start->data_type == MONO_PROFILER_EVENT_DATA_TYPE_CLASS) {
3146 ClassIdMappingElement *element = class_id_mapping_element_get (start->data.address);
3147 if (element == NULL) {
3148 MonoClass *klass = start->data.address;
3149 class_id_mapping_element_new (klass);
3151 } else if (start->data_type == MONO_PROFILER_EVENT_DATA_TYPE_METHOD) {
3152 MethodIdMappingElement *element = method_id_mapping_element_get (start->data.address);
3153 if (element == NULL) {
3154 MonoMethod *method = start->data.address;
3155 method_id_mapping_element_new (method);
3159 if (start->value == MAX_EVENT_VALUE) {
3164 #if (DEBUG_LOGGING_PROFILER)
3165 printf ("[update_mapping][TID %ld] END\n", data->thread_id);
3170 flush_all_mappings (void) {
3171 ProfilerPerThreadData *data;
3173 for (data = profiler->per_thread_data; data != NULL; data = data->next) {
3174 update_mapping (data);
3176 for (data = profiler->per_thread_data; data != NULL; data = data->next) {
3177 write_mapping_block (data->thread_id);
3182 flush_full_event_data_buffer (ProfilerPerThreadData *data) {
3185 // We flush all mappings because some id definitions could come
3186 // from other threads
3187 flush_all_mappings ();
3188 g_assert (data->first_unmapped_event >= data->end_event);
3190 write_thread_data_block (data);
3192 data->next_free_event = data->events;
3193 data->first_unwritten_event = data->events;
3194 data->first_unmapped_event = data->events;
3195 MONO_PROFILER_GET_CURRENT_COUNTER (data->start_event_counter);
3196 data->last_event_counter = data->start_event_counter;
3201 #define GET_NEXT_FREE_EVENT(d,e) {\
3202 if ((d)->next_free_event >= (d)->end_event) {\
3203 flush_full_event_data_buffer (d);\
3205 (e) = (d)->next_free_event;\
3206 (d)->next_free_event ++;\
3210 flush_everything (void) {
3211 ProfilerPerThreadData *data;
3213 flush_all_mappings ();
3214 for (data = profiler->per_thread_data; data != NULL; data = data->next) {
3215 write_thread_data_block (data);
3217 write_statistical_data_block (profiler->statistical_data);
3221 writer_thread_flush_everything (void) {
3222 if (CHECK_WRITER_THREAD ()) {
3223 profiler->writer_thread_flush_everything = TRUE;
3224 LOG_WRITER_THREAD ("writer_thread_flush_everything: raising event...");
3225 WRITER_EVENT_RAISE ();
3226 LOG_WRITER_THREAD ("writer_thread_flush_everything: waiting event...");
3227 WRITER_EVENT_DONE_WAIT ();
3228 LOG_WRITER_THREAD ("writer_thread_flush_everything: got event.");
3230 LOG_WRITER_THREAD ("writer_thread_flush_everything: no thread.");
3234 #define RESULT_TO_LOAD_CODE(r) (((r)==MONO_PROFILE_OK)?MONO_PROFILER_LOADED_EVENT_SUCCESS:MONO_PROFILER_LOADED_EVENT_FAILURE)
3236 appdomain_start_load (MonoProfiler *profiler, MonoDomain *domain) {
3238 loaded_element_load_start (profiler->loaded_appdomains, domain);
3243 appdomain_end_load (MonoProfiler *profiler, MonoDomain *domain, int result) {
3245 LoadedElement *element;
3247 name = g_strdup_printf ("%d", mono_domain_get_id (domain));
3249 element = loaded_element_load_end (profiler->loaded_appdomains, domain, name);
3250 write_element_load_block (element, MONO_PROFILER_LOADED_EVENT_APPDOMAIN | RESULT_TO_LOAD_CODE (result), CURRENT_THREAD_ID ());
3255 appdomain_start_unload (MonoProfiler *profiler, MonoDomain *domain) {
3257 loaded_element_unload_start (profiler->loaded_appdomains, domain);
3258 writer_thread_flush_everything ();
3263 appdomain_end_unload (MonoProfiler *profiler, MonoDomain *domain) {
3264 LoadedElement *element;
3267 element = loaded_element_unload_end (profiler->loaded_appdomains, domain);
3268 write_element_unload_block (element, MONO_PROFILER_LOADED_EVENT_APPDOMAIN, CURRENT_THREAD_ID ());
3273 module_start_load (MonoProfiler *profiler, MonoImage *module) {
3275 loaded_element_load_start (profiler->loaded_modules, module);
3280 module_end_load (MonoProfiler *profiler, MonoImage *module, int result) {
3282 MonoAssemblyName aname;
3283 LoadedElement *element;
3285 if (mono_assembly_fill_assembly_name (module, &aname)) {
3286 name = mono_stringify_assembly_name (&aname);
3288 name = g_strdup_printf ("Dynamic module \"%p\"", module);
3291 element = loaded_element_load_end (profiler->loaded_modules, module, name);
3292 write_element_load_block (element, MONO_PROFILER_LOADED_EVENT_MODULE | RESULT_TO_LOAD_CODE (result), CURRENT_THREAD_ID ());
3297 module_start_unload (MonoProfiler *profiler, MonoImage *module) {
3299 loaded_element_unload_start (profiler->loaded_modules, module);
3300 writer_thread_flush_everything ();
3305 module_end_unload (MonoProfiler *profiler, MonoImage *module) {
3306 LoadedElement *element;
3309 element = loaded_element_unload_end (profiler->loaded_modules, module);
3310 write_element_unload_block (element, MONO_PROFILER_LOADED_EVENT_MODULE, CURRENT_THREAD_ID ());
3315 assembly_start_load (MonoProfiler *profiler, MonoAssembly *assembly) {
3317 loaded_element_load_start (profiler->loaded_assemblies, assembly);
3322 assembly_end_load (MonoProfiler *profiler, MonoAssembly *assembly, int result) {
3324 MonoAssemblyName aname;
3325 LoadedElement *element;
3327 if (mono_assembly_fill_assembly_name (mono_assembly_get_image (assembly), &aname)) {
3328 name = mono_stringify_assembly_name (&aname);
3330 name = g_strdup_printf ("Dynamic assembly \"%p\"", assembly);
3333 element = loaded_element_load_end (profiler->loaded_assemblies, assembly, name);
3334 write_element_load_block (element, MONO_PROFILER_LOADED_EVENT_ASSEMBLY | RESULT_TO_LOAD_CODE (result), CURRENT_THREAD_ID ());
3339 assembly_start_unload (MonoProfiler *profiler, MonoAssembly *assembly) {
3341 loaded_element_unload_start (profiler->loaded_assemblies, assembly);
3342 writer_thread_flush_everything ();
3346 assembly_end_unload (MonoProfiler *profiler, MonoAssembly *assembly) {
3347 LoadedElement *element;
3350 element = loaded_element_unload_end (profiler->loaded_assemblies, assembly);
3351 write_element_unload_block (element, MONO_PROFILER_LOADED_EVENT_ASSEMBLY, CURRENT_THREAD_ID ());
3355 #if (DEBUG_LOGGING_PROFILER)
3357 class_event_code_to_string (MonoProfilerClassEvents code) {
3359 case MONO_PROFILER_EVENT_CLASS_LOAD: return "LOAD";
3360 case MONO_PROFILER_EVENT_CLASS_UNLOAD: return "UNLOAD";
3361 case MONO_PROFILER_EVENT_CLASS_ALLOCATION: return "ALLOCATION";
3362 case MONO_PROFILER_EVENT_CLASS_EXCEPTION: return "EXCEPTION";
3363 default: g_assert_not_reached (); return "";
3367 method_event_code_to_string (MonoProfilerClassEvents code) {
3369 case MONO_PROFILER_EVENT_METHOD_CALL: return "CALL";
3370 case MONO_PROFILER_EVENT_METHOD_JIT: return "JIT";
3371 case MONO_PROFILER_EVENT_METHOD_FREED: return "FREED";
3372 default: g_assert_not_reached (); return "";
3376 number_event_code_to_string (MonoProfilerEvents code) {
3378 case MONO_PROFILER_EVENT_THREAD: return "THREAD";
3379 case MONO_PROFILER_EVENT_GC_COLLECTION: return "GC_COLLECTION";
3380 case MONO_PROFILER_EVENT_GC_MARK: return "GC_MARK";
3381 case MONO_PROFILER_EVENT_GC_SWEEP: return "GC_SWEEP";
3382 case MONO_PROFILER_EVENT_GC_RESIZE: return "GC_RESIZE";
3383 case MONO_PROFILER_EVENT_GC_STOP_WORLD: return "GC_STOP_WORLD";
3384 case MONO_PROFILER_EVENT_GC_START_WORLD: return "GC_START_WORLD";
3385 default: g_assert_not_reached (); return "";
3389 event_result_to_string (MonoProfilerEventResult code) {
3391 case MONO_PROFILER_EVENT_RESULT_SUCCESS: return "SUCCESS";
3392 case MONO_PROFILER_EVENT_RESULT_FAILURE: return "FAILURE";
3393 default: g_assert_not_reached (); return "";
3397 event_kind_to_string (MonoProfilerEventKind code) {
3399 case MONO_PROFILER_EVENT_KIND_START: return "START";
3400 case MONO_PROFILER_EVENT_KIND_END: return "END";
3401 default: g_assert_not_reached (); return "";
3405 print_event_data (gsize thread_id, ProfilerEventData *event, guint64 value) {
3406 if (event->data_type == MONO_PROFILER_EVENT_DATA_TYPE_CLASS) {
3407 printf ("[TID %ld] CLASS[%p] event [%p] %s:%s:%s[%d-%d-%d] %ld (%s.%s)\n",
3409 event->data.address,
3411 class_event_code_to_string (event->code & ~MONO_PROFILER_EVENT_RESULT_MASK),
3412 event_result_to_string (event->code & MONO_PROFILER_EVENT_RESULT_MASK),
3413 event_kind_to_string (event->kind),
3418 mono_class_get_namespace ((MonoClass*) event->data.address),
3419 mono_class_get_name ((MonoClass*) event->data.address));
3420 } else if (event->data_type == MONO_PROFILER_EVENT_DATA_TYPE_METHOD) {
3421 printf ("[TID %ld] METHOD[%p] event [%p] %s:%s:%s[%d-%d-%d] %ld (%s.%s:%s (?))\n",
3423 event->data.address,
3425 method_event_code_to_string (event->code & ~MONO_PROFILER_EVENT_RESULT_MASK),
3426 event_result_to_string (event->code & MONO_PROFILER_EVENT_RESULT_MASK),
3427 event_kind_to_string (event->kind),
3432 mono_class_get_namespace (mono_method_get_class ((MonoMethod*) event->data.address)),
3433 mono_class_get_name (mono_method_get_class ((MonoMethod*) event->data.address)),
3434 mono_method_get_name ((MonoMethod*) event->data.address));
3436 printf ("[TID %ld] NUMBER[%ld] event [%p] %s:%s[%d-%d-%d] %ld\n",
3438 (guint64) event->data.number,
3440 number_event_code_to_string (event->code),
3441 event_kind_to_string (event->kind),
3448 #define LOG_EVENT(tid,ev,val) print_event_data ((tid),(ev),(val))
3450 #define LOG_EVENT(tid,ev,val)
3453 #define RESULT_TO_EVENT_CODE(r) (((r)==MONO_PROFILE_OK)?MONO_PROFILER_EVENT_RESULT_SUCCESS:MONO_PROFILER_EVENT_RESULT_FAILURE)
3455 #define STORE_EVENT_ITEM_COUNTER(p,i,dt,c,k) do {\
3456 ProfilerPerThreadData *data;\
3457 ProfilerEventData *event;\
3460 GET_PROFILER_THREAD_DATA (data);\
3461 GET_NEXT_FREE_EVENT (data, event);\
3462 MONO_PROFILER_GET_CURRENT_COUNTER (counter);\
3463 event->data.address = (i);\
3464 event->data_type = (dt);\
3467 delta = counter - data->last_event_counter;\
3468 if (delta < MAX_EVENT_VALUE) {\
3469 event->value = delta;\
3471 ProfilerEventData *extension = data->next_free_event;\
3472 data->next_free_event ++;\
3473 event->value = MAX_EVENT_VALUE;\
3474 *(guint64*)extension = delta;\
3476 data->last_event_counter = counter;\
3477 LOG_EVENT (data->thread_id, event, delta);\
3479 #define STORE_EVENT_ITEM_VALUE(p,i,dt,c,k,v) do {\
3480 ProfilerPerThreadData *data;\
3481 ProfilerEventData *event;\
3482 GET_PROFILER_THREAD_DATA (data);\
3483 GET_NEXT_FREE_EVENT (data, event);\
3484 event->data.address = (i);\
3485 event->data_type = (dt);\
3488 if ((v) < MAX_EVENT_VALUE) {\
3489 event->value = (v);\
3491 ProfilerEventData *extension = data->next_free_event;\
3492 data->next_free_event ++;\
3493 event->value = MAX_EVENT_VALUE;\
3494 *(guint64*)extension = (v);\
3496 LOG_EVENT (data->thread_id, event, (v));\
3498 #define STORE_EVENT_NUMBER_COUNTER(p,n,dt,c,k) do {\
3499 ProfilerPerThreadData *data;\
3500 ProfilerEventData *event;\
3503 GET_PROFILER_THREAD_DATA (data);\
3504 GET_NEXT_FREE_EVENT (data, event);\
3505 MONO_PROFILER_GET_CURRENT_COUNTER (counter);\
3506 event->data.number = (n);\
3507 event->data_type = (dt);\
3510 delta = counter - data->last_event_counter;\
3511 if (delta < MAX_EVENT_VALUE) {\
3512 event->value = delta;\
3514 ProfilerEventData *extension = data->next_free_event;\
3515 data->next_free_event ++;\
3516 event->value = MAX_EVENT_VALUE;\
3517 *(guint64*)extension = delta;\
3519 data->last_event_counter = counter;\
3520 LOG_EVENT (data->thread_id, event, delta);\
3522 #define STORE_EVENT_NUMBER_VALUE(p,n,dt,c,k,v) do {\
3523 ProfilerPerThreadData *data;\
3524 ProfilerEventData *event;\
3525 GET_PROFILER_THREAD_DATA (data);\
3526 GET_NEXT_FREE_EVENT (data, event);\
3527 event->data.number = (n);\
3528 event->data_type = (dt);\
3531 if ((v) < MAX_EVENT_VALUE) {\
3532 event->value = (v);\
3534 ProfilerEventData *extension = data->next_free_event;\
3535 data->next_free_event ++;\
3536 event->value = MAX_EVENT_VALUE;\
3537 *(guint64*)extension = (v);\
3539 LOG_EVENT (data->thread_id, event, (v));\
3544 class_start_load (MonoProfiler *profiler, MonoClass *klass) {
3545 STORE_EVENT_ITEM_COUNTER (profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_LOAD, MONO_PROFILER_EVENT_KIND_START);
3548 class_end_load (MonoProfiler *profiler, MonoClass *klass, int result) {
3549 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);
3552 class_start_unload (MonoProfiler *profiler, MonoClass *klass) {
3553 STORE_EVENT_ITEM_COUNTER (profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_UNLOAD, MONO_PROFILER_EVENT_KIND_START);
3556 class_end_unload (MonoProfiler *profiler, MonoClass *klass) {
3557 STORE_EVENT_ITEM_COUNTER (profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_UNLOAD, MONO_PROFILER_EVENT_KIND_END);
3561 method_start_jit (MonoProfiler *profiler, MonoMethod *method) {
3562 if (profiler->action_flags.jit_time) {
3563 STORE_EVENT_ITEM_COUNTER (profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_JIT, MONO_PROFILER_EVENT_KIND_START);
3567 method_end_jit (MonoProfiler *profiler, MonoMethod *method, int result) {
3568 if (profiler->action_flags.jit_time) {
3569 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);
3572 if (! profiler->writer_thread_enabled) {
3573 WRITER_EVENT_ENABLE_RAISE ();
3579 method_jit_result (MonoProfiler *prof, MonoMethod *method, MonoJitInfo* jinfo, int result) {
3580 if (profiler->action_flags.oprofile && (result == MONO_PROFILE_OK)) {
3581 MonoClass *klass = mono_method_get_class (method);
3582 char *signature = mono_signature_get_desc (mono_method_signature (method), TRUE);
3583 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);
3584 gpointer code_start = mono_jit_info_get_code_start (jinfo);
3585 int code_size = mono_jit_info_get_code_size (jinfo);
3587 if (op_write_native_code (name, code_start, code_size)) {
3588 g_warning ("Problem calling op_write_native_code\n");
3599 method_enter (MonoProfiler *profiler, MonoMethod *method) {
3600 CHECK_PROFILER_ENABLED ();
3601 STORE_EVENT_ITEM_COUNTER (profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_CALL, MONO_PROFILER_EVENT_KIND_START);
3604 method_leave (MonoProfiler *profiler, MonoMethod *method) {
3605 CHECK_PROFILER_ENABLED ();
3606 STORE_EVENT_ITEM_COUNTER (profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_CALL, MONO_PROFILER_EVENT_KIND_END);
3610 method_free (MonoProfiler *profiler, MonoMethod *method) {
3611 STORE_EVENT_ITEM_COUNTER (profiler, method, MONO_PROFILER_EVENT_DATA_TYPE_METHOD, MONO_PROFILER_EVENT_METHOD_FREED, 0);
3615 thread_start (MonoProfiler *profiler, gsize tid) {
3616 STORE_EVENT_NUMBER_COUNTER (profiler, tid, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, MONO_PROFILER_EVENT_THREAD, MONO_PROFILER_EVENT_KIND_START);
3619 thread_end (MonoProfiler *profiler, gsize tid) {
3620 STORE_EVENT_NUMBER_COUNTER (profiler, tid, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, MONO_PROFILER_EVENT_THREAD, MONO_PROFILER_EVENT_KIND_END);
3624 object_allocated (MonoProfiler *profiler, MonoObject *obj, MonoClass *klass) {
3625 ProfilerPerThreadData *thread_data;
3627 STORE_EVENT_ITEM_VALUE (profiler, klass, MONO_PROFILER_EVENT_DATA_TYPE_CLASS, MONO_PROFILER_EVENT_CLASS_ALLOCATION, 0, (guint64) mono_object_get_size (obj));
3628 if (profiler->action_flags.unreachable_objects || profiler->action_flags.heap_shot) {
3629 GET_PROFILER_THREAD_DATA (thread_data);
3630 STORE_ALLOCATED_OBJECT (thread_data, obj);
3635 statistical_call_chain (MonoProfiler *profiler, int call_chain_depth, guchar **ips, void *context) {
3636 MonoDomain *domain = mono_domain_get ();
3637 ProfilerStatisticalData *data;
3640 CHECK_PROFILER_ENABLED ();
3642 data = profiler->statistical_data;
3643 index = InterlockedIncrement (&data->next_free_index);
3645 if (index <= data->end_index) {
3646 int base_index = (index - 1) * (profiler->statistical_call_chain_depth + 1);
3647 int call_chain_index = 0;
3649 //printf ("[statistical_call_chain] (%d)\n", call_chain_depth);
3650 while (call_chain_index < call_chain_depth) {
3651 ProfilerStatisticalHit *hit = & (data->hits [base_index + call_chain_index]);
3652 //printf ("[statistical_call_chain] [%d] = %p\n", base_index + call_chain_index, ips [call_chain_index]);
3653 hit->address = (gpointer) ips [call_chain_index];
3654 hit->domain = domain;
3655 call_chain_index ++;
3657 while (call_chain_index <= profiler->statistical_call_chain_depth) {
3658 ProfilerStatisticalHit *hit = & (data->hits [base_index + call_chain_index]);
3659 //printf ("[statistical_call_chain] [%d] = NULL\n", base_index + call_chain_index);
3660 hit->address = NULL;
3662 call_chain_index ++;
3665 /* Check if we are the one that must swap the buffers */
3666 if (index == data->end_index + 1) {
3667 ProfilerStatisticalData *new_data;
3669 /* In the *impossible* case that the writer thread has not finished yet, */
3670 /* loop waiting for it and meanwhile lose all statistical events... */
3672 /* First, wait that it consumed the ready buffer */
3673 while (profiler->statistical_data_ready != NULL);
3674 /* Then, wait that it produced the free buffer */
3675 new_data = profiler->statistical_data_second_buffer;
3676 } while (new_data == NULL);
3678 profiler->statistical_data_ready = data;
3679 profiler->statistical_data = new_data;
3680 profiler->statistical_data_second_buffer = NULL;
3681 WRITER_EVENT_RAISE ();
3684 /* Loop again, hoping to acquire a free slot this time */
3687 } while (data == NULL);
3691 statistical_hit (MonoProfiler *profiler, guchar *ip, void *context) {
3692 MonoDomain *domain = mono_domain_get ();
3693 ProfilerStatisticalData *data;
3696 CHECK_PROFILER_ENABLED ();
3698 data = profiler->statistical_data;
3699 index = InterlockedIncrement (&data->next_free_index);
3701 if (index <= data->end_index) {
3702 ProfilerStatisticalHit *hit = & (data->hits [index - 1]);
3703 hit->address = (gpointer) ip;
3704 hit->domain = domain;
3706 /* Check if we are the one that must swap the buffers */
3707 if (index == data->end_index + 1) {
3708 ProfilerStatisticalData *new_data;
3710 /* In the *impossible* case that the writer thread has not finished yet, */
3711 /* loop waiting for it and meanwhile lose all statistical events... */
3713 /* First, wait that it consumed the ready buffer */
3714 while (profiler->statistical_data_ready != NULL);
3715 /* Then, wait that it produced the free buffer */
3716 new_data = profiler->statistical_data_second_buffer;
3717 } while (new_data == NULL);
3719 profiler->statistical_data_ready = data;
3720 profiler->statistical_data = new_data;
3721 profiler->statistical_data_second_buffer = NULL;
3722 WRITER_EVENT_RAISE ();
3725 /* Loop again, hoping to acquire a free slot this time */
3728 } while (data == NULL);
3731 static MonoProfilerEvents
3732 gc_event_code_from_profiler_event (MonoGCEvent event) {
3734 case MONO_GC_EVENT_START:
3735 case MONO_GC_EVENT_END:
3736 return MONO_PROFILER_EVENT_GC_COLLECTION;
3737 case MONO_GC_EVENT_MARK_START:
3738 case MONO_GC_EVENT_MARK_END:
3739 return MONO_PROFILER_EVENT_GC_MARK;
3740 case MONO_GC_EVENT_RECLAIM_START:
3741 case MONO_GC_EVENT_RECLAIM_END:
3742 return MONO_PROFILER_EVENT_GC_SWEEP;
3743 case MONO_GC_EVENT_PRE_STOP_WORLD:
3744 case MONO_GC_EVENT_POST_STOP_WORLD:
3745 return MONO_PROFILER_EVENT_GC_STOP_WORLD;
3746 case MONO_GC_EVENT_PRE_START_WORLD:
3747 case MONO_GC_EVENT_POST_START_WORLD:
3748 return MONO_PROFILER_EVENT_GC_START_WORLD;
3750 g_assert_not_reached ();
3755 static MonoProfilerEventKind
3756 gc_event_kind_from_profiler_event (MonoGCEvent event) {
3758 case MONO_GC_EVENT_START:
3759 case MONO_GC_EVENT_MARK_START:
3760 case MONO_GC_EVENT_RECLAIM_START:
3761 case MONO_GC_EVENT_PRE_STOP_WORLD:
3762 case MONO_GC_EVENT_PRE_START_WORLD:
3763 return MONO_PROFILER_EVENT_KIND_START;
3764 case MONO_GC_EVENT_END:
3765 case MONO_GC_EVENT_MARK_END:
3766 case MONO_GC_EVENT_RECLAIM_END:
3767 case MONO_GC_EVENT_POST_START_WORLD:
3768 case MONO_GC_EVENT_POST_STOP_WORLD:
3769 return MONO_PROFILER_EVENT_KIND_END;
3771 g_assert_not_reached ();
3776 #define HEAP_SHOT_COMMAND_FILE_MAX_LENGTH 64
3778 profiler_heap_shot_process_command_file (void) {
3779 //FIXME: Port to Windows as well
3780 struct stat stat_buf;
3782 char buffer [HEAP_SHOT_COMMAND_FILE_MAX_LENGTH + 1];
3784 if (profiler->heap_shot_command_file_name == NULL)
3786 if (stat (profiler->heap_shot_command_file_name, &stat_buf) != 0)
3788 if (stat_buf.st_size > HEAP_SHOT_COMMAND_FILE_MAX_LENGTH)
3790 if ((stat_buf.st_mtim.tv_sec * 1000000) < profiler->heap_shot_command_file_access_time)
3793 fd = open (profiler->heap_shot_command_file_name, O_RDONLY);
3797 if (read (fd, &(buffer [0]), stat_buf.st_size) != stat_buf.st_size) {
3800 buffer [stat_buf.st_size] = 0;
3801 profiler->dump_next_heap_snapshots = atoi (buffer);
3802 MONO_PROFILER_GET_CURRENT_TIME (profiler->heap_shot_command_file_access_time);
3809 dump_current_heap_snapshot (void) {
3812 if (profiler->heap_shot_was_signalled) {
3815 profiler_heap_shot_process_command_file ();
3816 if (profiler->dump_next_heap_snapshots > 0) {
3817 profiler->dump_next_heap_snapshots--;
3819 } else if (profiler->dump_next_heap_snapshots < 0) {
3830 profiler_heap_buffers_setup (ProfilerHeapShotHeapBuffers *heap) {
3831 heap->buffers = g_new (ProfilerHeapShotHeapBuffer, 1);
3832 heap->buffers->previous = NULL;
3833 heap->buffers->next = NULL;
3834 heap->buffers->start_slot = &(heap->buffers->buffer [0]);
3835 heap->buffers->end_slot = &(heap->buffers->buffer [PROFILER_HEAP_SHOT_HEAP_BUFFER_SIZE]);
3836 heap->last = heap->buffers;
3837 heap->current = heap->buffers;
3838 heap->first_free_slot = & (heap->buffers->buffer [0]);
3841 profiler_heap_buffers_clear (ProfilerHeapShotHeapBuffers *heap) {
3842 heap->buffers = NULL;
3844 heap->current = NULL;
3845 heap->first_free_slot = NULL;
3848 profiler_heap_buffers_free (ProfilerHeapShotHeapBuffers *heap) {
3849 ProfilerHeapShotHeapBuffer *current = heap->buffers;
3850 while (current != NULL) {
3851 ProfilerHeapShotHeapBuffer *next = current->next;
3855 profiler_heap_buffers_clear (heap);
3859 report_object_references (gpointer *start, ClassIdMappingElement *layout, ProfilerHeapShotWriteJob *job) {
3860 int reported_references = 0;
3863 for (slot = 0; slot < layout->data.layout.slots; slot ++) {
3864 gboolean slot_has_reference;
3865 if (layout->data.layout.slots <= CLASS_LAYOUT_PACKED_BITMAP_SIZE) {
3866 if (layout->data.bitmap.compact & (((guint64)1) << slot)) {
3867 slot_has_reference = TRUE;
3869 slot_has_reference = FALSE;
3872 if (layout->data.bitmap.extended [slot >> 3] & (1 << (slot & 7))) {
3873 slot_has_reference = TRUE;
3875 slot_has_reference = FALSE;
3879 if (slot_has_reference) {
3880 gpointer field = start [slot];
3882 if ((field != NULL) && mono_object_is_alive (field)) {
3883 reported_references ++;
3884 WRITE_HEAP_SHOT_JOB_VALUE (job, field);
3889 return reported_references;
3893 profiler_heap_report_object_reachable (ProfilerHeapShotWriteJob *job, MonoObject *obj) {
3895 MonoClass *klass = mono_object_get_class (obj);
3896 ClassIdMappingElement *class_id = class_id_mapping_element_get (klass);
3897 if (class_id == NULL) {
3898 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));
3900 g_assert (class_id != NULL);
3902 if (job->summary.capacity > 0) {
3903 guint32 id = class_id->id;
3904 g_assert (id < job->summary.capacity);
3906 job->summary.per_class_data [id].reachable.instances ++;
3907 job->summary.per_class_data [id].reachable.bytes += mono_object_get_size (obj);
3909 if (profiler->action_flags.heap_shot && job->dump_heap_data) {
3910 int reference_counter = 0;
3911 gpointer *reference_counter_location;
3913 WRITE_HEAP_SHOT_JOB_VALUE_WITH_CODE (job, obj, HEAP_CODE_OBJECT);
3914 #if DEBUG_HEAP_PROFILER
3915 printf ("profiler_heap_report_object_reachable: reported object %p at cursor %p\n", obj, (job->cursor - 1));
3917 WRITE_HEAP_SHOT_JOB_VALUE (job, NULL);
3918 reference_counter_location = job->cursor - 1;
3920 if (mono_class_get_rank (klass)) {
3921 MonoArray *array = (MonoArray *) obj;
3922 MonoClass *element_class = mono_class_get_element_class (klass);
3923 ClassIdMappingElement *element_id = class_id_mapping_element_get (element_class);
3925 g_assert (element_id != NULL);
3926 if (element_id->data.layout.slots == CLASS_LAYOUT_NOT_INITIALIZED) {
3927 class_id_mapping_element_build_layout_bitmap (element_class, element_id);
3929 if (! mono_class_is_valuetype (element_class)) {
3930 int length = mono_array_length (array);
3932 for (i = 0; i < length; i++) {
3933 MonoObject *array_element = mono_array_get (array, MonoObject*, i);
3934 if ((array_element != NULL) && mono_object_is_alive (array_element)) {
3935 reference_counter ++;
3936 WRITE_HEAP_SHOT_JOB_VALUE (job, array_element);
3939 } else if (element_id->data.layout.references > 0) {
3940 int length = mono_array_length (array);
3941 int array_element_size = mono_array_element_size (klass);
3943 for (i = 0; i < length; i++) {
3944 gpointer array_element_address = mono_array_addr_with_size (array, array_element_size, i);
3945 reference_counter += report_object_references (array_element_address, element_id, job);
3949 if (class_id->data.layout.slots == CLASS_LAYOUT_NOT_INITIALIZED) {
3950 class_id_mapping_element_build_layout_bitmap (klass, class_id);
3952 if (class_id->data.layout.references > 0) {
3953 reference_counter += report_object_references ((gpointer)(((char*)obj) + sizeof (MonoObject)), class_id, job);
3957 *reference_counter_location = GINT_TO_POINTER (reference_counter);
3958 #if DEBUG_HEAP_PROFILER
3959 printf ("profiler_heap_report_object_reachable: updated reference_counter_location %p with value %d\n", reference_counter_location, reference_counter);
3965 profiler_heap_report_object_unreachable (ProfilerHeapShotWriteJob *job, MonoObject *obj) {
3967 MonoClass *klass = mono_object_get_class (obj);
3968 guint32 size = mono_object_get_size (obj);
3970 if (job->summary.capacity > 0) {
3971 ClassIdMappingElement *class_id = class_id_mapping_element_get (klass);
3974 if (class_id == NULL) {
3975 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));
3977 g_assert (class_id != NULL);
3979 g_assert (id < job->summary.capacity);
3981 job->summary.per_class_data [id].unreachable.instances ++;
3982 job->summary.per_class_data [id].unreachable.bytes += size;
3984 if (profiler->action_flags.unreachable_objects && job->dump_heap_data) {
3985 #if DEBUG_HEAP_PROFILER
3986 printf ("profiler_heap_report_object_unreachable: at job %p writing klass %p\n", job, klass);
3988 WRITE_HEAP_SHOT_JOB_VALUE_WITH_CODE (job, klass, HEAP_CODE_FREE_OBJECT_CLASS);
3990 #if DEBUG_HEAP_PROFILER
3991 printf ("profiler_heap_report_object_unreachable: at job %p writing size %p\n", job, GUINT_TO_POINTER (size));
3993 WRITE_HEAP_SHOT_JOB_VALUE (job, GUINT_TO_POINTER (size));
3999 profiler_heap_add_object (ProfilerHeapShotHeapBuffers *heap, ProfilerHeapShotWriteJob *job, MonoObject *obj) {
4000 if (heap->first_free_slot >= heap->current->end_slot) {
4001 if (heap->current->next != NULL) {
4002 heap->current = heap->current->next;
4004 ProfilerHeapShotHeapBuffer *buffer = g_new (ProfilerHeapShotHeapBuffer, 1);
4005 buffer->previous = heap->last;
4006 buffer->next = NULL;
4007 buffer->start_slot = &(buffer->buffer [0]);
4008 buffer->end_slot = &(buffer->buffer [PROFILER_HEAP_SHOT_HEAP_BUFFER_SIZE]);
4009 heap->current = buffer;
4010 heap->last->next = buffer;
4011 heap->last = buffer;
4013 heap->first_free_slot = &(heap->current->buffer [0]);
4016 *(heap->first_free_slot) = obj;
4017 heap->first_free_slot ++;
4018 profiler_heap_report_object_reachable (job, obj);
4022 profiler_heap_pop_object_from_end (ProfilerHeapShotHeapBuffers *heap, ProfilerHeapShotWriteJob *job, MonoObject** current_slot) {
4023 while (heap->first_free_slot != current_slot) {
4026 if (heap->first_free_slot > heap->current->start_slot) {
4027 heap->first_free_slot --;
4029 heap->current = heap->current->previous;
4030 g_assert (heap->current != NULL);
4031 heap->first_free_slot = heap->current->end_slot - 1;
4034 obj = *(heap->first_free_slot);
4036 if (mono_object_is_alive (obj)) {
4037 profiler_heap_report_object_reachable (job, obj);
4040 profiler_heap_report_object_unreachable (job, obj);
4047 profiler_heap_scan (ProfilerHeapShotHeapBuffers *heap, ProfilerHeapShotWriteJob *job) {
4048 ProfilerHeapShotHeapBuffer *current_buffer = heap->buffers;
4049 MonoObject** current_slot = current_buffer->start_slot;
4051 while (current_slot != heap->first_free_slot) {
4052 MonoObject *obj = *current_slot;
4053 if (mono_object_is_alive (obj)) {
4054 profiler_heap_report_object_reachable (job, obj);
4056 profiler_heap_report_object_unreachable (job, obj);
4057 *current_slot = profiler_heap_pop_object_from_end (heap, job, current_slot);
4060 if (*current_slot != NULL) {
4063 if (current_slot == current_buffer->end_slot) {
4064 current_buffer = current_buffer->next;
4065 g_assert (current_buffer != NULL);
4066 current_slot = current_buffer->start_slot;
4072 static inline gboolean
4073 heap_shot_write_job_should_be_created (gboolean dump_heap_data) {
4074 return dump_heap_data || profiler->action_flags.unreachable_objects || profiler->action_flags.collection_summary;
4078 handle_heap_profiling (MonoProfiler *profiler, MonoGCEvent ev) {
4079 static gboolean dump_heap_data;
4082 case MONO_GC_EVENT_PRE_STOP_WORLD:
4083 // Get the lock, so we are sure nobody is flushing events during the collection,
4084 // and we can update all mappings (building the class descriptors).
4087 case MONO_GC_EVENT_POST_STOP_WORLD:
4088 dump_heap_data = dump_current_heap_snapshot ();
4089 if (heap_shot_write_job_should_be_created (dump_heap_data)) {
4090 ProfilerPerThreadData *data;
4091 // Update all mappings, so that we have built all the class descriptors.
4092 flush_all_mappings ();
4093 // Also write all event buffers, so that allocations are recorded.
4094 for (data = profiler->per_thread_data; data != NULL; data = data->next) {
4095 write_thread_data_block (data);
4101 case MONO_GC_EVENT_MARK_END: {
4102 ProfilerHeapShotWriteJob *job;
4103 ProfilerPerThreadData *data;
4105 if (heap_shot_write_job_should_be_created (dump_heap_data)) {
4106 job = profiler_heap_shot_write_job_new (profiler->heap_shot_was_signalled, dump_heap_data, profiler->garbage_collection_counter);
4107 profiler->heap_shot_was_signalled = FALSE;
4108 MONO_PROFILER_GET_CURRENT_COUNTER (job->start_counter);
4109 MONO_PROFILER_GET_CURRENT_TIME (job->start_time);
4114 profiler_heap_scan (&(profiler->heap), job);
4116 for (data = profiler->per_thread_data; data != NULL; data = data->next) {
4117 ProfilerHeapShotObjectBuffer *buffer;
4118 for (buffer = data->heap_shot_object_buffers; buffer != NULL; buffer = buffer->next) {
4119 MonoObject **cursor;
4120 for (cursor = buffer->first_unprocessed_slot; cursor < buffer->next_free_slot; cursor ++) {
4121 MonoObject *obj = *cursor;
4122 #if DEBUG_HEAP_PROFILER
4123 printf ("gc_event: in object buffer %p(%p-%p) cursor at %p has object %p ", buffer, &(buffer->buffer [0]), buffer->end, cursor, obj);
4125 if (mono_object_is_alive (obj)) {
4126 #if DEBUG_HEAP_PROFILER
4127 printf ("(object is alive, adding to heap)\n");
4129 profiler_heap_add_object (&(profiler->heap), job, obj);
4131 #if DEBUG_HEAP_PROFILER
4132 printf ("(object is unreachable, reporting in job)\n");
4134 profiler_heap_report_object_unreachable (job, obj);
4137 buffer->first_unprocessed_slot = cursor;
4142 MONO_PROFILER_GET_CURRENT_COUNTER (job->end_counter);
4143 MONO_PROFILER_GET_CURRENT_TIME (job->end_time);
4145 profiler_add_heap_shot_write_job (job);
4146 profiler_free_heap_shot_write_jobs ();
4147 WRITER_EVENT_RAISE ();
4157 gc_event (MonoProfiler *profiler, MonoGCEvent ev, int generation) {
4158 gboolean do_heap_profiling = profiler->action_flags.unreachable_objects || profiler->action_flags.heap_shot;
4159 guint32 event_value;
4161 if (ev == MONO_GC_EVENT_START) {
4162 profiler->garbage_collection_counter ++;
4165 event_value = (profiler->garbage_collection_counter << 8) | generation;
4167 if (do_heap_profiling && (ev == MONO_GC_EVENT_POST_STOP_WORLD)) {
4168 handle_heap_profiling (profiler, ev);
4170 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));
4171 if (do_heap_profiling && (ev != MONO_GC_EVENT_POST_STOP_WORLD)) {
4172 handle_heap_profiling (profiler, ev);
4177 gc_resize (MonoProfiler *profiler, gint64 new_size) {
4178 profiler->garbage_collection_counter ++;
4179 STORE_EVENT_NUMBER_VALUE (profiler, new_size, MONO_PROFILER_EVENT_DATA_TYPE_OTHER, MONO_PROFILER_EVENT_GC_RESIZE, 0, profiler->garbage_collection_counter);
4182 /* called at the end of the program */
4184 profiler_shutdown (MonoProfiler *prof)
4186 ProfilerPerThreadData* current_thread_data;
4188 LOG_WRITER_THREAD ("profiler_shutdown: zeroing relevant flags");
4189 mono_profiler_set_events (0);
4190 //profiler->flags = 0;
4191 //profiler->action_flags.unreachable_objects = FALSE;
4192 //profiler->action_flags.heap_shot = FALSE;
4194 LOG_WRITER_THREAD ("profiler_shutdown: asking stats thread to exit");
4195 profiler->terminate_writer_thread = TRUE;
4196 WRITER_EVENT_RAISE ();
4197 LOG_WRITER_THREAD ("profiler_shutdown: waiting for stats thread to exit");
4198 WAIT_WRITER_THREAD ();
4199 LOG_WRITER_THREAD ("profiler_shutdown: stats thread should be dead now");
4200 WRITER_EVENT_DESTROY ();
4203 writer_thread_flush_everything ();
4204 MONO_PROFILER_GET_CURRENT_TIME (profiler->end_time);
4205 MONO_PROFILER_GET_CURRENT_COUNTER (profiler->end_counter);
4211 g_free (profiler->file_name);
4212 if (profiler->file_name_suffix != NULL) {
4213 g_free (profiler->file_name_suffix);
4216 method_id_mapping_destroy (profiler->methods);
4217 class_id_mapping_destroy (profiler->classes);
4218 g_hash_table_destroy (profiler->loaded_assemblies);
4219 g_hash_table_destroy (profiler->loaded_modules);
4220 g_hash_table_destroy (profiler->loaded_appdomains);
4222 FREE_PROFILER_THREAD_DATA ();
4224 for (current_thread_data = profiler->per_thread_data; current_thread_data != NULL; current_thread_data = current_thread_data->next) {
4225 profiler_per_thread_data_destroy (current_thread_data);
4227 if (profiler->statistical_data != NULL) {
4228 profiler_statistical_data_destroy (profiler->statistical_data);
4230 if (profiler->statistical_data_ready != NULL) {
4231 profiler_statistical_data_destroy (profiler->statistical_data_ready);
4233 if (profiler->statistical_data_second_buffer != NULL) {
4234 profiler_statistical_data_destroy (profiler->statistical_data_second_buffer);
4236 if (profiler->executable_regions != NULL) {
4237 profiler_executable_memory_regions_destroy (profiler->executable_regions);
4240 profiler_heap_buffers_free (&(profiler->heap));
4241 if (profiler->heap_shot_command_file_name != NULL) {
4242 g_free (profiler->heap_shot_command_file_name);
4245 profiler_free_write_buffers ();
4246 profiler_destroy_heap_shot_write_jobs ();
4248 DELETE_PROFILER_MUTEX ();
4251 if (profiler->action_flags.oprofile) {
4260 #ifndef PLATFORM_WIN32
4262 parse_signal_name (const char *signal_name) {
4263 if (! strcasecmp (signal_name, "SIGUSR1")) {
4265 } else if (! strcasecmp (signal_name, "SIGUSR2")) {
4267 } else if (! strcasecmp (signal_name, "SIGPROF")) {
4270 return atoi (signal_name);
4274 check_signal_number (int signal_number) {
4275 if (((signal_number == SIGPROF) && ! (profiler->flags & MONO_PROFILE_STATISTICAL)) ||
4276 (signal_number == SIGUSR1) ||
4277 (signal_number == SIGUSR2)) {
4285 #define DEFAULT_ARGUMENTS "s"
4287 setup_user_options (const char *arguments) {
4288 gchar **arguments_array, **current_argument;
4289 #ifndef PLATFORM_WIN32
4290 int gc_request_signal_number = 0;
4291 int toggle_signal_number = 0;
4293 detect_fast_timer ();
4295 profiler->file_name = NULL;
4296 profiler->file_name_suffix = NULL;
4297 profiler->per_thread_buffer_size = 10000;
4298 profiler->statistical_buffer_size = 10000;
4299 profiler->statistical_call_chain_depth = 0;
4300 profiler->write_buffer_size = 1024;
4301 profiler->heap_shot_command_file_name = NULL;
4302 profiler->dump_next_heap_snapshots = 0;
4303 profiler->heap_shot_command_file_access_time = 0;
4304 profiler->heap_shot_was_signalled = FALSE;
4305 profiler->flags = MONO_PROFILE_APPDOMAIN_EVENTS|
4306 MONO_PROFILE_ASSEMBLY_EVENTS|
4307 MONO_PROFILE_MODULE_EVENTS|
4308 MONO_PROFILE_CLASS_EVENTS|
4309 MONO_PROFILE_METHOD_EVENTS|
4310 MONO_PROFILE_JIT_COMPILATION;
4311 profiler->profiler_enabled = TRUE;
4313 if (arguments == NULL) {
4314 arguments = DEFAULT_ARGUMENTS;
4315 } else if (strstr (arguments, ":")) {
4316 arguments = strstr (arguments, ":") + 1;
4317 if (arguments [0] == 0) {
4318 arguments = DEFAULT_ARGUMENTS;
4322 arguments_array = g_strsplit (arguments, ",", -1);
4324 for (current_argument = arguments_array; ((current_argument != NULL) && (current_argument [0] != 0)); current_argument ++) {
4325 char *argument = *current_argument;
4326 char *equals = strstr (argument, "=");
4328 if (equals != NULL) {
4329 int equals_position = equals - argument;
4331 if (! (strncmp (argument, "per-thread-buffer-size", equals_position) && strncmp (argument, "tbs", equals_position))) {
4332 int value = atoi (equals + 1);
4334 profiler->per_thread_buffer_size = value;
4336 } else if (! (strncmp (argument, "statistical", equals_position) && strncmp (argument, "stat", equals_position) && strncmp (argument, "s", equals_position))) {
4337 int value = atoi (equals + 1);
4342 profiler->statistical_call_chain_depth = value;
4343 profiler->flags |= MONO_PROFILE_STATISTICAL|MONO_PROFILE_JIT_COMPILATION;
4344 profiler->action_flags.jit_time = TRUE;
4346 } else if (! (strncmp (argument, "statistical-thread-buffer-size", equals_position) && strncmp (argument, "sbs", equals_position))) {
4347 int value = atoi (equals + 1);
4349 profiler->statistical_buffer_size = value;
4351 } else if (! (strncmp (argument, "write-buffer-size", equals_position) && strncmp (argument, "wbs", equals_position))) {
4352 int value = atoi (equals + 1);
4354 profiler->write_buffer_size = value;
4356 } else if (! (strncmp (argument, "output", equals_position) && strncmp (argument, "out", equals_position) && strncmp (argument, "o", equals_position) && strncmp (argument, "O", equals_position))) {
4357 if (strlen (equals + 1) > 0) {
4358 profiler->file_name = g_strdup (equals + 1);
4360 } else if (! (strncmp (argument, "output-suffix", equals_position) && strncmp (argument, "suffix", equals_position) && strncmp (argument, "os", equals_position) && strncmp (argument, "OS", equals_position))) {
4361 if (strlen (equals + 1) > 0) {
4362 profiler->file_name_suffix = g_strdup (equals + 1);
4364 } else if (! (strncmp (argument, "gc-commands", equals_position) && strncmp (argument, "gc-c", equals_position) && strncmp (argument, "gcc", equals_position))) {
4365 if (strlen (equals + 1) > 0) {
4366 profiler->heap_shot_command_file_name = g_strdup (equals + 1);
4368 } else if (! (strncmp (argument, "gc-dumps", equals_position) && strncmp (argument, "gc-d", equals_position) && strncmp (argument, "gcd", equals_position))) {
4369 if (strlen (equals + 1) > 0) {
4370 profiler->dump_next_heap_snapshots = atoi (equals + 1);
4372 #ifndef PLATFORM_WIN32
4373 } else if (! (strncmp (argument, "gc-signal", equals_position) && strncmp (argument, "gc-s", equals_position) && strncmp (argument, "gcs", equals_position))) {
4374 if (strlen (equals + 1) > 0) {
4375 char *signal_name = equals + 1;
4376 gc_request_signal_number = parse_signal_name (signal_name);
4378 } else if (! (strncmp (argument, "toggle-signal", equals_position) && strncmp (argument, "ts", equals_position))) {
4379 if (strlen (equals + 1) > 0) {
4380 char *signal_name = equals + 1;
4381 toggle_signal_number = parse_signal_name (signal_name);
4385 g_warning ("Cannot parse valued argument %s\n", argument);
4388 if (! (strcmp (argument, "jit") && strcmp (argument, "j"))) {
4389 profiler->flags |= MONO_PROFILE_JIT_COMPILATION;
4390 profiler->action_flags.jit_time = TRUE;
4391 } else if (! (strcmp (argument, "allocations") && strcmp (argument, "alloc") && strcmp (argument, "a"))) {
4392 profiler->flags |= MONO_PROFILE_ALLOCATIONS|MONO_PROFILE_GC;
4393 } else if (! (strcmp (argument, "gc") && strcmp (argument, "g"))) {
4394 profiler->flags |= MONO_PROFILE_GC;
4395 } else if (! (strcmp (argument, "allocations-summary") && strcmp (argument, "as"))) {
4396 profiler->flags |= MONO_PROFILE_ALLOCATIONS|MONO_PROFILE_GC;
4397 profiler->action_flags.collection_summary = TRUE;
4398 } else if (! (strcmp (argument, "heap-shot") && strcmp (argument, "heap") && strcmp (argument, "h"))) {
4399 profiler->flags |= MONO_PROFILE_ALLOCATIONS|MONO_PROFILE_GC;
4400 profiler->action_flags.unreachable_objects = TRUE;
4401 profiler->action_flags.heap_shot = TRUE;
4402 } else if (! (strcmp (argument, "unreachable") && strcmp (argument, "free") && strcmp (argument, "f"))) {
4403 profiler->flags |= MONO_PROFILE_ALLOCATIONS|MONO_PROFILE_GC;
4404 profiler->action_flags.unreachable_objects = TRUE;
4405 } else if (! (strcmp (argument, "threads") && strcmp (argument, "t"))) {
4406 profiler->flags |= MONO_PROFILE_THREADS;
4407 } else if (! (strcmp (argument, "enter-leave") && strcmp (argument, "calls") && strcmp (argument, "c"))) {
4408 profiler->flags |= MONO_PROFILE_ENTER_LEAVE;
4409 } else if (! (strcmp (argument, "statistical") && strcmp (argument, "stat") && strcmp (argument, "s"))) {
4410 profiler->flags |= MONO_PROFILE_STATISTICAL;
4411 profiler->action_flags.jit_time = TRUE;
4412 } else if (! (strcmp (argument, "start-enabled") && strcmp (argument, "se"))) {
4413 profiler->profiler_enabled = TRUE;
4414 } else if (! (strcmp (argument, "start-disabled") && strcmp (argument, "sd"))) {
4415 profiler->profiler_enabled = FALSE;
4416 } else if (! (strcmp (argument, "force-accurate-timer") && strcmp (argument, "fac"))) {
4417 use_fast_timer = FALSE;
4419 } else if (! (strcmp (argument, "oprofile") && strcmp (argument, "oprof"))) {
4420 profiler->flags |= MONO_PROFILE_JIT_COMPILATION;
4421 profiler->action_flags.oprofile = TRUE;
4422 if (op_open_agent ()) {
4423 g_warning ("Problem calling op_open_agent\n");
4426 } else if (strcmp (argument, "logging")) {
4427 g_warning ("Cannot parse flag argument %s\n", argument);
4432 g_free (arguments_array);
4434 #ifndef PLATFORM_WIN32
4435 if (gc_request_signal_number != 0) {
4436 if (check_signal_number (gc_request_signal_number) && (gc_request_signal_number != toggle_signal_number)) {
4437 add_gc_request_handler (gc_request_signal_number);
4439 g_error ("Cannot use signal %d", gc_request_signal_number);
4442 if (toggle_signal_number != 0) {
4443 if (check_signal_number (toggle_signal_number) && (toggle_signal_number != gc_request_signal_number)) {
4444 add_toggle_handler (toggle_signal_number);
4446 g_error ("Cannot use signal %d", gc_request_signal_number);
4451 if (profiler->file_name == NULL) {
4452 char *program_name = g_get_prgname ();
4454 if (program_name != NULL) {
4455 char *name_buffer = g_strdup (program_name);
4456 char *name_start = name_buffer;
4459 /* Jump over the last '/' */
4460 cursor = strrchr (name_buffer, '/');
4461 if (cursor == NULL) {
4462 cursor = name_buffer;
4466 name_start = cursor;
4468 /* Then jump over the last '\\' */
4469 cursor = strrchr (name_start, '\\');
4470 if (cursor == NULL) {
4471 cursor = name_start;
4475 name_start = cursor;
4477 /* Finally, find the last '.' */
4478 cursor = strrchr (name_start, '.');
4479 if (cursor != NULL) {
4483 if (profiler->file_name_suffix == NULL) {
4484 profiler->file_name = g_strdup_printf ("%s.mprof", name_start);
4486 profiler->file_name = g_strdup_printf ("%s-%s.mprof", name_start, profiler->file_name_suffix);
4488 g_free (name_buffer);
4490 profiler->file_name = g_strdup_printf ("%s.mprof", "profiler-log");
4496 thread_detach_callback (MonoThread *thread) {
4497 LOG_WRITER_THREAD ("thread_detach_callback: asking writer thread to detach");
4498 profiler->detach_writer_thread = TRUE;
4499 WRITER_EVENT_RAISE ();
4500 LOG_WRITER_THREAD ("thread_detach_callback: done");
4505 data_writer_thread (gpointer nothing) {
4506 static gboolean thread_attached = FALSE;
4507 static gboolean thread_detached = FALSE;
4508 static MonoThread *this_thread = NULL;
4510 WRITER_EVENT_ENABLE_WAIT ();
4511 if (! profiler->terminate_writer_thread) {
4512 MonoDomain * root_domain = mono_get_root_domain ();
4513 if (root_domain != NULL) {
4514 LOG_WRITER_THREAD ("data_writer_thread: attaching thread");
4515 this_thread = mono_thread_attach (root_domain);
4516 mono_thread_set_manage_callback (this_thread, thread_detach_callback);
4517 thread_attached = TRUE;
4519 g_error ("Cannot get root domain\n");
4522 /* Execution was too short, pretend we attached and detached. */
4523 thread_attached = TRUE;
4524 thread_detached = TRUE;
4526 profiler->writer_thread_enabled = TRUE;
4529 ProfilerStatisticalData *statistical_data;
4532 LOG_WRITER_THREAD ("data_writer_thread: going to sleep");
4533 WRITER_EVENT_WAIT ();
4534 LOG_WRITER_THREAD ("data_writer_thread: just woke up");
4536 if (profiler->heap_shot_was_signalled) {
4537 LOG_WRITER_THREAD ("data_writer_thread: starting requested collection");
4538 mono_gc_collect (mono_gc_max_generation ());
4539 LOG_WRITER_THREAD ("data_writer_thread: requested collection done");
4542 statistical_data = profiler->statistical_data_ready;
4543 done = (statistical_data == NULL) && (profiler->heap_shot_write_jobs == NULL) && (profiler->writer_thread_flush_everything == FALSE);
4545 if ((!done) && thread_attached) {
4546 if (profiler->writer_thread_flush_everything) {
4547 if (! thread_detached) {
4548 LOG_WRITER_THREAD ("data_writer_thread: flushing everything...");
4549 flush_everything ();
4550 profiler->writer_thread_flush_everything = FALSE;
4551 WRITER_EVENT_DONE_RAISE ();
4552 LOG_WRITER_THREAD ("data_writer_thread: flushed everything.");
4554 LOG_WRITER_THREAD ("data_writer_thread: flushing requested, but thread is detached...");
4555 profiler->writer_thread_flush_everything = FALSE;
4556 WRITER_EVENT_DONE_RAISE ();
4557 LOG_WRITER_THREAD ("data_writer_thread: done event raised.");
4560 LOG_WRITER_THREAD ("data_writer_thread: acquiring lock and writing data");
4563 // This makes sure that all method ids are in place
4564 LOG_WRITER_THREAD ("data_writer_thread: writing mapping...");
4565 flush_all_mappings ();
4566 LOG_WRITER_THREAD ("data_writer_thread: wrote mapping");
4568 if ((statistical_data != NULL) && ! thread_detached) {
4569 LOG_WRITER_THREAD ("data_writer_thread: writing statistical data...");
4570 profiler->statistical_data_ready = NULL;
4571 write_statistical_data_block (statistical_data);
4572 statistical_data->next_free_index = 0;
4573 statistical_data->first_unwritten_index = 0;
4574 profiler->statistical_data_second_buffer = statistical_data;
4575 LOG_WRITER_THREAD ("data_writer_thread: wrote statistical data");
4578 profiler_process_heap_shot_write_jobs ();
4581 LOG_WRITER_THREAD ("data_writer_thread: wrote data and released lock");
4584 if (profiler->writer_thread_flush_everything) {
4585 LOG_WRITER_THREAD ("data_writer_thread: flushing requested, but thread is not attached...");
4586 profiler->writer_thread_flush_everything = FALSE;
4587 WRITER_EVENT_DONE_RAISE ();
4588 LOG_WRITER_THREAD ("data_writer_thread: done event raised.");
4592 if (profiler->detach_writer_thread) {
4593 if (this_thread != NULL) {
4594 LOG_WRITER_THREAD ("data_writer_thread: detach requested, acquiring lock and flushing data");
4596 flush_everything ();
4598 LOG_WRITER_THREAD ("data_writer_thread: flushed data and released lock");
4599 LOG_WRITER_THREAD ("data_writer_thread: detaching thread");
4600 mono_thread_detach (this_thread);
4602 profiler->detach_writer_thread = FALSE;
4603 thread_detached = TRUE;
4605 LOG_WRITER_THREAD ("data_writer_thread: warning: thread has already been detached");
4609 if (profiler->terminate_writer_thread) {
4610 LOG_WRITER_THREAD ("data_writer_thread: exiting thread");
4611 CLEANUP_WRITER_THREAD ();
4619 mono_profiler_startup (const char *desc);
4621 /* the entry point (mono_profiler_load?) */
4623 mono_profiler_startup (const char *desc)
4625 profiler = g_new0 (MonoProfiler, 1);
4627 setup_user_options ((desc != NULL) ? desc : DEFAULT_ARGUMENTS);
4629 INITIALIZE_PROFILER_MUTEX ();
4630 MONO_PROFILER_GET_CURRENT_TIME (profiler->start_time);
4631 MONO_PROFILER_GET_CURRENT_COUNTER (profiler->start_counter);
4632 profiler->last_header_counter = 0;
4634 profiler->methods = method_id_mapping_new ();
4635 profiler->classes = class_id_mapping_new ();
4636 profiler->loaded_assemblies = g_hash_table_new_full (g_direct_hash, NULL, NULL, loaded_element_destroy);
4637 profiler->loaded_modules = g_hash_table_new_full (g_direct_hash, NULL, NULL, loaded_element_destroy);
4638 profiler->loaded_appdomains = g_hash_table_new_full (g_direct_hash, NULL, NULL, loaded_element_destroy);
4640 profiler->statistical_data = profiler_statistical_data_new (profiler);
4641 profiler->statistical_data_second_buffer = profiler_statistical_data_new (profiler);
4643 profiler->write_buffers = g_malloc (sizeof (ProfilerFileWriteBuffer) + PROFILER_FILE_WRITE_BUFFER_SIZE);
4644 profiler->write_buffers->next = NULL;
4645 profiler->current_write_buffer = profiler->write_buffers;
4646 profiler->current_write_position = 0;
4647 profiler->full_write_buffers = 0;
4649 profiler->executable_regions = profiler_executable_memory_regions_new (1, 1);
4651 profiler->executable_files.table = g_hash_table_new (g_str_hash, g_str_equal);
4652 profiler->executable_files.new_files = NULL;
4654 profiler->heap_shot_write_jobs = NULL;
4655 if (profiler->action_flags.unreachable_objects || profiler->action_flags.heap_shot) {
4656 profiler_heap_buffers_setup (&(profiler->heap));
4658 profiler_heap_buffers_clear (&(profiler->heap));
4660 profiler->garbage_collection_counter = 0;
4662 WRITER_EVENT_INIT ();
4663 LOG_WRITER_THREAD ("mono_profiler_startup: creating writer thread");
4664 CREATE_WRITER_THREAD (data_writer_thread);
4665 LOG_WRITER_THREAD ("mono_profiler_startup: created writer thread");
4667 ALLOCATE_PROFILER_THREAD_DATA ();
4671 write_intro_block ();
4673 mono_profiler_install (profiler, profiler_shutdown);
4675 mono_profiler_install_appdomain (appdomain_start_load, appdomain_end_load,
4676 appdomain_start_unload, appdomain_end_unload);
4677 mono_profiler_install_assembly (assembly_start_load, assembly_end_load,
4678 assembly_start_unload, assembly_end_unload);
4679 mono_profiler_install_module (module_start_load, module_end_load,
4680 module_start_unload, module_end_unload);
4681 mono_profiler_install_class (class_start_load, class_end_load,
4682 class_start_unload, class_end_unload);
4683 mono_profiler_install_jit_compile (method_start_jit, method_end_jit);
4684 mono_profiler_install_enter_leave (method_enter, method_leave);
4685 mono_profiler_install_method_free (method_free);
4686 mono_profiler_install_thread (thread_start, thread_end);
4687 mono_profiler_install_allocation (object_allocated);
4688 mono_profiler_install_statistical (statistical_hit);
4689 mono_profiler_install_statistical_call_chain (statistical_call_chain, profiler->statistical_call_chain_depth);
4690 mono_profiler_install_gc (gc_event, gc_resize);
4692 mono_profiler_install_jit_end (method_jit_result);
4695 mono_profiler_set_events (profiler->flags);