2 * mono-profiler-log.c: mono log profiler
5 * Paolo Molaro (lupus@ximian.com)
6 * Alex Rønne Petersen (alexrp@xamarin.com)
8 * Copyright 2010 Novell, Inc (http://www.novell.com)
9 * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
10 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
14 #include "../mini/jit.h"
15 #include "../metadata/metadata-internals.h"
16 #include <mono/metadata/profiler.h>
17 #include <mono/metadata/threads.h>
18 #include <mono/metadata/debug-helpers.h>
19 #include <mono/metadata/mono-config.h>
20 #include <mono/metadata/mono-gc.h>
21 #include <mono/metadata/mono-perfcounters.h>
22 #include <mono/metadata/appdomain.h>
23 #include <mono/metadata/assembly.h>
24 #include <mono/metadata/tokentype.h>
25 #include <mono/metadata/tabledefs.h>
26 #include <mono/utils/atomic.h>
27 #include <mono/utils/mono-membar.h>
28 #include <mono/utils/mono-mmap.h>
29 #include <mono/utils/mono-counters.h>
30 #include <mono/utils/mono-os-mutex.h>
31 #include <mono/utils/mono-os-semaphore.h>
32 #include <mono/utils/mono-conc-hashtable.h>
33 #include <mono/utils/mono-linked-list-set.h>
34 #include <mono/utils/lock-free-alloc.h>
35 #include <mono/utils/lock-free-queue.h>
36 #include <mono/utils/hazard-pointer.h>
37 #include <mono/utils/mono-threads.h>
38 #include <mono/utils/mono-threads-api.h>
46 #ifdef HAVE_SCHED_GETAFFINITY
52 #ifdef HAVE_SYS_TIME_H
55 #if defined(__APPLE__)
56 #include <mach/mach_time.h>
65 #ifdef HAVE_EXECINFO_H
72 #include <sys/types.h>
73 #include <sys/socket.h>
74 #include <netinet/in.h>
75 #include <sys/select.h>
83 #ifdef HAVE_SYS_STAT_H
87 #include "mono-profiler-log.h"
89 #if defined (HAVE_SYS_ZLIB)
93 #if defined(__linux__)
96 #include <sys/syscall.h>
98 #ifdef ENABLE_PERF_EVENTS
99 #include <linux/perf_event.h>
101 #define USE_PERF_EVENTS 1
103 static int read_perf_mmap (MonoProfiler
* prof
, int cpu
);
108 #define BUFFER_SIZE (4096 * 16)
110 /* Worst-case size in bytes of a 64-bit value encoded with LEB128. */
111 #define LEB128_SIZE 10
112 /* Size of a value encoded as a single byte. */
114 /* Size in bytes of the event prefix (ID + time). */
115 #define EVENT_SIZE (BYTE_SIZE + LEB128_SIZE)
117 static int nocalls
= 0;
118 static int notraces
= 0;
119 static int use_zip
= 0;
120 static int do_report
= 0;
121 static int do_heap_shot
= 0;
122 static int max_call_depth
= 100;
123 static volatile int runtime_inited
= 0;
124 static int command_port
= 0;
125 static int heapshot_requested
= 0;
126 static int sample_type
= 0;
127 static int sample_freq
= 0;
128 static int do_mono_sample
= 0;
129 static int in_shutdown
= 0;
130 static int do_debug
= 0;
131 static int do_counters
= 0;
132 static int do_coverage
= 0;
133 static gboolean debug_coverage
= FALSE
;
134 static MonoProfileSamplingMode sampling_mode
= MONO_PROFILER_STAT_MODE_PROCESS
;
135 static int max_allocated_sample_hits
;
137 // Statistics for internal profiler data structures.
138 static gint32 sample_allocations_ctr
,
139 buffer_allocations_ctr
;
141 // Statistics for profiler events.
142 static gint32 sync_points_ctr
,
151 gc_handle_creations_ctr
,
152 gc_handle_deletions_ctr
,
155 finalize_object_begins_ctr
,
156 finalize_object_ends_ctr
,
160 assembly_unloads_ctr
,
165 method_exception_exits_ctr
,
168 exception_throws_ctr
,
169 exception_clauses_ctr
,
170 monitor_contentions_ctr
,
171 monitor_acquisitions_ctr
,
172 monitor_failures_ctr
,
184 counter_descriptors_ctr
,
186 perfcounter_descriptors_ctr
,
187 perfcounter_samples_ctr
,
188 coverage_methods_ctr
,
189 coverage_statements_ctr
,
190 coverage_classes_ctr
,
191 coverage_assemblies_ctr
;
193 static MonoLinkedListSet profiler_thread_list
;
199 * The file is composed by a header followed by 0 or more buffers.
200 * Each buffer contains events that happened on a thread: for a given thread
201 * buffers that appear later in the file are guaranteed to contain events
202 * that happened later in time. Buffers from separate threads could be interleaved,
204 * Buffers are not required to be aligned.
207 * [id: 4 bytes] constant value: LOG_HEADER_ID
208 * [major: 1 byte] [minor: 1 byte] major and minor version of the log profiler
209 * [format: 1 byte] version of the data format for the rest of the file
210 * [ptrsize: 1 byte] size in bytes of a pointer in the profiled program
211 * [startup time: 8 bytes] time in milliseconds since the unix epoch when the program started
212 * [timer overhead: 4 bytes] approximate overhead in nanoseconds of the timer
213 * [flags: 4 bytes] file format flags, should be 0 for now
214 * [pid: 4 bytes] pid of the profiled process
215 * [port: 2 bytes] tcp port for server if != 0
216 * [args size: 4 bytes] size of args
217 * [args: string] arguments passed to the profiler
218 * [arch size: 4 bytes] size of arch
219 * [arch: string] architecture the profiler is running on
220 * [os size: 4 bytes] size of os
221 * [os: string] operating system the profiler is running on
223 * The multiple byte integers are in little-endian format.
226 * [buffer header] [event]*
227 * Buffers have a fixed-size header followed by 0 or more bytes of event data.
228 * Timing information and other values in the event data are usually stored
229 * as uleb128 or sleb128 integers. To save space, as noted for each item below,
230 * some data is represented as a difference between the actual value and
231 * either the last value of the same type (like for timing information) or
232 * as the difference from a value stored in a buffer header.
234 * For timing information the data is stored as uleb128, since timing
235 * increases in a monotonic way in each thread: the value is the number of
236 * nanoseconds to add to the last seen timing data in a buffer. The first value
237 * in a buffer will be calculated from the time_base field in the buffer head.
239 * Object or heap sizes are stored as uleb128.
240 * Pointer differences are stored as sleb128, instead.
242 * If an unexpected value is found, the rest of the buffer should be ignored,
243 * as generally the later values need the former to be interpreted correctly.
245 * buffer header format:
246 * [bufid: 4 bytes] constant value: BUF_ID
247 * [len: 4 bytes] size of the data following the buffer header
248 * [time_base: 8 bytes] time base in nanoseconds since an unspecified epoch
249 * [ptr_base: 8 bytes] base value for pointers
250 * [obj_base: 8 bytes] base value for object addresses
251 * [thread id: 8 bytes] system-specific thread ID (pthread_t for example)
252 * [method_base: 8 bytes] base value for MonoMethod pointers
255 * [extended info: upper 4 bits] [type: lower 4 bits]
256 * [time diff: uleb128] nanoseconds since last timing
258 * The data that follows depends on type and the extended info.
259 * Type is one of the enum values in mono-profiler-log.h: TYPE_ALLOC, TYPE_GC,
260 * TYPE_METADATA, TYPE_METHOD, TYPE_EXCEPTION, TYPE_MONITOR, TYPE_HEAP.
261 * The extended info bits are interpreted based on type, see
262 * each individual event description below.
263 * strings are represented as a 0-terminated utf8 sequence.
266 * [num: uleb128] number of frames following
267 * [frame: sleb128]* mum MonoMethod* as a pointer difference from the last such
268 * pointer or the buffer method_base
272 * exinfo: flags: TYPE_ALLOC_BT
273 * [ptr: sleb128] class as a byte difference from ptr_base
274 * [obj: sleb128] object address as a byte difference from obj_base
275 * [size: uleb128] size of the object in the heap
276 * If the TYPE_ALLOC_BT flag is set, a backtrace follows.
280 * exinfo: one of TYPE_GC_EVENT, TYPE_GC_RESIZE, TYPE_GC_MOVE, TYPE_GC_HANDLE_CREATED[_BT],
281 * TYPE_GC_HANDLE_DESTROYED[_BT], TYPE_GC_FINALIZE_START, TYPE_GC_FINALIZE_END,
282 * TYPE_GC_FINALIZE_OBJECT_START, TYPE_GC_FINALIZE_OBJECT_END
283 * if exinfo == TYPE_GC_RESIZE
284 * [heap_size: uleb128] new heap size
285 * if exinfo == TYPE_GC_EVENT
286 * [event type: byte] GC event (MONO_GC_EVENT_* from profiler.h)
287 * [generation: byte] GC generation event refers to
288 * if exinfo == TYPE_GC_MOVE
289 * [num_objects: uleb128] number of object moves that follow
290 * [objaddr: sleb128]+ num_objects object pointer differences from obj_base
291 * num is always an even number: the even items are the old
292 * addresses, the odd numbers are the respective new object addresses
293 * if exinfo == TYPE_GC_HANDLE_CREATED[_BT]
294 * [handle_type: uleb128] GC handle type (System.Runtime.InteropServices.GCHandleType)
295 * upper bits reserved as flags
296 * [handle: uleb128] GC handle value
297 * [objaddr: sleb128] object pointer differences from obj_base
298 * If exinfo == TYPE_GC_HANDLE_CREATED_BT, a backtrace follows.
299 * if exinfo == TYPE_GC_HANDLE_DESTROYED[_BT]
300 * [handle_type: uleb128] GC handle type (System.Runtime.InteropServices.GCHandleType)
301 * upper bits reserved as flags
302 * [handle: uleb128] GC handle value
303 * If exinfo == TYPE_GC_HANDLE_DESTROYED_BT, a backtrace follows.
304 * if exinfo == TYPE_GC_FINALIZE_OBJECT_{START,END}
305 * [object: sleb128] the object as a difference from obj_base
307 * type metadata format:
308 * type: TYPE_METADATA
309 * exinfo: one of: TYPE_END_LOAD, TYPE_END_UNLOAD (optional for TYPE_THREAD and TYPE_DOMAIN)
310 * [mtype: byte] metadata type, one of: TYPE_CLASS, TYPE_IMAGE, TYPE_ASSEMBLY, TYPE_DOMAIN,
311 * TYPE_THREAD, TYPE_CONTEXT
312 * [pointer: sleb128] pointer of the metadata type depending on mtype
313 * if mtype == TYPE_CLASS
314 * [image: sleb128] MonoImage* as a pointer difference from ptr_base
315 * [name: string] full class name
316 * if mtype == TYPE_IMAGE
317 * [name: string] image file name
318 * if mtype == TYPE_ASSEMBLY
319 * [name: string] assembly name
320 * if mtype == TYPE_DOMAIN && exinfo == 0
321 * [name: string] domain friendly name
322 * if mtype == TYPE_CONTEXT
323 * [domain: sleb128] domain id as pointer
324 * if mtype == TYPE_THREAD && exinfo == 0
325 * [name: string] thread name
327 * type method format:
329 * exinfo: one of: TYPE_LEAVE, TYPE_ENTER, TYPE_EXC_LEAVE, TYPE_JIT
330 * [method: sleb128] MonoMethod* as a pointer difference from the last such
331 * pointer or the buffer method_base
332 * if exinfo == TYPE_JIT
333 * [code address: sleb128] pointer to the native code as a diff from ptr_base
334 * [code size: uleb128] size of the generated code
335 * [name: string] full method name
337 * type exception format:
338 * type: TYPE_EXCEPTION
339 * exinfo: TYPE_THROW_BT flag or one of: TYPE_CLAUSE
340 * if exinfo == TYPE_CLAUSE
341 * [clause type: byte] MonoExceptionEnum enum value
342 * [clause index: uleb128] index of the current clause
343 * [method: sleb128] MonoMethod* as a pointer difference from the last such
344 * pointer or the buffer method_base
346 * [object: sleb128] the exception object as a difference from obj_base
347 * if exinfo has TYPE_THROW_BT set, a backtrace follows.
349 * type runtime format:
351 * exinfo: one of: TYPE_JITHELPER
352 * if exinfo == TYPE_JITHELPER
353 * [type: byte] MonoProfilerCodeBufferType enum value
354 * [buffer address: sleb128] pointer to the native code as a diff from ptr_base
355 * [buffer size: uleb128] size of the generated code
356 * if type == MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE
357 * [name: string] buffer description name
359 * type monitor format:
361 * exinfo: TYPE_MONITOR_BT flag and one of: MONO_PROFILER_MONITOR_(CONTENTION|FAIL|DONE)
362 * [object: sleb128] the lock object as a difference from obj_base
363 * if exinfo.low3bits == MONO_PROFILER_MONITOR_CONTENTION
364 * If the TYPE_MONITOR_BT flag is set, a backtrace follows.
368 * exinfo: one of TYPE_HEAP_START, TYPE_HEAP_END, TYPE_HEAP_OBJECT, TYPE_HEAP_ROOT
369 * if exinfo == TYPE_HEAP_OBJECT
370 * [object: sleb128] the object as a difference from obj_base
371 * [class: sleb128] the object MonoClass* as a difference from ptr_base
372 * [size: uleb128] size of the object on the heap
373 * [num_refs: uleb128] number of object references
374 * each referenced objref is preceded by a uleb128 encoded offset: the
375 * first offset is from the object address and each next offset is relative
376 * to the previous one
377 * [objrefs: sleb128]+ object referenced as a difference from obj_base
378 * The same object can appear multiple times, but only the first time
379 * with size != 0: in the other cases this data will only be used to
380 * provide additional referenced objects.
381 * if exinfo == TYPE_HEAP_ROOT
382 * [num_roots: uleb128] number of root references
383 * [num_gc: uleb128] number of major gcs
384 * [object: sleb128] the object as a difference from obj_base
385 * [root_type: byte] the root_type: MonoProfileGCRootType (profiler.h)
386 * [extra_info: uleb128] the extra_info value
387 * object, root_type and extra_info are repeated num_roots times
391 * exinfo: one of TYPE_SAMPLE_HIT, TYPE_SAMPLE_USYM, TYPE_SAMPLE_UBIN, TYPE_SAMPLE_COUNTERS_DESC, TYPE_SAMPLE_COUNTERS
392 * if exinfo == TYPE_SAMPLE_HIT
393 * [sample_type: byte] type of sample (SAMPLE_*)
394 * [thread: sleb128] thread id as difference from ptr_base
395 * [count: uleb128] number of following instruction addresses
396 * [ip: sleb128]* instruction pointer as difference from ptr_base
397 * [mbt_count: uleb128] number of managed backtrace frames
398 * [method: sleb128]* MonoMethod* as a pointer difference from the last such
399 * pointer or the buffer method_base (the first such method can be also indentified by ip, but this is not neccessarily true)
400 * if exinfo == TYPE_SAMPLE_USYM
401 * [address: sleb128] symbol address as a difference from ptr_base
402 * [size: uleb128] symbol size (may be 0 if unknown)
403 * [name: string] symbol name
404 * if exinfo == TYPE_SAMPLE_UBIN
405 * [address: sleb128] address where binary has been loaded
406 * [offset: uleb128] file offset of mapping (the same file can be mapped multiple times)
407 * [size: uleb128] memory size
408 * [name: string] binary name
409 * if exinfo == TYPE_SAMPLE_COUNTERS_DESC
410 * [len: uleb128] number of counters
412 * [section: uleb128] section of counter
413 * if section == MONO_COUNTER_PERFCOUNTERS:
414 * [section_name: string] section name of counter
415 * [name: string] name of counter
416 * [type: byte] type of counter
417 * [unit: byte] unit of counter
418 * [variance: byte] variance of counter
419 * [index: uleb128] unique index of counter
420 * if exinfo == TYPE_SAMPLE_COUNTERS
422 * [index: uleb128] unique index of counter
425 * [type: byte] type of counter value
428 * [0: uleb128] 0 -> value is null
430 * [1: uleb128] 1 -> value is not null
431 * [value: string] counter value
433 * [value: uleb128/sleb128/double] counter value, can be sleb128, uleb128 or double (determined by using type)
435 * type coverage format
436 * type: TYPE_COVERAGE
437 * exinfo: one of TYPE_COVERAGE_METHOD, TYPE_COVERAGE_STATEMENT, TYPE_COVERAGE_ASSEMBLY, TYPE_COVERAGE_CLASS
438 * if exinfo == TYPE_COVERAGE_METHOD
439 * [assembly: string] name of assembly
440 * [class: string] name of the class
441 * [name: string] name of the method
442 * [signature: string] the signature of the method
443 * [filename: string] the file path of the file that contains this method
444 * [token: uleb128] the method token
445 * [method_id: uleb128] an ID for this data to associate with the buffers of TYPE_COVERAGE_STATEMENTS
446 * [len: uleb128] the number of TYPE_COVERAGE_BUFFERS associated with this method
447 * if exinfo == TYPE_COVERAGE_STATEMENTS
448 * [method_id: uleb128] an the TYPE_COVERAGE_METHOD buffer to associate this with
449 * [offset: uleb128] the il offset relative to the previous offset
450 * [counter: uleb128] the counter for this instruction
451 * [line: uleb128] the line of filename containing this instruction
452 * [column: uleb128] the column containing this instruction
453 * if exinfo == TYPE_COVERAGE_ASSEMBLY
454 * [name: string] assembly name
455 * [guid: string] assembly GUID
456 * [filename: string] assembly filename
457 * [number_of_methods: uleb128] the number of methods in this assembly
458 * [fully_covered: uleb128] the number of fully covered methods
459 * [partially_covered: uleb128] the number of partially covered methods
460 * currently partially_covered will always be 0, and fully_covered is the
461 * number of methods that are fully and partially covered.
462 * if exinfo == TYPE_COVERAGE_CLASS
463 * [name: string] assembly name
464 * [class: string] class name
465 * [number_of_methods: uleb128] the number of methods in this class
466 * [fully_covered: uleb128] the number of fully covered methods
467 * [partially_covered: uleb128] the number of partially covered methods
468 * currently partially_covered will always be 0, and fully_covered is the
469 * number of methods that are fully and partially covered.
473 * exinfo: one of: TYPE_SYNC_POINT
474 * if exinfo == TYPE_SYNC_POINT
475 * [type: byte] MonoProfilerSyncPointType enum value
478 // Pending data to be written to the log, for a single thread.
479 // Threads periodically flush their own LogBuffers by calling safe_send
480 typedef struct _LogBuffer LogBuffer
;
482 // Next (older) LogBuffer in processing queue
488 uintptr_t method_base
;
489 uintptr_t last_method
;
493 // Bytes allocated for this LogBuffer
496 // Start of currently unused space in buffer
497 unsigned char* cursor
;
499 // Pointer to start-of-structure-plus-size (for convenience)
500 unsigned char* buf_end
;
502 // Start of data in buffer. Contents follow "buffer format" described above.
503 unsigned char buf
[1];
507 MonoLinkedListSetNode node
;
509 // Was this thread added to the LLS?
512 // The current log buffer for this thread.
515 // Methods referenced by events in `buffer`, see `MethodInfo`.
518 // Current call depth for enter/leave events.
521 // Indicates whether this thread is currently writing to its `buffer`.
523 } MonoProfilerThread
;
526 ign_res (int G_GNUC_UNUSED unused
, ...)
533 return (uintptr_t) mono_native_thread_id_get ();
540 return (uintptr_t) GetCurrentProcessId ();
542 return (uintptr_t) getpid ();
547 static mach_timebase_info_data_t timebase_info
;
548 #elif defined (HOST_WIN32)
549 static LARGE_INTEGER pcounter_freq
;
552 #define TICKS_PER_SEC 1000000000LL
558 uint64_t time
= mach_absolute_time ();
560 time
*= timebase_info
.numer
;
561 time
/= timebase_info
.denom
;
564 #elif defined (HOST_WIN32)
567 QueryPerformanceCounter (&value
);
569 return value
.QuadPart
* TICKS_PER_SEC
/ pcounter_freq
.QuadPart
;
570 #elif defined (CLOCK_MONOTONIC)
571 struct timespec tspec
;
573 clock_gettime (CLOCK_MONOTONIC
, &tspec
);
575 return ((uint64_t) tspec
.tv_sec
* TICKS_PER_SEC
+ tspec
.tv_nsec
);
579 gettimeofday (&tv
, NULL
);
581 return ((uint64_t) tv
.tv_sec
* TICKS_PER_SEC
+ tv
.tv_usec
* 1000);
585 static int timer_overhead
;
591 mach_timebase_info (&timebase_info
);
592 #elif defined (HOST_WIN32)
593 QueryPerformanceFrequency (&pcounter_freq
);
596 uint64_t time_start
= current_time ();
598 for (int i
= 0; i
< 256; ++i
)
601 uint64_t time_end
= current_time ();
603 timer_overhead
= (time_end
- time_start
) / 256;
607 * These macros create a scope to avoid leaking the buffer returned
608 * from ensure_logbuf () as it may have been invalidated by a GC
609 * thread during STW. If you called init_thread () with add_to_lls =
610 * FALSE, then don't use these macros.
613 #define ENTER_LOG(COUNTER, BUFFER, SIZE) \
615 MonoProfilerThread *thread__ = PROF_TLS_GET (); \
616 if (thread__->attached) \
618 g_assert (!thread__->busy && "Why are we trying to write a new event while already writing one?"); \
619 thread__->busy = TRUE; \
620 InterlockedIncrement ((COUNTER)); \
621 LogBuffer *BUFFER = ensure_logbuf_unsafe ((SIZE))
623 #define EXIT_LOG_EXPLICIT(PROFILER, SEND, REQUESTS) \
624 thread__->busy = FALSE; \
626 send_log_unsafe ((PROFILER), FALSE, TRUE); \
627 if (thread__->attached) \
630 process_requests ((PROFILER)); \
633 #define EXIT_LOG(PROFILER) EXIT_LOG_EXPLICIT ((PROFILER), TRUE, TRUE)
635 static volatile gint32 buffer_rwlock_count
;
636 static volatile gpointer buffer_rwlock_exclusive
;
638 // Can be used recursively.
643 * If the thread holding the exclusive lock tries to modify the
644 * reader count, just make it a no-op. This way, we also avoid
645 * invoking the GC safe point macros below, which could break if
646 * done from a thread that is currently the initiator of STW.
648 * In other words, we rely on the fact that the GC thread takes
649 * the exclusive lock in the gc_event () callback when the world
652 if (InterlockedReadPointer (&buffer_rwlock_exclusive
) != (gpointer
) thread_id ()) {
655 while (InterlockedReadPointer (&buffer_rwlock_exclusive
))
656 mono_thread_info_yield ();
658 InterlockedIncrement (&buffer_rwlock_count
);
663 mono_memory_barrier ();
669 mono_memory_barrier ();
671 // See the comment in buffer_lock ().
672 if (InterlockedReadPointer (&buffer_rwlock_exclusive
) == (gpointer
) thread_id ())
675 g_assert (InterlockedRead (&buffer_rwlock_count
) && "Why are we trying to decrement a zero reader count?");
677 InterlockedDecrement (&buffer_rwlock_count
);
680 // Cannot be used recursively.
682 buffer_lock_excl (void)
684 gpointer tid
= (gpointer
) thread_id ();
686 g_assert (InterlockedReadPointer (&buffer_rwlock_exclusive
) != tid
&& "Why are we taking the exclusive lock twice?");
690 while (InterlockedCompareExchangePointer (&buffer_rwlock_exclusive
, tid
, 0))
691 mono_thread_info_yield ();
693 while (InterlockedRead (&buffer_rwlock_count
))
694 mono_thread_info_yield ();
698 mono_memory_barrier ();
702 buffer_unlock_excl (void)
704 mono_memory_barrier ();
706 g_assert (InterlockedReadPointer (&buffer_rwlock_exclusive
) && "Why is the exclusive lock not held?");
707 g_assert (InterlockedReadPointer (&buffer_rwlock_exclusive
) == (gpointer
) thread_id () && "Why does another thread hold the exclusive lock?");
708 g_assert (!InterlockedRead (&buffer_rwlock_count
) && "Why are there readers when the exclusive lock is held?");
710 InterlockedWritePointer (&buffer_rwlock_exclusive
, NULL
);
713 typedef struct _BinaryObject BinaryObject
;
714 struct _BinaryObject
{
720 struct _MonoProfiler
{
722 #if defined (HAVE_SYS_ZLIB)
726 uint64_t startup_time
;
732 pthread_t helper_thread
;
733 pthread_t writer_thread
;
734 pthread_t dumper_thread
;
736 volatile gint32 run_writer_thread
;
737 MonoLockFreeAllocSizeClass writer_entry_size_class
;
738 MonoLockFreeAllocator writer_entry_allocator
;
739 MonoLockFreeQueue writer_queue
;
740 MonoSemType writer_queue_sem
;
741 MonoConcurrentHashTable
*method_table
;
742 mono_mutex_t method_table_mutex
;
743 volatile gint32 run_dumper_thread
;
744 MonoLockFreeQueue dumper_queue
;
745 MonoSemType dumper_queue_sem
;
746 MonoLockFreeAllocSizeClass sample_size_class
;
747 MonoLockFreeAllocator sample_allocator
;
748 MonoLockFreeQueue sample_reuse_queue
;
749 BinaryObject
*binary_objects
;
750 GPtrArray
*coverage_filters
;
754 MonoLockFreeQueueNode node
;
759 #define WRITER_ENTRY_BLOCK_SIZE (mono_pagesize ())
769 #define PROF_TLS_SET(VAL) (TlsSetValue (profiler_tls, (VAL)))
770 #define PROF_TLS_GET() ((MonoProfilerThread *) TlsGetValue (profiler_tls))
771 #define PROF_TLS_INIT() (profiler_tls = TlsAlloc ())
772 #define PROF_TLS_FREE() (TlsFree (profiler_tls))
774 static DWORD profiler_tls
;
778 #define PROF_TLS_SET(VAL) (profiler_tls = (VAL))
779 #define PROF_TLS_GET() (profiler_tls)
780 #define PROF_TLS_INIT()
781 #define PROF_TLS_FREE()
783 static __thread MonoProfilerThread
*profiler_tls
;
787 #define PROF_TLS_SET(VAL) (pthread_setspecific (profiler_tls, (VAL)))
788 #define PROF_TLS_GET() ((MonoProfilerThread *) pthread_getspecific (profiler_tls))
789 #define PROF_TLS_INIT() (pthread_key_create (&profiler_tls, NULL))
790 #define PROF_TLS_FREE() (pthread_key_delete (&profiler_tls))
792 static pthread_key_t profiler_tls
;
797 pstrdup (const char *s
)
799 int len
= strlen (s
) + 1;
800 char *p
= (char *)malloc (len
);
806 alloc_buffer (int size
)
808 return mono_valloc (NULL
, size
, MONO_MMAP_READ
| MONO_MMAP_WRITE
| MONO_MMAP_ANON
| MONO_MMAP_PRIVATE
, MONO_MEM_ACCOUNT_PROFILER
);
812 free_buffer (void *buf
, int size
)
814 mono_vfree (buf
, size
, MONO_MEM_ACCOUNT_PROFILER
);
820 LogBuffer
* buf
= (LogBuffer
*) alloc_buffer (BUFFER_SIZE
);
822 InterlockedIncrement (&buffer_allocations_ctr
);
824 buf
->size
= BUFFER_SIZE
;
825 buf
->time_base
= current_time ();
826 buf
->last_time
= buf
->time_base
;
827 buf
->buf_end
= (unsigned char*)buf
+ buf
->size
;
828 buf
->cursor
= buf
->buf
;
834 * Must be called with the reader lock held if thread is the current thread, or
835 * the exclusive lock if thread is a different thread. However, if thread is
836 * the current thread, and init_thread () was called with add_to_lls = FALSE,
837 * then no locking is necessary.
840 init_buffer_state (MonoProfilerThread
*thread
)
842 thread
->buffer
= create_buffer ();
843 thread
->methods
= NULL
;
847 clear_hazard_pointers (MonoThreadHazardPointers
*hp
)
849 mono_hazard_pointer_clear (hp
, 0);
850 mono_hazard_pointer_clear (hp
, 1);
851 mono_hazard_pointer_clear (hp
, 2);
854 static MonoProfilerThread
*
855 init_thread (gboolean add_to_lls
)
857 MonoProfilerThread
*thread
= PROF_TLS_GET ();
860 * Sometimes we may try to initialize a thread twice. One example is the
861 * main thread: We initialize it when setting up the profiler, but we will
862 * also get a thread_start () callback for it. Another example is when
863 * attaching new threads to the runtime: We may get a gc_alloc () callback
864 * for that thread's thread object (where we initialize it), soon followed
865 * by a thread_start () callback.
867 * These cases are harmless anyhow. Just return if we've already done the
868 * initialization work.
873 thread
= malloc (sizeof (MonoProfilerThread
));
874 thread
->node
.key
= thread_id ();
875 thread
->attached
= add_to_lls
;
876 thread
->call_depth
= 0;
879 init_buffer_state (thread
);
882 * Some internal profiler threads don't need to be cleaned up
883 * by the main thread on shutdown.
886 MonoThreadHazardPointers
*hp
= mono_hazard_pointer_get ();
887 g_assert (mono_lls_insert (&profiler_thread_list
, hp
, &thread
->node
) && "Why can't we insert the thread in the LLS?");
888 clear_hazard_pointers (hp
);
891 PROF_TLS_SET (thread
);
896 // Only valid if init_thread () was called with add_to_lls = FALSE.
898 deinit_thread (MonoProfilerThread
*thread
)
900 g_assert (!thread
->attached
&& "Why are we manually freeing an attached thread?");
907 ensure_logbuf_inner (LogBuffer
*old
, int bytes
)
909 if (old
&& old
->cursor
+ bytes
+ 100 < old
->buf_end
)
912 LogBuffer
*new_
= create_buffer ();
918 // Only valid if init_thread () was called with add_to_lls = FALSE.
920 ensure_logbuf_unsafe (int bytes
)
922 MonoProfilerThread
*thread
= PROF_TLS_GET ();
923 LogBuffer
*old
= thread
->buffer
;
924 LogBuffer
*new_
= ensure_logbuf_inner (old
, bytes
);
927 return old
; // Still enough space.
929 thread
->buffer
= new_
;
935 encode_uleb128 (uint64_t value
, uint8_t *buf
, uint8_t **endbuf
)
940 uint8_t b
= value
& 0x7f;
943 if (value
!= 0) /* more bytes to come */
953 encode_sleb128 (intptr_t value
, uint8_t *buf
, uint8_t **endbuf
)
956 int negative
= (value
< 0);
957 unsigned int size
= sizeof (intptr_t) * 8;
965 /* the following is unnecessary if the
966 * implementation of >>= uses an arithmetic rather
967 * than logical shift for a signed left operand
971 value
|= - ((intptr_t) 1 <<(size
- 7));
973 /* sign bit of byte is second high order bit (0x40) */
974 if ((value
== 0 && !(byte
& 0x40)) ||
975 (value
== -1 && (byte
& 0x40)))
987 emit_byte (LogBuffer
*logbuffer
, int value
)
989 logbuffer
->cursor
[0] = value
;
991 assert (logbuffer
->cursor
<= logbuffer
->buf_end
);
995 emit_value (LogBuffer
*logbuffer
, int value
)
997 encode_uleb128 (value
, logbuffer
->cursor
, &logbuffer
->cursor
);
998 assert (logbuffer
->cursor
<= logbuffer
->buf_end
);
1002 emit_time (LogBuffer
*logbuffer
, uint64_t value
)
1004 uint64_t tdiff
= value
- logbuffer
->last_time
;
1005 //if (value < logbuffer->last_time)
1006 // printf ("time went backwards\n");
1007 //if (tdiff > 1000000)
1008 // printf ("large time offset: %llu\n", tdiff);
1009 encode_uleb128 (tdiff
, logbuffer
->cursor
, &logbuffer
->cursor
);
1010 /*if (tdiff != decode_uleb128 (p, &p))
1011 printf ("incorrect encoding: %llu\n", tdiff);*/
1012 logbuffer
->last_time
= value
;
1013 assert (logbuffer
->cursor
<= logbuffer
->buf_end
);
1017 emit_event_time (LogBuffer
*logbuffer
, int event
, uint64_t time
)
1019 emit_byte (logbuffer
, event
);
1020 emit_time (logbuffer
, time
);
1024 emit_event (LogBuffer
*logbuffer
, int event
)
1026 emit_event_time (logbuffer
, event
, current_time ());
1030 emit_svalue (LogBuffer
*logbuffer
, int64_t value
)
1032 encode_sleb128 (value
, logbuffer
->cursor
, &logbuffer
->cursor
);
1033 assert (logbuffer
->cursor
<= logbuffer
->buf_end
);
1037 emit_uvalue (LogBuffer
*logbuffer
, uint64_t value
)
1039 encode_uleb128 (value
, logbuffer
->cursor
, &logbuffer
->cursor
);
1040 assert (logbuffer
->cursor
<= logbuffer
->buf_end
);
1044 emit_ptr (LogBuffer
*logbuffer
, void *ptr
)
1046 if (!logbuffer
->ptr_base
)
1047 logbuffer
->ptr_base
= (uintptr_t)ptr
;
1048 emit_svalue (logbuffer
, (intptr_t)ptr
- logbuffer
->ptr_base
);
1049 assert (logbuffer
->cursor
<= logbuffer
->buf_end
);
1053 emit_method_inner (LogBuffer
*logbuffer
, void *method
)
1055 if (!logbuffer
->method_base
) {
1056 logbuffer
->method_base
= (intptr_t)method
;
1057 logbuffer
->last_method
= (intptr_t)method
;
1059 encode_sleb128 ((intptr_t)((char*)method
- (char*)logbuffer
->last_method
), logbuffer
->cursor
, &logbuffer
->cursor
);
1060 logbuffer
->last_method
= (intptr_t)method
;
1061 assert (logbuffer
->cursor
<= logbuffer
->buf_end
);
1071 find_method (MonoDomain *domain, void *user_data)
1073 MethodSearch *search = user_data;
1078 MonoJitInfo *ji = mono_get_jit_info_from_method (domain, search->method);
1080 // It could be AOT'd, so we need to get it from the AOT runtime's cache.
1082 void *ip = mono_aot_get_method (domain, search->method);
1084 // Avoid a slow path in mono_jit_info_table_find ().
1086 ji = mono_jit_info_table_find (domain, ip);
1095 register_method_local (MonoProfiler
*prof
, MonoMethod
*method
, MonoJitInfo
*ji
)
1097 if (!mono_conc_hashtable_lookup (prof
->method_table
, method
)) {
1099 * FIXME: In some cases, we crash while looking up JIT info for AOT'd methods.
1100 * This usually happens for static constructors. This code is disabled for now
1101 * as we don't need this info for anything critical.
1103 * https://bugzilla.xamarin.com/show_bug.cgi?id=35171
1107 MethodSearch search = { method, NULL };
1109 mono_domain_foreach (find_method, &search);
1116 * FIXME: We can't always find JIT info for a generic shared method, especially
1117 * if we obtained the MonoMethod during an async stack walk. For now, we deal
1118 * with this by giving the generic shared method name and dummy code start/size
1119 * information (i.e. zeroes).
1123 MethodInfo
*info
= (MethodInfo
*) malloc (sizeof (MethodInfo
));
1125 info
->method
= method
;
1127 info
->time
= current_time ();
1129 MonoProfilerThread
*thread
= PROF_TLS_GET ();
1130 GPtrArray
*arr
= thread
->methods
? thread
->methods
: (thread
->methods
= g_ptr_array_new ());
1131 g_ptr_array_add (arr
, info
);
1136 emit_method (MonoProfiler
*prof
, LogBuffer
*logbuffer
, MonoMethod
*method
)
1138 register_method_local (prof
, method
, NULL
);
1139 emit_method_inner (logbuffer
, method
);
1143 emit_obj (LogBuffer
*logbuffer
, void *ptr
)
1145 if (!logbuffer
->obj_base
)
1146 logbuffer
->obj_base
= (uintptr_t)ptr
>> 3;
1147 emit_svalue (logbuffer
, ((uintptr_t)ptr
>> 3) - logbuffer
->obj_base
);
1148 assert (logbuffer
->cursor
<= logbuffer
->buf_end
);
1152 emit_string (LogBuffer
*logbuffer
, const char *str
, size_t size
)
1156 for (; i
< size
; i
++) {
1159 emit_byte (logbuffer
, str
[i
]);
1162 emit_byte (logbuffer
, '\0');
1166 emit_double (LogBuffer
*logbuffer
, double value
)
1169 unsigned char buffer
[8];
1170 memcpy (buffer
, &value
, 8);
1171 #if G_BYTE_ORDER == G_BIG_ENDIAN
1172 for (i
= 7; i
>= 0; i
--)
1174 for (i
= 0; i
< 8; i
++)
1176 emit_byte (logbuffer
, buffer
[i
]);
1180 write_int16 (char *buf
, int32_t value
)
1183 for (i
= 0; i
< 2; ++i
) {
1191 write_int32 (char *buf
, int32_t value
)
1194 for (i
= 0; i
< 4; ++i
) {
1202 write_int64 (char *buf
, int64_t value
)
1205 for (i
= 0; i
< 8; ++i
) {
1213 write_header_string (char *p
, const char *str
)
1215 size_t len
= strlen (str
) + 1;
1217 p
= write_int32 (p
, len
);
1224 dump_header (MonoProfiler
*profiler
)
1226 const char *args
= profiler
->args
;
1227 const char *arch
= mono_config_get_cpu ();
1228 const char *os
= mono_config_get_os ();
1230 char *hbuf
= malloc (
1231 sizeof (gint32
) /* header id */ +
1232 sizeof (gint8
) /* major version */ +
1233 sizeof (gint8
) /* minor version */ +
1234 sizeof (gint8
) /* data version */ +
1235 sizeof (gint8
) /* word size */ +
1236 sizeof (gint64
) /* startup time */ +
1237 sizeof (gint32
) /* timer overhead */ +
1238 sizeof (gint32
) /* flags */ +
1239 sizeof (gint32
) /* process id */ +
1240 sizeof (gint16
) /* command port */ +
1241 sizeof (gint32
) + strlen (args
) + 1 /* arguments */ +
1242 sizeof (gint32
) + strlen (arch
) + 1 /* architecture */ +
1243 sizeof (gint32
) + strlen (os
) + 1 /* operating system */
1247 p
= write_int32 (p
, LOG_HEADER_ID
);
1248 *p
++ = LOG_VERSION_MAJOR
;
1249 *p
++ = LOG_VERSION_MINOR
;
1250 *p
++ = LOG_DATA_VERSION
;
1251 *p
++ = sizeof (void *);
1252 p
= write_int64 (p
, ((uint64_t) time (NULL
)) * 1000);
1253 p
= write_int32 (p
, timer_overhead
);
1254 p
= write_int32 (p
, 0); /* flags */
1255 p
= write_int32 (p
, process_id ());
1256 p
= write_int16 (p
, profiler
->command_port
);
1257 p
= write_header_string (p
, args
);
1258 p
= write_header_string (p
, arch
);
1259 p
= write_header_string (p
, os
);
1261 #if defined (HAVE_SYS_ZLIB)
1262 if (profiler
->gzfile
) {
1263 gzwrite (profiler
->gzfile
, hbuf
, p
- hbuf
);
1267 fwrite (hbuf
, p
- hbuf
, 1, profiler
->file
);
1268 fflush (profiler
->file
);
1275 * Must be called with the reader lock held if thread is the current thread, or
1276 * the exclusive lock if thread is a different thread. However, if thread is
1277 * the current thread, and init_thread () was called with add_to_lls = FALSE,
1278 * then no locking is necessary.
1281 send_buffer (MonoProfiler
*prof
, MonoProfilerThread
*thread
)
1283 WriterQueueEntry
*entry
= mono_lock_free_alloc (&prof
->writer_entry_allocator
);
1284 entry
->methods
= thread
->methods
;
1285 entry
->buffer
= thread
->buffer
;
1287 mono_lock_free_queue_node_init (&entry
->node
, FALSE
);
1289 mono_lock_free_queue_enqueue (&prof
->writer_queue
, &entry
->node
);
1290 mono_os_sem_post (&prof
->writer_queue_sem
);
1294 remove_thread (MonoProfiler
*prof
, MonoProfilerThread
*thread
, gboolean from_callback
)
1296 MonoThreadHazardPointers
*hp
= mono_hazard_pointer_get ();
1298 if (mono_lls_remove (&profiler_thread_list
, hp
, &thread
->node
)) {
1300 * No need to take the buffer lock here as no other threads can
1301 * be accessing this buffer anymore.
1304 if (!from_callback
) {
1306 * The thread is being cleaned up by the main thread during
1307 * shutdown. This typically happens for internal runtime
1308 * threads. We need to synthesize a thread end event.
1311 InterlockedIncrement (&thread_ends_ctr
);
1313 thread
->buffer
= ensure_logbuf_inner (thread
->buffer
,
1314 EVENT_SIZE
/* event */ +
1315 BYTE_SIZE
/* type */ +
1316 LEB128_SIZE
/* tid */
1319 emit_event (thread
->buffer
, TYPE_END_UNLOAD
| TYPE_METADATA
);
1320 emit_byte (thread
->buffer
, TYPE_THREAD
);
1321 emit_ptr (thread
->buffer
, (void *) thread
->node
.key
);
1324 send_buffer (prof
, thread
);
1326 mono_thread_hazardous_try_free (thread
, free
);
1329 clear_hazard_pointers (hp
);
1332 PROF_TLS_SET (NULL
);
1336 dump_buffer (MonoProfiler
*profiler
, LogBuffer
*buf
)
1342 dump_buffer (profiler
, buf
->next
);
1344 if (buf
->cursor
- buf
->buf
) {
1345 p
= write_int32 (p
, BUF_ID
);
1346 p
= write_int32 (p
, buf
->cursor
- buf
->buf
);
1347 p
= write_int64 (p
, buf
->time_base
);
1348 p
= write_int64 (p
, buf
->ptr_base
);
1349 p
= write_int64 (p
, buf
->obj_base
);
1350 p
= write_int64 (p
, buf
->thread_id
);
1351 p
= write_int64 (p
, buf
->method_base
);
1353 #if defined (HAVE_SYS_ZLIB)
1354 if (profiler
->gzfile
) {
1355 gzwrite (profiler
->gzfile
, hbuf
, p
- hbuf
);
1356 gzwrite (profiler
->gzfile
, buf
->buf
, buf
->cursor
- buf
->buf
);
1360 fwrite (hbuf
, p
- hbuf
, 1, profiler
->file
);
1361 fwrite (buf
->buf
, buf
->cursor
- buf
->buf
, 1, profiler
->file
);
1362 fflush (profiler
->file
);
1366 free_buffer (buf
, buf
->size
);
1370 dump_buffer_threadless (MonoProfiler
*profiler
, LogBuffer
*buf
)
1372 for (LogBuffer
*iter
= buf
; iter
; iter
= iter
->next
)
1373 iter
->thread_id
= 0;
1375 dump_buffer (profiler
, buf
);
1379 process_requests (MonoProfiler
*profiler
)
1381 if (heapshot_requested
)
1382 mono_gc_collect (mono_gc_max_generation ());
1385 // Avoid calling this directly if possible. Use the functions below.
1387 send_log_unsafe (MonoProfiler
*profiler
, gboolean lock
, gboolean if_needed
)
1389 MonoProfilerThread
*thread
= PROF_TLS_GET ();
1394 if (!if_needed
|| (if_needed
&& thread
->buffer
->next
)) {
1395 if (!thread
->attached
)
1396 for (LogBuffer
*iter
= thread
->buffer
; iter
; iter
= iter
->next
)
1397 iter
->thread_id
= 0;
1399 send_buffer (profiler
, thread
);
1400 init_buffer_state (thread
);
1407 // Assumes that the exclusive lock is held.
1409 sync_point_flush (MonoProfiler
*prof
)
1411 g_assert (InterlockedReadPointer (&buffer_rwlock_exclusive
) == (gpointer
) thread_id () && "Why don't we hold the exclusive lock?");
1413 MONO_LLS_FOREACH_SAFE (&profiler_thread_list
, MonoProfilerThread
, thread
) {
1414 send_buffer (prof
, thread
);
1415 init_buffer_state (thread
);
1416 } MONO_LLS_FOREACH_SAFE_END
1419 // Assumes that the exclusive lock is held.
1421 sync_point_mark (MonoProfiler
*prof
, MonoProfilerSyncPointType type
)
1423 g_assert (InterlockedReadPointer (&buffer_rwlock_exclusive
) == (gpointer
) thread_id () && "Why don't we hold the exclusive lock?");
1425 ENTER_LOG (&sync_points_ctr
, logbuffer
,
1426 EVENT_SIZE
/* event */ +
1427 LEB128_SIZE
/* type */
1430 emit_event (logbuffer
, TYPE_META
| TYPE_SYNC_POINT
);
1431 emit_byte (logbuffer
, type
);
1433 EXIT_LOG_EXPLICIT (prof
, FALSE
, FALSE
);
1435 send_log_unsafe (prof
, FALSE
, FALSE
);
1438 // Assumes that the exclusive lock is held.
1440 sync_point (MonoProfiler
*prof
, MonoProfilerSyncPointType type
)
1442 sync_point_flush (prof
);
1443 sync_point_mark (prof
, type
);
1447 gc_reference (MonoObject
*obj
, MonoClass
*klass
, uintptr_t size
, uintptr_t num
, MonoObject
**refs
, uintptr_t *offsets
, void *data
)
1449 MonoProfiler
*prof
= data
;
1451 /* account for object alignment in the heap */
1455 ENTER_LOG (&heap_objects_ctr
, logbuffer
,
1456 EVENT_SIZE
/* event */ +
1457 LEB128_SIZE
/* obj */ +
1458 LEB128_SIZE
/* klass */ +
1459 LEB128_SIZE
/* size */ +
1460 LEB128_SIZE
/* num */ +
1462 LEB128_SIZE
/* offset */ +
1463 LEB128_SIZE
/* ref */
1467 emit_event (logbuffer
, TYPE_HEAP_OBJECT
| TYPE_HEAP
);
1468 emit_obj (logbuffer
, obj
);
1469 emit_ptr (logbuffer
, klass
);
1470 emit_value (logbuffer
, size
);
1471 emit_value (logbuffer
, num
);
1473 uintptr_t last_offset
= 0;
1475 for (int i
= 0; i
< num
; ++i
) {
1476 emit_value (logbuffer
, offsets
[i
] - last_offset
);
1477 last_offset
= offsets
[i
];
1478 emit_obj (logbuffer
, refs
[i
]);
1481 EXIT_LOG_EXPLICIT (prof
, TRUE
, FALSE
);
1486 static unsigned int hs_mode_ms
= 0;
1487 static unsigned int hs_mode_gc
= 0;
1488 static unsigned int hs_mode_ondemand
= 0;
1489 static unsigned int gc_count
= 0;
1490 static uint64_t last_hs_time
= 0;
1491 static gboolean do_heap_walk
= FALSE
;
1494 heap_walk (MonoProfiler
*profiler
)
1496 ENTER_LOG (&heap_starts_ctr
, logbuffer
,
1497 EVENT_SIZE
/* event */
1500 emit_event (logbuffer
, TYPE_HEAP_START
| TYPE_HEAP
);
1502 EXIT_LOG_EXPLICIT (profiler
, TRUE
, FALSE
);
1504 mono_gc_walk_heap (0, gc_reference
, profiler
);
1506 ENTER_LOG (&heap_ends_ctr
, logbuffer
,
1507 EVENT_SIZE
/* event */
1510 emit_event (logbuffer
, TYPE_HEAP_END
| TYPE_HEAP
);
1512 EXIT_LOG_EXPLICIT (profiler
, TRUE
, FALSE
);
1516 gc_roots (MonoProfiler
*prof
, int num
, void **objects
, int *root_types
, uintptr_t *extra_info
)
1518 ENTER_LOG (&heap_roots_ctr
, logbuffer
,
1519 EVENT_SIZE
/* event */ +
1520 LEB128_SIZE
/* num */ +
1521 LEB128_SIZE
/* collections */ +
1523 LEB128_SIZE
/* object */ +
1524 LEB128_SIZE
/* root type */ +
1525 LEB128_SIZE
/* extra info */
1529 emit_event (logbuffer
, TYPE_HEAP_ROOT
| TYPE_HEAP
);
1530 emit_value (logbuffer
, num
);
1531 emit_value (logbuffer
, mono_gc_collection_count (mono_gc_max_generation ()));
1533 for (int i
= 0; i
< num
; ++i
) {
1534 emit_obj (logbuffer
, objects
[i
]);
1535 emit_byte (logbuffer
, root_types
[i
]);
1536 emit_value (logbuffer
, extra_info
[i
]);
1539 EXIT_LOG_EXPLICIT (prof
, TRUE
, FALSE
);
1543 gc_event (MonoProfiler
*profiler
, MonoGCEvent ev
, int generation
)
1545 ENTER_LOG (&gc_events_ctr
, logbuffer
,
1546 EVENT_SIZE
/* event */ +
1547 BYTE_SIZE
/* gc event */ +
1548 BYTE_SIZE
/* generation */
1551 emit_event (logbuffer
, TYPE_GC_EVENT
| TYPE_GC
);
1552 emit_byte (logbuffer
, ev
);
1553 emit_byte (logbuffer
, generation
);
1555 EXIT_LOG_EXPLICIT (profiler
, FALSE
, FALSE
);
1558 case MONO_GC_EVENT_START
:
1559 if (generation
== mono_gc_max_generation ())
1562 uint64_t now
= current_time ();
1564 if (hs_mode_ms
&& (now
- last_hs_time
) / 1000 * 1000 >= hs_mode_ms
)
1565 do_heap_walk
= TRUE
;
1566 else if (hs_mode_gc
&& !(gc_count
% hs_mode_gc
))
1567 do_heap_walk
= TRUE
;
1568 else if (hs_mode_ondemand
)
1569 do_heap_walk
= heapshot_requested
;
1570 else if (!hs_mode_ms
&& !hs_mode_gc
&& generation
== mono_gc_max_generation ())
1571 do_heap_walk
= TRUE
;
1573 case MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED
:
1575 * Ensure that no thread can be in the middle of writing to
1576 * a buffer when the world stops...
1578 buffer_lock_excl ();
1580 case MONO_GC_EVENT_POST_STOP_WORLD
:
1582 * ... So that we now have a consistent view of all buffers.
1583 * This allows us to flush them. We need to do this because
1584 * they may contain object allocation events that need to be
1585 * committed to the log file before any object move events
1586 * that will be produced during this GC.
1588 sync_point (profiler
, SYNC_POINT_WORLD_STOP
);
1590 case MONO_GC_EVENT_PRE_START_WORLD
:
1591 if (do_heap_shot
&& do_heap_walk
) {
1592 heap_walk (profiler
);
1594 do_heap_walk
= FALSE
;
1595 heapshot_requested
= 0;
1596 last_hs_time
= current_time ();
1599 case MONO_GC_EVENT_POST_START_WORLD_UNLOCKED
:
1601 * Similarly, we must now make sure that any object moves
1602 * written to the GC thread's buffer are flushed. Otherwise,
1603 * object allocation events for certain addresses could come
1604 * after the move events that made those addresses available.
1606 sync_point_mark (profiler
, SYNC_POINT_WORLD_START
);
1609 * Finally, it is safe to allow other threads to write to
1610 * their buffers again.
1612 buffer_unlock_excl ();
1620 gc_resize (MonoProfiler
*profiler
, int64_t new_size
)
1622 ENTER_LOG (&gc_resizes_ctr
, logbuffer
,
1623 EVENT_SIZE
/* event */ +
1624 LEB128_SIZE
/* new size */
1627 emit_event (logbuffer
, TYPE_GC_RESIZE
| TYPE_GC
);
1628 emit_value (logbuffer
, new_size
);
1630 EXIT_LOG_EXPLICIT (profiler
, TRUE
, FALSE
);
1633 // If you alter MAX_FRAMES, you may need to alter SAMPLE_BLOCK_SIZE too.
1634 #define MAX_FRAMES 32
1638 MonoMethod
* methods
[MAX_FRAMES
];
1639 int32_t il_offsets
[MAX_FRAMES
];
1640 int32_t native_offsets
[MAX_FRAMES
];
1643 static int num_frames
= MAX_FRAMES
;
1646 walk_stack (MonoMethod
*method
, int32_t native_offset
, int32_t il_offset
, mono_bool managed
, void* data
)
1648 FrameData
*frame
= (FrameData
*)data
;
1649 if (method
&& frame
->count
< num_frames
) {
1650 frame
->il_offsets
[frame
->count
] = il_offset
;
1651 frame
->native_offsets
[frame
->count
] = native_offset
;
1652 frame
->methods
[frame
->count
++] = method
;
1653 //printf ("In %d %s at %d (native: %d)\n", frame->count, mono_method_get_name (method), il_offset, native_offset);
1655 return frame
->count
== num_frames
;
1659 * a note about stack walks: they can cause more profiler events to fire,
1660 * so we need to make sure they don't happen after we started emitting an
1661 * event, hence the collect_bt/emit_bt split.
1664 collect_bt (FrameData
*data
)
1667 mono_stack_walk_no_il (walk_stack
, data
);
1671 emit_bt (MonoProfiler
*prof
, LogBuffer
*logbuffer
, FrameData
*data
)
1673 /* FIXME: this is actually tons of data and we should
1674 * just output it the first time and use an id the next
1676 if (data
->count
> num_frames
)
1677 printf ("bad num frames: %d\n", data
->count
);
1678 emit_value (logbuffer
, data
->count
);
1679 //if (*p != data.count) {
1680 // printf ("bad num frames enc at %d: %d -> %d\n", count, data.count, *p); printf ("frames end: %p->%p\n", p, logbuffer->cursor); exit(0);}
1681 while (data
->count
) {
1682 emit_method (prof
, logbuffer
, data
->methods
[--data
->count
]);
1687 gc_alloc (MonoProfiler
*prof
, MonoObject
*obj
, MonoClass
*klass
)
1691 int do_bt
= (nocalls
&& InterlockedRead (&runtime_inited
) && !notraces
) ? TYPE_ALLOC_BT
: 0;
1693 uintptr_t len
= mono_object_get_size (obj
);
1694 /* account for object alignment in the heap */
1701 ENTER_LOG (&gc_allocs_ctr
, logbuffer
,
1702 EVENT_SIZE
/* event */ +
1703 LEB128_SIZE
/* klass */ +
1704 LEB128_SIZE
/* obj */ +
1705 LEB128_SIZE
/* size */ +
1707 LEB128_SIZE
/* count */ +
1709 LEB128_SIZE
/* method */
1714 emit_event (logbuffer
, do_bt
| TYPE_ALLOC
);
1715 emit_ptr (logbuffer
, klass
);
1716 emit_obj (logbuffer
, obj
);
1717 emit_value (logbuffer
, len
);
1720 emit_bt (prof
, logbuffer
, &data
);
1726 gc_moves (MonoProfiler
*prof
, void **objects
, int num
)
1728 ENTER_LOG (&gc_moves_ctr
, logbuffer
,
1729 EVENT_SIZE
/* event */ +
1730 LEB128_SIZE
/* num */ +
1732 LEB128_SIZE
/* object */
1736 emit_event (logbuffer
, TYPE_GC_MOVE
| TYPE_GC
);
1737 emit_value (logbuffer
, num
);
1739 for (int i
= 0; i
< num
; ++i
)
1740 emit_obj (logbuffer
, objects
[i
]);
1742 EXIT_LOG_EXPLICIT (prof
, TRUE
, FALSE
);
1746 gc_handle (MonoProfiler
*prof
, int op
, int type
, uintptr_t handle
, MonoObject
*obj
)
1748 int do_bt
= nocalls
&& InterlockedRead (&runtime_inited
) && !notraces
;
1754 gint32
*ctr
= op
== MONO_PROFILER_GC_HANDLE_CREATED
? &gc_handle_creations_ctr
: &gc_handle_deletions_ctr
;
1756 ENTER_LOG (ctr
, logbuffer
,
1757 EVENT_SIZE
/* event */ +
1758 LEB128_SIZE
/* type */ +
1759 LEB128_SIZE
/* handle */ +
1760 (op
== MONO_PROFILER_GC_HANDLE_CREATED
? (
1761 LEB128_SIZE
/* obj */
1764 LEB128_SIZE
/* count */ +
1766 LEB128_SIZE
/* method */
1771 if (op
== MONO_PROFILER_GC_HANDLE_CREATED
)
1772 emit_event (logbuffer
, (do_bt
? TYPE_GC_HANDLE_CREATED_BT
: TYPE_GC_HANDLE_CREATED
) | TYPE_GC
);
1773 else if (op
== MONO_PROFILER_GC_HANDLE_DESTROYED
)
1774 emit_event (logbuffer
, (do_bt
? TYPE_GC_HANDLE_DESTROYED_BT
: TYPE_GC_HANDLE_DESTROYED
) | TYPE_GC
);
1776 g_assert_not_reached ();
1778 emit_value (logbuffer
, type
);
1779 emit_value (logbuffer
, handle
);
1781 if (op
== MONO_PROFILER_GC_HANDLE_CREATED
)
1782 emit_obj (logbuffer
, obj
);
1785 emit_bt (prof
, logbuffer
, &data
);
1791 finalize_begin (MonoProfiler
*prof
)
1793 ENTER_LOG (&finalize_begins_ctr
, buf
,
1794 EVENT_SIZE
/* event */
1797 emit_event (buf
, TYPE_GC_FINALIZE_START
| TYPE_GC
);
1803 finalize_end (MonoProfiler
*prof
)
1805 ENTER_LOG (&finalize_ends_ctr
, buf
,
1806 EVENT_SIZE
/* event */
1809 emit_event (buf
, TYPE_GC_FINALIZE_END
| TYPE_GC
);
1815 finalize_object_begin (MonoProfiler
*prof
, MonoObject
*obj
)
1817 ENTER_LOG (&finalize_object_begins_ctr
, buf
,
1818 EVENT_SIZE
/* event */ +
1819 LEB128_SIZE
/* obj */
1822 emit_event (buf
, TYPE_GC_FINALIZE_OBJECT_START
| TYPE_GC
);
1823 emit_obj (buf
, obj
);
1829 finalize_object_end (MonoProfiler
*prof
, MonoObject
*obj
)
1831 ENTER_LOG (&finalize_object_ends_ctr
, buf
,
1832 EVENT_SIZE
/* event */ +
1833 LEB128_SIZE
/* obj */
1836 emit_event (buf
, TYPE_GC_FINALIZE_OBJECT_END
| TYPE_GC
);
1837 emit_obj (buf
, obj
);
1843 push_nesting (char *p
, MonoClass
*klass
)
1848 nesting
= mono_class_get_nesting_type (klass
);
1850 p
= push_nesting (p
, nesting
);
1854 name
= mono_class_get_name (klass
);
1855 nspace
= mono_class_get_namespace (klass
);
1858 p
+= strlen (nspace
);
1868 type_name (MonoClass
*klass
)
1872 push_nesting (buf
, klass
);
1873 p
= (char *)malloc (strlen (buf
) + 1);
1879 image_loaded (MonoProfiler
*prof
, MonoImage
*image
, int result
)
1881 if (result
!= MONO_PROFILE_OK
)
1884 const char *name
= mono_image_get_filename (image
);
1885 int nlen
= strlen (name
) + 1;
1887 ENTER_LOG (&image_loads_ctr
, logbuffer
,
1888 EVENT_SIZE
/* event */ +
1889 BYTE_SIZE
/* type */ +
1890 LEB128_SIZE
/* image */ +
1894 emit_event (logbuffer
, TYPE_END_LOAD
| TYPE_METADATA
);
1895 emit_byte (logbuffer
, TYPE_IMAGE
);
1896 emit_ptr (logbuffer
, image
);
1897 memcpy (logbuffer
->cursor
, name
, nlen
);
1898 logbuffer
->cursor
+= nlen
;
1904 image_unloaded (MonoProfiler
*prof
, MonoImage
*image
)
1906 const char *name
= mono_image_get_filename (image
);
1907 int nlen
= strlen (name
) + 1;
1909 ENTER_LOG (&image_unloads_ctr
, logbuffer
,
1910 EVENT_SIZE
/* event */ +
1911 BYTE_SIZE
/* type */ +
1912 LEB128_SIZE
/* image */ +
1916 emit_event (logbuffer
, TYPE_END_UNLOAD
| TYPE_METADATA
);
1917 emit_byte (logbuffer
, TYPE_IMAGE
);
1918 emit_ptr (logbuffer
, image
);
1919 memcpy (logbuffer
->cursor
, name
, nlen
);
1920 logbuffer
->cursor
+= nlen
;
1926 assembly_loaded (MonoProfiler
*prof
, MonoAssembly
*assembly
, int result
)
1928 if (result
!= MONO_PROFILE_OK
)
1931 char *name
= mono_stringify_assembly_name (mono_assembly_get_name (assembly
));
1932 int nlen
= strlen (name
) + 1;
1934 ENTER_LOG (&assembly_loads_ctr
, logbuffer
,
1935 EVENT_SIZE
/* event */ +
1936 BYTE_SIZE
/* type */ +
1937 LEB128_SIZE
/* assembly */ +
1941 emit_event (logbuffer
, TYPE_END_LOAD
| TYPE_METADATA
);
1942 emit_byte (logbuffer
, TYPE_ASSEMBLY
);
1943 emit_ptr (logbuffer
, assembly
);
1944 memcpy (logbuffer
->cursor
, name
, nlen
);
1945 logbuffer
->cursor
+= nlen
;
1953 assembly_unloaded (MonoProfiler
*prof
, MonoAssembly
*assembly
)
1955 char *name
= mono_stringify_assembly_name (mono_assembly_get_name (assembly
));
1956 int nlen
= strlen (name
) + 1;
1958 ENTER_LOG (&assembly_unloads_ctr
, logbuffer
,
1959 EVENT_SIZE
/* event */ +
1960 BYTE_SIZE
/* type */ +
1961 LEB128_SIZE
/* assembly */ +
1965 emit_event (logbuffer
, TYPE_END_UNLOAD
| TYPE_METADATA
);
1966 emit_byte (logbuffer
, TYPE_ASSEMBLY
);
1967 emit_ptr (logbuffer
, assembly
);
1968 memcpy (logbuffer
->cursor
, name
, nlen
);
1969 logbuffer
->cursor
+= nlen
;
1977 class_loaded (MonoProfiler
*prof
, MonoClass
*klass
, int result
)
1979 if (result
!= MONO_PROFILE_OK
)
1984 if (InterlockedRead (&runtime_inited
))
1985 name
= mono_type_get_name (mono_class_get_type (klass
));
1987 name
= type_name (klass
);
1989 int nlen
= strlen (name
) + 1;
1990 MonoImage
*image
= mono_class_get_image (klass
);
1992 ENTER_LOG (&class_loads_ctr
, logbuffer
,
1993 EVENT_SIZE
/* event */ +
1994 BYTE_SIZE
/* type */ +
1995 LEB128_SIZE
/* klass */ +
1996 LEB128_SIZE
/* image */ +
2000 emit_event (logbuffer
, TYPE_END_LOAD
| TYPE_METADATA
);
2001 emit_byte (logbuffer
, TYPE_CLASS
);
2002 emit_ptr (logbuffer
, klass
);
2003 emit_ptr (logbuffer
, image
);
2004 memcpy (logbuffer
->cursor
, name
, nlen
);
2005 logbuffer
->cursor
+= nlen
;
2016 class_unloaded (MonoProfiler
*prof
, MonoClass
*klass
)
2020 if (InterlockedRead (&runtime_inited
))
2021 name
= mono_type_get_name (mono_class_get_type (klass
));
2023 name
= type_name (klass
);
2025 int nlen
= strlen (name
) + 1;
2026 MonoImage
*image
= mono_class_get_image (klass
);
2028 ENTER_LOG (&class_unloads_ctr
, logbuffer
,
2029 EVENT_SIZE
/* event */ +
2030 BYTE_SIZE
/* type */ +
2031 LEB128_SIZE
/* klass */ +
2032 LEB128_SIZE
/* image */ +
2036 emit_event (logbuffer
, TYPE_END_UNLOAD
| TYPE_METADATA
);
2037 emit_byte (logbuffer
, TYPE_CLASS
);
2038 emit_ptr (logbuffer
, klass
);
2039 emit_ptr (logbuffer
, image
);
2040 memcpy (logbuffer
->cursor
, name
, nlen
);
2041 logbuffer
->cursor
+= nlen
;
2051 static void process_method_enter_coverage (MonoProfiler
*prof
, MonoMethod
*method
);
2054 method_enter (MonoProfiler
*prof
, MonoMethod
*method
)
2056 process_method_enter_coverage (prof
, method
);
2058 if (PROF_TLS_GET ()->call_depth
++ <= max_call_depth
) {
2059 ENTER_LOG (&method_entries_ctr
, logbuffer
,
2060 EVENT_SIZE
/* event */ +
2061 LEB128_SIZE
/* method */
2064 emit_event (logbuffer
, TYPE_ENTER
| TYPE_METHOD
);
2065 emit_method (prof
, logbuffer
, method
);
2072 method_leave (MonoProfiler
*prof
, MonoMethod
*method
)
2074 if (--PROF_TLS_GET ()->call_depth
<= max_call_depth
) {
2075 ENTER_LOG (&method_exits_ctr
, logbuffer
,
2076 EVENT_SIZE
/* event */ +
2077 LEB128_SIZE
/* method */
2080 emit_event (logbuffer
, TYPE_LEAVE
| TYPE_METHOD
);
2081 emit_method (prof
, logbuffer
, method
);
2088 method_exc_leave (MonoProfiler
*prof
, MonoMethod
*method
)
2090 if (!nocalls
&& --PROF_TLS_GET ()->call_depth
<= max_call_depth
) {
2091 ENTER_LOG (&method_exception_exits_ctr
, logbuffer
,
2092 EVENT_SIZE
/* event */ +
2093 LEB128_SIZE
/* method */
2096 emit_event (logbuffer
, TYPE_EXC_LEAVE
| TYPE_METHOD
);
2097 emit_method (prof
, logbuffer
, method
);
2104 method_jitted (MonoProfiler
*prof
, MonoMethod
*method
, MonoJitInfo
*ji
, int result
)
2106 if (result
!= MONO_PROFILE_OK
)
2109 register_method_local (prof
, method
, ji
);
2113 code_buffer_new (MonoProfiler
*prof
, void *buffer
, int size
, MonoProfilerCodeBufferType type
, void *data
)
2118 if (type
== MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE
) {
2119 name
= (char *) data
;
2120 nlen
= strlen (name
) + 1;
2126 ENTER_LOG (&code_buffers_ctr
, logbuffer
,
2127 EVENT_SIZE
/* event */ +
2128 BYTE_SIZE
/* type */ +
2129 LEB128_SIZE
/* buffer */ +
2130 LEB128_SIZE
/* size */ +
2136 emit_event (logbuffer
, TYPE_JITHELPER
| TYPE_RUNTIME
);
2137 emit_byte (logbuffer
, type
);
2138 emit_ptr (logbuffer
, buffer
);
2139 emit_value (logbuffer
, size
);
2142 memcpy (logbuffer
->cursor
, name
, nlen
);
2143 logbuffer
->cursor
+= nlen
;
2150 throw_exc (MonoProfiler
*prof
, MonoObject
*object
)
2152 int do_bt
= (nocalls
&& InterlockedRead (&runtime_inited
) && !notraces
) ? TYPE_THROW_BT
: 0;
2158 ENTER_LOG (&exception_throws_ctr
, logbuffer
,
2159 EVENT_SIZE
/* event */ +
2160 LEB128_SIZE
/* object */ +
2162 LEB128_SIZE
/* count */ +
2164 LEB128_SIZE
/* method */
2169 emit_event (logbuffer
, do_bt
| TYPE_EXCEPTION
);
2170 emit_obj (logbuffer
, object
);
2173 emit_bt (prof
, logbuffer
, &data
);
2179 clause_exc (MonoProfiler
*prof
, MonoMethod
*method
, int clause_type
, int clause_num
)
2181 ENTER_LOG (&exception_clauses_ctr
, logbuffer
,
2182 EVENT_SIZE
/* event */ +
2183 BYTE_SIZE
/* clause type */ +
2184 LEB128_SIZE
/* clause num */ +
2185 LEB128_SIZE
/* method */
2188 emit_event (logbuffer
, TYPE_EXCEPTION
| TYPE_CLAUSE
);
2189 emit_byte (logbuffer
, clause_type
);
2190 emit_value (logbuffer
, clause_num
);
2191 emit_method (prof
, logbuffer
, method
);
2197 monitor_event (MonoProfiler
*profiler
, MonoObject
*object
, MonoProfilerMonitorEvent event
)
2199 int do_bt
= (nocalls
&& InterlockedRead (&runtime_inited
) && !notraces
&& event
== MONO_PROFILER_MONITOR_CONTENTION
) ? TYPE_MONITOR_BT
: 0;
2208 case MONO_PROFILER_MONITOR_CONTENTION
:
2209 ctr
= &monitor_contentions_ctr
;
2211 case MONO_PROFILER_MONITOR_DONE
:
2212 ctr
= &monitor_acquisitions_ctr
;
2214 case MONO_PROFILER_MONITOR_FAIL
:
2215 ctr
= &monitor_failures_ctr
;
2218 g_assert_not_reached ();
2222 ENTER_LOG (ctr
, logbuffer
,
2223 EVENT_SIZE
/* event */ +
2224 LEB128_SIZE
/* object */ +
2226 LEB128_SIZE
/* count */ +
2228 LEB128_SIZE
/* method */
2233 emit_event (logbuffer
, (event
<< 4) | do_bt
| TYPE_MONITOR
);
2234 emit_obj (logbuffer
, object
);
2237 emit_bt (profiler
, logbuffer
, &data
);
2239 EXIT_LOG (profiler
);
2243 thread_start (MonoProfiler
*prof
, uintptr_t tid
)
2247 ENTER_LOG (&thread_starts_ctr
, logbuffer
,
2248 EVENT_SIZE
/* event */ +
2249 BYTE_SIZE
/* type */ +
2250 LEB128_SIZE
/* tid */
2253 emit_event (logbuffer
, TYPE_END_LOAD
| TYPE_METADATA
);
2254 emit_byte (logbuffer
, TYPE_THREAD
);
2255 emit_ptr (logbuffer
, (void*) tid
);
2261 thread_end (MonoProfiler
*prof
, uintptr_t tid
)
2263 ENTER_LOG (&thread_ends_ctr
, logbuffer
,
2264 EVENT_SIZE
/* event */ +
2265 BYTE_SIZE
/* type */ +
2266 LEB128_SIZE
/* tid */
2269 emit_event (logbuffer
, TYPE_END_UNLOAD
| TYPE_METADATA
);
2270 emit_byte (logbuffer
, TYPE_THREAD
);
2271 emit_ptr (logbuffer
, (void*) tid
);
2273 EXIT_LOG_EXPLICIT (prof
, FALSE
, FALSE
);
2275 remove_thread (prof
, PROF_TLS_GET (), TRUE
);
2279 thread_name (MonoProfiler
*prof
, uintptr_t tid
, const char *name
)
2281 int len
= strlen (name
) + 1;
2283 ENTER_LOG (&thread_names_ctr
, logbuffer
,
2284 EVENT_SIZE
/* event */ +
2285 BYTE_SIZE
/* type */ +
2286 LEB128_SIZE
/* tid */ +
2290 emit_event (logbuffer
, TYPE_METADATA
);
2291 emit_byte (logbuffer
, TYPE_THREAD
);
2292 emit_ptr (logbuffer
, (void*)tid
);
2293 memcpy (logbuffer
->cursor
, name
, len
);
2294 logbuffer
->cursor
+= len
;
2300 domain_loaded (MonoProfiler
*prof
, MonoDomain
*domain
, int result
)
2302 if (result
!= MONO_PROFILE_OK
)
2305 ENTER_LOG (&domain_loads_ctr
, logbuffer
,
2306 EVENT_SIZE
/* event */ +
2307 BYTE_SIZE
/* type */ +
2308 LEB128_SIZE
/* domain id */
2311 emit_event (logbuffer
, TYPE_END_LOAD
| TYPE_METADATA
);
2312 emit_byte (logbuffer
, TYPE_DOMAIN
);
2313 emit_ptr (logbuffer
, (void*)(uintptr_t) mono_domain_get_id (domain
));
2319 domain_unloaded (MonoProfiler
*prof
, MonoDomain
*domain
)
2321 ENTER_LOG (&domain_unloads_ctr
, logbuffer
,
2322 EVENT_SIZE
/* event */ +
2323 BYTE_SIZE
/* type */ +
2324 LEB128_SIZE
/* domain id */
2327 emit_event (logbuffer
, TYPE_END_UNLOAD
| TYPE_METADATA
);
2328 emit_byte (logbuffer
, TYPE_DOMAIN
);
2329 emit_ptr (logbuffer
, (void*)(uintptr_t) mono_domain_get_id (domain
));
2335 domain_name (MonoProfiler
*prof
, MonoDomain
*domain
, const char *name
)
2337 int nlen
= strlen (name
) + 1;
2339 ENTER_LOG (&domain_names_ctr
, logbuffer
,
2340 EVENT_SIZE
/* event */ +
2341 BYTE_SIZE
/* type */ +
2342 LEB128_SIZE
/* domain id */ +
2346 emit_event (logbuffer
, TYPE_METADATA
);
2347 emit_byte (logbuffer
, TYPE_DOMAIN
);
2348 emit_ptr (logbuffer
, (void*)(uintptr_t) mono_domain_get_id (domain
));
2349 memcpy (logbuffer
->cursor
, name
, nlen
);
2350 logbuffer
->cursor
+= nlen
;
2356 context_loaded (MonoProfiler
*prof
, MonoAppContext
*context
)
2358 ENTER_LOG (&context_loads_ctr
, logbuffer
,
2359 EVENT_SIZE
/* event */ +
2360 BYTE_SIZE
/* type */ +
2361 LEB128_SIZE
/* context id */ +
2362 LEB128_SIZE
/* domain id */
2365 emit_event (logbuffer
, TYPE_END_LOAD
| TYPE_METADATA
);
2366 emit_byte (logbuffer
, TYPE_CONTEXT
);
2367 emit_ptr (logbuffer
, (void*)(uintptr_t) mono_context_get_id (context
));
2368 emit_ptr (logbuffer
, (void*)(uintptr_t) mono_context_get_domain_id (context
));
2374 context_unloaded (MonoProfiler
*prof
, MonoAppContext
*context
)
2376 ENTER_LOG (&context_unloads_ctr
, logbuffer
,
2377 EVENT_SIZE
/* event */ +
2378 BYTE_SIZE
/* type */ +
2379 LEB128_SIZE
/* context id */ +
2380 LEB128_SIZE
/* domain id */
2383 emit_event (logbuffer
, TYPE_END_UNLOAD
| TYPE_METADATA
);
2384 emit_byte (logbuffer
, TYPE_CONTEXT
);
2385 emit_ptr (logbuffer
, (void*)(uintptr_t) mono_context_get_id (context
));
2386 emit_ptr (logbuffer
, (void*)(uintptr_t) mono_context_get_domain_id (context
));
2399 MonoLockFreeQueueNode node
;
2405 AsyncFrameInfo frames
[MONO_ZERO_LEN_ARRAY
];
2409 async_walk_stack (MonoMethod
*method
, MonoDomain
*domain
, void *base_address
, int offset
, void *data
)
2411 SampleHit
*sample
= (SampleHit
*) data
;
2413 if (sample
->count
< num_frames
) {
2414 int i
= sample
->count
;
2416 sample
->frames
[i
].method
= method
;
2417 sample
->frames
[i
].domain
= domain
;
2418 sample
->frames
[i
].base_address
= base_address
;
2419 sample
->frames
[i
].offset
= offset
;
2424 return sample
->count
== num_frames
;
2427 #define SAMPLE_SLOT_SIZE(FRAMES) (sizeof (SampleHit) + sizeof (AsyncFrameInfo) * (FRAMES - MONO_ZERO_LEN_ARRAY))
2428 #define SAMPLE_BLOCK_SIZE (mono_pagesize ())
2431 enqueue_sample_hit (gpointer p
)
2433 SampleHit
*sample
= p
;
2435 mono_lock_free_queue_node_unpoison (&sample
->node
);
2436 mono_lock_free_queue_enqueue (&sample
->prof
->dumper_queue
, &sample
->node
);
2437 mono_os_sem_post (&sample
->prof
->dumper_queue_sem
);
2441 mono_sample_hit (MonoProfiler
*profiler
, unsigned char *ip
, void *context
)
2444 * Please note: We rely on the runtime loading the profiler with
2445 * MONO_DL_EAGER (RTLD_NOW) so that references to runtime functions within
2446 * this function (and its siblings) are resolved when the profiler is
2447 * loaded. Otherwise, we would potentially invoke the dynamic linker when
2448 * invoking runtime functions, which is not async-signal-safe.
2454 SampleHit
*sample
= (SampleHit
*) mono_lock_free_queue_dequeue (&profiler
->sample_reuse_queue
);
2458 * If we're out of reusable sample events and we're not allowed to
2459 * allocate more, we have no choice but to drop the event.
2461 if (InterlockedRead (&sample_allocations_ctr
) >= max_allocated_sample_hits
)
2464 sample
= mono_lock_free_alloc (&profiler
->sample_allocator
);
2465 sample
->prof
= profiler
;
2466 mono_lock_free_queue_node_init (&sample
->node
, TRUE
);
2468 InterlockedIncrement (&sample_allocations_ctr
);
2472 mono_stack_walk_async_safe (&async_walk_stack
, context
, sample
);
2474 sample
->time
= current_time ();
2475 sample
->tid
= thread_id ();
2481 snprintf (buf
, sizeof (buf
), "hit at %p in thread %p after %llu ms\n", ip
, (void *) sample
->tid
, (unsigned long long int) ((sample
->time
- profiler
->startup_time
) / 10000 / 100));
2483 ign_res (write (2, buf
, len
));
2486 mono_thread_hazardous_try_free (sample
, enqueue_sample_hit
);
2489 static uintptr_t *code_pages
= 0;
2490 static int num_code_pages
= 0;
2491 static int size_code_pages
= 0;
2492 #define CPAGE_SHIFT (9)
2493 #define CPAGE_SIZE (1 << CPAGE_SHIFT)
2494 #define CPAGE_MASK (~(CPAGE_SIZE - 1))
2495 #define CPAGE_ADDR(p) ((p) & CPAGE_MASK)
2498 add_code_page (uintptr_t *hash
, uintptr_t hsize
, uintptr_t page
)
2501 uintptr_t start_pos
;
2502 start_pos
= (page
>> CPAGE_SHIFT
) % hsize
;
2505 if (hash
[i
] && CPAGE_ADDR (hash
[i
]) == CPAGE_ADDR (page
)) {
2507 } else if (!hash
[i
]) {
2514 } while (i
!= start_pos
);
2515 /* should not happen */
2516 printf ("failed code page store\n");
2521 add_code_pointer (uintptr_t ip
)
2524 if (num_code_pages
* 2 >= size_code_pages
) {
2526 uintptr_t old_size
= size_code_pages
;
2527 size_code_pages
*= 2;
2528 if (size_code_pages
== 0)
2529 size_code_pages
= 16;
2530 n
= (uintptr_t *)calloc (sizeof (uintptr_t) * size_code_pages
, 1);
2531 for (i
= 0; i
< old_size
; ++i
) {
2533 add_code_page (n
, size_code_pages
, code_pages
[i
]);
2536 g_free (code_pages
);
2539 num_code_pages
+= add_code_page (code_pages
, size_code_pages
, ip
& CPAGE_MASK
);
2542 /* ELF code crashes on some systems. */
2543 //#if defined(HAVE_DL_ITERATE_PHDR) && defined(ELFMAG0)
2546 dump_ubin (MonoProfiler
*prof
, const char *filename
, uintptr_t load_addr
, uint64_t offset
, uintptr_t size
)
2548 int len
= strlen (filename
) + 1;
2550 ENTER_LOG (&sample_ubins_ctr
, logbuffer
,
2551 EVENT_SIZE
/* event */ +
2552 LEB128_SIZE
/* load address */ +
2553 LEB128_SIZE
/* offset */ +
2554 LEB128_SIZE
/* size */ +
2555 nlen
/* file name */
2558 emit_event (logbuffer
, TYPE_SAMPLE
| TYPE_SAMPLE_UBIN
);
2559 emit_svalue (logbuffer
, load_addr
);
2560 emit_uvalue (logbuffer
, offset
);
2561 emit_uvalue (logbuffer
, size
);
2562 memcpy (logbuffer
->cursor
, filename
, len
);
2563 logbuffer
->cursor
+= len
;
2565 EXIT_LOG_EXPLICIT (prof
, TRUE
, FALSE
);
2570 dump_usym (MonoProfiler
*prof
, const char *name
, uintptr_t value
, uintptr_t size
)
2572 int len
= strlen (name
) + 1;
2574 ENTER_LOG (&sample_usyms_ctr
, logbuffer
,
2575 EVENT_SIZE
/* event */ +
2576 LEB128_SIZE
/* value */ +
2577 LEB128_SIZE
/* size */ +
2581 emit_event (logbuffer
, TYPE_SAMPLE
| TYPE_SAMPLE_USYM
);
2582 emit_ptr (logbuffer
, (void*)value
);
2583 emit_value (logbuffer
, size
);
2584 memcpy (logbuffer
->cursor
, name
, len
);
2585 logbuffer
->cursor
+= len
;
2587 EXIT_LOG_EXPLICIT (prof
, TRUE
, FALSE
);
2590 /* ELF code crashes on some systems. */
2591 //#if defined(ELFMAG0)
2594 #if SIZEOF_VOID_P == 4
2595 #define ELF_WSIZE 32
2597 #define ELF_WSIZE 64
2600 #define ElfW(type) _ElfW (Elf, ELF_WSIZE, type)
2601 #define _ElfW(e,w,t) _ElfW_1 (e, w, _##t)
2602 #define _ElfW_1(e,w,t) e##w##t
2606 dump_elf_symbols (MonoProfiler
*prof
, ElfW(Sym
) *symbols
, int num_symbols
, const char *strtab
, void *load_addr
)
2609 for (i
= 0; i
< num_symbols
; ++i
) {
2611 sym
= strtab
+ symbols
[i
].st_name
;
2612 if (!symbols
[i
].st_name
|| !symbols
[i
].st_size
|| (symbols
[i
].st_info
& 0xf) != STT_FUNC
)
2614 //printf ("symbol %s at %d\n", sym, symbols [i].st_value);
2615 dump_usym (sym
, (uintptr_t)load_addr
+ symbols
[i
].st_value
, symbols
[i
].st_size
);
2620 read_elf_symbols (MonoProfiler
*prof
, const char *filename
, void *load_addr
)
2627 ElfW(Shdr
) *sheader
;
2628 ElfW(Shdr
) *shstrtabh
;
2629 ElfW(Shdr
) *symtabh
= NULL
;
2630 ElfW(Shdr
) *strtabh
= NULL
;
2631 ElfW(Sym
) *symbols
= NULL
;
2635 fd
= open (filename
, O_RDONLY
);
2638 if (fstat (fd
, &statb
) != 0) {
2642 file_size
= statb
.st_size
;
2643 data
= mmap (NULL
, file_size
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
2645 if (data
== MAP_FAILED
)
2648 if (header
->e_ident
[EI_MAG0
] != ELFMAG0
||
2649 header
->e_ident
[EI_MAG1
] != ELFMAG1
||
2650 header
->e_ident
[EI_MAG2
] != ELFMAG2
||
2651 header
->e_ident
[EI_MAG3
] != ELFMAG3
) {
2652 munmap (data
, file_size
);
2655 sheader
= (void*)((char*)data
+ header
->e_shoff
);
2656 shstrtabh
= (void*)((char*)sheader
+ (header
->e_shentsize
* header
->e_shstrndx
));
2657 strtab
= (const char*)data
+ shstrtabh
->sh_offset
;
2658 for (i
= 0; i
< header
->e_shnum
; ++i
) {
2659 //printf ("section header: %d\n", sheader->sh_type);
2660 if (sheader
->sh_type
== SHT_SYMTAB
) {
2662 strtabh
= (void*)((char*)data
+ header
->e_shoff
+ sheader
->sh_link
* header
->e_shentsize
);
2663 /*printf ("symtab section header: %d, .strstr: %d\n", i, sheader->sh_link);*/
2666 sheader
= (void*)((char*)sheader
+ header
->e_shentsize
);
2668 if (!symtabh
|| !strtabh
) {
2669 munmap (data
, file_size
);
2672 strtab
= (const char*)data
+ strtabh
->sh_offset
;
2673 num_symbols
= symtabh
->sh_size
/ symtabh
->sh_entsize
;
2674 symbols
= (void*)((char*)data
+ symtabh
->sh_offset
);
2675 dump_elf_symbols (symbols
, num_symbols
, strtab
, load_addr
);
2676 munmap (data
, file_size
);
2681 /* ELF code crashes on some systems. */
2682 //#if defined(HAVE_DL_ITERATE_PHDR) && defined(ELFMAG0)
2685 elf_dl_callback (struct dl_phdr_info
*info
, size_t size
, void *data
)
2687 MonoProfiler
*prof
= data
;
2689 const char *filename
;
2691 char *a
= (void*)info
->dlpi_addr
;
2693 ElfW(Dyn
) *dyn
= NULL
;
2694 ElfW(Sym
) *symtab
= NULL
;
2695 ElfW(Word
) *hash_table
= NULL
;
2696 ElfW(Ehdr
) *header
= NULL
;
2697 const char* strtab
= NULL
;
2698 for (obj
= prof
->binary_objects
; obj
; obj
= obj
->next
) {
2702 filename
= info
->dlpi_name
;
2705 if (!info
->dlpi_addr
&& !filename
[0]) {
2706 int l
= readlink ("/proc/self/exe", buf
, sizeof (buf
) - 1);
2712 obj
= g_calloc (sizeof (BinaryObject
), 1);
2713 obj
->addr
= (void*)info
->dlpi_addr
;
2714 obj
->name
= pstrdup (filename
);
2715 obj
->next
= prof
->binary_objects
;
2716 prof
->binary_objects
= obj
;
2717 //printf ("loaded file: %s at %p, segments: %d\n", filename, (void*)info->dlpi_addr, info->dlpi_phnum);
2719 for (i
= 0; i
< info
->dlpi_phnum
; ++i
) {
2720 //printf ("segment type %d file offset: %d, size: %d\n", info->dlpi_phdr[i].p_type, info->dlpi_phdr[i].p_offset, info->dlpi_phdr[i].p_memsz);
2721 if (info
->dlpi_phdr
[i
].p_type
== PT_LOAD
&& !header
) {
2722 header
= (ElfW(Ehdr
)*)(info
->dlpi_addr
+ info
->dlpi_phdr
[i
].p_vaddr
);
2723 if (header
->e_ident
[EI_MAG0
] != ELFMAG0
||
2724 header
->e_ident
[EI_MAG1
] != ELFMAG1
||
2725 header
->e_ident
[EI_MAG2
] != ELFMAG2
||
2726 header
->e_ident
[EI_MAG3
] != ELFMAG3
) {
2729 dump_ubin (prof
, filename
, info
->dlpi_addr
+ info
->dlpi_phdr
[i
].p_vaddr
, info
->dlpi_phdr
[i
].p_offset
, info
->dlpi_phdr
[i
].p_memsz
);
2730 } else if (info
->dlpi_phdr
[i
].p_type
== PT_DYNAMIC
) {
2731 dyn
= (ElfW(Dyn
) *)(info
->dlpi_addr
+ info
->dlpi_phdr
[i
].p_vaddr
);
2734 if (read_elf_symbols (prof
, filename
, (void*)info
->dlpi_addr
))
2736 if (!info
->dlpi_name
|| !info
->dlpi_name
[0])
2740 for (i
= 0; dyn
[i
].d_tag
!= DT_NULL
; ++i
) {
2741 if (dyn
[i
].d_tag
== DT_SYMTAB
) {
2742 if (symtab
&& do_debug
)
2743 printf ("multiple symtabs: %d\n", i
);
2744 symtab
= (ElfW(Sym
) *)(a
+ dyn
[i
].d_un
.d_ptr
);
2745 } else if (dyn
[i
].d_tag
== DT_HASH
) {
2746 hash_table
= (ElfW(Word
) *)(a
+ dyn
[i
].d_un
.d_ptr
);
2747 } else if (dyn
[i
].d_tag
== DT_STRTAB
) {
2748 strtab
= (const char*)(a
+ dyn
[i
].d_un
.d_ptr
);
2753 num_sym
= hash_table
[1];
2754 dump_elf_symbols (prof
, symtab
, num_sym
, strtab
, (void*)info
->dlpi_addr
);
2759 load_binaries (MonoProfiler
*prof
)
2761 dl_iterate_phdr (elf_dl_callback
, prof
);
2766 load_binaries (MonoProfiler
*prof
)
2773 symbol_for (uintptr_t code
)
2776 void *ip
= (void*)code
;
2778 if (dladdr (ip
, &di
)) {
2780 return di
.dli_sname
;
2783 names = backtrace_symbols (&ip, 1);
2785 const char* p = names [0];
2796 dump_unmanaged_coderefs (MonoProfiler
*prof
)
2799 const char* last_symbol
;
2800 uintptr_t addr
, page_end
;
2802 if (load_binaries (prof
))
2804 for (i
= 0; i
< size_code_pages
; ++i
) {
2806 if (!code_pages
[i
] || code_pages
[i
] & 1)
2809 addr
= CPAGE_ADDR (code_pages
[i
]);
2810 page_end
= addr
+ CPAGE_SIZE
;
2811 code_pages
[i
] |= 1;
2812 /* we dump the symbols for the whole page */
2813 for (; addr
< page_end
; addr
+= 16) {
2814 sym
= symbol_for (addr
);
2815 if (sym
&& sym
== last_symbol
)
2820 dump_usym (prof
, sym
, addr
, 0); /* let's not guess the size */
2821 //printf ("found symbol at %p: %s\n", (void*)addr, sym);
2827 mono_cpu_count (void)
2829 #ifdef PLATFORM_ANDROID
2830 /* Android tries really hard to save power by powering off CPUs on SMP phones which
2831 * means the normal way to query cpu count returns a wrong value with userspace API.
2832 * Instead we use /sys entries to query the actual hardware CPU count.
2835 char buffer
[8] = {'\0'};
2836 int present
= open ("/sys/devices/system/cpu/present", O_RDONLY
);
2837 /* Format of the /sys entry is a cpulist of indexes which in the case
2838 * of present is always of the form "0-(n-1)" when there is more than
2839 * 1 core, n being the number of CPU cores in the system. Otherwise
2840 * the value is simply 0
2842 if (present
!= -1 && read (present
, (char*)buffer
, sizeof (buffer
)) > 3)
2843 count
= strtol (((char*)buffer
) + 2, NULL
, 10);
2850 #if defined(HOST_ARM) || defined (HOST_ARM64)
2852 /* ARM platforms tries really hard to save power by powering off CPUs on SMP phones which
2853 * means the normal way to query cpu count returns a wrong value with userspace API. */
2855 #ifdef _SC_NPROCESSORS_CONF
2857 int count
= sysconf (_SC_NPROCESSORS_CONF
);
2865 #ifdef HAVE_SCHED_GETAFFINITY
2868 if (sched_getaffinity (getpid (), sizeof (set
), &set
) == 0)
2869 return CPU_COUNT (&set
);
2872 #ifdef _SC_NPROCESSORS_ONLN
2874 int count
= sysconf (_SC_NPROCESSORS_ONLN
);
2880 #endif /* defined(HOST_ARM) || defined (HOST_ARM64) */
2886 size_t len
= sizeof (int);
2889 if (sysctl (mib
, 2, &count
, &len
, NULL
, 0) == 0)
2896 GetSystemInfo (&info
);
2897 return info
.dwNumberOfProcessors
;
2908 unsigned int prev_pos
;
2910 struct perf_event_mmap_page
*page_desc
;
2913 static PerfData
*perf_data
= NULL
;
2914 static int num_perf
;
2915 #define PERF_PAGES_SHIFT 4
2916 static int num_pages
= 1 << PERF_PAGES_SHIFT
;
2917 static unsigned int mmap_mask
;
2920 struct perf_event_header h
;
2930 perf_event_syscall (struct perf_event_attr
*attr
, pid_t pid
, int cpu
, int group_fd
, unsigned long flags
)
2932 attr
->size
= PERF_ATTR_SIZE_VER0
;
2933 //printf ("perf attr size: %d\n", attr->size);
2934 #if defined(__x86_64__)
2935 return syscall(/*__NR_perf_event_open*/ 298, attr
, pid
, cpu
, group_fd
, flags
);
2936 #elif defined(__i386__)
2937 return syscall(/*__NR_perf_event_open*/ 336, attr
, pid
, cpu
, group_fd
, flags
);
2938 #elif defined(__arm__) || defined (__aarch64__)
2939 return syscall(/*__NR_perf_event_open*/ 364, attr
, pid
, cpu
, group_fd
, flags
);
2946 setup_perf_map (PerfData
*perf
)
2948 perf
->mmap_base
= mmap (NULL
, (num_pages
+ 1) * getpagesize (), PROT_READ
|PROT_WRITE
, MAP_SHARED
, perf
->perf_fd
, 0);
2949 if (perf
->mmap_base
== MAP_FAILED
) {
2951 printf ("failed mmap\n");
2954 perf
->page_desc
= perf
->mmap_base
;
2956 printf ("mmap version: %d\n", perf
->page_desc
->version
);
2961 dump_perf_hits (MonoProfiler
*prof
, void *buf
, int size
)
2965 void *end
= (char*)buf
+ size
;
2967 int pid
= getpid ();
2973 if (pid
!= s
->pid
) {
2975 printf ("event for different pid: %d\n", s
->pid
);
2976 buf
= (char*)buf
+ s
->h
.size
;
2979 /*ip = (void*)s->ip;
2980 printf ("sample: %d, size: %d, ip: %p (%s), timestamp: %llu, nframes: %llu\n",
2981 s->h.type, s->h.size, ip, symbol_for (ip), s->timestamp, s->nframes);*/
2983 InterlockedIncrement (&sample_hits_ctr
);
2985 ENTER_LOG (&sample_hits_ctr
, logbuffer
,
2986 EVENT_SIZE
/* event */ +
2987 BYTE_SIZE
/* type */ +
2988 LEB128_SIZE
/* tid */ +
2989 LEB128_SIZE
/* count */ +
2991 LEB128_SIZE
/* ip */
2993 LEB128_SIZE
/* managed count */ +
2995 LEB128_SIZE
/* method */
2999 emit_event (logbuffer
, TYPE_SAMPLE
| TYPE_SAMPLE_HIT
);
3000 emit_byte (logbuffer
, sample_type
);
3002 * No useful thread ID to write here, since throughout the
3003 * profiler we use pthread_self () but the ID we get from
3004 * perf is the kernel's thread ID.
3006 emit_ptr (logbuffer
, 0);
3007 emit_value (logbuffer
, count
);
3008 emit_ptr (logbuffer
, (void*)(uintptr_t)s
->ip
);
3009 /* no support here yet for the managed backtrace */
3010 emit_uvalue (logbuffer
, mbt_count
);
3012 EXIT_LOG_EXPLICIT (prof
, TRUE
, FALSE
);
3014 add_code_pointer (s
->ip
);
3015 buf
= (char*)buf
+ s
->h
.size
;
3019 printf ("dumped %d samples\n", samples
);
3020 dump_unmanaged_coderefs (prof
);
3023 /* read events from the ring buffer */
3025 read_perf_mmap (MonoProfiler
* prof
, int cpu
)
3027 PerfData
*perf
= perf_data
+ cpu
;
3029 unsigned char *data
= (unsigned char*)perf
->mmap_base
+ getpagesize ();
3030 unsigned int head
= perf
->page_desc
->data_head
;
3034 mono_memory_read_barrier ();
3036 old
= perf
->prev_pos
;
3040 printf ("lost mmap events: old: %d, head: %d\n", old
, head
);
3044 if ((old
& mmap_mask
) + size
!= (head
& mmap_mask
)) {
3045 buf
= data
+ (old
& mmap_mask
);
3046 size
= mmap_mask
+ 1 - (old
& mmap_mask
);
3048 /* size bytes at buf */
3050 printf ("found1 bytes of events: %d\n", size
);
3051 dump_perf_hits (prof
, buf
, size
);
3053 buf
= data
+ (old
& mmap_mask
);
3055 /* size bytes at buf */
3057 printf ("found bytes of events: %d\n", size
);
3058 dump_perf_hits (prof
, buf
, size
);
3060 perf
->prev_pos
= old
;
3061 perf
->page_desc
->data_tail
= old
;
3066 setup_perf_event_for_cpu (PerfData
*perf
, int cpu
)
3068 struct perf_event_attr attr
;
3069 memset (&attr
, 0, sizeof (attr
));
3070 attr
.type
= PERF_TYPE_HARDWARE
;
3071 switch (sample_type
) {
3072 case SAMPLE_CYCLES
: attr
.config
= PERF_COUNT_HW_CPU_CYCLES
; break;
3073 case SAMPLE_INSTRUCTIONS
: attr
.config
= PERF_COUNT_HW_INSTRUCTIONS
; break;
3074 case SAMPLE_CACHE_MISSES
: attr
.config
= PERF_COUNT_HW_CACHE_MISSES
; break;
3075 case SAMPLE_CACHE_REFS
: attr
.config
= PERF_COUNT_HW_CACHE_REFERENCES
; break;
3076 case SAMPLE_BRANCHES
: attr
.config
= PERF_COUNT_HW_BRANCH_INSTRUCTIONS
; break;
3077 case SAMPLE_BRANCH_MISSES
: attr
.config
= PERF_COUNT_HW_BRANCH_MISSES
; break;
3078 default: attr
.config
= PERF_COUNT_HW_CPU_CYCLES
; break;
3080 attr
.sample_type
= PERF_SAMPLE_IP
| PERF_SAMPLE_TID
| PERF_SAMPLE_PERIOD
| PERF_SAMPLE_TIME
;
3081 // attr.sample_type |= PERF_SAMPLE_CALLCHAIN;
3082 attr
.read_format
= PERF_FORMAT_TOTAL_TIME_ENABLED
| PERF_FORMAT_TOTAL_TIME_RUNNING
| PERF_FORMAT_ID
;
3085 attr
.sample_freq
= sample_freq
;
3087 perf
->perf_fd
= perf_event_syscall (&attr
, getpid (), cpu
, -1, 0);
3089 printf ("perf fd: %d, freq: %d, event: %llu\n", perf
->perf_fd
, sample_freq
, attr
.config
);
3090 if (perf
->perf_fd
< 0) {
3091 if (perf
->perf_fd
== -EPERM
) {
3092 fprintf (stderr
, "Perf syscall denied, do \"echo 1 > /proc/sys/kernel/perf_event_paranoid\" as root to enable.\n");
3095 perror ("open perf event");
3099 if (!setup_perf_map (perf
)) {
3100 close (perf
->perf_fd
);
3108 setup_perf_event (void)
3111 mmap_mask
= num_pages
* getpagesize () - 1;
3112 num_perf
= mono_cpu_count ();
3113 perf_data
= g_calloc (num_perf
, sizeof (PerfData
));
3114 for (i
= 0; i
< num_perf
; ++i
) {
3115 count
+= setup_perf_event_for_cpu (perf_data
+ i
, i
);
3124 #endif /* USE_PERF_EVENTS */
3126 typedef struct MonoCounterAgent
{
3127 MonoCounter
*counter
;
3128 // MonoCounterAgent specific data :
3133 struct MonoCounterAgent
*next
;
3136 static MonoCounterAgent
* counters
;
3137 static int counters_index
= 1;
3138 static mono_mutex_t counters_mutex
;
3141 counters_add_agent (MonoCounter
*counter
)
3146 MonoCounterAgent
*agent
, *item
;
3148 mono_os_mutex_lock (&counters_mutex
);
3150 for (agent
= counters
; agent
; agent
= agent
->next
) {
3151 if (agent
->counter
== counter
) {
3152 agent
->value_size
= 0;
3154 g_free (agent
->value
);
3155 agent
->value
= NULL
;
3161 agent
= (MonoCounterAgent
*)malloc (sizeof (MonoCounterAgent
));
3162 agent
->counter
= counter
;
3163 agent
->value
= NULL
;
3164 agent
->value_size
= 0;
3165 agent
->index
= counters_index
++;
3179 mono_os_mutex_unlock (&counters_mutex
);
3183 counters_init_foreach_callback (MonoCounter
*counter
, gpointer data
)
3185 counters_add_agent (counter
);
3190 counters_init (MonoProfiler
*profiler
)
3192 mono_os_mutex_init (&counters_mutex
);
3194 mono_counters_on_register (&counters_add_agent
);
3195 mono_counters_foreach (counters_init_foreach_callback
, NULL
);
3199 counters_emit (MonoProfiler
*profiler
)
3201 MonoCounterAgent
*agent
;
3204 EVENT_SIZE
/* event */ +
3205 LEB128_SIZE
/* len */
3208 mono_os_mutex_lock (&counters_mutex
);
3210 for (agent
= counters
; agent
; agent
= agent
->next
) {
3215 LEB128_SIZE
/* section */ +
3216 strlen (mono_counter_get_name (agent
->counter
)) + 1 /* name */ +
3217 BYTE_SIZE
/* type */ +
3218 BYTE_SIZE
/* unit */ +
3219 BYTE_SIZE
/* variance */ +
3220 LEB128_SIZE
/* index */
3229 ENTER_LOG (&counter_descriptors_ctr
, logbuffer
, size
);
3231 emit_event (logbuffer
, TYPE_SAMPLE_COUNTERS_DESC
| TYPE_SAMPLE
);
3232 emit_value (logbuffer
, len
);
3234 for (agent
= counters
; agent
; agent
= agent
->next
) {
3240 name
= mono_counter_get_name (agent
->counter
);
3241 emit_value (logbuffer
, mono_counter_get_section (agent
->counter
));
3242 emit_string (logbuffer
, name
, strlen (name
) + 1);
3243 emit_byte (logbuffer
, mono_counter_get_type (agent
->counter
));
3244 emit_byte (logbuffer
, mono_counter_get_unit (agent
->counter
));
3245 emit_byte (logbuffer
, mono_counter_get_variance (agent
->counter
));
3246 emit_value (logbuffer
, agent
->index
);
3251 EXIT_LOG_EXPLICIT (profiler
, TRUE
, FALSE
);
3254 mono_os_mutex_unlock (&counters_mutex
);
3258 counters_sample (MonoProfiler
*profiler
, uint64_t timestamp
)
3260 MonoCounterAgent
*agent
;
3261 MonoCounter
*counter
;
3267 counters_emit (profiler
);
3270 buffer
= g_calloc (1, buffer_size
);
3272 mono_os_mutex_lock (&counters_mutex
);
3275 EVENT_SIZE
/* event */
3278 for (agent
= counters
; agent
; agent
= agent
->next
) {
3280 LEB128_SIZE
/* index */ +
3281 BYTE_SIZE
/* type */ +
3282 mono_counter_get_size (agent
->counter
) /* value */
3287 LEB128_SIZE
/* stop marker */
3290 ENTER_LOG (&counter_samples_ctr
, logbuffer
, size
);
3292 emit_event_time (logbuffer
, TYPE_SAMPLE_COUNTERS
| TYPE_SAMPLE
, timestamp
);
3294 for (agent
= counters
; agent
; agent
= agent
->next
) {
3297 counter
= agent
->counter
;
3299 size
= mono_counter_get_size (counter
);
3301 continue; // FIXME error
3302 } else if (size
> buffer_size
) {
3304 buffer
= g_realloc (buffer
, buffer_size
);
3307 memset (buffer
, 0, buffer_size
);
3309 if (mono_counters_sample (counter
, buffer
, size
) < 0)
3310 continue; // FIXME error
3312 type
= mono_counter_get_type (counter
);
3314 if (!agent
->value
) {
3315 agent
->value
= g_calloc (1, size
);
3316 agent
->value_size
= size
;
3318 if (type
== MONO_COUNTER_STRING
) {
3319 if (strcmp (agent
->value
, buffer
) == 0)
3322 if (agent
->value_size
== size
&& memcmp (agent
->value
, buffer
, size
) == 0)
3327 emit_uvalue (logbuffer
, agent
->index
);
3328 emit_byte (logbuffer
, type
);
3330 case MONO_COUNTER_INT
:
3331 #if SIZEOF_VOID_P == 4
3332 case MONO_COUNTER_WORD
:
3334 emit_svalue (logbuffer
, *(int*)buffer
- *(int*)agent
->value
);
3336 case MONO_COUNTER_UINT
:
3337 emit_uvalue (logbuffer
, *(guint
*)buffer
- *(guint
*)agent
->value
);
3339 case MONO_COUNTER_TIME_INTERVAL
:
3340 case MONO_COUNTER_LONG
:
3341 #if SIZEOF_VOID_P == 8
3342 case MONO_COUNTER_WORD
:
3344 emit_svalue (logbuffer
, *(gint64
*)buffer
- *(gint64
*)agent
->value
);
3346 case MONO_COUNTER_ULONG
:
3347 emit_uvalue (logbuffer
, *(guint64
*)buffer
- *(guint64
*)agent
->value
);
3349 case MONO_COUNTER_DOUBLE
:
3350 emit_double (logbuffer
, *(double*)buffer
);
3352 case MONO_COUNTER_STRING
:
3354 emit_byte (logbuffer
, 0);
3356 emit_byte (logbuffer
, 1);
3357 emit_string (logbuffer
, (char*)buffer
, size
);
3364 if (type
== MONO_COUNTER_STRING
&& size
> agent
->value_size
) {
3365 agent
->value
= g_realloc (agent
->value
, size
);
3366 agent
->value_size
= size
;
3370 memcpy (agent
->value
, buffer
, size
);
3374 emit_value (logbuffer
, 0);
3376 EXIT_LOG_EXPLICIT (profiler
, TRUE
, FALSE
);
3378 mono_os_mutex_unlock (&counters_mutex
);
3381 typedef struct _PerfCounterAgent PerfCounterAgent
;
3382 struct _PerfCounterAgent
{
3383 PerfCounterAgent
*next
;
3385 char *category_name
;
3394 static PerfCounterAgent
*perfcounters
= NULL
;
3397 perfcounters_emit (MonoProfiler
*profiler
)
3399 PerfCounterAgent
*pcagent
;
3402 EVENT_SIZE
/* event */ +
3403 LEB128_SIZE
/* len */
3406 for (pcagent
= perfcounters
; pcagent
; pcagent
= pcagent
->next
) {
3407 if (pcagent
->emitted
)
3411 LEB128_SIZE
/* section */ +
3412 strlen (pcagent
->category_name
) + 1 /* category name */ +
3413 strlen (pcagent
->name
) + 1 /* name */ +
3414 BYTE_SIZE
/* type */ +
3415 BYTE_SIZE
/* unit */ +
3416 BYTE_SIZE
/* variance */ +
3417 LEB128_SIZE
/* index */
3426 ENTER_LOG (&perfcounter_descriptors_ctr
, logbuffer
, size
);
3428 emit_event (logbuffer
, TYPE_SAMPLE_COUNTERS_DESC
| TYPE_SAMPLE
);
3429 emit_value (logbuffer
, len
);
3431 for (pcagent
= perfcounters
; pcagent
; pcagent
= pcagent
->next
) {
3432 if (pcagent
->emitted
)
3435 emit_value (logbuffer
, MONO_COUNTER_PERFCOUNTERS
);
3436 emit_string (logbuffer
, pcagent
->category_name
, strlen (pcagent
->category_name
) + 1);
3437 emit_string (logbuffer
, pcagent
->name
, strlen (pcagent
->name
) + 1);
3438 emit_byte (logbuffer
, MONO_COUNTER_LONG
);
3439 emit_byte (logbuffer
, MONO_COUNTER_RAW
);
3440 emit_byte (logbuffer
, MONO_COUNTER_VARIABLE
);
3441 emit_value (logbuffer
, pcagent
->index
);
3443 pcagent
->emitted
= 1;
3446 EXIT_LOG_EXPLICIT (profiler
, TRUE
, FALSE
);
3450 perfcounters_foreach (char *category_name
, char *name
, unsigned char type
, gint64 value
, gpointer user_data
)
3452 PerfCounterAgent
*pcagent
;
3454 for (pcagent
= perfcounters
; pcagent
; pcagent
= pcagent
->next
) {
3455 if (strcmp (pcagent
->category_name
, category_name
) != 0 || strcmp (pcagent
->name
, name
) != 0)
3457 if (pcagent
->value
== value
)
3460 pcagent
->value
= value
;
3461 pcagent
->updated
= 1;
3462 pcagent
->deleted
= 0;
3466 pcagent
= g_new0 (PerfCounterAgent
, 1);
3467 pcagent
->next
= perfcounters
;
3468 pcagent
->index
= counters_index
++;
3469 pcagent
->category_name
= g_strdup (category_name
);
3470 pcagent
->name
= g_strdup (name
);
3471 pcagent
->type
= (int) type
;
3472 pcagent
->value
= value
;
3473 pcagent
->emitted
= 0;
3474 pcagent
->updated
= 1;
3475 pcagent
->deleted
= 0;
3477 perfcounters
= pcagent
;
3483 perfcounters_sample (MonoProfiler
*profiler
, uint64_t timestamp
)
3485 PerfCounterAgent
*pcagent
;
3489 mono_os_mutex_lock (&counters_mutex
);
3491 /* mark all perfcounters as deleted, foreach will unmark them as necessary */
3492 for (pcagent
= perfcounters
; pcagent
; pcagent
= pcagent
->next
)
3493 pcagent
->deleted
= 1;
3495 mono_perfcounter_foreach (perfcounters_foreach
, perfcounters
);
3497 perfcounters_emit (profiler
);
3500 EVENT_SIZE
/* event */
3503 for (pcagent
= perfcounters
; pcagent
; pcagent
= pcagent
->next
) {
3504 if (pcagent
->deleted
|| !pcagent
->updated
)
3508 LEB128_SIZE
/* index */ +
3509 BYTE_SIZE
/* type */ +
3510 LEB128_SIZE
/* value */
3520 LEB128_SIZE
/* stop marker */
3523 ENTER_LOG (&perfcounter_samples_ctr
, logbuffer
, size
);
3525 emit_event_time (logbuffer
, TYPE_SAMPLE_COUNTERS
| TYPE_SAMPLE
, timestamp
);
3527 for (pcagent
= perfcounters
; pcagent
; pcagent
= pcagent
->next
) {
3528 if (pcagent
->deleted
|| !pcagent
->updated
)
3530 emit_uvalue (logbuffer
, pcagent
->index
);
3531 emit_byte (logbuffer
, MONO_COUNTER_LONG
);
3532 emit_svalue (logbuffer
, pcagent
->value
);
3534 pcagent
->updated
= 0;
3537 emit_value (logbuffer
, 0);
3539 EXIT_LOG_EXPLICIT (profiler
, TRUE
, FALSE
);
3542 mono_os_mutex_unlock (&counters_mutex
);
3546 counters_and_perfcounters_sample (MonoProfiler
*prof
)
3548 uint64_t now
= current_time ();
3550 counters_sample (prof
, now
);
3551 perfcounters_sample (prof
, now
);
3554 #define COVERAGE_DEBUG(x) if (debug_coverage) {x}
3555 static mono_mutex_t coverage_mutex
;
3556 static MonoConcurrentHashTable
*coverage_methods
= NULL
;
3557 static MonoConcurrentHashTable
*coverage_assemblies
= NULL
;
3558 static MonoConcurrentHashTable
*coverage_classes
= NULL
;
3560 static MonoConcurrentHashTable
*filtered_classes
= NULL
;
3561 static MonoConcurrentHashTable
*entered_methods
= NULL
;
3562 static MonoConcurrentHashTable
*image_to_methods
= NULL
;
3563 static MonoConcurrentHashTable
*suppressed_assemblies
= NULL
;
3564 static gboolean coverage_initialized
= FALSE
;
3566 static GPtrArray
*coverage_data
= NULL
;
3567 static int previous_offset
= 0;
3570 MonoLockFreeQueueNode node
;
3583 free_coverage_entry (gpointer data
, gpointer userdata
)
3585 CoverageEntry
*entry
= (CoverageEntry
*)data
;
3586 g_free (entry
->filename
);
3591 obtain_coverage_for_method (MonoProfiler
*prof
, const MonoProfileCoverageEntry
*entry
)
3593 int offset
= entry
->iloffset
- previous_offset
;
3594 CoverageEntry
*e
= g_new (CoverageEntry
, 1);
3596 previous_offset
= entry
->iloffset
;
3599 e
->counter
= entry
->counter
;
3600 e
->filename
= g_strdup(entry
->filename
? entry
->filename
: "");
3601 e
->line
= entry
->line
;
3602 e
->column
= entry
->col
;
3604 g_ptr_array_add (coverage_data
, e
);
3608 parse_generic_type_names(char *name
)
3610 char *new_name
, *ret
;
3611 int within_generic_declaration
= 0, generic_members
= 1;
3613 if (name
== NULL
|| *name
== '\0')
3614 return g_strdup ("");
3616 if (!(ret
= new_name
= (char *)calloc (strlen (name
) * 4 + 1, sizeof (char))))
3622 within_generic_declaration
= 1;
3626 within_generic_declaration
= 0;
3628 if (*(name
- 1) != '<') {
3630 *new_name
++ = '0' + generic_members
;
3632 memcpy (new_name
, "<>", 8);
3636 generic_members
= 0;
3644 if (!within_generic_declaration
)
3645 *new_name
++ = *name
;
3654 static int method_id
;
3656 build_method_buffer (gpointer key
, gpointer value
, gpointer userdata
)
3658 MonoMethod
*method
= (MonoMethod
*)value
;
3659 MonoProfiler
*prof
= (MonoProfiler
*)userdata
;
3663 const char *image_name
, *method_name
, *sig
, *first_filename
;
3666 previous_offset
= 0;
3667 coverage_data
= g_ptr_array_new ();
3669 mono_profiler_coverage_get (prof
, method
, obtain_coverage_for_method
);
3671 klass
= mono_method_get_class (method
);
3672 image
= mono_class_get_image (klass
);
3673 image_name
= mono_image_get_name (image
);
3675 sig
= mono_signature_get_desc (mono_method_signature (method
), TRUE
);
3676 class_name
= parse_generic_type_names (mono_type_get_name (mono_class_get_type (klass
)));
3677 method_name
= mono_method_get_name (method
);
3679 if (coverage_data
->len
!= 0) {
3680 CoverageEntry
*entry
= (CoverageEntry
*)coverage_data
->pdata
[0];
3681 first_filename
= entry
->filename
? entry
->filename
: "";
3683 first_filename
= "";
3685 image_name
= image_name
? image_name
: "";
3686 sig
= sig
? sig
: "";
3687 method_name
= method_name
? method_name
: "";
3689 ENTER_LOG (&coverage_methods_ctr
, logbuffer
,
3690 EVENT_SIZE
/* event */ +
3691 strlen (image_name
) + 1 /* image name */ +
3692 strlen (class_name
) + 1 /* class name */ +
3693 strlen (method_name
) + 1 /* method name */ +
3694 strlen (sig
) + 1 /* signature */ +
3695 strlen (first_filename
) + 1 /* first file name */ +
3696 LEB128_SIZE
/* token */ +
3697 LEB128_SIZE
/* method id */ +
3698 LEB128_SIZE
/* entries */
3701 emit_event (logbuffer
, TYPE_COVERAGE_METHOD
| TYPE_COVERAGE
);
3702 emit_string (logbuffer
, image_name
, strlen (image_name
) + 1);
3703 emit_string (logbuffer
, class_name
, strlen (class_name
) + 1);
3704 emit_string (logbuffer
, method_name
, strlen (method_name
) + 1);
3705 emit_string (logbuffer
, sig
, strlen (sig
) + 1);
3706 emit_string (logbuffer
, first_filename
, strlen (first_filename
) + 1);
3708 emit_uvalue (logbuffer
, mono_method_get_token (method
));
3709 emit_uvalue (logbuffer
, method_id
);
3710 emit_value (logbuffer
, coverage_data
->len
);
3712 EXIT_LOG_EXPLICIT (prof
, TRUE
, FALSE
);
3714 for (i
= 0; i
< coverage_data
->len
; i
++) {
3715 CoverageEntry
*entry
= (CoverageEntry
*)coverage_data
->pdata
[i
];
3717 ENTER_LOG (&coverage_statements_ctr
, logbuffer
,
3718 EVENT_SIZE
/* event */ +
3719 LEB128_SIZE
/* method id */ +
3720 LEB128_SIZE
/* offset */ +
3721 LEB128_SIZE
/* counter */ +
3722 LEB128_SIZE
/* line */ +
3723 LEB128_SIZE
/* column */
3726 emit_event (logbuffer
, TYPE_COVERAGE_STATEMENT
| TYPE_COVERAGE
);
3727 emit_uvalue (logbuffer
, method_id
);
3728 emit_uvalue (logbuffer
, entry
->offset
);
3729 emit_uvalue (logbuffer
, entry
->counter
);
3730 emit_uvalue (logbuffer
, entry
->line
);
3731 emit_uvalue (logbuffer
, entry
->column
);
3733 EXIT_LOG_EXPLICIT (prof
, TRUE
, FALSE
);
3738 g_free (class_name
);
3740 g_ptr_array_foreach (coverage_data
, free_coverage_entry
, NULL
);
3741 g_ptr_array_free (coverage_data
, TRUE
);
3742 coverage_data
= NULL
;
3745 /* This empties the queue */
3747 count_queue (MonoLockFreeQueue
*queue
)
3749 MonoLockFreeQueueNode
*node
;
3752 while ((node
= mono_lock_free_queue_dequeue (queue
))) {
3754 mono_thread_hazardous_try_free (node
, free
);
3761 build_class_buffer (gpointer key
, gpointer value
, gpointer userdata
)
3763 MonoClass
*klass
= (MonoClass
*)key
;
3764 MonoLockFreeQueue
*class_methods
= (MonoLockFreeQueue
*)value
;
3765 MonoProfiler
*prof
= (MonoProfiler
*)userdata
;
3768 const char *assembly_name
;
3769 int number_of_methods
, partially_covered
;
3770 guint fully_covered
;
3772 image
= mono_class_get_image (klass
);
3773 assembly_name
= mono_image_get_name (image
);
3774 class_name
= mono_type_get_name (mono_class_get_type (klass
));
3776 assembly_name
= assembly_name
? assembly_name
: "";
3777 number_of_methods
= mono_class_num_methods (klass
);
3778 fully_covered
= count_queue (class_methods
);
3779 /* We don't handle partial covered yet */
3780 partially_covered
= 0;
3782 ENTER_LOG (&coverage_classes_ctr
, logbuffer
,
3783 EVENT_SIZE
/* event */ +
3784 strlen (assembly_name
) + 1 /* assembly name */ +
3785 strlen (class_name
) + 1 /* class name */ +
3786 LEB128_SIZE
/* no. methods */ +
3787 LEB128_SIZE
/* fully covered */ +
3788 LEB128_SIZE
/* partially covered */
3791 emit_event (logbuffer
, TYPE_COVERAGE_CLASS
| TYPE_COVERAGE
);
3792 emit_string (logbuffer
, assembly_name
, strlen (assembly_name
) + 1);
3793 emit_string (logbuffer
, class_name
, strlen (class_name
) + 1);
3794 emit_uvalue (logbuffer
, number_of_methods
);
3795 emit_uvalue (logbuffer
, fully_covered
);
3796 emit_uvalue (logbuffer
, partially_covered
);
3798 EXIT_LOG_EXPLICIT (prof
, TRUE
, FALSE
);
3800 g_free (class_name
);
3804 get_coverage_for_image (MonoImage
*image
, int *number_of_methods
, guint
*fully_covered
, int *partially_covered
)
3806 MonoLockFreeQueue
*image_methods
= (MonoLockFreeQueue
*)mono_conc_hashtable_lookup (image_to_methods
, image
);
3808 *number_of_methods
= mono_image_get_table_rows (image
, MONO_TABLE_METHOD
);
3810 *fully_covered
= count_queue (image_methods
);
3814 // FIXME: We don't handle partially covered yet.
3815 *partially_covered
= 0;
3819 build_assembly_buffer (gpointer key
, gpointer value
, gpointer userdata
)
3821 MonoAssembly
*assembly
= (MonoAssembly
*)value
;
3822 MonoProfiler
*prof
= (MonoProfiler
*)userdata
;
3823 MonoImage
*image
= mono_assembly_get_image (assembly
);
3824 const char *name
, *guid
, *filename
;
3825 int number_of_methods
= 0, partially_covered
= 0;
3826 guint fully_covered
= 0;
3828 name
= mono_image_get_name (image
);
3829 guid
= mono_image_get_guid (image
);
3830 filename
= mono_image_get_filename (image
);
3832 name
= name
? name
: "";
3833 guid
= guid
? guid
: "";
3834 filename
= filename
? filename
: "";
3836 get_coverage_for_image (image
, &number_of_methods
, &fully_covered
, &partially_covered
);
3838 ENTER_LOG (&coverage_assemblies_ctr
, logbuffer
,
3839 EVENT_SIZE
/* event */ +
3840 strlen (name
) + 1 /* name */ +
3841 strlen (guid
) + 1 /* guid */ +
3842 strlen (filename
) + 1 /* file name */ +
3843 LEB128_SIZE
/* no. methods */ +
3844 LEB128_SIZE
/* fully covered */ +
3845 LEB128_SIZE
/* partially covered */
3848 emit_event (logbuffer
, TYPE_COVERAGE_ASSEMBLY
| TYPE_COVERAGE
);
3849 emit_string (logbuffer
, name
, strlen (name
) + 1);
3850 emit_string (logbuffer
, guid
, strlen (guid
) + 1);
3851 emit_string (logbuffer
, filename
, strlen (filename
) + 1);
3852 emit_uvalue (logbuffer
, number_of_methods
);
3853 emit_uvalue (logbuffer
, fully_covered
);
3854 emit_uvalue (logbuffer
, partially_covered
);
3856 EXIT_LOG_EXPLICIT (prof
, TRUE
, FALSE
);
3860 dump_coverage (MonoProfiler
*prof
)
3862 if (!coverage_initialized
)
3865 COVERAGE_DEBUG(fprintf (stderr
, "Coverage: Started dump\n");)
3868 mono_os_mutex_lock (&coverage_mutex
);
3869 mono_conc_hashtable_foreach (coverage_assemblies
, build_assembly_buffer
, prof
);
3870 mono_conc_hashtable_foreach (coverage_classes
, build_class_buffer
, prof
);
3871 mono_conc_hashtable_foreach (coverage_methods
, build_method_buffer
, prof
);
3872 mono_os_mutex_unlock (&coverage_mutex
);
3874 COVERAGE_DEBUG(fprintf (stderr
, "Coverage: Finished dump\n");)
3878 process_method_enter_coverage (MonoProfiler
*prof
, MonoMethod
*method
)
3883 if (!coverage_initialized
)
3886 klass
= mono_method_get_class (method
);
3887 image
= mono_class_get_image (klass
);
3889 if (mono_conc_hashtable_lookup (suppressed_assemblies
, (gpointer
) mono_image_get_name (image
)))
3892 mono_os_mutex_lock (&coverage_mutex
);
3893 mono_conc_hashtable_insert (entered_methods
, method
, method
);
3894 mono_os_mutex_unlock (&coverage_mutex
);
3897 static MonoLockFreeQueueNode
*
3898 create_method_node (MonoMethod
*method
)
3900 MethodNode
*node
= (MethodNode
*)g_malloc (sizeof (MethodNode
));
3901 mono_lock_free_queue_node_init ((MonoLockFreeQueueNode
*) node
, FALSE
);
3902 node
->method
= method
;
3904 return (MonoLockFreeQueueNode
*) node
;
3908 coverage_filter (MonoProfiler
*prof
, MonoMethod
*method
)
3913 MonoAssembly
*assembly
;
3914 MonoMethodHeader
*header
;
3915 guint32 iflags
, flags
, code_size
;
3916 char *fqn
, *classname
;
3917 gboolean has_positive
, found
;
3918 MonoLockFreeQueue
*image_methods
, *class_methods
;
3919 MonoLockFreeQueueNode
*node
;
3921 if (!coverage_initialized
)
3924 COVERAGE_DEBUG(fprintf (stderr
, "Coverage filter for %s\n", mono_method_get_name (method
));)
3926 flags
= mono_method_get_flags (method
, &iflags
);
3927 if ((iflags
& 0x1000 /*METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL*/) ||
3928 (flags
& 0x2000 /*METHOD_ATTRIBUTE_PINVOKE_IMPL*/)) {
3929 COVERAGE_DEBUG(fprintf (stderr
, " Internal call or pinvoke - ignoring\n");)
3933 // Don't need to do anything else if we're already tracking this method
3934 if (mono_conc_hashtable_lookup (coverage_methods
, method
)) {
3935 COVERAGE_DEBUG(fprintf (stderr
, " Already tracking\n");)
3939 klass
= mono_method_get_class (method
);
3940 image
= mono_class_get_image (klass
);
3942 // Don't handle coverage for the core assemblies
3943 if (mono_conc_hashtable_lookup (suppressed_assemblies
, (gpointer
) mono_image_get_name (image
)) != NULL
)
3946 if (prof
->coverage_filters
) {
3947 /* Check already filtered classes first */
3948 if (mono_conc_hashtable_lookup (filtered_classes
, klass
)) {
3949 COVERAGE_DEBUG(fprintf (stderr
, " Already filtered\n");)
3953 classname
= mono_type_get_name (mono_class_get_type (klass
));
3955 fqn
= g_strdup_printf ("[%s]%s", mono_image_get_name (image
), classname
);
3957 COVERAGE_DEBUG(fprintf (stderr
, " Looking for %s in filter\n", fqn
);)
3958 // Check positive filters first
3959 has_positive
= FALSE
;
3961 for (guint i
= 0; i
< prof
->coverage_filters
->len
; ++i
) {
3962 char *filter
= (char *)g_ptr_array_index (prof
->coverage_filters
, i
);
3964 if (filter
[0] == '+') {
3965 filter
= &filter
[1];
3967 COVERAGE_DEBUG(fprintf (stderr
, " Checking against +%s ...", filter
);)
3969 if (strstr (fqn
, filter
) != NULL
) {
3970 COVERAGE_DEBUG(fprintf (stderr
, "matched\n");)
3973 COVERAGE_DEBUG(fprintf (stderr
, "no match\n");)
3975 has_positive
= TRUE
;
3979 if (has_positive
&& !found
) {
3980 COVERAGE_DEBUG(fprintf (stderr
, " Positive match was not found\n");)
3982 mono_os_mutex_lock (&coverage_mutex
);
3983 mono_conc_hashtable_insert (filtered_classes
, klass
, klass
);
3984 mono_os_mutex_unlock (&coverage_mutex
);
3991 for (guint i
= 0; i
< prof
->coverage_filters
->len
; ++i
) {
3992 // FIXME: Is substring search sufficient?
3993 char *filter
= (char *)g_ptr_array_index (prof
->coverage_filters
, i
);
3994 if (filter
[0] == '+')
3998 filter
= &filter
[1];
3999 COVERAGE_DEBUG(fprintf (stderr
, " Checking against -%s ...", filter
);)
4001 if (strstr (fqn
, filter
) != NULL
) {
4002 COVERAGE_DEBUG(fprintf (stderr
, "matched\n");)
4004 mono_os_mutex_lock (&coverage_mutex
);
4005 mono_conc_hashtable_insert (filtered_classes
, klass
, klass
);
4006 mono_os_mutex_unlock (&coverage_mutex
);
4012 COVERAGE_DEBUG(fprintf (stderr
, "no match\n");)
4020 COVERAGE_DEBUG(fprintf (stderr
, " Handling coverage for %s\n", mono_method_get_name (method
));)
4021 header
= mono_method_get_header_checked (method
, &error
);
4022 mono_error_cleanup (&error
);
4024 mono_method_header_get_code (header
, &code_size
, NULL
);
4026 assembly
= mono_image_get_assembly (image
);
4028 // Need to keep the assemblies around for as long as they are kept in the hashtable
4029 // Nunit, for example, has a habit of unloading them before the coverage statistics are
4030 // generated causing a crash. See https://bugzilla.xamarin.com/show_bug.cgi?id=39325
4031 mono_assembly_addref (assembly
);
4033 mono_os_mutex_lock (&coverage_mutex
);
4034 mono_conc_hashtable_insert (coverage_methods
, method
, method
);
4035 mono_conc_hashtable_insert (coverage_assemblies
, assembly
, assembly
);
4036 mono_os_mutex_unlock (&coverage_mutex
);
4038 image_methods
= (MonoLockFreeQueue
*)mono_conc_hashtable_lookup (image_to_methods
, image
);
4040 if (image_methods
== NULL
) {
4041 image_methods
= (MonoLockFreeQueue
*)g_malloc (sizeof (MonoLockFreeQueue
));
4042 mono_lock_free_queue_init (image_methods
);
4043 mono_os_mutex_lock (&coverage_mutex
);
4044 mono_conc_hashtable_insert (image_to_methods
, image
, image_methods
);
4045 mono_os_mutex_unlock (&coverage_mutex
);
4048 node
= create_method_node (method
);
4049 mono_lock_free_queue_enqueue (image_methods
, node
);
4051 class_methods
= (MonoLockFreeQueue
*)mono_conc_hashtable_lookup (coverage_classes
, klass
);
4053 if (class_methods
== NULL
) {
4054 class_methods
= (MonoLockFreeQueue
*)g_malloc (sizeof (MonoLockFreeQueue
));
4055 mono_lock_free_queue_init (class_methods
);
4056 mono_os_mutex_lock (&coverage_mutex
);
4057 mono_conc_hashtable_insert (coverage_classes
, klass
, class_methods
);
4058 mono_os_mutex_unlock (&coverage_mutex
);
4061 node
= create_method_node (method
);
4062 mono_lock_free_queue_enqueue (class_methods
, node
);
4067 #define LINE_BUFFER_SIZE 4096
4068 /* Max file limit of 128KB */
4069 #define MAX_FILE_SIZE 128 * 1024
4071 get_file_content (FILE *stream
)
4076 int res
, offset
= 0;
4078 res
= fseek (stream
, 0, SEEK_END
);
4082 filesize
= ftell (stream
);
4086 res
= fseek (stream
, 0, SEEK_SET
);
4090 if (filesize
> MAX_FILE_SIZE
)
4093 buffer
= (char *)g_malloc ((filesize
+ 1) * sizeof (char));
4094 while ((bytes_read
= fread (buffer
+ offset
, 1, LINE_BUFFER_SIZE
, stream
)) > 0)
4095 offset
+= bytes_read
;
4097 /* NULL terminate our buffer */
4098 buffer
[filesize
] = '\0';
4103 get_next_line (char *contents
, char **next_start
)
4107 if (p
== NULL
|| *p
== '\0') {
4112 while (*p
!= '\n' && *p
!= '\0')
4117 *next_start
= p
+ 1;
4125 init_suppressed_assemblies (void)
4131 suppressed_assemblies
= mono_conc_hashtable_new (g_str_hash
, g_str_equal
);
4132 sa_file
= fopen (SUPPRESSION_DIR
"/mono-profiler-log.suppression", "r");
4133 if (sa_file
== NULL
)
4136 /* Don't need to free @content as it is referred to by the lines stored in @suppressed_assemblies */
4137 content
= get_file_content (sa_file
);
4138 if (content
== NULL
) {
4139 g_error ("mono-profiler-log.suppression is greater than 128kb - aborting\n");
4142 while ((line
= get_next_line (content
, &content
))) {
4143 line
= g_strchomp (g_strchug (line
));
4144 /* No locking needed as we're doing initialization */
4145 mono_conc_hashtable_insert (suppressed_assemblies
, line
, line
);
4152 coverage_init (MonoProfiler
*prof
)
4154 assert (!coverage_initialized
);
4156 COVERAGE_DEBUG(fprintf (stderr
, "Coverage initialized\n");)
4158 mono_os_mutex_init (&coverage_mutex
);
4159 coverage_methods
= mono_conc_hashtable_new (NULL
, NULL
);
4160 coverage_assemblies
= mono_conc_hashtable_new (NULL
, NULL
);
4161 coverage_classes
= mono_conc_hashtable_new (NULL
, NULL
);
4162 filtered_classes
= mono_conc_hashtable_new (NULL
, NULL
);
4163 entered_methods
= mono_conc_hashtable_new (NULL
, NULL
);
4164 image_to_methods
= mono_conc_hashtable_new (NULL
, NULL
);
4165 init_suppressed_assemblies ();
4167 coverage_initialized
= TRUE
;
4171 unref_coverage_assemblies (gpointer key
, gpointer value
, gpointer userdata
)
4173 MonoAssembly
*assembly
= (MonoAssembly
*)value
;
4174 mono_assembly_close (assembly
);
4178 free_sample_hit (gpointer p
)
4180 mono_lock_free_free (p
, SAMPLE_BLOCK_SIZE
);
4184 cleanup_reusable_samples (MonoProfiler
*prof
)
4188 while ((sample
= (SampleHit
*) mono_lock_free_queue_dequeue (&prof
->sample_reuse_queue
)))
4189 mono_thread_hazardous_try_free (sample
, free_sample_hit
);
4193 log_shutdown (MonoProfiler
*prof
)
4199 counters_and_perfcounters_sample (prof
);
4201 dump_coverage (prof
);
4204 ign_res (write (prof
->pipes
[1], &c
, 1));
4205 pthread_join (prof
->helper_thread
, &res
);
4207 mono_os_mutex_destroy (&counters_mutex
);
4209 MonoCounterAgent
*mc_next
;
4211 for (MonoCounterAgent
*cur
= counters
; cur
; cur
= mc_next
) {
4212 mc_next
= cur
->next
;
4216 PerfCounterAgent
*pc_next
;
4218 for (PerfCounterAgent
*cur
= perfcounters
; cur
; cur
= pc_next
) {
4219 pc_next
= cur
->next
;
4226 for (i
= 0; i
< num_perf
; ++i
)
4227 read_perf_mmap (prof
, i
);
4232 * Ensure that we empty the LLS completely, even if some nodes are
4233 * not immediately removed upon calling mono_lls_remove (), by
4234 * iterating until the head is NULL.
4236 while (profiler_thread_list
.head
) {
4237 MONO_LLS_FOREACH_SAFE (&profiler_thread_list
, MonoProfilerThread
, thread
) {
4238 remove_thread (prof
, thread
, FALSE
);
4239 } MONO_LLS_FOREACH_SAFE_END
4242 InterlockedWrite (&prof
->run_dumper_thread
, 0);
4243 mono_os_sem_post (&prof
->dumper_queue_sem
);
4244 pthread_join (prof
->dumper_thread
, &res
);
4245 mono_os_sem_destroy (&prof
->dumper_queue_sem
);
4247 InterlockedWrite (&prof
->run_writer_thread
, 0);
4248 mono_os_sem_post (&prof
->writer_queue_sem
);
4249 pthread_join (prof
->writer_thread
, &res
);
4250 mono_os_sem_destroy (&prof
->writer_queue_sem
);
4252 cleanup_reusable_samples (prof
);
4255 * Pump the entire hazard free queue to make sure that anything we allocated
4256 * in the profiler will be freed. If we don't do this, the runtime could get
4257 * around to freeing some items after the profiler has been unloaded, which
4258 * would mean calling into functions in the profiler library, leading to a
4261 mono_thread_hazardous_try_free_all ();
4263 g_assert (!InterlockedRead (&buffer_rwlock_count
) && "Why is the reader count still non-zero?");
4264 g_assert (!InterlockedReadPointer (&buffer_rwlock_exclusive
) && "Why does someone still hold the exclusive lock?");
4266 #if defined (HAVE_SYS_ZLIB)
4268 gzclose (prof
->gzfile
);
4270 if (prof
->pipe_output
)
4271 pclose (prof
->file
);
4273 fclose (prof
->file
);
4275 mono_conc_hashtable_destroy (prof
->method_table
);
4276 mono_os_mutex_destroy (&prof
->method_table_mutex
);
4278 if (coverage_initialized
) {
4279 mono_os_mutex_lock (&coverage_mutex
);
4280 mono_conc_hashtable_foreach (coverage_assemblies
, unref_coverage_assemblies
, prof
);
4281 mono_os_mutex_unlock (&coverage_mutex
);
4283 mono_conc_hashtable_destroy (coverage_methods
);
4284 mono_conc_hashtable_destroy (coverage_assemblies
);
4285 mono_conc_hashtable_destroy (coverage_classes
);
4286 mono_conc_hashtable_destroy (filtered_classes
);
4288 mono_conc_hashtable_destroy (entered_methods
);
4289 mono_conc_hashtable_destroy (image_to_methods
);
4290 mono_conc_hashtable_destroy (suppressed_assemblies
);
4291 mono_os_mutex_destroy (&coverage_mutex
);
4296 g_free (prof
->args
);
4301 new_filename (const char* filename
)
4303 time_t t
= time (NULL
);
4304 int pid
= process_id ();
4309 int count_dates
= 0;
4313 for (p
= filename
; *p
; p
++) {
4324 if (!count_dates
&& !count_pids
)
4325 return pstrdup (filename
);
4326 snprintf (pid_buf
, sizeof (pid_buf
), "%d", pid
);
4328 snprintf (time_buf
, sizeof (time_buf
), "%d%02d%02d%02d%02d%02d",
4329 1900 + ts
->tm_year
, 1 + ts
->tm_mon
, ts
->tm_mday
, ts
->tm_hour
, ts
->tm_min
, ts
->tm_sec
);
4330 s_date
= strlen (time_buf
);
4331 s_pid
= strlen (pid_buf
);
4332 d
= res
= (char *)malloc (strlen (filename
) + s_date
* count_dates
+ s_pid
* count_pids
);
4333 for (p
= filename
; *p
; p
++) {
4340 strcpy (d
, time_buf
);
4343 } else if (*p
== 'p') {
4344 strcpy (d
, pid_buf
);
4347 } else if (*p
== '%') {
4359 //this is exposed by the JIT, but it's not meant to be a supported API for now.
4360 extern void mono_threads_attach_tools_thread (void);
4363 helper_thread (void* arg
)
4365 MonoProfiler
* prof
= (MonoProfiler
*)arg
;
4370 mono_threads_attach_tools_thread ();
4371 mono_native_thread_set_name (mono_native_thread_id_get (), "Profiler helper");
4373 MonoProfilerThread
*thread
= init_thread (FALSE
);
4375 //fprintf (stderr, "Server listening\n");
4376 command_socket
= -1;
4382 FD_SET (prof
->server_socket
, &rfds
);
4383 max_fd
= prof
->server_socket
;
4384 FD_SET (prof
->pipes
[0], &rfds
);
4385 if (max_fd
< prof
->pipes
[0])
4386 max_fd
= prof
->pipes
[0];
4387 if (command_socket
>= 0) {
4388 FD_SET (command_socket
, &rfds
);
4389 if (max_fd
< command_socket
)
4390 max_fd
= command_socket
;
4395 for ( i
= 0; i
< num_perf
; ++i
) {
4396 if (perf_data
[i
].perf_fd
< 0)
4398 FD_SET (perf_data
[i
].perf_fd
, &rfds
);
4399 if (max_fd
< perf_data
[i
].perf_fd
)
4400 max_fd
= perf_data
[i
].perf_fd
;
4405 counters_and_perfcounters_sample (prof
);
4407 buffer_lock_excl ();
4409 sync_point (prof
, SYNC_POINT_PERIODIC
);
4411 buffer_unlock_excl ();
4415 len
= select (max_fd
+ 1, &rfds
, NULL
, NULL
, &tv
);
4421 g_warning ("Error in mono-profiler-log server: %s", strerror (errno
));
4425 if (FD_ISSET (prof
->pipes
[0], &rfds
)) {
4427 read (prof
->pipes
[0], &c
, 1);
4429 fprintf (stderr
, "helper shutdown\n");
4433 for ( i
= 0; i
< num_perf
; ++i
) {
4434 if (perf_data
[i
].perf_fd
< 0)
4436 if (FD_ISSET (perf_data
[i
].perf_fd
, &rfds
))
4437 read_perf_mmap (prof
, i
);
4441 send_log_unsafe (prof
, FALSE
, FALSE
);
4447 for ( i
= 0; i
< num_perf
; ++i
) {
4448 if (perf_data
[i
].perf_fd
< 0)
4450 if (FD_ISSET (perf_data
[i
].perf_fd
, &rfds
))
4451 read_perf_mmap (prof
, i
);
4455 if (command_socket
>= 0 && FD_ISSET (command_socket
, &rfds
)) {
4456 len
= read (command_socket
, buf
, sizeof (buf
) - 1);
4460 close (command_socket
);
4461 command_socket
= -1;
4465 if (strcmp (buf
, "heapshot\n") == 0 && hs_mode_ondemand
) {
4466 // Rely on the finalization callbacks invoking process_requests ().
4467 heapshot_requested
= 1;
4468 mono_gc_finalize_notify ();
4472 if (!FD_ISSET (prof
->server_socket
, &rfds
)) {
4475 command_socket
= accept (prof
->server_socket
, NULL
, NULL
);
4476 if (command_socket
< 0)
4478 //fprintf (stderr, "Accepted connection\n");
4481 deinit_thread (thread
);
4483 mono_thread_info_detach ();
4489 start_helper_thread (MonoProfiler
* prof
)
4491 if (pipe (prof
->pipes
) == -1) {
4492 fprintf (stderr
, "Cannot create pipe: %s\n", strerror (errno
));
4496 prof
->server_socket
= socket (PF_INET
, SOCK_STREAM
, 0);
4498 if (prof
->server_socket
== -1) {
4499 fprintf (stderr
, "Cannot create server socket: %s\n", strerror (errno
));
4503 struct sockaddr_in server_address
;
4505 memset (&server_address
, 0, sizeof (server_address
));
4506 server_address
.sin_family
= AF_INET
;
4507 server_address
.sin_addr
.s_addr
= INADDR_ANY
;
4508 server_address
.sin_port
= htons (prof
->command_port
);
4510 if (bind (prof
->server_socket
, (struct sockaddr
*) &server_address
, sizeof (server_address
)) == -1) {
4511 fprintf (stderr
, "Cannot bind server socket on port %d: %s\n", prof
->command_port
, strerror (errno
));
4512 close (prof
->server_socket
);
4516 if (listen (prof
->server_socket
, 1) == -1) {
4517 fprintf (stderr
, "Cannot listen on server socket: %s\n", strerror (errno
));
4518 close (prof
->server_socket
);
4522 socklen_t slen
= sizeof (server_address
);
4524 if (getsockname (prof
->server_socket
, (struct sockaddr
*)&server_address
, &slen
)) {
4525 fprintf (stderr
, "Could not get assigned port: %s\n", strerror (errno
));
4526 close (prof
->server_socket
);
4530 prof
->command_port
= ntohs (server_address
.sin_port
);
4534 if ((r
= pthread_create (&prof
->helper_thread
, NULL
, helper_thread
, prof
))) {
4535 fprintf (stderr
, "Could not start helper thread: %s\n", strerror (r
));
4536 close (prof
->server_socket
);
4542 free_writer_entry (gpointer p
)
4544 mono_lock_free_free (p
, WRITER_ENTRY_BLOCK_SIZE
);
4548 handle_writer_queue_entry (MonoProfiler
*prof
)
4550 WriterQueueEntry
*entry
;
4552 if ((entry
= (WriterQueueEntry
*) mono_lock_free_queue_dequeue (&prof
->writer_queue
))) {
4553 if (!entry
->methods
)
4556 gboolean wrote_methods
= FALSE
;
4559 * Encode the method events in a temporary log buffer that we
4560 * flush to disk before the main buffer, ensuring that all
4561 * methods have metadata emitted before they're referenced.
4563 * We use a 'proper' thread-local buffer for this as opposed
4564 * to allocating and freeing a buffer by hand because the call
4565 * to mono_method_full_name () below may trigger class load
4566 * events when it retrieves the signature of the method. So a
4567 * thread-local buffer needs to exist when such events occur.
4569 for (guint i
= 0; i
< entry
->methods
->len
; i
++) {
4570 MethodInfo
*info
= (MethodInfo
*) g_ptr_array_index (entry
->methods
, i
);
4572 if (mono_conc_hashtable_lookup (prof
->method_table
, info
->method
))
4573 goto free_info
; // This method already has metadata emitted.
4576 * Other threads use this hash table to get a general
4577 * idea of whether a method has already been emitted to
4578 * the stream. Due to the way we add to this table, it
4579 * can easily happen that multiple threads queue up the
4580 * same methods, but that's OK since eventually all
4581 * methods will be in this table and the thread-local
4582 * method lists will just be empty for the rest of the
4585 mono_os_mutex_lock (&prof
->method_table_mutex
);
4586 mono_conc_hashtable_insert (prof
->method_table
, info
->method
, info
->method
);
4587 mono_os_mutex_unlock (&prof
->method_table_mutex
);
4589 char *name
= mono_method_full_name (info
->method
, 1);
4590 int nlen
= strlen (name
) + 1;
4591 void *cstart
= info
->ji
? mono_jit_info_get_code_start (info
->ji
) : NULL
;
4592 int csize
= info
->ji
? mono_jit_info_get_code_size (info
->ji
) : 0;
4594 ENTER_LOG (&method_jits_ctr
, logbuffer
,
4595 EVENT_SIZE
/* event */ +
4596 LEB128_SIZE
/* method */ +
4597 LEB128_SIZE
/* start */ +
4598 LEB128_SIZE
/* size */ +
4602 emit_event_time (logbuffer
, TYPE_JIT
| TYPE_METHOD
, info
->time
);
4603 emit_method_inner (logbuffer
, info
->method
);
4604 emit_ptr (logbuffer
, cstart
);
4605 emit_value (logbuffer
, csize
);
4607 memcpy (logbuffer
->cursor
, name
, nlen
);
4608 logbuffer
->cursor
+= nlen
;
4610 EXIT_LOG_EXPLICIT (prof
, FALSE
, FALSE
);
4614 wrote_methods
= TRUE
;
4620 g_ptr_array_free (entry
->methods
, TRUE
);
4622 if (wrote_methods
) {
4623 dump_buffer_threadless (prof
, PROF_TLS_GET ()->buffer
);
4624 init_buffer_state (PROF_TLS_GET ());
4628 dump_buffer (prof
, entry
->buffer
);
4630 mono_thread_hazardous_try_free (entry
, free_writer_entry
);
4639 writer_thread (void *arg
)
4641 MonoProfiler
*prof
= (MonoProfiler
*)arg
;
4643 mono_threads_attach_tools_thread ();
4644 mono_native_thread_set_name (mono_native_thread_id_get (), "Profiler writer");
4648 MonoProfilerThread
*thread
= init_thread (FALSE
);
4650 while (InterlockedRead (&prof
->run_writer_thread
)) {
4651 mono_os_sem_wait (&prof
->writer_queue_sem
, MONO_SEM_FLAGS_NONE
);
4652 handle_writer_queue_entry (prof
);
4655 /* Drain any remaining entries on shutdown. */
4656 while (handle_writer_queue_entry (prof
));
4658 free_buffer (thread
->buffer
, thread
->buffer
->size
);
4659 deinit_thread (thread
);
4661 mono_thread_info_detach ();
4667 start_writer_thread (MonoProfiler
* prof
)
4669 InterlockedWrite (&prof
->run_writer_thread
, 1);
4673 if ((r
= pthread_create (&prof
->writer_thread
, NULL
, writer_thread
, prof
))) {
4674 fprintf (stderr
, "Could not start writer thread: %s\n", strerror (r
));
4680 reuse_sample_hit (gpointer p
)
4682 SampleHit
*sample
= p
;
4684 mono_lock_free_queue_node_unpoison (&sample
->node
);
4685 mono_lock_free_queue_enqueue (&sample
->prof
->sample_reuse_queue
, &sample
->node
);
4689 handle_dumper_queue_entry (MonoProfiler
*prof
)
4693 if ((sample
= (SampleHit
*) mono_lock_free_queue_dequeue (&prof
->dumper_queue
))) {
4694 for (int i
= 0; i
< sample
->count
; ++i
) {
4695 MonoMethod
*method
= sample
->frames
[i
].method
;
4696 MonoDomain
*domain
= sample
->frames
[i
].domain
;
4697 void *address
= sample
->frames
[i
].base_address
;
4700 g_assert (domain
&& "What happened to the domain pointer?");
4701 g_assert (address
&& "What happened to the instruction pointer?");
4703 MonoJitInfo
*ji
= mono_jit_info_table_find (domain
, (char *) address
);
4706 sample
->frames
[i
].method
= mono_jit_info_get_method (ji
);
4710 ENTER_LOG (&sample_hits_ctr
, logbuffer
,
4711 EVENT_SIZE
/* event */ +
4712 BYTE_SIZE
/* type */ +
4713 LEB128_SIZE
/* tid */ +
4714 LEB128_SIZE
/* count */ +
4716 LEB128_SIZE
/* ip */
4718 LEB128_SIZE
/* managed count */ +
4720 LEB128_SIZE
/* method */
4724 emit_event_time (logbuffer
, TYPE_SAMPLE
| TYPE_SAMPLE_HIT
, sample
->time
);
4725 emit_byte (logbuffer
, sample_type
);
4726 emit_ptr (logbuffer
, (void *) sample
->tid
);
4727 emit_value (logbuffer
, 1);
4729 // TODO: Actual native unwinding.
4730 for (int i
= 0; i
< 1; ++i
) {
4731 emit_ptr (logbuffer
, sample
->ip
);
4732 add_code_pointer ((uintptr_t) sample
->ip
);
4735 /* new in data version 6 */
4736 emit_uvalue (logbuffer
, sample
->count
);
4738 for (int i
= 0; i
< sample
->count
; ++i
)
4739 emit_method (prof
, logbuffer
, sample
->frames
[i
].method
);
4741 EXIT_LOG_EXPLICIT (prof
, TRUE
, FALSE
);
4743 mono_thread_hazardous_try_free (sample
, reuse_sample_hit
);
4745 dump_unmanaged_coderefs (prof
);
4752 dumper_thread (void *arg
)
4754 MonoProfiler
*prof
= (MonoProfiler
*)arg
;
4756 mono_threads_attach_tools_thread ();
4757 mono_native_thread_set_name (mono_native_thread_id_get (), "Profiler dumper");
4759 MonoProfilerThread
*thread
= init_thread (FALSE
);
4761 while (InterlockedRead (&prof
->run_dumper_thread
)) {
4762 mono_os_sem_wait (&prof
->dumper_queue_sem
, MONO_SEM_FLAGS_NONE
);
4763 handle_dumper_queue_entry (prof
);
4766 /* Drain any remaining entries on shutdown. */
4767 while (handle_dumper_queue_entry (prof
));
4769 send_log_unsafe (prof
, FALSE
, FALSE
);
4770 deinit_thread (thread
);
4772 mono_thread_info_detach ();
4778 start_dumper_thread (MonoProfiler
* prof
)
4780 InterlockedWrite (&prof
->run_dumper_thread
, 1);
4784 if ((r
= pthread_create (&prof
->dumper_thread
, NULL
, dumper_thread
, prof
))) {
4785 fprintf (stderr
, "Could not start dumper thread: %s\n", strerror (r
));
4791 register_counter (const char *name
, gint32
*counter
)
4793 mono_counters_register (name
, MONO_COUNTER_UINT
| MONO_COUNTER_PROFILER
| MONO_COUNTER_MONOTONIC
, counter
);
4797 runtime_initialized (MonoProfiler
*profiler
)
4799 InterlockedWrite (&runtime_inited
, 1);
4801 start_writer_thread (profiler
);
4802 start_dumper_thread (profiler
);
4804 register_counter ("Sample events allocated", &sample_allocations_ctr
);
4805 register_counter ("Log buffers allocated", &buffer_allocations_ctr
);
4807 register_counter ("Event: Sync points", &sync_points_ctr
);
4808 register_counter ("Event: Heap objects", &heap_objects_ctr
);
4809 register_counter ("Event: Heap starts", &heap_starts_ctr
);
4810 register_counter ("Event: Heap ends", &heap_ends_ctr
);
4811 register_counter ("Event: Heap roots", &heap_roots_ctr
);
4812 register_counter ("Event: GC events", &gc_events_ctr
);
4813 register_counter ("Event: GC resizes", &gc_resizes_ctr
);
4814 register_counter ("Event: GC allocations", &gc_allocs_ctr
);
4815 register_counter ("Event: GC moves", &gc_moves_ctr
);
4816 register_counter ("Event: GC handle creations", &gc_handle_creations_ctr
);
4817 register_counter ("Event: GC handle deletions", &gc_handle_deletions_ctr
);
4818 register_counter ("Event: GC finalize starts", &finalize_begins_ctr
);
4819 register_counter ("Event: GC finalize ends", &finalize_ends_ctr
);
4820 register_counter ("Event: GC finalize object starts", &finalize_object_begins_ctr
);
4821 register_counter ("Event: GC finalize object ends", &finalize_object_ends_ctr
);
4822 register_counter ("Event: Image loads", &image_loads_ctr
);
4823 register_counter ("Event: Image unloads", &image_unloads_ctr
);
4824 register_counter ("Event: Assembly loads", &assembly_loads_ctr
);
4825 register_counter ("Event: Assembly unloads", &assembly_unloads_ctr
);
4826 register_counter ("Event: Class loads", &class_loads_ctr
);
4827 register_counter ("Event: Class unloads", &class_unloads_ctr
);
4828 register_counter ("Event: Method entries", &method_entries_ctr
);
4829 register_counter ("Event: Method exits", &method_exits_ctr
);
4830 register_counter ("Event: Method exception leaves", &method_exception_exits_ctr
);
4831 register_counter ("Event: Method JITs", &method_jits_ctr
);
4832 register_counter ("Event: Code buffers", &code_buffers_ctr
);
4833 register_counter ("Event: Exception throws", &exception_throws_ctr
);
4834 register_counter ("Event: Exception clauses", &exception_clauses_ctr
);
4835 register_counter ("Event: Monitor contentions", &monitor_contentions_ctr
);
4836 register_counter ("Event: Monitor acquisitions", &monitor_acquisitions_ctr
);
4837 register_counter ("Event: Monitor failures", &monitor_failures_ctr
);
4838 register_counter ("Event: Thread starts", &thread_starts_ctr
);
4839 register_counter ("Event: Thread ends", &thread_ends_ctr
);
4840 register_counter ("Event: Thread names", &thread_names_ctr
);
4841 register_counter ("Event: Domain loads", &domain_loads_ctr
);
4842 register_counter ("Event: Domain unloads", &domain_unloads_ctr
);
4843 register_counter ("Event: Domain names", &domain_names_ctr
);
4844 register_counter ("Event: Context loads", &context_loads_ctr
);
4845 register_counter ("Event: Context unloads", &context_unloads_ctr
);
4846 register_counter ("Event: Sample binaries", &sample_ubins_ctr
);
4847 register_counter ("Event: Sample symbols", &sample_usyms_ctr
);
4848 register_counter ("Event: Sample hits", &sample_hits_ctr
);
4849 register_counter ("Event: Counter descriptors", &counter_descriptors_ctr
);
4850 register_counter ("Event: Counter samples", &counter_samples_ctr
);
4851 register_counter ("Event: Performance counter descriptors", &perfcounter_descriptors_ctr
);
4852 register_counter ("Event: Performance counter samples", &perfcounter_samples_ctr
);
4853 register_counter ("Event: Coverage methods", &coverage_methods_ctr
);
4854 register_counter ("Event: Coverage statements", &coverage_statements_ctr
);
4855 register_counter ("Event: Coverage classes", &coverage_classes_ctr
);
4856 register_counter ("Event: Coverage assemblies", &coverage_assemblies_ctr
);
4858 counters_init (profiler
);
4860 start_helper_thread (profiler
);
4863 static MonoProfiler
*
4864 create_profiler (const char *args
, const char *filename
, GPtrArray
*filters
)
4868 int force_delete
= 0;
4869 prof
= (MonoProfiler
*)calloc (1, sizeof (MonoProfiler
));
4871 prof
->args
= pstrdup (args
);
4872 prof
->command_port
= command_port
;
4873 if (filename
&& *filename
== '-') {
4879 filename
= "|mprof-report -";
4881 filename
= "output.mlpd";
4882 nf
= (char*)filename
;
4884 nf
= new_filename (filename
);
4886 int s
= strlen (nf
) + 32;
4887 char *p
= (char *)malloc (s
);
4888 snprintf (p
, s
, "|mprof-report '--out=%s' -", nf
);
4894 prof
->file
= popen (nf
+ 1, "w");
4895 prof
->pipe_output
= 1;
4896 } else if (*nf
== '#') {
4897 int fd
= strtol (nf
+ 1, NULL
, 10);
4898 prof
->file
= fdopen (fd
, "a");
4902 prof
->file
= fopen (nf
, "wb");
4905 fprintf (stderr
, "Cannot create profiler output: %s\n", nf
);
4909 #if defined (HAVE_SYS_ZLIB)
4911 prof
->gzfile
= gzdopen (fileno (prof
->file
), "wb");
4915 setup_perf_event ();
4918 /* FIXME: warn if different freq or sample type */
4924 * If you hit this assert while increasing MAX_FRAMES, you need to increase
4925 * SAMPLE_BLOCK_SIZE as well.
4927 g_assert (SAMPLE_SLOT_SIZE (MAX_FRAMES
) * 2 < LOCK_FREE_ALLOC_SB_USABLE_SIZE (SAMPLE_BLOCK_SIZE
));
4929 // FIXME: We should free this stuff too.
4930 mono_lock_free_allocator_init_size_class (&prof
->sample_size_class
, SAMPLE_SLOT_SIZE (num_frames
), SAMPLE_BLOCK_SIZE
);
4931 mono_lock_free_allocator_init_allocator (&prof
->sample_allocator
, &prof
->sample_size_class
);
4933 mono_lock_free_queue_init (&prof
->sample_reuse_queue
);
4935 g_assert (sizeof (WriterQueueEntry
) * 2 < LOCK_FREE_ALLOC_SB_USABLE_SIZE (WRITER_ENTRY_BLOCK_SIZE
));
4937 // FIXME: We should free this stuff too.
4938 mono_lock_free_allocator_init_size_class (&prof
->writer_entry_size_class
, sizeof (WriterQueueEntry
), WRITER_ENTRY_BLOCK_SIZE
);
4939 mono_lock_free_allocator_init_allocator (&prof
->writer_entry_allocator
, &prof
->writer_entry_size_class
);
4941 mono_lock_free_queue_init (&prof
->writer_queue
);
4942 mono_os_sem_init (&prof
->writer_queue_sem
, 0);
4944 mono_lock_free_queue_init (&prof
->dumper_queue
);
4945 mono_os_sem_init (&prof
->dumper_queue_sem
, 0);
4947 mono_os_mutex_init (&prof
->method_table_mutex
);
4948 prof
->method_table
= mono_conc_hashtable_new (NULL
, NULL
);
4951 coverage_init (prof
);
4952 prof
->coverage_filters
= filters
;
4954 prof
->startup_time
= current_time ();
4961 printf ("Log profiler version %d.%d (format: %d)\n", LOG_VERSION_MAJOR
, LOG_VERSION_MINOR
, LOG_DATA_VERSION
);
4962 printf ("Usage: mono --profile=log[:OPTION1[,OPTION2...]] program.exe\n");
4963 printf ("Options:\n");
4964 printf ("\thelp show this usage info\n");
4965 printf ("\t[no]alloc enable/disable recording allocation info\n");
4966 printf ("\t[no]calls enable/disable recording enter/leave method events\n");
4967 printf ("\theapshot[=MODE] record heap shot info (by default at each major collection)\n");
4968 printf ("\t MODE: every XXms milliseconds, every YYgc collections, ondemand\n");
4969 printf ("\tcounters sample counters every 1s\n");
4970 printf ("\tsample[=TYPE] use statistical sampling mode (by default cycles/100)\n");
4971 printf ("\t TYPE: cycles,instr,cacherefs,cachemiss,branches,branchmiss\n");
4972 printf ("\t TYPE can be followed by /FREQUENCY\n");
4973 printf ("\tmaxframes=NUM collect up to NUM stack frames\n");
4974 printf ("\tcalldepth=NUM ignore method events for call chain depth bigger than NUM\n");
4975 printf ("\toutput=FILENAME write the data to file FILENAME (-FILENAME to overwrite)\n");
4976 printf ("\toutput=|PROGRAM write the data to the stdin of PROGRAM\n");
4977 printf ("\t %%t is subtituted with date and time, %%p with the pid\n");
4978 printf ("\treport create a report instead of writing the raw data to a file\n");
4979 printf ("\tzip compress the output data\n");
4980 printf ("\tport=PORTNUM use PORTNUM for the listening command server\n");
4981 printf ("\tcoverage enable collection of code coverage data\n");
4982 printf ("\tcovfilter=ASSEMBLY add an assembly to the code coverage filters\n");
4983 printf ("\t add a + to include the assembly or a - to exclude it\n");
4984 printf ("\t filter=-mscorlib\n");
4985 printf ("\tcovfilter-file=FILE use FILE to generate the list of assemblies to be filtered\n");
4991 match_option (const char* p
, const char *opt
, char **rval
)
4993 int len
= strlen (opt
);
4994 if (strncmp (p
, opt
, len
) == 0) {
4996 if (p
[len
] == '=' && p
[len
+ 1]) {
4997 const char *opt
= p
+ len
+ 1;
4998 const char *end
= strchr (opt
, ',');
5006 val
= (char *)malloc (l
+ 1);
5007 memcpy (val
, opt
, l
);
5012 if (p
[len
] == 0 || p
[len
] == ',') {
5014 return p
+ len
+ (p
[len
] == ',');
5032 static const SampleMode sample_modes
[] = {
5033 {"cycles", SAMPLE_CYCLES
},
5034 {"instr", SAMPLE_INSTRUCTIONS
},
5035 {"cachemiss", SAMPLE_CACHE_MISSES
},
5036 {"cacherefs", SAMPLE_CACHE_REFS
},
5037 {"branches", SAMPLE_BRANCHES
},
5038 {"branchmiss", SAMPLE_BRANCH_MISSES
},
5043 set_sample_mode (char* val
, int allow_empty
)
5046 char *maybe_freq
= NULL
;
5048 const SampleMode
*smode
= sample_modes
;
5049 #ifndef USE_PERF_EVENTS
5052 if (allow_empty
&& !val
) {
5053 sample_type
= SAMPLE_CYCLES
;
5057 if (strcmp (val
, "mono") == 0) {
5059 sample_type
= SAMPLE_CYCLES
;
5063 for (smode
= sample_modes
; smode
->name
; smode
++) {
5064 int l
= strlen (smode
->name
);
5065 if (strncmp (val
, smode
->name
, l
) == 0) {
5066 sample_type
= smode
->sample_mode
;
5067 maybe_freq
= val
+ l
;
5073 if (*maybe_freq
== '/') {
5074 count
= strtoul (maybe_freq
+ 1, &end
, 10);
5075 if (maybe_freq
+ 1 == end
)
5077 sample_freq
= count
;
5078 } else if (*maybe_freq
!= 0) {
5087 set_hsmode (char* val
, int allow_empty
)
5091 if (allow_empty
&& !val
)
5093 if (strcmp (val
, "ondemand") == 0) {
5094 hs_mode_ondemand
= 1;
5098 count
= strtoul (val
, &end
, 10);
5101 if (strcmp (end
, "ms") == 0)
5103 else if (strcmp (end
, "gc") == 0)
5111 * declaration to silence the compiler: this is the entry point that
5112 * mono will load from the shared library and call.
5115 mono_profiler_startup (const char *desc
);
5118 mono_profiler_startup_log (const char *desc
);
5121 * this is the entry point that will be used when the profiler
5122 * is embedded inside the main executable.
5125 mono_profiler_startup_log (const char *desc
)
5127 mono_profiler_startup (desc
);
5131 mono_profiler_startup (const char *desc
)
5134 GPtrArray
*filters
= NULL
;
5135 char *filename
= NULL
;
5138 int calls_enabled
= 0;
5139 int allocs_enabled
= 0;
5140 int only_coverage
= 0;
5141 int events
= MONO_PROFILE_GC
|MONO_PROFILE_ALLOCATIONS
|
5142 MONO_PROFILE_GC_MOVES
|MONO_PROFILE_CLASS_EVENTS
|MONO_PROFILE_THREADS
|
5143 MONO_PROFILE_ENTER_LEAVE
|MONO_PROFILE_JIT_COMPILATION
|MONO_PROFILE_EXCEPTIONS
|
5144 MONO_PROFILE_MONITOR_EVENTS
|MONO_PROFILE_MODULE_EVENTS
|MONO_PROFILE_GC_ROOTS
|
5145 MONO_PROFILE_INS_COVERAGE
|MONO_PROFILE_APPDOMAIN_EVENTS
|MONO_PROFILE_CONTEXT_EVENTS
|
5146 MONO_PROFILE_ASSEMBLY_EVENTS
|MONO_PROFILE_GC_FINALIZATION
;
5148 max_allocated_sample_hits
= mono_cpu_count () * 1000;
5151 if (strncmp (p
, "log", 3))
5156 for (; *p
; p
= opt
) {
5162 if ((opt
= match_option (p
, "help", NULL
)) != p
) {
5166 if ((opt
= match_option (p
, "calls", NULL
)) != p
) {
5170 if ((opt
= match_option (p
, "nocalls", NULL
)) != p
) {
5171 events
&= ~MONO_PROFILE_ENTER_LEAVE
;
5175 if ((opt
= match_option (p
, "alloc", NULL
)) != p
) {
5179 if ((opt
= match_option (p
, "noalloc", NULL
)) != p
) {
5180 events
&= ~MONO_PROFILE_ALLOCATIONS
;
5183 if ((opt
= match_option (p
, "time", &val
)) != p
) {
5184 if (strcmp (val
, "fast") && strcmp (val
, "null"))
5189 if ((opt
= match_option (p
, "report", NULL
)) != p
) {
5193 if ((opt
= match_option (p
, "debug", NULL
)) != p
) {
5197 if ((opt
= match_option (p
, "sampling-real", NULL
)) != p
) {
5198 sampling_mode
= MONO_PROFILER_STAT_MODE_REAL
;
5201 if ((opt
= match_option (p
, "sampling-process", NULL
)) != p
) {
5202 sampling_mode
= MONO_PROFILER_STAT_MODE_PROCESS
;
5205 if ((opt
= match_option (p
, "heapshot", &val
)) != p
) {
5206 events
&= ~MONO_PROFILE_ALLOCATIONS
;
5207 events
&= ~MONO_PROFILE_ENTER_LEAVE
;
5210 set_hsmode (val
, 1);
5213 if ((opt
= match_option (p
, "sample", &val
)) != p
) {
5214 events
&= ~MONO_PROFILE_ALLOCATIONS
;
5215 events
&= ~MONO_PROFILE_ENTER_LEAVE
;
5217 set_sample_mode (val
, 1);
5220 if ((opt
= match_option (p
, "zip", NULL
)) != p
) {
5224 if ((opt
= match_option (p
, "output", &val
)) != p
) {
5228 if ((opt
= match_option (p
, "port", &val
)) != p
) {
5230 command_port
= strtoul (val
, &end
, 10);
5234 if ((opt
= match_option (p
, "maxframes", &val
)) != p
) {
5236 num_frames
= strtoul (val
, &end
, 10);
5237 if (num_frames
> MAX_FRAMES
)
5238 num_frames
= MAX_FRAMES
;
5240 notraces
= num_frames
== 0;
5243 if ((opt
= match_option (p
, "maxsamples", &val
)) != p
) {
5245 max_allocated_sample_hits
= strtoul (val
, &end
, 10);
5246 if (!max_allocated_sample_hits
)
5247 max_allocated_sample_hits
= G_MAXINT32
;
5251 if ((opt
= match_option (p
, "calldepth", &val
)) != p
) {
5253 max_call_depth
= strtoul (val
, &end
, 10);
5257 if ((opt
= match_option (p
, "counters", NULL
)) != p
) {
5261 if ((opt
= match_option (p
, "coverage", NULL
)) != p
) {
5263 events
|= MONO_PROFILE_ENTER_LEAVE
;
5264 debug_coverage
= (g_getenv ("MONO_PROFILER_DEBUG_COVERAGE") != NULL
);
5267 if ((opt
= match_option (p
, "onlycoverage", NULL
)) != p
) {
5271 if ((opt
= match_option (p
, "covfilter-file", &val
)) != p
) {
5273 char *line
, *content
;
5275 if (filters
== NULL
)
5276 filters
= g_ptr_array_new ();
5278 filter_file
= fopen (val
, "r");
5279 if (filter_file
== NULL
) {
5280 fprintf (stderr
, "Unable to open %s\n", val
);
5284 /* Don't need to free content as it is referred to by the lines stored in @filters */
5285 content
= get_file_content (filter_file
);
5286 if (content
== NULL
)
5287 fprintf (stderr
, "WARNING: %s is greater than 128kb - ignoring\n", val
);
5289 while ((line
= get_next_line (content
, &content
)))
5290 g_ptr_array_add (filters
, g_strchug (g_strchomp (line
)));
5292 fclose (filter_file
);
5295 if ((opt
= match_option (p
, "covfilter", &val
)) != p
) {
5296 if (filters
== NULL
)
5297 filters
= g_ptr_array_new ();
5299 g_ptr_array_add (filters
, val
);
5307 if (calls_enabled
) {
5308 events
|= MONO_PROFILE_ENTER_LEAVE
;
5312 events
|= MONO_PROFILE_ALLOCATIONS
;
5314 // Only activate the bare minimum events the profiler needs to function.
5315 if (only_coverage
) {
5317 fprintf (stderr
, "The onlycoverage option is only valid when paired with the coverage option\n");
5321 events
= MONO_PROFILE_GC
| MONO_PROFILE_THREADS
| MONO_PROFILE_ENTER_LEAVE
| MONO_PROFILE_INS_COVERAGE
;
5328 prof
= create_profiler (desc
, filename
, filters
);
5334 mono_lls_init (&profiler_thread_list
, NULL
);
5338 mono_profiler_install (prof
, log_shutdown
);
5339 mono_profiler_install_gc (gc_event
, gc_resize
);
5340 mono_profiler_install_allocation (gc_alloc
);
5341 mono_profiler_install_gc_moves (gc_moves
);
5342 mono_profiler_install_gc_roots (gc_handle
, gc_roots
);
5343 mono_profiler_install_gc_finalize (finalize_begin
, finalize_object_begin
, finalize_object_end
, finalize_end
);
5344 mono_profiler_install_appdomain (NULL
, domain_loaded
, domain_unloaded
, NULL
);
5345 mono_profiler_install_appdomain_name (domain_name
);
5346 mono_profiler_install_context (context_loaded
, context_unloaded
);
5347 mono_profiler_install_class (NULL
, class_loaded
, class_unloaded
, NULL
);
5348 mono_profiler_install_module (NULL
, image_loaded
, image_unloaded
, NULL
);
5349 mono_profiler_install_assembly (NULL
, assembly_loaded
, assembly_unloaded
, NULL
);
5350 mono_profiler_install_thread (thread_start
, thread_end
);
5351 mono_profiler_install_thread_name (thread_name
);
5352 mono_profiler_install_enter_leave (method_enter
, method_leave
);
5353 mono_profiler_install_jit_end (method_jitted
);
5354 mono_profiler_install_code_buffer_new (code_buffer_new
);
5355 mono_profiler_install_exception (throw_exc
, method_exc_leave
, clause_exc
);
5356 mono_profiler_install_monitor (monitor_event
);
5357 mono_profiler_install_runtime_initialized (runtime_initialized
);
5359 mono_profiler_install_coverage_filter (coverage_filter
);
5361 if (do_mono_sample
&& sample_type
== SAMPLE_CYCLES
&& sample_freq
) {
5362 events
|= MONO_PROFILE_STATISTICAL
;
5363 mono_profiler_set_statistical_mode (sampling_mode
, sample_freq
);
5364 mono_profiler_install_statistical (mono_sample_hit
);
5367 mono_profiler_set_events ((MonoProfileFlags
)events
);