2 * proflog.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)
13 #include "../mini/jit.h"
14 #include <mono/metadata/profiler.h>
15 #include <mono/metadata/threads.h>
16 #include <mono/metadata/mono-gc.h>
17 #include <mono/metadata/debug-helpers.h>
18 #include <mono/metadata/mono-perfcounters.h>
19 #include <mono/metadata/appdomain.h>
20 #include <mono/metadata/assembly.h>
21 #include <mono/metadata/tokentype.h>
22 #include <mono/metadata/tabledefs.h>
23 #include <mono/utils/atomic.h>
24 #include <mono/utils/mono-membar.h>
25 #include <mono/utils/mono-counters.h>
26 #include <mono/utils/mono-os-mutex.h>
27 #include <mono/utils/mono-conc-hashtable.h>
28 #include <mono/utils/lock-free-queue.h>
38 #if defined(HOST_WIN32) || defined(DISABLE_SOCKETS)
39 #define DISABLE_HELPER_THREAD 1
48 #ifdef HAVE_EXECINFO_H
55 #ifndef DISABLE_HELPER_THREAD
56 #include <sys/types.h>
57 #include <sys/socket.h>
58 #include <netinet/in.h>
59 #include <sys/select.h>
68 #ifdef HAVE_SYS_STAT_H
75 #if defined (HAVE_SYS_ZLIB)
79 #if defined(__linux__)
82 #include <sys/syscall.h>
83 #include "perf_event.h"
85 #ifdef ENABLE_PERF_EVENTS
86 #define USE_PERF_EVENTS 1
88 static int read_perf_mmap (MonoProfiler
* prof
, int cpu
);
93 #define BUFFER_SIZE (4096 * 16)
95 /* Worst-case size in bytes of a 64-bit value encoded with LEB128. */
96 #define LEB128_SIZE 10
97 /* Size in bytes of the event ID prefix. */
100 static int nocalls
= 0;
101 static int notraces
= 0;
102 static int use_zip
= 0;
103 static int do_report
= 0;
104 static int do_heap_shot
= 0;
105 static int max_call_depth
= 100;
106 static volatile int runtime_inited
= 0;
107 static int need_helper_thread
= 0;
108 static int command_port
= 0;
109 static int heapshot_requested
= 0;
110 static int sample_type
= 0;
111 static int sample_freq
= 0;
112 static int do_mono_sample
= 0;
113 static int in_shutdown
= 0;
114 static int do_debug
= 0;
115 static int do_counters
= 0;
116 static int do_coverage
= 0;
117 static gboolean debug_coverage
= FALSE
;
118 static MonoProfileSamplingMode sampling_mode
= MONO_PROFILER_STAT_MODE_PROCESS
;
120 typedef struct _LogBuffer LogBuffer
;
126 * The file is composed by a header followed by 0 or more buffers.
127 * Each buffer contains events that happened on a thread: for a given thread
128 * buffers that appear later in the file are guaranteed to contain events
129 * that happened later in time. Buffers from separate threads could be interleaved,
131 * Buffers are not required to be aligned.
134 * [id: 4 bytes] constant value: LOG_HEADER_ID
135 * [major: 1 byte] [minor: 1 byte] major and minor version of the log profiler
136 * [format: 1 byte] version of the data format for the rest of the file
137 * [ptrsize: 1 byte] size in bytes of a pointer in the profiled program
138 * [startup time: 8 bytes] time in milliseconds since the unix epoch when the program started
139 * [timer overhead: 4 bytes] approximate overhead in nanoseconds of the timer
140 * [flags: 4 bytes] file format flags, should be 0 for now
141 * [pid: 4 bytes] pid of the profiled process
142 * [port: 2 bytes] tcp port for server if != 0
143 * [sysid: 2 bytes] operating system and architecture identifier
145 * The multiple byte integers are in little-endian format.
148 * [buffer header] [event]*
149 * Buffers have a fixed-size header followed by 0 or more bytes of event data.
150 * Timing information and other values in the event data are usually stored
151 * as uleb128 or sleb128 integers. To save space, as noted for each item below,
152 * some data is represented as a difference between the actual value and
153 * either the last value of the same type (like for timing information) or
154 * as the difference from a value stored in a buffer header.
156 * For timing information the data is stored as uleb128, since timing
157 * increases in a monotonic way in each thread: the value is the number of
158 * nanoseconds to add to the last seen timing data in a buffer. The first value
159 * in a buffer will be calculated from the time_base field in the buffer head.
161 * Object or heap sizes are stored as uleb128.
162 * Pointer differences are stored as sleb128, instead.
164 * If an unexpected value is found, the rest of the buffer should be ignored,
165 * as generally the later values need the former to be interpreted correctly.
167 * buffer header format:
168 * [bufid: 4 bytes] constant value: BUF_ID
169 * [len: 4 bytes] size of the data following the buffer header
170 * [time_base: 8 bytes] time base in nanoseconds since an unspecified epoch
171 * [ptr_base: 8 bytes] base value for pointers
172 * [obj_base: 8 bytes] base value for object addresses
173 * [thread id: 8 bytes] system-specific thread ID (pthread_t for example)
174 * [method_base: 8 bytes] base value for MonoMethod pointers
177 * [extended info: upper 4 bits] [type: lower 4 bits] [data]*
178 * The data that follows depends on type and the extended info.
179 * Type is one of the enum values in proflog.h: TYPE_ALLOC, TYPE_GC,
180 * TYPE_METADATA, TYPE_METHOD, TYPE_EXCEPTION, TYPE_MONITOR, TYPE_HEAP.
181 * The extended info bits are interpreted based on type, see
182 * each individual event description below.
183 * strings are represented as a 0-terminated utf8 sequence.
186 * [flags: uleb128] must be 0
187 * [num: uleb128] number of frames following
188 * [frame: sleb128]* num MonoMethod pointers as differences from ptr_base
192 * exinfo: flags: TYPE_ALLOC_BT
193 * [time diff: uleb128] nanoseconds since last timing
194 * [ptr: sleb128] class as a byte difference from ptr_base
195 * [obj: sleb128] object address as a byte difference from obj_base
196 * [size: uleb128] size of the object in the heap
197 * If the TYPE_ALLOC_BT flag is set, a backtrace follows.
201 * exinfo: one of TYPE_GC_EVENT, TYPE_GC_RESIZE, TYPE_GC_MOVE, TYPE_GC_HANDLE_CREATED[_BT],
202 * TYPE_GC_HANDLE_DESTROYED[_BT]
203 * [time diff: uleb128] nanoseconds since last timing
204 * if exinfo == TYPE_GC_RESIZE
205 * [heap_size: uleb128] new heap size
206 * if exinfo == TYPE_GC_EVENT
207 * [event type: uleb128] GC event (MONO_GC_EVENT_* from profiler.h)
208 * [generation: uleb128] GC generation event refers to
209 * if exinfo == TYPE_GC_MOVE
210 * [num_objects: uleb128] number of object moves that follow
211 * [objaddr: sleb128]+ num_objects object pointer differences from obj_base
212 * num is always an even number: the even items are the old
213 * addresses, the odd numbers are the respective new object addresses
214 * if exinfo == TYPE_GC_HANDLE_CREATED[_BT]
215 * [handle_type: uleb128] GC handle type (System.Runtime.InteropServices.GCHandleType)
216 * upper bits reserved as flags
217 * [handle: uleb128] GC handle value
218 * [objaddr: sleb128] object pointer differences from obj_base
219 * If exinfo == TYPE_GC_HANDLE_CREATED_BT, a backtrace follows.
220 * if exinfo == TYPE_GC_HANDLE_DESTROYED[_BT]
221 * [handle_type: uleb128] GC handle type (System.Runtime.InteropServices.GCHandleType)
222 * upper bits reserved as flags
223 * [handle: uleb128] GC handle value
224 * If exinfo == TYPE_GC_HANDLE_DESTROYED_BT, a backtrace follows.
226 * type metadata format:
227 * type: TYPE_METADATA
228 * exinfo: one of: TYPE_END_LOAD, TYPE_END_UNLOAD (optional for TYPE_THREAD and TYPE_DOMAIN)
229 * [time diff: uleb128] nanoseconds since last timing
230 * [mtype: byte] metadata type, one of: TYPE_CLASS, TYPE_IMAGE, TYPE_ASSEMBLY, TYPE_DOMAIN,
231 * TYPE_THREAD, TYPE_CONTEXT
232 * [pointer: sleb128] pointer of the metadata type depending on mtype
233 * if mtype == TYPE_CLASS
234 * [image: sleb128] MonoImage* as a pointer difference from ptr_base
235 * [flags: uleb128] must be 0
236 * [name: string] full class name
237 * if mtype == TYPE_IMAGE
238 * [flags: uleb128] must be 0
239 * [name: string] image file name
240 * if mtype == TYPE_ASSEMBLY
241 * [flags: uleb128] must be 0
242 * [name: string] assembly name
243 * if mtype == TYPE_DOMAIN
244 * [flags: uleb128] must be 0
245 * if mtype == TYPE_DOMAIN && exinfo == 0
246 * [name: string] domain friendly name
247 * if mtype == TYPE_CONTEXT
248 * [flags: uleb128] must be 0
249 * [domain: sleb128] domain id as pointer
250 * if mtype == TYPE_THREAD && (format_version < 11 || (format_version > 10 && exinfo == 0))
251 * [flags: uleb128] must be 0
252 * [name: string] thread name
254 * type method format:
256 * exinfo: one of: TYPE_LEAVE, TYPE_ENTER, TYPE_EXC_LEAVE, TYPE_JIT
257 * [time diff: uleb128] nanoseconds since last timing
258 * [method: sleb128] MonoMethod* as a pointer difference from the last such
259 * pointer or the buffer method_base
260 * if exinfo == TYPE_JIT
261 * [code address: sleb128] pointer to the native code as a diff from ptr_base
262 * [code size: uleb128] size of the generated code
263 * [name: string] full method name
265 * type runtime format:
267 * exinfo: one of: TYPE_JITHELPER
268 * [time diff: uleb128] nanoseconds since last timing
269 * if exinfo == TYPE_JITHELPER
270 * [type: uleb128] MonoProfilerCodeBufferType enum value
271 * [buffer address: sleb128] pointer to the native code as a diff from ptr_base
272 * [buffer size: uleb128] size of the generated code
273 * if type == MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE
274 * [name: string] buffer description name
276 * type monitor format:
278 * exinfo: TYPE_MONITOR_BT flag and one of: MONO_PROFILER_MONITOR_(CONTENTION|FAIL|DONE)
279 * [time diff: uleb128] nanoseconds since last timing
280 * [object: sleb128] the lock object as a difference from obj_base
281 * if exinfo.low3bits == MONO_PROFILER_MONITOR_CONTENTION
282 * If the TYPE_MONITOR_BT flag is set, a backtrace follows.
286 * exinfo: one of TYPE_HEAP_START, TYPE_HEAP_END, TYPE_HEAP_OBJECT, TYPE_HEAP_ROOT
287 * if exinfo == TYPE_HEAP_START
288 * [time diff: uleb128] nanoseconds since last timing
289 * if exinfo == TYPE_HEAP_END
290 * [time diff: uleb128] nanoseconds since last timing
291 * if exinfo == TYPE_HEAP_OBJECT
292 * [object: sleb128] the object as a difference from obj_base
293 * [class: sleb128] the object MonoClass* as a difference from ptr_base
294 * [size: uleb128] size of the object on the heap
295 * [num_refs: uleb128] number of object references
296 * if (format version > 1) each referenced objref is preceded by a
297 * uleb128 encoded offset: the first offset is from the object address
298 * and each next offset is relative to the previous one
299 * [objrefs: sleb128]+ object referenced as a difference from obj_base
300 * The same object can appear multiple times, but only the first time
301 * with size != 0: in the other cases this data will only be used to
302 * provide additional referenced objects.
303 * if exinfo == TYPE_HEAP_ROOT
304 * [num_roots: uleb128] number of root references
305 * [num_gc: uleb128] number of major gcs
306 * [object: sleb128] the object as a difference from obj_base
307 * [root_type: uleb128] the root_type: MonoProfileGCRootType (profiler.h)
308 * [extra_info: uleb128] the extra_info value
309 * object, root_type and extra_info are repeated num_roots times
313 * exinfo: one of TYPE_SAMPLE_HIT, TYPE_SAMPLE_USYM, TYPE_SAMPLE_UBIN, TYPE_SAMPLE_COUNTERS_DESC, TYPE_SAMPLE_COUNTERS
314 * if exinfo == TYPE_SAMPLE_HIT
315 * [sample_type: uleb128] type of sample (SAMPLE_*)
316 * [timestamp: uleb128] nanoseconds since startup (note: different from other timestamps!)
317 * if (format_version > 10)
318 * [thread: sleb128] thread id as difference from ptr_base
319 * [count: uleb128] number of following instruction addresses
320 * [ip: sleb128]* instruction pointer as difference from ptr_base
321 * if (format_version > 5)
322 * [mbt_count: uleb128] number of managed backtrace info triplets (method + IL offset + native offset)
323 * [method: sleb128]* MonoMethod* as a pointer difference from the last such
324 * pointer or the buffer method_base (the first such method can be also indentified by ip, but this is not neccessarily true)
325 * [il_offset: sleb128]* IL offset inside method where the hit occurred
326 * [native_offset: sleb128]* native offset inside method where the hit occurred
327 * if exinfo == TYPE_SAMPLE_USYM
328 * [address: sleb128] symbol address as a difference from ptr_base
329 * [size: uleb128] symbol size (may be 0 if unknown)
330 * [name: string] symbol name
331 * if exinfo == TYPE_SAMPLE_UBIN
332 * [time diff: uleb128] nanoseconds since last timing
333 * [address: sleb128] address where binary has been loaded
334 * [offset: uleb128] file offset of mapping (the same file can be mapped multiple times)
335 * [size: uleb128] memory size
336 * [name: string] binary name
337 * if exinfo == TYPE_SAMPLE_COUNTERS_DESC
338 * [len: uleb128] number of counters
340 * [section: uleb128] section of counter
341 * if section == MONO_COUNTER_PERFCOUNTERS:
342 * [section_name: string] section name of counter
343 * [name: string] name of counter
344 * [type: uleb128] type of counter
345 * [unit: uleb128] unit of counter
346 * [variance: uleb128] variance of counter
347 * [index: uleb128] unique index of counter
348 * if exinfo == TYPE_SAMPLE_COUNTERS
349 * [timestamp: uleb128] sampling timestamp
351 * [index: uleb128] unique index of counter
354 * [type: uleb128] type of counter value
357 * [0: uleb128] 0 -> value is null
359 * [1: uleb128] 1 -> value is not null
360 * [value: string] counter value
362 * [value: uleb128/sleb128/double] counter value, can be sleb128, uleb128 or double (determined by using type)
364 * type coverage format
365 * type: TYPE_COVERAGE
366 * exinfo: one of TYPE_COVERAGE_METHOD, TYPE_COVERAGE_STATEMENT, TYPE_COVERAGE_ASSEMBLY, TYPE_COVERAGE_CLASS
367 * if exinfo == TYPE_COVERAGE_METHOD
368 * [assembly: string] name of assembly
369 * [class: string] name of the class
370 * [name: string] name of the method
371 * [signature: string] the signature of the method
372 * [filename: string] the file path of the file that contains this method
373 * [token: uleb128] the method token
374 * [method_id: uleb128] an ID for this data to associate with the buffers of TYPE_COVERAGE_STATEMENTS
375 * [len: uleb128] the number of TYPE_COVERAGE_BUFFERS associated with this method
376 * if exinfo == TYPE_COVERAGE_STATEMENTS
377 * [method_id: uleb128] an the TYPE_COVERAGE_METHOD buffer to associate this with
378 * [offset: uleb128] the il offset relative to the previous offset
379 * [counter: uleb128] the counter for this instruction
380 * [line: uleb128] the line of filename containing this instruction
381 * [column: uleb128] the column containing this instruction
382 * if exinfo == TYPE_COVERAGE_ASSEMBLY
383 * [name: string] assembly name
384 * [guid: string] assembly GUID
385 * [filename: string] assembly filename
386 * [number_of_methods: uleb128] the number of methods in this assembly
387 * [fully_covered: uleb128] the number of fully covered methods
388 * [partially_covered: uleb128] the number of partially covered methods
389 * currently partially_covered will always be 0, and fully_covered is the
390 * number of methods that are fully and partially covered.
391 * if exinfo == TYPE_COVERAGE_CLASS
392 * [name: string] assembly name
393 * [class: string] class name
394 * [number_of_methods: uleb128] the number of methods in this class
395 * [fully_covered: uleb128] the number of fully covered methods
396 * [partially_covered: uleb128] the number of partially covered methods
397 * currently partially_covered will always be 0, and fully_covered is the
398 * number of methods that are fully and partially covered.
402 * Format oddities that we ought to fix:
404 * - Methods written in emit_bt () should be based on the buffer's base
405 * method instead of the base pointer.
406 * - The TYPE_SAMPLE_HIT event contains (currently) pointless data like
407 * always-one unmanaged frame count and always-zero IL offsets.
409 * These are mostly small things and are not worth a format change by
410 * themselves. They should be done when some other major change has to
411 * be done to the format.
419 uintptr_t method_base
;
420 uintptr_t last_method
;
423 unsigned char* data_end
;
428 unsigned char buf
[1];
432 ign_res (int G_GNUC_UNUSED unused
, ...)
436 #define ENTER_LOG(lb,str) if ((lb)->locked) {ign_res (write(2, str, strlen(str))); ign_res (write(2, "\n", 1));return;} else {(lb)->locked++;}
437 #define EXIT_LOG(lb) (lb)->locked--;
439 typedef struct _StatBuffer StatBuffer
;
448 typedef struct _BinaryObject BinaryObject
;
450 struct _BinaryObject
{
456 struct _MonoProfiler
{
457 StatBuffer
*stat_buffers
;
459 #if defined (HAVE_SYS_ZLIB)
462 uint64_t startup_time
;
464 int last_gc_gen_started
;
469 pthread_t helper_thread
;
470 pthread_t writer_thread
;
472 volatile gint32 run_writer_thread
;
473 MonoLockFreeQueue writer_queue
;
474 MonoConcurrentHashTable
*method_table
;
475 mono_mutex_t method_table_mutex
;
476 BinaryObject
*binary_objects
;
477 GPtrArray
*coverage_filters
;
478 GPtrArray
*sorted_sample_events
;
481 typedef struct _WriterQueueEntry WriterQueueEntry
;
482 struct _WriterQueueEntry
{
483 MonoLockFreeQueueNode node
;
488 typedef struct _MethodInfo MethodInfo
;
500 #define TLS_SET(x,y) (TlsSetValue (x, y))
501 #define TLS_GET(t,x) ((t *) TlsGetValue (x))
502 #define TLS_INIT(x) (x = TlsAlloc ())
503 static int tlsbuffer
;
504 static int tlsmethodlist
;
506 #define TLS_SET(x,y) (x = y)
507 #define TLS_GET(t,x) (x)
509 static __thread LogBuffer
* tlsbuffer
= NULL
;
510 static __thread GPtrArray
* tlsmethodlist
= NULL
;
512 #define TLS_SET(x,y) (pthread_setspecific (x, y))
513 #define TLS_GET(t,x) ((t *) pthread_getspecific (x))
514 #define TLS_INIT(x) (pthread_key_create (&x, NULL))
515 static pthread_key_t tlsbuffer
;
516 static pthread_key_t tlsmethodlist
;
519 static void safe_send (MonoProfiler
*profiler
, LogBuffer
*logbuffer
);
522 pstrdup (const char *s
)
524 int len
= strlen (s
) + 1;
525 char *p
= (char *)malloc (len
);
531 create_stat_buffer (void)
533 StatBuffer
* buf
= (StatBuffer
*)alloc_buffer (BUFFER_SIZE
);
534 buf
->size
= BUFFER_SIZE
;
535 buf
->data_end
= (uintptr_t*)((unsigned char*)buf
+ buf
->size
);
536 buf
->data
= buf
->buf
;
543 LogBuffer
* buf
= (LogBuffer
*)alloc_buffer (BUFFER_SIZE
);
544 buf
->size
= BUFFER_SIZE
;
545 buf
->time_base
= current_time ();
546 buf
->last_time
= buf
->time_base
;
547 buf
->data_end
= (unsigned char*)buf
+ buf
->size
;
548 buf
->data
= buf
->buf
;
555 if (!TLS_GET (LogBuffer
, tlsbuffer
)) {
556 LogBuffer
*logbuffer
= create_buffer ();
557 TLS_SET (tlsbuffer
, logbuffer
);
558 logbuffer
->thread_id
= thread_id ();
560 if (!TLS_GET (GPtrArray
, tlsmethodlist
)) {
561 GPtrArray
*methodlist
= g_ptr_array_new ();
562 TLS_SET (tlsmethodlist
, methodlist
);
565 //printf ("thread %p at time %llu\n", (void*)logbuffer->thread_id, logbuffer->time_base);
569 ensure_logbuf_inner (LogBuffer
*old
, int bytes
)
571 if (old
&& old
->data
+ bytes
+ 100 < old
->data_end
)
574 LogBuffer
*new_
= (LogBuffer
*)create_buffer ();
575 new_
->thread_id
= thread_id ();
579 new_
->call_depth
= old
->call_depth
;
585 ensure_logbuf (int bytes
)
587 LogBuffer
*old
= TLS_GET (LogBuffer
, tlsbuffer
);
588 LogBuffer
*new_
= ensure_logbuf_inner (old
, bytes
);
591 return old
; // Still enough space.
593 TLS_SET (tlsbuffer
, new_
);
600 emit_byte (LogBuffer
*logbuffer
, int value
)
602 logbuffer
->data
[0] = value
;
604 assert (logbuffer
->data
<= logbuffer
->data_end
);
608 emit_value (LogBuffer
*logbuffer
, int value
)
610 encode_uleb128 (value
, logbuffer
->data
, &logbuffer
->data
);
611 assert (logbuffer
->data
<= logbuffer
->data_end
);
615 emit_time (LogBuffer
*logbuffer
, uint64_t value
)
617 uint64_t tdiff
= value
- logbuffer
->last_time
;
618 //if (value < logbuffer->last_time)
619 // printf ("time went backwards\n");
620 //if (tdiff > 1000000)
621 // printf ("large time offset: %llu\n", tdiff);
622 encode_uleb128 (tdiff
, logbuffer
->data
, &logbuffer
->data
);
623 /*if (tdiff != decode_uleb128 (p, &p))
624 printf ("incorrect encoding: %llu\n", tdiff);*/
625 logbuffer
->last_time
= value
;
626 assert (logbuffer
->data
<= logbuffer
->data_end
);
630 emit_svalue (LogBuffer
*logbuffer
, int64_t value
)
632 encode_sleb128 (value
, logbuffer
->data
, &logbuffer
->data
);
633 assert (logbuffer
->data
<= logbuffer
->data_end
);
637 emit_uvalue (LogBuffer
*logbuffer
, uint64_t value
)
639 encode_uleb128 (value
, logbuffer
->data
, &logbuffer
->data
);
640 assert (logbuffer
->data
<= logbuffer
->data_end
);
644 emit_ptr (LogBuffer
*logbuffer
, void *ptr
)
646 if (!logbuffer
->ptr_base
)
647 logbuffer
->ptr_base
= (uintptr_t)ptr
;
648 emit_svalue (logbuffer
, (intptr_t)ptr
- logbuffer
->ptr_base
);
649 assert (logbuffer
->data
<= logbuffer
->data_end
);
653 emit_method_inner (LogBuffer
*logbuffer
, void *method
)
655 if (!logbuffer
->method_base
) {
656 logbuffer
->method_base
= (intptr_t)method
;
657 logbuffer
->last_method
= (intptr_t)method
;
659 encode_sleb128 ((intptr_t)((char*)method
- (char*)logbuffer
->last_method
), logbuffer
->data
, &logbuffer
->data
);
660 logbuffer
->last_method
= (intptr_t)method
;
661 assert (logbuffer
->data
<= logbuffer
->data_end
);
671 find_method (MonoDomain *domain, void *user_data)
673 MethodSearch *search = user_data;
678 MonoJitInfo *ji = mono_get_jit_info_from_method (domain, search->method);
680 // It could be AOT'd, so we need to get it from the AOT runtime's cache.
682 void *ip = mono_aot_get_method (domain, search->method);
684 // Avoid a slow path in mono_jit_info_table_find ().
686 ji = mono_jit_info_table_find (domain, ip);
695 register_method_local (MonoProfiler
*prof
, MonoMethod
*method
, MonoJitInfo
*ji
)
697 if (!mono_conc_hashtable_lookup (prof
->method_table
, method
)) {
699 * FIXME: In some cases, we crash while looking up JIT info for AOT'd methods.
700 * This usually happens for static constructors. This code is disabled for now
701 * as we don't need this info for anything critical.
703 * https://bugzilla.xamarin.com/show_bug.cgi?id=35171
707 MethodSearch search = { method, NULL };
709 mono_domain_foreach (find_method, &search);
716 * FIXME: We can't always find JIT info for a generic shared method, especially
717 * if we obtained the MonoMethod during an async stack walk. For now, we deal
718 * with this by giving the generic shared method name and dummy code start/size
719 * information (i.e. zeroes).
723 MethodInfo
*info
= (MethodInfo
*)malloc (sizeof (MethodInfo
));
725 info
->method
= method
;
727 info
->time
= current_time ();
729 g_ptr_array_add (TLS_GET (GPtrArray
, tlsmethodlist
), info
);
734 emit_method (MonoProfiler
*prof
, LogBuffer
*logbuffer
, MonoMethod
*method
)
736 register_method_local (prof
, method
, NULL
);
737 emit_method_inner (logbuffer
, method
);
741 emit_method_as_ptr (MonoProfiler
*prof
, LogBuffer
*logbuffer
, MonoMethod
*method
)
743 register_method_local (prof
, method
, NULL
);
744 emit_ptr (logbuffer
, method
);
748 emit_obj (LogBuffer
*logbuffer
, void *ptr
)
750 if (!logbuffer
->obj_base
)
751 logbuffer
->obj_base
= (uintptr_t)ptr
>> 3;
752 emit_svalue (logbuffer
, ((uintptr_t)ptr
>> 3) - logbuffer
->obj_base
);
753 assert (logbuffer
->data
<= logbuffer
->data_end
);
757 emit_string (LogBuffer
*logbuffer
, const char *str
, size_t size
)
761 for (; i
< size
; i
++) {
764 emit_byte (logbuffer
, str
[i
]);
767 emit_byte (logbuffer
, '\0');
771 emit_double (LogBuffer
*logbuffer
, double value
)
774 unsigned char buffer
[8];
775 memcpy (buffer
, &value
, 8);
776 #if G_BYTE_ORDER == G_BIG_ENDIAN
777 for (i
= 7; i
>= 0; i
--)
779 for (i
= 0; i
< 8; i
++)
781 emit_byte (logbuffer
, buffer
[i
]);
785 write_int16 (char *buf
, int32_t value
)
788 for (i
= 0; i
< 2; ++i
) {
796 write_int32 (char *buf
, int32_t value
)
799 for (i
= 0; i
< 4; ++i
) {
807 write_int64 (char *buf
, int64_t value
)
810 for (i
= 0; i
< 8; ++i
) {
818 dump_header (MonoProfiler
*profiler
)
822 p
= write_int32 (p
, LOG_HEADER_ID
);
823 *p
++ = LOG_VERSION_MAJOR
;
824 *p
++ = LOG_VERSION_MINOR
;
825 *p
++ = LOG_DATA_VERSION
;
826 *p
++ = sizeof (void*);
827 p
= write_int64 (p
, ((uint64_t)time (NULL
)) * 1000); /* startup time */
828 p
= write_int32 (p
, get_timer_overhead ()); /* timer overhead */
829 p
= write_int32 (p
, 0); /* flags */
830 p
= write_int32 (p
, process_id ()); /* pid */
831 p
= write_int16 (p
, profiler
->command_port
); /* port */
832 p
= write_int16 (p
, 0); /* opsystem */
833 #if defined (HAVE_SYS_ZLIB)
834 if (profiler
->gzfile
) {
835 gzwrite (profiler
->gzfile
, hbuf
, p
- hbuf
);
837 fwrite (hbuf
, p
- hbuf
, 1, profiler
->file
);
840 fwrite (hbuf
, p
- hbuf
, 1, profiler
->file
);
841 fflush (profiler
->file
);
846 send_buffer (MonoProfiler
*prof
, GPtrArray
*methods
, LogBuffer
*buffer
)
848 WriterQueueEntry
*entry
= (WriterQueueEntry
*)calloc (1, sizeof (WriterQueueEntry
));
849 mono_lock_free_queue_node_init (&entry
->node
, FALSE
);
850 entry
->methods
= methods
;
851 entry
->buffer
= buffer
;
852 mono_lock_free_queue_enqueue (&prof
->writer_queue
, &entry
->node
);
856 dump_buffer (MonoProfiler
*profiler
, LogBuffer
*buf
)
861 dump_buffer (profiler
, buf
->next
);
862 p
= write_int32 (p
, BUF_ID
);
863 p
= write_int32 (p
, buf
->data
- buf
->buf
);
864 p
= write_int64 (p
, buf
->time_base
);
865 p
= write_int64 (p
, buf
->ptr_base
);
866 p
= write_int64 (p
, buf
->obj_base
);
867 p
= write_int64 (p
, buf
->thread_id
);
868 p
= write_int64 (p
, buf
->method_base
);
869 #if defined (HAVE_SYS_ZLIB)
870 if (profiler
->gzfile
) {
871 gzwrite (profiler
->gzfile
, hbuf
, p
- hbuf
);
872 gzwrite (profiler
->gzfile
, buf
->buf
, buf
->data
- buf
->buf
);
875 fwrite (hbuf
, p
- hbuf
, 1, profiler
->file
);
876 fwrite (buf
->buf
, buf
->data
- buf
->buf
, 1, profiler
->file
);
877 fflush (profiler
->file
);
878 #if defined (HAVE_SYS_ZLIB)
881 free_buffer (buf
, buf
->size
);
885 process_requests (MonoProfiler
*profiler
)
887 if (heapshot_requested
)
888 mono_gc_collect (mono_gc_max_generation ());
891 static void counters_init (MonoProfiler
*profiler
);
892 static void counters_sample (MonoProfiler
*profiler
, uint64_t timestamp
);
895 * Can be called only at safe callback locations.
898 safe_send (MonoProfiler
*profiler
, LogBuffer
*logbuffer
)
900 /* We need the runtime initialized so that we have threads and hazard
901 * pointers available. Otherwise, the lock free queue will not work and
902 * there won't be a thread to process the data.
904 * While the runtime isn't initialized, we just accumulate data in the
905 * thread local buffer list.
907 if (!InterlockedRead (&runtime_inited
))
910 int cd
= logbuffer
->call_depth
;
912 send_buffer (profiler
, TLS_GET (GPtrArray
, tlsmethodlist
), TLS_GET (LogBuffer
, tlsbuffer
));
914 TLS_SET (tlsbuffer
, NULL
);
915 TLS_SET (tlsmethodlist
, NULL
);
919 TLS_GET (LogBuffer
, tlsbuffer
)->call_depth
= cd
;
923 gc_reference (MonoObject
*obj
, MonoClass
*klass
, uintptr_t size
, uintptr_t num
, MonoObject
**refs
, uintptr_t *offsets
, void *data
)
926 uintptr_t last_offset
= 0;
927 //const char *name = mono_class_get_name (klass);
928 LogBuffer
*logbuffer
= ensure_logbuf (
929 EVENT_SIZE
/* event */ +
930 LEB128_SIZE
/* obj */ +
931 LEB128_SIZE
/* klass */ +
932 LEB128_SIZE
/* size */ +
933 LEB128_SIZE
/* num */ +
935 LEB128_SIZE
/* offset */ +
936 LEB128_SIZE
/* ref */
939 emit_byte (logbuffer
, TYPE_HEAP_OBJECT
| TYPE_HEAP
);
940 emit_obj (logbuffer
, obj
);
941 emit_ptr (logbuffer
, klass
);
942 /* account for object alignment in the heap */
945 emit_value (logbuffer
, size
);
946 emit_value (logbuffer
, num
);
947 for (i
= 0; i
< num
; ++i
) {
948 emit_value (logbuffer
, offsets
[i
] - last_offset
);
949 last_offset
= offsets
[i
];
950 emit_obj (logbuffer
, refs
[i
]);
953 // printf ("obj: %p, klass: %s, refs: %d, size: %d\n", obj, name, (int)num, (int)size);
957 static unsigned int hs_mode_ms
= 0;
958 static unsigned int hs_mode_gc
= 0;
959 static unsigned int hs_mode_ondemand
= 0;
960 static unsigned int gc_count
= 0;
961 static uint64_t last_hs_time
= 0;
964 heap_walk (MonoProfiler
*profiler
)
968 LogBuffer
*logbuffer
;
971 logbuffer
= ensure_logbuf (
972 EVENT_SIZE
/* event */ +
973 LEB128_SIZE
/* time */
975 now
= current_time ();
976 if (hs_mode_ms
&& (now
- last_hs_time
)/1000000 >= hs_mode_ms
)
978 else if (hs_mode_gc
&& (gc_count
% hs_mode_gc
) == 0)
980 else if (hs_mode_ondemand
)
981 do_walk
= heapshot_requested
;
982 else if (!hs_mode_ms
&& !hs_mode_gc
&& profiler
->last_gc_gen_started
== mono_gc_max_generation ())
987 heapshot_requested
= 0;
988 emit_byte (logbuffer
, TYPE_HEAP_START
| TYPE_HEAP
);
989 emit_time (logbuffer
, now
);
990 mono_gc_walk_heap (0, gc_reference
, NULL
);
991 logbuffer
= ensure_logbuf (
992 EVENT_SIZE
/* event */ +
993 LEB128_SIZE
/* time */
995 now
= current_time ();
996 emit_byte (logbuffer
, TYPE_HEAP_END
| TYPE_HEAP
);
997 emit_time (logbuffer
, now
);
1002 gc_event (MonoProfiler
*profiler
, MonoGCEvent ev
, int generation
) {
1004 LogBuffer
*logbuffer
= ensure_logbuf (
1005 EVENT_SIZE
/* event */ +
1006 LEB128_SIZE
/* time */ +
1007 LEB128_SIZE
/* gc event */ +
1008 LEB128_SIZE
/* generation */
1010 now
= current_time ();
1011 ENTER_LOG (logbuffer
, "gcevent");
1012 emit_byte (logbuffer
, TYPE_GC_EVENT
| TYPE_GC
);
1013 emit_time (logbuffer
, now
);
1014 emit_value (logbuffer
, ev
);
1015 emit_value (logbuffer
, generation
);
1016 /* to deal with nested gen1 after gen0 started */
1017 if (ev
== MONO_GC_EVENT_START
) {
1018 profiler
->last_gc_gen_started
= generation
;
1019 if (generation
== mono_gc_max_generation ())
1022 if (ev
== MONO_GC_EVENT_PRE_START_WORLD
)
1023 heap_walk (profiler
);
1024 EXIT_LOG (logbuffer
);
1025 if (ev
== MONO_GC_EVENT_POST_START_WORLD
)
1026 safe_send (profiler
, logbuffer
);
1027 //printf ("gc event %d for generation %d\n", ev, generation);
1031 gc_resize (MonoProfiler
*profiler
, int64_t new_size
) {
1033 LogBuffer
*logbuffer
= ensure_logbuf (
1034 EVENT_SIZE
/* event */ +
1035 LEB128_SIZE
/* time */ +
1036 LEB128_SIZE
/* new size */
1038 now
= current_time ();
1039 ENTER_LOG (logbuffer
, "gcresize");
1040 emit_byte (logbuffer
, TYPE_GC_RESIZE
| TYPE_GC
);
1041 emit_time (logbuffer
, now
);
1042 emit_value (logbuffer
, new_size
);
1043 //printf ("gc resized to %lld\n", new_size);
1044 EXIT_LOG (logbuffer
);
1047 #define MAX_FRAMES 32
1050 MonoMethod
* methods
[MAX_FRAMES
];
1051 int32_t il_offsets
[MAX_FRAMES
];
1052 int32_t native_offsets
[MAX_FRAMES
];
1054 static int num_frames
= MAX_FRAMES
;
1057 walk_stack (MonoMethod
*method
, int32_t native_offset
, int32_t il_offset
, mono_bool managed
, void* data
)
1059 FrameData
*frame
= (FrameData
*)data
;
1060 if (method
&& frame
->count
< num_frames
) {
1061 frame
->il_offsets
[frame
->count
] = il_offset
;
1062 frame
->native_offsets
[frame
->count
] = native_offset
;
1063 frame
->methods
[frame
->count
++] = method
;
1064 //printf ("In %d %s at %d (native: %d)\n", frame->count, mono_method_get_name (method), il_offset, native_offset);
1066 return frame
->count
== num_frames
;
1070 * a note about stack walks: they can cause more profiler events to fire,
1071 * so we need to make sure they don't happen after we started emitting an
1072 * event, hence the collect_bt/emit_bt split.
1075 collect_bt (FrameData
*data
)
1078 mono_stack_walk_no_il (walk_stack
, data
);
1082 emit_bt (MonoProfiler
*prof
, LogBuffer
*logbuffer
, FrameData
*data
)
1084 /* FIXME: this is actually tons of data and we should
1085 * just output it the first time and use an id the next
1087 if (data
->count
> num_frames
)
1088 printf ("bad num frames: %d\n", data
->count
);
1089 emit_value (logbuffer
, 0); /* flags */
1090 emit_value (logbuffer
, data
->count
);
1091 //if (*p != data.count) {
1092 // printf ("bad num frames enc at %d: %d -> %d\n", count, data.count, *p); printf ("frames end: %p->%p\n", p, logbuffer->data); exit(0);}
1093 while (data
->count
) {
1094 emit_method_as_ptr (prof
, logbuffer
, data
->methods
[--data
->count
]);
1099 gc_alloc (MonoProfiler
*prof
, MonoObject
*obj
, MonoClass
*klass
)
1103 int do_bt
= (nocalls
&& InterlockedRead (&runtime_inited
) && !notraces
)? TYPE_ALLOC_BT
: 0;
1105 LogBuffer
*logbuffer
;
1106 len
= mono_object_get_size (obj
);
1107 /* account for object alignment in the heap */
1112 logbuffer
= ensure_logbuf (
1113 EVENT_SIZE
/* event */ +
1114 LEB128_SIZE
/* time */ +
1115 LEB128_SIZE
/* klass */ +
1116 LEB128_SIZE
/* obj */ +
1117 LEB128_SIZE
/* size */ +
1119 LEB128_SIZE
/* flags */ +
1120 LEB128_SIZE
/* count */ +
1122 LEB128_SIZE
/* method */
1126 now
= current_time ();
1127 ENTER_LOG (logbuffer
, "gcalloc");
1128 emit_byte (logbuffer
, do_bt
| TYPE_ALLOC
);
1129 emit_time (logbuffer
, now
);
1130 emit_ptr (logbuffer
, klass
);
1131 emit_obj (logbuffer
, obj
);
1132 emit_value (logbuffer
, len
);
1134 emit_bt (prof
, logbuffer
, &data
);
1135 EXIT_LOG (logbuffer
);
1136 if (logbuffer
->next
)
1137 safe_send (prof
, logbuffer
);
1138 process_requests (prof
);
1139 //printf ("gc alloc %s at %p\n", mono_class_get_name (klass), obj);
1143 gc_moves (MonoProfiler
*prof
, void **objects
, int num
)
1147 LogBuffer
*logbuffer
= ensure_logbuf (
1148 EVENT_SIZE
/* event */ +
1149 LEB128_SIZE
/* time */ +
1150 LEB128_SIZE
/* num */ +
1152 LEB128_SIZE
/* object */
1155 now
= current_time ();
1156 ENTER_LOG (logbuffer
, "gcmove");
1157 emit_byte (logbuffer
, TYPE_GC_MOVE
| TYPE_GC
);
1158 emit_time (logbuffer
, now
);
1159 emit_value (logbuffer
, num
);
1160 for (i
= 0; i
< num
; ++i
)
1161 emit_obj (logbuffer
, objects
[i
]);
1162 //printf ("gc moved %d objects\n", num/2);
1163 EXIT_LOG (logbuffer
);
1167 gc_roots (MonoProfiler
*prof
, int num
, void **objects
, int *root_types
, uintptr_t *extra_info
)
1170 LogBuffer
*logbuffer
= ensure_logbuf (
1171 EVENT_SIZE
/* event */ +
1172 LEB128_SIZE
/* num */ +
1173 LEB128_SIZE
/* collections */ +
1175 LEB128_SIZE
/* object */ +
1176 LEB128_SIZE
/* root type */ +
1177 LEB128_SIZE
/* extra info */
1180 ENTER_LOG (logbuffer
, "gcroots");
1181 emit_byte (logbuffer
, TYPE_HEAP_ROOT
| TYPE_HEAP
);
1182 emit_value (logbuffer
, num
);
1183 emit_value (logbuffer
, mono_gc_collection_count (mono_gc_max_generation ()));
1184 for (i
= 0; i
< num
; ++i
) {
1185 emit_obj (logbuffer
, objects
[i
]);
1186 emit_value (logbuffer
, root_types
[i
]);
1187 emit_value (logbuffer
, extra_info
[i
]);
1189 EXIT_LOG (logbuffer
);
1193 gc_handle (MonoProfiler
*prof
, int op
, int type
, uintptr_t handle
, MonoObject
*obj
)
1195 int do_bt
= nocalls
&& InterlockedRead (&runtime_inited
) && !notraces
;
1202 LogBuffer
*logbuffer
= ensure_logbuf (
1203 EVENT_SIZE
/* event */ +
1204 LEB128_SIZE
/* time */ +
1205 LEB128_SIZE
/* type */ +
1206 LEB128_SIZE
/* handle */ +
1207 (op
== MONO_PROFILER_GC_HANDLE_CREATED
? (
1208 LEB128_SIZE
/* obj */
1211 LEB128_SIZE
/* flags */ +
1212 LEB128_SIZE
/* count */ +
1214 LEB128_SIZE
/* method */
1219 now
= current_time ();
1220 ENTER_LOG (logbuffer
, "gchandle");
1222 if (op
== MONO_PROFILER_GC_HANDLE_CREATED
)
1223 emit_byte (logbuffer
, (do_bt
? TYPE_GC_HANDLE_CREATED_BT
: TYPE_GC_HANDLE_CREATED
) | TYPE_GC
);
1224 else if (op
== MONO_PROFILER_GC_HANDLE_DESTROYED
)
1225 emit_byte (logbuffer
, (do_bt
? TYPE_GC_HANDLE_DESTROYED_BT
: TYPE_GC_HANDLE_DESTROYED
) | TYPE_GC
);
1227 g_assert_not_reached ();
1229 emit_time (logbuffer
, now
);
1230 emit_value (logbuffer
, type
);
1231 emit_value (logbuffer
, handle
);
1233 if (op
== MONO_PROFILER_GC_HANDLE_CREATED
)
1234 emit_obj (logbuffer
, obj
);
1237 emit_bt (prof
, logbuffer
, &data
);
1239 EXIT_LOG (logbuffer
);
1240 process_requests (prof
);
1244 push_nesting (char *p
, MonoClass
*klass
)
1249 nesting
= mono_class_get_nesting_type (klass
);
1251 p
= push_nesting (p
, nesting
);
1255 name
= mono_class_get_name (klass
);
1256 nspace
= mono_class_get_namespace (klass
);
1259 p
+= strlen (nspace
);
1269 type_name (MonoClass
*klass
)
1273 push_nesting (buf
, klass
);
1274 p
= (char *)malloc (strlen (buf
) + 1);
1280 image_loaded (MonoProfiler
*prof
, MonoImage
*image
, int result
)
1285 LogBuffer
*logbuffer
;
1286 if (result
!= MONO_PROFILE_OK
)
1288 name
= mono_image_get_filename (image
);
1289 nlen
= strlen (name
) + 1;
1290 logbuffer
= ensure_logbuf (
1291 EVENT_SIZE
/* event */ +
1292 LEB128_SIZE
/* time */ +
1293 EVENT_SIZE
/* type */ +
1294 LEB128_SIZE
/* image */ +
1295 LEB128_SIZE
/* flags */ +
1298 now
= current_time ();
1299 ENTER_LOG (logbuffer
, "image");
1300 emit_byte (logbuffer
, TYPE_END_LOAD
| TYPE_METADATA
);
1301 emit_time (logbuffer
, now
);
1302 emit_byte (logbuffer
, TYPE_IMAGE
);
1303 emit_ptr (logbuffer
, image
);
1304 emit_value (logbuffer
, 0); /* flags */
1305 memcpy (logbuffer
->data
, name
, nlen
);
1306 logbuffer
->data
+= nlen
;
1307 //printf ("loaded image %p (%s)\n", image, name);
1308 EXIT_LOG (logbuffer
);
1309 if (logbuffer
->next
)
1310 safe_send (prof
, logbuffer
);
1311 process_requests (prof
);
1315 image_unloaded (MonoProfiler
*prof
, MonoImage
*image
)
1317 const char *name
= mono_image_get_filename (image
);
1318 int nlen
= strlen (name
) + 1;
1319 LogBuffer
*logbuffer
= ensure_logbuf (
1320 EVENT_SIZE
/* event */ +
1321 LEB128_SIZE
/* time */ +
1322 EVENT_SIZE
/* type */ +
1323 LEB128_SIZE
/* image */ +
1324 LEB128_SIZE
/* flags */ +
1327 uint64_t now
= current_time ();
1329 ENTER_LOG (logbuffer
, "image-unload");
1330 emit_byte (logbuffer
, TYPE_END_UNLOAD
| TYPE_METADATA
);
1331 emit_time (logbuffer
, now
);
1332 emit_byte (logbuffer
, TYPE_IMAGE
);
1333 emit_ptr (logbuffer
, image
);
1334 emit_value (logbuffer
, 0); /* flags */
1335 memcpy (logbuffer
->data
, name
, nlen
);
1336 logbuffer
->data
+= nlen
;
1337 EXIT_LOG (logbuffer
);
1339 if (logbuffer
->next
)
1340 safe_send (prof
, logbuffer
);
1342 process_requests (prof
);
1346 assembly_loaded (MonoProfiler
*prof
, MonoAssembly
*assembly
, int result
)
1348 if (result
!= MONO_PROFILE_OK
)
1351 char *name
= mono_stringify_assembly_name (mono_assembly_get_name (assembly
));
1352 int nlen
= strlen (name
) + 1;
1353 LogBuffer
*logbuffer
= ensure_logbuf (
1354 EVENT_SIZE
/* event */ +
1355 LEB128_SIZE
/* time */ +
1356 EVENT_SIZE
/* type */ +
1357 LEB128_SIZE
/* assembly */ +
1358 LEB128_SIZE
/* flags */ +
1361 uint64_t now
= current_time ();
1363 ENTER_LOG (logbuffer
, "assembly-load");
1364 emit_byte (logbuffer
, TYPE_END_LOAD
| TYPE_METADATA
);
1365 emit_time (logbuffer
, now
);
1366 emit_byte (logbuffer
, TYPE_ASSEMBLY
);
1367 emit_ptr (logbuffer
, assembly
);
1368 emit_value (logbuffer
, 0); /* flags */
1369 memcpy (logbuffer
->data
, name
, nlen
);
1370 logbuffer
->data
+= nlen
;
1371 EXIT_LOG (logbuffer
);
1375 if (logbuffer
->next
)
1376 safe_send (prof
, logbuffer
);
1378 process_requests (prof
);
1382 assembly_unloaded (MonoProfiler
*prof
, MonoAssembly
*assembly
)
1384 char *name
= mono_stringify_assembly_name (mono_assembly_get_name (assembly
));
1385 int nlen
= strlen (name
) + 1;
1386 LogBuffer
*logbuffer
= ensure_logbuf (
1387 EVENT_SIZE
/* event */ +
1388 LEB128_SIZE
/* time */ +
1389 EVENT_SIZE
/* type */ +
1390 LEB128_SIZE
/* assembly */ +
1391 LEB128_SIZE
/* flags */ +
1394 uint64_t now
= current_time ();
1396 ENTER_LOG (logbuffer
, "assembly-unload");
1397 emit_byte (logbuffer
, TYPE_END_UNLOAD
| TYPE_METADATA
);
1398 emit_time (logbuffer
, now
);
1399 emit_byte (logbuffer
, TYPE_ASSEMBLY
);
1400 emit_ptr (logbuffer
, assembly
);
1401 emit_value (logbuffer
, 0); /* flags */
1402 memcpy (logbuffer
->data
, name
, nlen
);
1403 logbuffer
->data
+= nlen
;
1404 EXIT_LOG (logbuffer
);
1408 if (logbuffer
->next
)
1409 safe_send (prof
, logbuffer
);
1411 process_requests (prof
);
1415 class_loaded (MonoProfiler
*prof
, MonoClass
*klass
, int result
)
1421 LogBuffer
*logbuffer
;
1422 if (result
!= MONO_PROFILE_OK
)
1424 if (InterlockedRead (&runtime_inited
))
1425 name
= mono_type_get_name (mono_class_get_type (klass
));
1427 name
= type_name (klass
);
1428 nlen
= strlen (name
) + 1;
1429 image
= mono_class_get_image (klass
);
1430 logbuffer
= ensure_logbuf (
1431 EVENT_SIZE
/* event */ +
1432 LEB128_SIZE
/* time */ +
1433 EVENT_SIZE
/* type */ +
1434 LEB128_SIZE
/* klass */ +
1435 LEB128_SIZE
/* image */ +
1436 LEB128_SIZE
/* flags */ +
1439 now
= current_time ();
1440 ENTER_LOG (logbuffer
, "class");
1441 emit_byte (logbuffer
, TYPE_END_LOAD
| TYPE_METADATA
);
1442 emit_time (logbuffer
, now
);
1443 emit_byte (logbuffer
, TYPE_CLASS
);
1444 emit_ptr (logbuffer
, klass
);
1445 emit_ptr (logbuffer
, image
);
1446 emit_value (logbuffer
, 0); /* flags */
1447 memcpy (logbuffer
->data
, name
, nlen
);
1448 logbuffer
->data
+= nlen
;
1449 //printf ("loaded class %p (%s)\n", klass, name);
1454 EXIT_LOG (logbuffer
);
1455 if (logbuffer
->next
)
1456 safe_send (prof
, logbuffer
);
1457 process_requests (prof
);
1461 class_unloaded (MonoProfiler
*prof
, MonoClass
*klass
)
1465 if (InterlockedRead (&runtime_inited
))
1466 name
= mono_type_get_name (mono_class_get_type (klass
));
1468 name
= type_name (klass
);
1470 int nlen
= strlen (name
) + 1;
1471 MonoImage
*image
= mono_class_get_image (klass
);
1472 LogBuffer
*logbuffer
= ensure_logbuf (
1473 EVENT_SIZE
/* event */ +
1474 LEB128_SIZE
/* time */ +
1475 EVENT_SIZE
/* type */ +
1476 LEB128_SIZE
/* klass */ +
1477 LEB128_SIZE
/* image */ +
1478 LEB128_SIZE
/* flags */ +
1481 uint64_t now
= current_time ();
1483 ENTER_LOG (logbuffer
, "class-unload");
1484 emit_byte (logbuffer
, TYPE_END_UNLOAD
| TYPE_METADATA
);
1485 emit_time (logbuffer
, now
);
1486 emit_byte (logbuffer
, TYPE_CLASS
);
1487 emit_ptr (logbuffer
, klass
);
1488 emit_ptr (logbuffer
, image
);
1489 emit_value (logbuffer
, 0); /* flags */
1490 memcpy (logbuffer
->data
, name
, nlen
);
1491 logbuffer
->data
+= nlen
;
1492 EXIT_LOG (logbuffer
);
1499 if (logbuffer
->next
)
1500 safe_send (prof
, logbuffer
);
1502 process_requests (prof
);
1505 #ifndef DISABLE_HELPER_THREAD
1506 static void process_method_enter_coverage (MonoProfiler
*prof
, MonoMethod
*method
);
1507 #endif /* DISABLE_HELPER_THREAD */
1510 method_enter (MonoProfiler
*prof
, MonoMethod
*method
)
1512 uint64_t now
= current_time ();
1514 #ifndef DISABLE_HELPER_THREAD
1515 process_method_enter_coverage (prof
, method
);
1516 #endif /* DISABLE_HELPER_THREAD */
1518 LogBuffer
*logbuffer
= ensure_logbuf (
1519 EVENT_SIZE
/* event */ +
1520 LEB128_SIZE
/* time */ +
1521 LEB128_SIZE
/* method */
1523 if (logbuffer
->call_depth
++ > max_call_depth
)
1525 ENTER_LOG (logbuffer
, "enter");
1526 emit_byte (logbuffer
, TYPE_ENTER
| TYPE_METHOD
);
1527 emit_time (logbuffer
, now
);
1528 emit_method (prof
, logbuffer
, method
);
1529 EXIT_LOG (logbuffer
);
1531 process_requests (prof
);
1535 method_leave (MonoProfiler
*prof
, MonoMethod
*method
)
1538 LogBuffer
*logbuffer
= ensure_logbuf (
1539 EVENT_SIZE
/* event */ +
1540 LEB128_SIZE
/* time */ +
1541 LEB128_SIZE
/* method */
1543 if (--logbuffer
->call_depth
> max_call_depth
)
1545 now
= current_time ();
1546 ENTER_LOG (logbuffer
, "leave");
1547 emit_byte (logbuffer
, TYPE_LEAVE
| TYPE_METHOD
);
1548 emit_time (logbuffer
, now
);
1549 emit_method (prof
, logbuffer
, method
);
1550 EXIT_LOG (logbuffer
);
1551 if (logbuffer
->next
)
1552 safe_send (prof
, logbuffer
);
1553 process_requests (prof
);
1557 method_exc_leave (MonoProfiler
*prof
, MonoMethod
*method
)
1560 LogBuffer
*logbuffer
;
1563 logbuffer
= ensure_logbuf (
1564 EVENT_SIZE
/* event */ +
1565 LEB128_SIZE
/* time */ +
1566 LEB128_SIZE
/* method */
1568 if (--logbuffer
->call_depth
> max_call_depth
)
1570 now
= current_time ();
1571 ENTER_LOG (logbuffer
, "eleave");
1572 emit_byte (logbuffer
, TYPE_EXC_LEAVE
| TYPE_METHOD
);
1573 emit_time (logbuffer
, now
);
1574 emit_method (prof
, logbuffer
, method
);
1575 EXIT_LOG (logbuffer
);
1576 process_requests (prof
);
1580 method_jitted (MonoProfiler
*prof
, MonoMethod
*method
, MonoJitInfo
*ji
, int result
)
1582 if (result
!= MONO_PROFILE_OK
)
1585 register_method_local (prof
, method
, ji
);
1587 process_requests (prof
);
1591 code_buffer_new (MonoProfiler
*prof
, void *buffer
, int size
, MonoProfilerCodeBufferType type
, void *data
)
1596 LogBuffer
*logbuffer
;
1597 if (type
== MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE
) {
1598 name
= (char *)data
;
1599 nlen
= strlen (name
) + 1;
1604 logbuffer
= ensure_logbuf (
1605 EVENT_SIZE
/* event */ +
1606 LEB128_SIZE
/* time */ +
1607 LEB128_SIZE
/* type */ +
1608 LEB128_SIZE
/* buffer */ +
1609 LEB128_SIZE
/* size */ +
1614 now
= current_time ();
1615 ENTER_LOG (logbuffer
, "code buffer");
1616 emit_byte (logbuffer
, TYPE_JITHELPER
| TYPE_RUNTIME
);
1617 emit_time (logbuffer
, now
);
1618 emit_value (logbuffer
, type
);
1619 emit_ptr (logbuffer
, buffer
);
1620 emit_value (logbuffer
, size
);
1622 memcpy (logbuffer
->data
, name
, nlen
);
1623 logbuffer
->data
+= nlen
;
1625 EXIT_LOG (logbuffer
);
1626 process_requests (prof
);
1630 throw_exc (MonoProfiler
*prof
, MonoObject
*object
)
1632 int do_bt
= (nocalls
&& InterlockedRead (&runtime_inited
) && !notraces
)? TYPE_EXCEPTION_BT
: 0;
1635 LogBuffer
*logbuffer
;
1638 logbuffer
= ensure_logbuf (
1639 EVENT_SIZE
/* event */ +
1640 LEB128_SIZE
/* time */ +
1641 LEB128_SIZE
/* object */ +
1643 LEB128_SIZE
/* flags */ +
1644 LEB128_SIZE
/* count */ +
1646 LEB128_SIZE
/* method */
1650 now
= current_time ();
1651 ENTER_LOG (logbuffer
, "throw");
1652 emit_byte (logbuffer
, do_bt
| TYPE_EXCEPTION
);
1653 emit_time (logbuffer
, now
);
1654 emit_obj (logbuffer
, object
);
1656 emit_bt (prof
, logbuffer
, &data
);
1657 EXIT_LOG (logbuffer
);
1658 process_requests (prof
);
1662 clause_exc (MonoProfiler
*prof
, MonoMethod
*method
, int clause_type
, int clause_num
)
1665 LogBuffer
*logbuffer
= ensure_logbuf (
1666 EVENT_SIZE
/* event */ +
1667 LEB128_SIZE
/* time */ +
1668 LEB128_SIZE
/* clause type */ +
1669 LEB128_SIZE
/* clause num */ +
1670 LEB128_SIZE
/* method */
1672 now
= current_time ();
1673 ENTER_LOG (logbuffer
, "clause");
1674 emit_byte (logbuffer
, TYPE_EXCEPTION
| TYPE_CLAUSE
);
1675 emit_time (logbuffer
, now
);
1676 emit_value (logbuffer
, clause_type
);
1677 emit_value (logbuffer
, clause_num
);
1678 emit_method (prof
, logbuffer
, method
);
1679 EXIT_LOG (logbuffer
);
1681 process_requests (prof
);
1685 monitor_event (MonoProfiler
*profiler
, MonoObject
*object
, MonoProfilerMonitorEvent event
)
1687 int do_bt
= (nocalls
&& InterlockedRead (&runtime_inited
) && !notraces
&& event
== MONO_PROFILER_MONITOR_CONTENTION
)? TYPE_MONITOR_BT
: 0;
1690 LogBuffer
*logbuffer
;
1693 logbuffer
= ensure_logbuf (
1694 EVENT_SIZE
/* event */ +
1695 LEB128_SIZE
/* time */ +
1696 LEB128_SIZE
/* object */ +
1698 LEB128_SIZE
/* flags */ +
1699 LEB128_SIZE
/* count */ +
1701 LEB128_SIZE
/* method */
1705 now
= current_time ();
1706 ENTER_LOG (logbuffer
, "monitor");
1707 emit_byte (logbuffer
, (event
<< 4) | do_bt
| TYPE_MONITOR
);
1708 emit_time (logbuffer
, now
);
1709 emit_obj (logbuffer
, object
);
1711 emit_bt (profiler
, logbuffer
, &data
);
1712 EXIT_LOG (logbuffer
);
1713 process_requests (profiler
);
1717 thread_start (MonoProfiler
*prof
, uintptr_t tid
)
1719 //printf ("thread start %p\n", (void*)tid);
1722 LogBuffer
*logbuffer
= ensure_logbuf (
1723 EVENT_SIZE
/* event */ +
1724 LEB128_SIZE
/* time */ +
1725 EVENT_SIZE
/* type */ +
1726 LEB128_SIZE
/* tid */ +
1727 LEB128_SIZE
/* flags */
1729 uint64_t now
= current_time ();
1731 ENTER_LOG (logbuffer
, "thread-start");
1732 emit_byte (logbuffer
, TYPE_END_LOAD
| TYPE_METADATA
);
1733 emit_time (logbuffer
, now
);
1734 emit_byte (logbuffer
, TYPE_THREAD
);
1735 emit_ptr (logbuffer
, (void*) tid
);
1736 emit_value (logbuffer
, 0); /* flags */
1737 EXIT_LOG (logbuffer
);
1739 if (logbuffer
->next
)
1740 safe_send (prof
, logbuffer
);
1742 process_requests (prof
);
1746 thread_end (MonoProfiler
*prof
, uintptr_t tid
)
1748 if (TLS_GET (LogBuffer
, tlsbuffer
)) {
1749 LogBuffer
*logbuffer
= ensure_logbuf (
1750 EVENT_SIZE
/* event */ +
1751 LEB128_SIZE
/* time */ +
1752 EVENT_SIZE
/* type */ +
1753 LEB128_SIZE
/* tid */ +
1754 LEB128_SIZE
/* flags */
1756 uint64_t now
= current_time ();
1758 ENTER_LOG (logbuffer
, "thread-end");
1759 emit_byte (logbuffer
, TYPE_END_UNLOAD
| TYPE_METADATA
);
1760 emit_time (logbuffer
, now
);
1761 emit_byte (logbuffer
, TYPE_THREAD
);
1762 emit_ptr (logbuffer
, (void*) tid
);
1763 emit_value (logbuffer
, 0); /* flags */
1764 EXIT_LOG (logbuffer
);
1766 send_buffer (prof
, TLS_GET (GPtrArray
, tlsmethodlist
), logbuffer
);
1768 /* Don't process requests as the thread is detached from the runtime. */
1771 TLS_SET (tlsbuffer
, NULL
);
1772 TLS_SET (tlsmethodlist
, NULL
);
1776 domain_loaded (MonoProfiler
*prof
, MonoDomain
*domain
, int result
)
1778 if (result
!= MONO_PROFILE_OK
)
1781 LogBuffer
*logbuffer
= ensure_logbuf (
1782 EVENT_SIZE
/* event */ +
1783 LEB128_SIZE
/* time */ +
1784 EVENT_SIZE
/* type */ +
1785 LEB128_SIZE
/* domain id */ +
1786 LEB128_SIZE
/* flags */
1788 uint64_t now
= current_time ();
1790 ENTER_LOG (logbuffer
, "domain-start");
1791 emit_byte (logbuffer
, TYPE_END_LOAD
| TYPE_METADATA
);
1792 emit_time (logbuffer
, now
);
1793 emit_byte (logbuffer
, TYPE_DOMAIN
);
1794 emit_ptr (logbuffer
, (void*)(uintptr_t) mono_domain_get_id (domain
));
1795 emit_value (logbuffer
, 0); /* flags */
1796 EXIT_LOG (logbuffer
);
1798 if (logbuffer
->next
)
1799 safe_send (prof
, logbuffer
);
1801 process_requests (prof
);
1805 domain_unloaded (MonoProfiler
*prof
, MonoDomain
*domain
)
1807 LogBuffer
*logbuffer
= ensure_logbuf (
1808 EVENT_SIZE
/* event */ +
1809 LEB128_SIZE
/* time */ +
1810 EVENT_SIZE
/* type */ +
1811 LEB128_SIZE
/* domain id */ +
1812 LEB128_SIZE
/* flags */
1814 uint64_t now
= current_time ();
1816 ENTER_LOG (logbuffer
, "domain-end");
1817 emit_byte (logbuffer
, TYPE_END_UNLOAD
| TYPE_METADATA
);
1818 emit_time (logbuffer
, now
);
1819 emit_byte (logbuffer
, TYPE_DOMAIN
);
1820 emit_ptr (logbuffer
, (void*)(uintptr_t) mono_domain_get_id (domain
));
1821 emit_value (logbuffer
, 0); /* flags */
1822 EXIT_LOG (logbuffer
);
1824 if (logbuffer
->next
)
1825 safe_send (prof
, logbuffer
);
1827 process_requests (prof
);
1831 domain_name (MonoProfiler
*prof
, MonoDomain
*domain
, const char *name
)
1833 int nlen
= strlen (name
) + 1;
1834 LogBuffer
*logbuffer
= ensure_logbuf (
1835 EVENT_SIZE
/* event */ +
1836 LEB128_SIZE
/* time */ +
1837 EVENT_SIZE
/* type */ +
1838 LEB128_SIZE
/* domain id */ +
1839 LEB128_SIZE
/* flags */ +
1842 uint64_t now
= current_time ();
1844 ENTER_LOG (logbuffer
, "domain-name");
1845 emit_byte (logbuffer
, TYPE_METADATA
);
1846 emit_time (logbuffer
, now
);
1847 emit_byte (logbuffer
, TYPE_DOMAIN
);
1848 emit_ptr (logbuffer
, (void*)(uintptr_t) mono_domain_get_id (domain
));
1849 emit_value (logbuffer
, 0); /* flags */
1850 memcpy (logbuffer
->data
, name
, nlen
);
1851 logbuffer
->data
+= nlen
;
1852 EXIT_LOG (logbuffer
);
1854 if (logbuffer
->next
)
1855 safe_send (prof
, logbuffer
);
1857 process_requests (prof
);
1861 context_loaded (MonoProfiler
*prof
, MonoAppContext
*context
)
1863 LogBuffer
*logbuffer
= ensure_logbuf (
1864 EVENT_SIZE
/* event */ +
1865 LEB128_SIZE
/* time */ +
1866 EVENT_SIZE
/* type */ +
1867 LEB128_SIZE
/* context id */ +
1868 LEB128_SIZE
/* flags */ +
1869 LEB128_SIZE
/* domain id */
1871 uint64_t now
= current_time ();
1873 ENTER_LOG (logbuffer
, "context-start");
1874 emit_byte (logbuffer
, TYPE_END_LOAD
| TYPE_METADATA
);
1875 emit_time (logbuffer
, now
);
1876 emit_byte (logbuffer
, TYPE_CONTEXT
);
1877 emit_ptr (logbuffer
, (void*)(uintptr_t) mono_context_get_id (context
));
1878 emit_value (logbuffer
, 0); /* flags */
1879 emit_ptr (logbuffer
, (void*)(uintptr_t) mono_context_get_domain_id (context
));
1880 EXIT_LOG (logbuffer
);
1882 if (logbuffer
->next
)
1883 safe_send (prof
, logbuffer
);
1885 process_requests (prof
);
1889 context_unloaded (MonoProfiler
*prof
, MonoAppContext
*context
)
1891 LogBuffer
*logbuffer
= ensure_logbuf (
1892 EVENT_SIZE
/* event */ +
1893 LEB128_SIZE
/* time */ +
1894 EVENT_SIZE
/* type */ +
1895 LEB128_SIZE
/* context id */ +
1896 LEB128_SIZE
/* flags */ +
1897 LEB128_SIZE
/* domain id */
1899 uint64_t now
= current_time ();
1901 ENTER_LOG (logbuffer
, "context-end");
1902 emit_byte (logbuffer
, TYPE_END_UNLOAD
| TYPE_METADATA
);
1903 emit_time (logbuffer
, now
);
1904 emit_byte (logbuffer
, TYPE_CONTEXT
);
1905 emit_ptr (logbuffer
, (void*)(uintptr_t) mono_context_get_id (context
));
1906 emit_value (logbuffer
, 0); /* flags */
1907 emit_ptr (logbuffer
, (void*)(uintptr_t) mono_context_get_domain_id (context
));
1908 EXIT_LOG (logbuffer
);
1910 if (logbuffer
->next
)
1911 safe_send (prof
, logbuffer
);
1913 process_requests (prof
);
1917 thread_name (MonoProfiler
*prof
, uintptr_t tid
, const char *name
)
1919 int len
= strlen (name
) + 1;
1921 LogBuffer
*logbuffer
;
1922 logbuffer
= ensure_logbuf (
1923 EVENT_SIZE
/* event */ +
1924 LEB128_SIZE
/* time */ +
1925 EVENT_SIZE
/* type */ +
1926 LEB128_SIZE
/* tid */ +
1927 LEB128_SIZE
/* flags */ +
1930 now
= current_time ();
1931 ENTER_LOG (logbuffer
, "tname");
1932 emit_byte (logbuffer
, TYPE_METADATA
);
1933 emit_time (logbuffer
, now
);
1934 emit_byte (logbuffer
, TYPE_THREAD
);
1935 emit_ptr (logbuffer
, (void*)tid
);
1936 emit_value (logbuffer
, 0); /* flags */
1937 memcpy (logbuffer
->data
, name
, len
);
1938 logbuffer
->data
+= len
;
1939 EXIT_LOG (logbuffer
);
1941 if (logbuffer
->next
)
1942 safe_send (prof
, logbuffer
);
1944 process_requests (prof
);
1956 AsyncFrameInfo
*data
;
1960 async_walk_stack (MonoMethod
*method
, MonoDomain
*domain
, void *base_address
, int offset
, void *data
)
1962 AsyncFrameData
*frame
= (AsyncFrameData
*)data
;
1963 if (frame
->count
< num_frames
) {
1964 frame
->data
[frame
->count
].method
= method
;
1965 frame
->data
[frame
->count
].domain
= domain
;
1966 frame
->data
[frame
->count
].base_address
= base_address
;
1967 frame
->data
[frame
->count
].offset
= offset
;
1968 // printf ("In %d at %p (dom %p) (native: %p)\n", frame->count, method, domain, base_address);
1971 return frame
->count
== num_frames
;
1975 (type | frame count), tid, time, ip, [method, domain, base address, offset] * frames
1977 #define SAMPLE_EVENT_SIZE_IN_SLOTS(FRAMES) (4 + (FRAMES) * 4)
1980 mono_sample_hit (MonoProfiler
*profiler
, unsigned char *ip
, void *context
)
1983 AsyncFrameInfo frames
[num_frames
];
1984 AsyncFrameData bt_data
= { 0, &frames
[0]};
1986 uintptr_t *data
, *new_data
, *old_data
;
1992 now
= current_time ();
1994 mono_stack_walk_async_safe (&async_walk_stack
, context
, &bt_data
);
1996 elapsed
= (now
- profiler
->startup_time
) / 10000;
2000 snprintf (buf
, sizeof (buf
), "hit at %p in thread %p after %llu ms\n", ip
, (void*)thread_id (), (unsigned long long int)elapsed
/100);
2002 ign_res (write (2, buf
, len
));
2004 sbuf
= profiler
->stat_buffers
;
2007 /* flush the buffer at 1 second intervals */
2008 if (sbuf
->data
> sbuf
->buf
&& (elapsed
- sbuf
->buf
[2]) > 100000) {
2011 /* overflow: 400 slots is a big enough number to reduce the chance of losing this event if many
2012 * threads hit this same spot at the same time
2014 if (timedout
|| (sbuf
->data
+ 400 >= sbuf
->data_end
)) {
2015 StatBuffer
*oldsb
, *foundsb
;
2016 sbuf
= create_stat_buffer ();
2018 oldsb
= profiler
->stat_buffers
;
2020 foundsb
= (StatBuffer
*)InterlockedCompareExchangePointer ((void * volatile*)&profiler
->stat_buffers
, sbuf
, oldsb
);
2021 } while (foundsb
!= oldsb
);
2023 ign_res (write (2, "overflow\n", 9));
2024 /* notify the helper thread */
2025 if (sbuf
->next
->next
) {
2027 ign_res (write (profiler
->pipes
[1], &c
, 1));
2029 ign_res (write (2, "notify\n", 7));
2033 old_data
= sbuf
->data
;
2034 new_data
= old_data
+ SAMPLE_EVENT_SIZE_IN_SLOTS (bt_data
.count
);
2035 data
= (uintptr_t *)InterlockedCompareExchangePointer ((void * volatile*)&sbuf
->data
, new_data
, old_data
);
2036 } while (data
!= old_data
);
2037 if (old_data
>= sbuf
->data_end
)
2038 return; /* lost event */
2039 old_data
[0] = 1 | (sample_type
<< 16) | (bt_data
.count
<< 8);
2040 old_data
[1] = thread_id ();
2041 old_data
[2] = elapsed
;
2042 old_data
[3] = (uintptr_t)ip
;
2043 for (i
= 0; i
< bt_data
.count
; ++i
) {
2044 old_data
[4 + 4 * i
+ 0] = (uintptr_t)frames
[i
].method
;
2045 old_data
[4 + 4 * i
+ 1] = (uintptr_t)frames
[i
].domain
;
2046 old_data
[4 + 4 * i
+ 2] = (uintptr_t)frames
[i
].base_address
;
2047 old_data
[4 + 4 * i
+ 3] = (uintptr_t)frames
[i
].offset
;
2051 static uintptr_t *code_pages
= 0;
2052 static int num_code_pages
= 0;
2053 static int size_code_pages
= 0;
2054 #define CPAGE_SHIFT (9)
2055 #define CPAGE_SIZE (1 << CPAGE_SHIFT)
2056 #define CPAGE_MASK (~(CPAGE_SIZE - 1))
2057 #define CPAGE_ADDR(p) ((p) & CPAGE_MASK)
2060 add_code_page (uintptr_t *hash
, uintptr_t hsize
, uintptr_t page
)
2063 uintptr_t start_pos
;
2064 start_pos
= (page
>> CPAGE_SHIFT
) % hsize
;
2067 if (hash
[i
] && CPAGE_ADDR (hash
[i
]) == CPAGE_ADDR (page
)) {
2069 } else if (!hash
[i
]) {
2076 } while (i
!= start_pos
);
2077 /* should not happen */
2078 printf ("failed code page store\n");
2083 add_code_pointer (uintptr_t ip
)
2086 if (num_code_pages
* 2 >= size_code_pages
) {
2088 uintptr_t old_size
= size_code_pages
;
2089 size_code_pages
*= 2;
2090 if (size_code_pages
== 0)
2091 size_code_pages
= 16;
2092 n
= (uintptr_t *)calloc (sizeof (uintptr_t) * size_code_pages
, 1);
2093 for (i
= 0; i
< old_size
; ++i
) {
2095 add_code_page (n
, size_code_pages
, code_pages
[i
]);
2101 num_code_pages
+= add_code_page (code_pages
, size_code_pages
, ip
& CPAGE_MASK
);
2104 /* ELF code crashes on some systems. */
2105 //#if defined(HAVE_DL_ITERATE_PHDR) && defined(ELFMAG0)
2108 dump_ubin (const char *filename
, uintptr_t load_addr
, uint64_t offset
, uintptr_t size
)
2111 LogBuffer
*logbuffer
;
2113 len
= strlen (filename
) + 1;
2114 now
= current_time ();
2115 logbuffer
= ensure_logbuf (
2116 EVENT_SIZE
/* event */ +
2117 LEB128_SIZE
/* time */ +
2118 LEB128_SIZE
/* load address */ +
2119 LEB128_SIZE
/* offset */ +
2120 LEB128_SIZE
/* size */ +
2121 nlen
/* file name */
2123 emit_byte (logbuffer
, TYPE_SAMPLE
| TYPE_SAMPLE_UBIN
);
2124 emit_time (logbuffer
, now
);
2125 emit_svalue (logbuffer
, load_addr
);
2126 emit_uvalue (logbuffer
, offset
);
2127 emit_uvalue (logbuffer
, size
);
2128 memcpy (logbuffer
->data
, filename
, len
);
2129 logbuffer
->data
+= len
;
2134 dump_usym (const char *name
, uintptr_t value
, uintptr_t size
)
2136 LogBuffer
*logbuffer
;
2138 len
= strlen (name
) + 1;
2139 logbuffer
= ensure_logbuf (
2140 EVENT_SIZE
/* event */ +
2141 LEB128_SIZE
/* value */ +
2142 LEB128_SIZE
/* size */ +
2145 emit_byte (logbuffer
, TYPE_SAMPLE
| TYPE_SAMPLE_USYM
);
2146 emit_ptr (logbuffer
, (void*)value
);
2147 emit_value (logbuffer
, size
);
2148 memcpy (logbuffer
->data
, name
, len
);
2149 logbuffer
->data
+= len
;
2152 /* ELF code crashes on some systems. */
2153 //#if defined(ELFMAG0)
2156 #if SIZEOF_VOID_P == 4
2157 #define ELF_WSIZE 32
2159 #define ELF_WSIZE 64
2162 #define ElfW(type) _ElfW (Elf, ELF_WSIZE, type)
2163 #define _ElfW(e,w,t) _ElfW_1 (e, w, _##t)
2164 #define _ElfW_1(e,w,t) e##w##t
2168 dump_elf_symbols (ElfW(Sym
) *symbols
, int num_symbols
, const char *strtab
, void *load_addr
)
2171 for (i
= 0; i
< num_symbols
; ++i
) {
2173 sym
= strtab
+ symbols
[i
].st_name
;
2174 if (!symbols
[i
].st_name
|| !symbols
[i
].st_size
|| (symbols
[i
].st_info
& 0xf) != STT_FUNC
)
2176 //printf ("symbol %s at %d\n", sym, symbols [i].st_value);
2177 dump_usym (sym
, (uintptr_t)load_addr
+ symbols
[i
].st_value
, symbols
[i
].st_size
);
2182 read_elf_symbols (MonoProfiler
*prof
, const char *filename
, void *load_addr
)
2189 ElfW(Shdr
) *sheader
;
2190 ElfW(Shdr
) *shstrtabh
;
2191 ElfW(Shdr
) *symtabh
= NULL
;
2192 ElfW(Shdr
) *strtabh
= NULL
;
2193 ElfW(Sym
) *symbols
= NULL
;
2197 fd
= open (filename
, O_RDONLY
);
2200 if (fstat (fd
, &statb
) != 0) {
2204 file_size
= statb
.st_size
;
2205 data
= mmap (NULL
, file_size
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
2207 if (data
== MAP_FAILED
)
2210 if (header
->e_ident
[EI_MAG0
] != ELFMAG0
||
2211 header
->e_ident
[EI_MAG1
] != ELFMAG1
||
2212 header
->e_ident
[EI_MAG2
] != ELFMAG2
||
2213 header
->e_ident
[EI_MAG3
] != ELFMAG3
) {
2214 munmap (data
, file_size
);
2217 sheader
= (void*)((char*)data
+ header
->e_shoff
);
2218 shstrtabh
= (void*)((char*)sheader
+ (header
->e_shentsize
* header
->e_shstrndx
));
2219 strtab
= (const char*)data
+ shstrtabh
->sh_offset
;
2220 for (i
= 0; i
< header
->e_shnum
; ++i
) {
2221 //printf ("section header: %d\n", sheader->sh_type);
2222 if (sheader
->sh_type
== SHT_SYMTAB
) {
2224 strtabh
= (void*)((char*)data
+ header
->e_shoff
+ sheader
->sh_link
* header
->e_shentsize
);
2225 /*printf ("symtab section header: %d, .strstr: %d\n", i, sheader->sh_link);*/
2228 sheader
= (void*)((char*)sheader
+ header
->e_shentsize
);
2230 if (!symtabh
|| !strtabh
) {
2231 munmap (data
, file_size
);
2234 strtab
= (const char*)data
+ strtabh
->sh_offset
;
2235 num_symbols
= symtabh
->sh_size
/ symtabh
->sh_entsize
;
2236 symbols
= (void*)((char*)data
+ symtabh
->sh_offset
);
2237 dump_elf_symbols (symbols
, num_symbols
, strtab
, load_addr
);
2238 munmap (data
, file_size
);
2243 /* ELF code crashes on some systems. */
2244 //#if defined(HAVE_DL_ITERATE_PHDR) && defined(ELFMAG0)
2247 elf_dl_callback (struct dl_phdr_info
*info
, size_t size
, void *data
)
2249 MonoProfiler
*prof
= data
;
2251 const char *filename
;
2253 char *a
= (void*)info
->dlpi_addr
;
2255 ElfW(Dyn
) *dyn
= NULL
;
2256 ElfW(Sym
) *symtab
= NULL
;
2257 ElfW(Word
) *hash_table
= NULL
;
2258 ElfW(Ehdr
) *header
= NULL
;
2259 const char* strtab
= NULL
;
2260 for (obj
= prof
->binary_objects
; obj
; obj
= obj
->next
) {
2264 filename
= info
->dlpi_name
;
2267 if (!info
->dlpi_addr
&& !filename
[0]) {
2268 int l
= readlink ("/proc/self/exe", buf
, sizeof (buf
) - 1);
2274 obj
= calloc (sizeof (BinaryObject
), 1);
2275 obj
->addr
= (void*)info
->dlpi_addr
;
2276 obj
->name
= pstrdup (filename
);
2277 obj
->next
= prof
->binary_objects
;
2278 prof
->binary_objects
= obj
;
2279 //printf ("loaded file: %s at %p, segments: %d\n", filename, (void*)info->dlpi_addr, info->dlpi_phnum);
2281 for (i
= 0; i
< info
->dlpi_phnum
; ++i
) {
2282 //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);
2283 if (info
->dlpi_phdr
[i
].p_type
== PT_LOAD
&& !header
) {
2284 header
= (ElfW(Ehdr
)*)(info
->dlpi_addr
+ info
->dlpi_phdr
[i
].p_vaddr
);
2285 if (header
->e_ident
[EI_MAG0
] != ELFMAG0
||
2286 header
->e_ident
[EI_MAG1
] != ELFMAG1
||
2287 header
->e_ident
[EI_MAG2
] != ELFMAG2
||
2288 header
->e_ident
[EI_MAG3
] != ELFMAG3
) {
2291 dump_ubin (filename
, info
->dlpi_addr
+ info
->dlpi_phdr
[i
].p_vaddr
, info
->dlpi_phdr
[i
].p_offset
, info
->dlpi_phdr
[i
].p_memsz
);
2292 } else if (info
->dlpi_phdr
[i
].p_type
== PT_DYNAMIC
) {
2293 dyn
= (ElfW(Dyn
) *)(info
->dlpi_addr
+ info
->dlpi_phdr
[i
].p_vaddr
);
2296 if (read_elf_symbols (prof
, filename
, (void*)info
->dlpi_addr
))
2298 if (!info
->dlpi_name
|| !info
->dlpi_name
[0])
2302 for (i
= 0; dyn
[i
].d_tag
!= DT_NULL
; ++i
) {
2303 if (dyn
[i
].d_tag
== DT_SYMTAB
) {
2304 if (symtab
&& do_debug
)
2305 printf ("multiple symtabs: %d\n", i
);
2306 symtab
= (ElfW(Sym
) *)(a
+ dyn
[i
].d_un
.d_ptr
);
2307 } else if (dyn
[i
].d_tag
== DT_HASH
) {
2308 hash_table
= (ElfW(Word
) *)(a
+ dyn
[i
].d_un
.d_ptr
);
2309 } else if (dyn
[i
].d_tag
== DT_STRTAB
) {
2310 strtab
= (const char*)(a
+ dyn
[i
].d_un
.d_ptr
);
2315 num_sym
= hash_table
[1];
2316 dump_elf_symbols (symtab
, num_sym
, strtab
, (void*)info
->dlpi_addr
);
2321 load_binaries (MonoProfiler
*prof
)
2323 dl_iterate_phdr (elf_dl_callback
, prof
);
2328 load_binaries (MonoProfiler
*prof
)
2335 symbol_for (uintptr_t code
)
2338 void *ip
= (void*)code
;
2340 if (dladdr (ip
, &di
)) {
2342 return di
.dli_sname
;
2345 names = backtrace_symbols (&ip, 1);
2347 const char* p = names [0];
2358 dump_unmanaged_coderefs (MonoProfiler
*prof
)
2361 const char* last_symbol
;
2362 uintptr_t addr
, page_end
;
2364 if (load_binaries (prof
))
2366 for (i
= 0; i
< size_code_pages
; ++i
) {
2368 if (!code_pages
[i
] || code_pages
[i
] & 1)
2371 addr
= CPAGE_ADDR (code_pages
[i
]);
2372 page_end
= addr
+ CPAGE_SIZE
;
2373 code_pages
[i
] |= 1;
2374 /* we dump the symbols for the whole page */
2375 for (; addr
< page_end
; addr
+= 16) {
2376 sym
= symbol_for (addr
);
2377 if (sym
&& sym
== last_symbol
)
2382 dump_usym (sym
, addr
, 0); /* let's not guess the size */
2383 //printf ("found symbol at %p: %s\n", (void*)addr, sym);
2389 compare_sample_events (gconstpointer a
, gconstpointer b
)
2391 uintptr_t tid1
= (*(uintptr_t **) a
) [1];
2392 uintptr_t tid2
= (*(uintptr_t **) b
) [1];
2394 return tid1
> tid2
? 1 :
2400 dump_sample_hits (MonoProfiler
*prof
, StatBuffer
*sbuf
)
2402 LogBuffer
*logbuffer
;
2406 dump_sample_hits (prof
, sbuf
->next
);
2407 free_buffer (sbuf
->next
, sbuf
->next
->size
);
2411 g_ptr_array_set_size (prof
->sorted_sample_events
, 0);
2413 for (uintptr_t *sample
= sbuf
->buf
; sample
< sbuf
->data
;) {
2414 int count
= sample
[0] & 0xff;
2415 int mbt_count
= (sample
[0] & 0xff00) >> 8;
2417 if (sample
+ SAMPLE_EVENT_SIZE_IN_SLOTS (mbt_count
) > sbuf
->data
)
2420 g_ptr_array_add (prof
->sorted_sample_events
, sample
);
2422 sample
+= count
+ 3 + 4 * mbt_count
;
2425 g_ptr_array_sort (prof
->sorted_sample_events
, compare_sample_events
);
2427 for (guint sidx
= 0; sidx
< prof
->sorted_sample_events
->len
; sidx
++) {
2428 uintptr_t *sample
= (uintptr_t *)g_ptr_array_index (prof
->sorted_sample_events
, sidx
);
2429 int count
= sample
[0] & 0xff;
2430 int mbt_count
= (sample
[0] & 0xff00) >> 8;
2431 int type
= sample
[0] >> 16;
2432 uintptr_t *managed_sample_base
= sample
+ count
+ 3;
2433 uintptr_t thread_id
= sample
[1];
2435 for (int i
= 0; i
< mbt_count
; ++i
) {
2436 MonoMethod
*method
= (MonoMethod
*)managed_sample_base
[i
* 4 + 0];
2437 MonoDomain
*domain
= (MonoDomain
*)managed_sample_base
[i
* 4 + 1];
2438 void *address
= (void*)managed_sample_base
[i
* 4 + 2];
2441 MonoJitInfo
*ji
= mono_jit_info_table_find (domain
, (char *)address
);
2444 managed_sample_base
[i
* 4 + 0] = (uintptr_t)mono_jit_info_get_method (ji
);
2448 logbuffer
= ensure_logbuf (
2449 EVENT_SIZE
/* event */ +
2450 LEB128_SIZE
/* type */ +
2451 LEB128_SIZE
/* time */ +
2452 LEB128_SIZE
/* tid */ +
2453 LEB128_SIZE
/* count */ +
2455 LEB128_SIZE
/* ip */
2457 LEB128_SIZE
/* managed count */ +
2459 LEB128_SIZE
/* method */ +
2460 LEB128_SIZE
/* il offset */ +
2461 LEB128_SIZE
/* native offset */
2464 emit_byte (logbuffer
, TYPE_SAMPLE
| TYPE_SAMPLE_HIT
);
2465 emit_value (logbuffer
, type
);
2466 emit_uvalue (logbuffer
, prof
->startup_time
+ (uint64_t)sample
[2] * (uint64_t)10000);
2467 emit_ptr (logbuffer
, (void *) thread_id
);
2468 emit_value (logbuffer
, count
);
2469 for (int i
= 0; i
< count
; ++i
) {
2470 emit_ptr (logbuffer
, (void*)sample
[i
+ 3]);
2471 add_code_pointer (sample
[i
+ 3]);
2474 sample
+= count
+ 3;
2475 /* new in data version 6 */
2476 emit_uvalue (logbuffer
, mbt_count
);
2477 for (int i
= 0; i
< mbt_count
; ++i
) {
2478 MonoMethod
*method
= (MonoMethod
*) sample
[i
* 4 + 0];
2479 uintptr_t native_offset
= sample
[i
* 4 + 3];
2481 emit_method (prof
, logbuffer
, method
);
2482 emit_svalue (logbuffer
, 0); /* il offset will always be 0 from now on */
2483 emit_svalue (logbuffer
, native_offset
);
2487 dump_unmanaged_coderefs (prof
);
2493 mono_cpu_count (void)
2496 #ifdef PLATFORM_ANDROID
2497 /* Android tries really hard to save power by powering off CPUs on SMP phones which
2498 * means the normal way to query cpu count returns a wrong value with userspace API.
2499 * Instead we use /sys entries to query the actual hardware CPU count.
2501 char buffer
[8] = {'\0'};
2502 int present
= open ("/sys/devices/system/cpu/present", O_RDONLY
);
2503 /* Format of the /sys entry is a cpulist of indexes which in the case
2504 * of present is always of the form "0-(n-1)" when there is more than
2505 * 1 core, n being the number of CPU cores in the system. Otherwise
2506 * the value is simply 0
2508 if (present
!= -1 && read (present
, (char*)buffer
, sizeof (buffer
)) > 3)
2509 count
= strtol (((char*)buffer
) + 2, NULL
, 10);
2515 #ifdef _SC_NPROCESSORS_ONLN
2516 count
= sysconf (_SC_NPROCESSORS_ONLN
);
2523 size_t len
= sizeof (int);
2526 if (sysctl (mib
, 2, &count
, &len
, NULL
, 0) == 0)
2533 GetSystemInfo (&info
);
2534 return info
.dwNumberOfProcessors
;
2543 unsigned int prev_pos
;
2545 struct perf_event_mmap_page
*page_desc
;
2548 static PerfData
*perf_data
= NULL
;
2549 static int num_perf
;
2550 #define PERF_PAGES_SHIFT 4
2551 static int num_pages
= 1 << PERF_PAGES_SHIFT
;
2552 static unsigned int mmap_mask
;
2555 struct perf_event_header h
;
2565 perf_event_syscall (struct perf_event_attr
*attr
, pid_t pid
, int cpu
, int group_fd
, unsigned long flags
)
2567 attr
->size
= PERF_ATTR_SIZE_VER0
;
2568 //printf ("perf attr size: %d\n", attr->size);
2569 #if defined(__x86_64__)
2570 return syscall(/*__NR_perf_event_open*/ 298, attr
, pid
, cpu
, group_fd
, flags
);
2571 #elif defined(__i386__)
2572 return syscall(/*__NR_perf_event_open*/ 336, attr
, pid
, cpu
, group_fd
, flags
);
2573 #elif defined(__arm__) || defined (__aarch64__)
2574 return syscall(/*__NR_perf_event_open*/ 364, attr
, pid
, cpu
, group_fd
, flags
);
2581 setup_perf_map (PerfData
*perf
)
2583 perf
->mmap_base
= mmap (NULL
, (num_pages
+ 1) * getpagesize (), PROT_READ
|PROT_WRITE
, MAP_SHARED
, perf
->perf_fd
, 0);
2584 if (perf
->mmap_base
== MAP_FAILED
) {
2586 printf ("failed mmap\n");
2589 perf
->page_desc
= perf
->mmap_base
;
2591 printf ("mmap version: %d\n", perf
->page_desc
->version
);
2596 dump_perf_hits (MonoProfiler
*prof
, void *buf
, int size
)
2598 LogBuffer
*logbuffer
;
2601 void *end
= (char*)buf
+ size
;
2603 int pid
= getpid ();
2609 if (pid
!= s
->pid
) {
2611 printf ("event for different pid: %d\n", s
->pid
);
2612 buf
= (char*)buf
+ s
->h
.size
;
2615 /*ip = (void*)s->ip;
2616 printf ("sample: %d, size: %d, ip: %p (%s), timestamp: %llu, nframes: %llu\n",
2617 s->h.type, s->h.size, ip, symbol_for (ip), s->timestamp, s->nframes);*/
2618 logbuffer
= ensure_logbuf (
2619 EVENT_SIZE
/* event */ +
2620 LEB128_SIZE
/* type */ +
2621 LEB128_SIZE
/* time */ +
2622 LEB128_SIZE
/* tid */ +
2623 LEB128_SIZE
/* count */ +
2625 LEB128_SIZE
/* ip */
2627 LEB128_SIZE
/* managed count */ +
2629 LEB128_SIZE
/* method */ +
2630 LEB128_SIZE
/* il offset */ +
2631 LEB128_SIZE
/* native offset */
2634 emit_byte (logbuffer
, TYPE_SAMPLE
| TYPE_SAMPLE_HIT
);
2635 emit_value (logbuffer
, sample_type
);
2636 emit_uvalue (logbuffer
, s
->timestamp
- prof
->startup_time
);
2638 * No useful thread ID to write here, since throughout the
2639 * profiler we use pthread_self () but the ID we get from
2640 * perf is the kernel's thread ID.
2642 emit_ptr (logbuffer
, 0);
2643 emit_value (logbuffer
, count
);
2644 emit_ptr (logbuffer
, (void*)(uintptr_t)s
->ip
);
2645 add_code_pointer (s
->ip
);
2646 /* no support here yet for the managed backtrace */
2647 emit_uvalue (logbuffer
, mbt_count
);
2648 buf
= (char*)buf
+ s
->h
.size
;
2652 printf ("dumped %d samples\n", samples
);
2653 dump_unmanaged_coderefs (prof
);
2656 /* read events from the ring buffer */
2658 read_perf_mmap (MonoProfiler
* prof
, int cpu
)
2660 PerfData
*perf
= perf_data
+ cpu
;
2662 unsigned char *data
= (unsigned char*)perf
->mmap_base
+ getpagesize ();
2663 unsigned int head
= perf
->page_desc
->data_head
;
2667 mono_memory_read_barrier ();
2669 old
= perf
->prev_pos
;
2673 printf ("lost mmap events: old: %d, head: %d\n", old
, head
);
2677 if ((old
& mmap_mask
) + size
!= (head
& mmap_mask
)) {
2678 buf
= data
+ (old
& mmap_mask
);
2679 size
= mmap_mask
+ 1 - (old
& mmap_mask
);
2681 /* size bytes at buf */
2683 printf ("found1 bytes of events: %d\n", size
);
2684 dump_perf_hits (prof
, buf
, size
);
2686 buf
= data
+ (old
& mmap_mask
);
2688 /* size bytes at buf */
2690 printf ("found bytes of events: %d\n", size
);
2691 dump_perf_hits (prof
, buf
, size
);
2693 perf
->prev_pos
= old
;
2694 perf
->page_desc
->data_tail
= old
;
2699 setup_perf_event_for_cpu (PerfData
*perf
, int cpu
)
2701 struct perf_event_attr attr
;
2702 memset (&attr
, 0, sizeof (attr
));
2703 attr
.type
= PERF_TYPE_HARDWARE
;
2704 switch (sample_type
) {
2705 case SAMPLE_CYCLES
: attr
.config
= PERF_COUNT_HW_CPU_CYCLES
; break;
2706 case SAMPLE_INSTRUCTIONS
: attr
.config
= PERF_COUNT_HW_INSTRUCTIONS
; break;
2707 case SAMPLE_CACHE_MISSES
: attr
.config
= PERF_COUNT_HW_CACHE_MISSES
; break;
2708 case SAMPLE_CACHE_REFS
: attr
.config
= PERF_COUNT_HW_CACHE_REFERENCES
; break;
2709 case SAMPLE_BRANCHES
: attr
.config
= PERF_COUNT_HW_BRANCH_INSTRUCTIONS
; break;
2710 case SAMPLE_BRANCH_MISSES
: attr
.config
= PERF_COUNT_HW_BRANCH_MISSES
; break;
2711 default: attr
.config
= PERF_COUNT_HW_CPU_CYCLES
; break;
2713 attr
.sample_type
= PERF_SAMPLE_IP
| PERF_SAMPLE_TID
| PERF_SAMPLE_PERIOD
| PERF_SAMPLE_TIME
;
2714 // attr.sample_type |= PERF_SAMPLE_CALLCHAIN;
2715 attr
.read_format
= PERF_FORMAT_TOTAL_TIME_ENABLED
| PERF_FORMAT_TOTAL_TIME_RUNNING
| PERF_FORMAT_ID
;
2718 attr
.sample_freq
= sample_freq
;
2720 perf
->perf_fd
= perf_event_syscall (&attr
, getpid (), cpu
, -1, 0);
2722 printf ("perf fd: %d, freq: %d, event: %llu\n", perf
->perf_fd
, sample_freq
, attr
.config
);
2723 if (perf
->perf_fd
< 0) {
2724 if (perf
->perf_fd
== -EPERM
) {
2725 fprintf (stderr
, "Perf syscall denied, do \"echo 1 > /proc/sys/kernel/perf_event_paranoid\" as root to enable.\n");
2728 perror ("open perf event");
2732 if (!setup_perf_map (perf
)) {
2733 close (perf
->perf_fd
);
2741 setup_perf_event (void)
2744 mmap_mask
= num_pages
* getpagesize () - 1;
2745 num_perf
= mono_cpu_count ();
2746 perf_data
= calloc (num_perf
, sizeof (PerfData
));
2747 for (i
= 0; i
< num_perf
; ++i
) {
2748 count
+= setup_perf_event_for_cpu (perf_data
+ i
, i
);
2757 #endif /* USE_PERF_EVENTS */
2759 #ifndef DISABLE_HELPER_THREAD
2761 typedef struct MonoCounterAgent
{
2762 MonoCounter
*counter
;
2763 // MonoCounterAgent specific data :
2768 struct MonoCounterAgent
*next
;
2771 static MonoCounterAgent
* counters
;
2772 static gboolean counters_initialized
= FALSE
;
2773 static int counters_index
= 1;
2774 static mono_mutex_t counters_mutex
;
2777 counters_add_agent (MonoCounter
*counter
)
2779 MonoCounterAgent
*agent
, *item
;
2781 if (!counters_initialized
)
2784 mono_os_mutex_lock (&counters_mutex
);
2786 for (agent
= counters
; agent
; agent
= agent
->next
) {
2787 if (agent
->counter
== counter
) {
2788 agent
->value_size
= 0;
2790 free (agent
->value
);
2791 agent
->value
= NULL
;
2793 mono_os_mutex_unlock (&counters_mutex
);
2798 agent
= (MonoCounterAgent
*)malloc (sizeof (MonoCounterAgent
));
2799 agent
->counter
= counter
;
2800 agent
->value
= NULL
;
2801 agent
->value_size
= 0;
2802 agent
->index
= counters_index
++;
2815 mono_os_mutex_unlock (&counters_mutex
);
2819 counters_init_foreach_callback (MonoCounter
*counter
, gpointer data
)
2821 counters_add_agent (counter
);
2826 counters_init (MonoProfiler
*profiler
)
2828 assert (!counters_initialized
);
2830 mono_os_mutex_init (&counters_mutex
);
2832 counters_initialized
= TRUE
;
2834 mono_counters_on_register (&counters_add_agent
);
2835 mono_counters_foreach (counters_init_foreach_callback
, NULL
);
2839 counters_emit (MonoProfiler
*profiler
)
2841 MonoCounterAgent
*agent
;
2842 LogBuffer
*logbuffer
;
2845 EVENT_SIZE
/* event */ +
2846 LEB128_SIZE
/* len */
2849 if (!counters_initialized
)
2852 mono_os_mutex_lock (&counters_mutex
);
2854 for (agent
= counters
; agent
; agent
= agent
->next
) {
2859 LEB128_SIZE
/* section */ +
2860 strlen (mono_counter_get_name (agent
->counter
)) + 1 /* name */ +
2861 LEB128_SIZE
/* type */ +
2862 LEB128_SIZE
/* unit */ +
2863 LEB128_SIZE
/* variance */ +
2864 LEB128_SIZE
/* index */
2871 mono_os_mutex_unlock (&counters_mutex
);
2875 logbuffer
= ensure_logbuf (size
);
2877 ENTER_LOG (logbuffer
, "counters");
2878 emit_byte (logbuffer
, TYPE_SAMPLE_COUNTERS_DESC
| TYPE_SAMPLE
);
2879 emit_value (logbuffer
, len
);
2880 for (agent
= counters
; agent
; agent
= agent
->next
) {
2886 name
= mono_counter_get_name (agent
->counter
);
2887 emit_value (logbuffer
, mono_counter_get_section (agent
->counter
));
2888 emit_string (logbuffer
, name
, strlen (name
) + 1);
2889 emit_value (logbuffer
, mono_counter_get_type (agent
->counter
));
2890 emit_value (logbuffer
, mono_counter_get_unit (agent
->counter
));
2891 emit_value (logbuffer
, mono_counter_get_variance (agent
->counter
));
2892 emit_value (logbuffer
, agent
->index
);
2896 EXIT_LOG (logbuffer
);
2898 safe_send (profiler
, logbuffer
);
2900 mono_os_mutex_unlock (&counters_mutex
);
2904 counters_sample (MonoProfiler
*profiler
, uint64_t timestamp
)
2906 MonoCounterAgent
*agent
;
2907 MonoCounter
*counter
;
2908 LogBuffer
*logbuffer
;
2914 if (!counters_initialized
)
2917 counters_emit (profiler
);
2920 buffer
= calloc (1, buffer_size
);
2922 mono_os_mutex_lock (&counters_mutex
);
2925 EVENT_SIZE
/* event */ +
2926 LEB128_SIZE
/* time */
2929 for (agent
= counters
; agent
; agent
= agent
->next
) {
2931 LEB128_SIZE
/* index */ +
2932 LEB128_SIZE
/* type */ +
2933 mono_counter_get_size (agent
->counter
) /* value */
2938 LEB128_SIZE
/* stop marker */
2941 logbuffer
= ensure_logbuf (size
);
2943 ENTER_LOG (logbuffer
, "counters");
2944 emit_byte (logbuffer
, TYPE_SAMPLE_COUNTERS
| TYPE_SAMPLE
);
2945 emit_uvalue (logbuffer
, timestamp
);
2946 for (agent
= counters
; agent
; agent
= agent
->next
) {
2949 counter
= agent
->counter
;
2951 size
= mono_counter_get_size (counter
);
2953 continue; // FIXME error
2954 } else if (size
> buffer_size
) {
2956 buffer
= realloc (buffer
, buffer_size
);
2959 memset (buffer
, 0, buffer_size
);
2961 if (mono_counters_sample (counter
, buffer
, size
) < 0)
2962 continue; // FIXME error
2964 type
= mono_counter_get_type (counter
);
2966 if (!agent
->value
) {
2967 agent
->value
= calloc (1, size
);
2968 agent
->value_size
= size
;
2970 if (type
== MONO_COUNTER_STRING
) {
2971 if (strcmp (agent
->value
, buffer
) == 0)
2974 if (agent
->value_size
== size
&& memcmp (agent
->value
, buffer
, size
) == 0)
2979 emit_uvalue (logbuffer
, agent
->index
);
2980 emit_uvalue (logbuffer
, type
);
2982 case MONO_COUNTER_INT
:
2983 #if SIZEOF_VOID_P == 4
2984 case MONO_COUNTER_WORD
:
2986 emit_svalue (logbuffer
, *(int*)buffer
- *(int*)agent
->value
);
2988 case MONO_COUNTER_UINT
:
2989 emit_uvalue (logbuffer
, *(guint
*)buffer
- *(guint
*)agent
->value
);
2991 case MONO_COUNTER_TIME_INTERVAL
:
2992 case MONO_COUNTER_LONG
:
2993 #if SIZEOF_VOID_P == 8
2994 case MONO_COUNTER_WORD
:
2996 emit_svalue (logbuffer
, *(gint64
*)buffer
- *(gint64
*)agent
->value
);
2998 case MONO_COUNTER_ULONG
:
2999 emit_uvalue (logbuffer
, *(guint64
*)buffer
- *(guint64
*)agent
->value
);
3001 case MONO_COUNTER_DOUBLE
:
3002 emit_double (logbuffer
, *(double*)buffer
);
3004 case MONO_COUNTER_STRING
:
3006 emit_byte (logbuffer
, 0);
3008 emit_byte (logbuffer
, 1);
3009 emit_string (logbuffer
, (char*)buffer
, size
);
3016 if (type
== MONO_COUNTER_STRING
&& size
> agent
->value_size
) {
3017 agent
->value
= realloc (agent
->value
, size
);
3018 agent
->value_size
= size
;
3022 memcpy (agent
->value
, buffer
, size
);
3026 emit_value (logbuffer
, 0);
3027 EXIT_LOG (logbuffer
);
3029 safe_send (profiler
, logbuffer
);
3031 mono_os_mutex_unlock (&counters_mutex
);
3034 typedef struct _PerfCounterAgent PerfCounterAgent
;
3035 struct _PerfCounterAgent
{
3036 PerfCounterAgent
*next
;
3038 char *category_name
;
3047 static PerfCounterAgent
*perfcounters
= NULL
;
3050 perfcounters_emit (MonoProfiler
*profiler
)
3052 PerfCounterAgent
*pcagent
;
3053 LogBuffer
*logbuffer
;
3056 EVENT_SIZE
/* event */ +
3057 LEB128_SIZE
/* len */
3060 for (pcagent
= perfcounters
; pcagent
; pcagent
= pcagent
->next
) {
3061 if (pcagent
->emitted
)
3065 LEB128_SIZE
/* section */ +
3066 strlen (pcagent
->category_name
) + 1 /* category name */ +
3067 strlen (pcagent
->name
) + 1 /* name */ +
3068 LEB128_SIZE
/* type */ +
3069 LEB128_SIZE
/* unit */ +
3070 LEB128_SIZE
/* variance */ +
3071 LEB128_SIZE
/* index */
3080 logbuffer
= ensure_logbuf (size
);
3082 ENTER_LOG (logbuffer
, "perfcounters");
3083 emit_byte (logbuffer
, TYPE_SAMPLE_COUNTERS_DESC
| TYPE_SAMPLE
);
3084 emit_value (logbuffer
, len
);
3085 for (pcagent
= perfcounters
; pcagent
; pcagent
= pcagent
->next
) {
3086 if (pcagent
->emitted
)
3089 emit_value (logbuffer
, MONO_COUNTER_PERFCOUNTERS
);
3090 emit_string (logbuffer
, pcagent
->category_name
, strlen (pcagent
->category_name
) + 1);
3091 emit_string (logbuffer
, pcagent
->name
, strlen (pcagent
->name
) + 1);
3092 emit_value (logbuffer
, MONO_COUNTER_LONG
);
3093 emit_value (logbuffer
, MONO_COUNTER_RAW
);
3094 emit_value (logbuffer
, MONO_COUNTER_VARIABLE
);
3095 emit_value (logbuffer
, pcagent
->index
);
3097 pcagent
->emitted
= 1;
3099 EXIT_LOG (logbuffer
);
3101 safe_send (profiler
, logbuffer
);
3105 perfcounters_foreach (char *category_name
, char *name
, unsigned char type
, gint64 value
, gpointer user_data
)
3107 PerfCounterAgent
*pcagent
;
3109 for (pcagent
= perfcounters
; pcagent
; pcagent
= pcagent
->next
) {
3110 if (strcmp (pcagent
->category_name
, category_name
) != 0 || strcmp (pcagent
->name
, name
) != 0)
3112 if (pcagent
->value
== value
)
3115 pcagent
->value
= value
;
3116 pcagent
->updated
= 1;
3117 pcagent
->deleted
= 0;
3121 pcagent
= g_new0 (PerfCounterAgent
, 1);
3122 pcagent
->next
= perfcounters
;
3123 pcagent
->index
= counters_index
++;
3124 pcagent
->category_name
= g_strdup (category_name
);
3125 pcagent
->name
= g_strdup (name
);
3126 pcagent
->type
= (int) type
;
3127 pcagent
->value
= value
;
3128 pcagent
->emitted
= 0;
3129 pcagent
->updated
= 1;
3130 pcagent
->deleted
= 0;
3132 perfcounters
= pcagent
;
3138 perfcounters_sample (MonoProfiler
*profiler
, uint64_t timestamp
)
3140 PerfCounterAgent
*pcagent
;
3141 LogBuffer
*logbuffer
;
3144 if (!counters_initialized
)
3147 mono_os_mutex_lock (&counters_mutex
);
3149 /* mark all perfcounters as deleted, foreach will unmark them as necessary */
3150 for (pcagent
= perfcounters
; pcagent
; pcagent
= pcagent
->next
)
3151 pcagent
->deleted
= 1;
3153 mono_perfcounter_foreach (perfcounters_foreach
, perfcounters
);
3155 perfcounters_emit (profiler
);
3158 EVENT_SIZE
/* event */ +
3159 LEB128_SIZE
/* time */
3162 for (pcagent
= perfcounters
; pcagent
; pcagent
= pcagent
->next
) {
3163 if (pcagent
->deleted
|| !pcagent
->updated
)
3167 LEB128_SIZE
/* index */ +
3168 LEB128_SIZE
/* type */ +
3169 LEB128_SIZE
/* value */
3174 LEB128_SIZE
/* stop marker */
3177 logbuffer
= ensure_logbuf (size
);
3179 ENTER_LOG (logbuffer
, "perfcounters");
3180 emit_byte (logbuffer
, TYPE_SAMPLE_COUNTERS
| TYPE_SAMPLE
);
3181 emit_uvalue (logbuffer
, timestamp
);
3182 for (pcagent
= perfcounters
; pcagent
; pcagent
= pcagent
->next
) {
3183 if (pcagent
->deleted
|| !pcagent
->updated
)
3185 emit_uvalue (logbuffer
, pcagent
->index
);
3186 emit_uvalue (logbuffer
, MONO_COUNTER_LONG
);
3187 emit_svalue (logbuffer
, pcagent
->value
);
3189 pcagent
->updated
= 0;
3192 emit_value (logbuffer
, 0);
3193 EXIT_LOG (logbuffer
);
3195 safe_send (profiler
, logbuffer
);
3197 mono_os_mutex_unlock (&counters_mutex
);
3201 counters_and_perfcounters_sample (MonoProfiler
*prof
)
3203 static uint64_t start
= -1;
3207 start
= current_time ();
3209 now
= current_time ();
3210 counters_sample (prof
, (now
- start
) / 1000/ 1000);
3211 perfcounters_sample (prof
, (now
- start
) / 1000/ 1000);
3214 #define COVERAGE_DEBUG(x) if (debug_coverage) {x}
3215 static mono_mutex_t coverage_mutex
;
3216 static MonoConcurrentHashTable
*coverage_methods
= NULL
;
3217 static MonoConcurrentHashTable
*coverage_assemblies
= NULL
;
3218 static MonoConcurrentHashTable
*coverage_classes
= NULL
;
3220 static MonoConcurrentHashTable
*filtered_classes
= NULL
;
3221 static MonoConcurrentHashTable
*entered_methods
= NULL
;
3222 static MonoConcurrentHashTable
*image_to_methods
= NULL
;
3223 static MonoConcurrentHashTable
*suppressed_assemblies
= NULL
;
3224 static gboolean coverage_initialized
= FALSE
;
3226 static GPtrArray
*coverage_data
= NULL
;
3227 static int previous_offset
= 0;
3229 typedef struct _MethodNode MethodNode
;
3230 struct _MethodNode
{
3231 MonoLockFreeQueueNode node
;
3235 typedef struct _CoverageEntry CoverageEntry
;
3236 struct _CoverageEntry
{
3245 free_coverage_entry (gpointer data
, gpointer userdata
)
3247 CoverageEntry
*entry
= (CoverageEntry
*)data
;
3248 g_free (entry
->filename
);
3253 obtain_coverage_for_method (MonoProfiler
*prof
, const MonoProfileCoverageEntry
*entry
)
3255 int offset
= entry
->iloffset
- previous_offset
;
3256 CoverageEntry
*e
= g_new (CoverageEntry
, 1);
3258 previous_offset
= entry
->iloffset
;
3261 e
->counter
= entry
->counter
;
3262 e
->filename
= g_strdup(entry
->filename
? entry
->filename
: "");
3263 e
->line
= entry
->line
;
3264 e
->column
= entry
->col
;
3266 g_ptr_array_add (coverage_data
, e
);
3270 parse_generic_type_names(char *name
)
3272 char *new_name
, *ret
;
3273 int within_generic_declaration
= 0, generic_members
= 1;
3275 if (name
== NULL
|| *name
== '\0')
3276 return g_strdup ("");
3278 if (!(ret
= new_name
= (char *)calloc (strlen (name
) * 4 + 1, sizeof (char))))
3284 within_generic_declaration
= 1;
3288 within_generic_declaration
= 0;
3290 if (*(name
- 1) != '<') {
3292 *new_name
++ = '0' + generic_members
;
3294 memcpy (new_name
, "<>", 8);
3298 generic_members
= 0;
3306 if (!within_generic_declaration
)
3307 *new_name
++ = *name
;
3316 static int method_id
;
3318 build_method_buffer (gpointer key
, gpointer value
, gpointer userdata
)
3320 MonoMethod
*method
= (MonoMethod
*)value
;
3321 MonoProfiler
*prof
= (MonoProfiler
*)userdata
;
3325 const char *image_name
, *method_name
, *sig
, *first_filename
;
3326 LogBuffer
*logbuffer
;
3329 previous_offset
= 0;
3330 coverage_data
= g_ptr_array_new ();
3332 mono_profiler_coverage_get (prof
, method
, obtain_coverage_for_method
);
3334 klass
= mono_method_get_class (method
);
3335 image
= mono_class_get_image (klass
);
3336 image_name
= mono_image_get_name (image
);
3338 sig
= mono_signature_get_desc (mono_method_signature (method
), TRUE
);
3339 class_name
= parse_generic_type_names (mono_type_get_name (mono_class_get_type (klass
)));
3340 method_name
= mono_method_get_name (method
);
3342 if (coverage_data
->len
!= 0) {
3343 CoverageEntry
*entry
= (CoverageEntry
*)coverage_data
->pdata
[0];
3344 first_filename
= entry
->filename
? entry
->filename
: "";
3346 first_filename
= "";
3348 image_name
= image_name
? image_name
: "";
3349 sig
= sig
? sig
: "";
3350 method_name
= method_name
? method_name
: "";
3352 logbuffer
= ensure_logbuf (
3353 EVENT_SIZE
/* event */ +
3354 strlen (image_name
) + 1 /* image name */ +
3355 strlen (class_name
) + 1 /* class name */ +
3356 strlen (method_name
) + 1 /* method name */ +
3357 strlen (sig
) + 1 /* signature */ +
3358 strlen (first_filename
) + 1 /* first file name */ +
3359 LEB128_SIZE
/* token */ +
3360 LEB128_SIZE
/* method id */ +
3361 LEB128_SIZE
/* entries */
3363 ENTER_LOG (logbuffer
, "coverage-methods");
3365 emit_byte (logbuffer
, TYPE_COVERAGE_METHOD
| TYPE_COVERAGE
);
3366 emit_string (logbuffer
, image_name
, strlen (image_name
) + 1);
3367 emit_string (logbuffer
, class_name
, strlen (class_name
) + 1);
3368 emit_string (logbuffer
, method_name
, strlen (method_name
) + 1);
3369 emit_string (logbuffer
, sig
, strlen (sig
) + 1);
3370 emit_string (logbuffer
, first_filename
, strlen (first_filename
) + 1);
3372 emit_uvalue (logbuffer
, mono_method_get_token (method
));
3373 emit_uvalue (logbuffer
, method_id
);
3374 emit_value (logbuffer
, coverage_data
->len
);
3376 EXIT_LOG (logbuffer
);
3377 safe_send (prof
, logbuffer
);
3379 for (i
= 0; i
< coverage_data
->len
; i
++) {
3380 CoverageEntry
*entry
= (CoverageEntry
*)coverage_data
->pdata
[i
];
3382 logbuffer
= ensure_logbuf (
3383 EVENT_SIZE
/* event */ +
3384 LEB128_SIZE
/* method id */ +
3385 LEB128_SIZE
/* offset */ +
3386 LEB128_SIZE
/* counter */ +
3387 LEB128_SIZE
/* line */ +
3388 LEB128_SIZE
/* column */
3390 ENTER_LOG (logbuffer
, "coverage-statement");
3392 emit_byte (logbuffer
, TYPE_COVERAGE_STATEMENT
| TYPE_COVERAGE
);
3393 emit_uvalue (logbuffer
, method_id
);
3394 emit_uvalue (logbuffer
, entry
->offset
);
3395 emit_uvalue (logbuffer
, entry
->counter
);
3396 emit_uvalue (logbuffer
, entry
->line
);
3397 emit_uvalue (logbuffer
, entry
->column
);
3399 EXIT_LOG (logbuffer
);
3400 safe_send (prof
, logbuffer
);
3405 g_free (class_name
);
3407 g_ptr_array_foreach (coverage_data
, free_coverage_entry
, NULL
);
3408 g_ptr_array_free (coverage_data
, TRUE
);
3409 coverage_data
= NULL
;
3412 /* This empties the queue */
3414 count_queue (MonoLockFreeQueue
*queue
)
3416 MonoLockFreeQueueNode
*node
;
3419 while ((node
= mono_lock_free_queue_dequeue (queue
))) {
3421 mono_lock_free_queue_node_free (node
);
3428 build_class_buffer (gpointer key
, gpointer value
, gpointer userdata
)
3430 MonoClass
*klass
= (MonoClass
*)key
;
3431 MonoLockFreeQueue
*class_methods
= (MonoLockFreeQueue
*)value
;
3432 MonoProfiler
*prof
= (MonoProfiler
*)userdata
;
3435 const char *assembly_name
;
3436 int number_of_methods
, partially_covered
;
3437 guint fully_covered
;
3438 LogBuffer
*logbuffer
;
3440 image
= mono_class_get_image (klass
);
3441 assembly_name
= mono_image_get_name (image
);
3442 class_name
= mono_type_get_name (mono_class_get_type (klass
));
3444 assembly_name
= assembly_name
? assembly_name
: "";
3445 number_of_methods
= mono_class_num_methods (klass
);
3446 fully_covered
= count_queue (class_methods
);
3447 /* We don't handle partial covered yet */
3448 partially_covered
= 0;
3450 logbuffer
= ensure_logbuf (
3451 EVENT_SIZE
/* event */ +
3452 strlen (assembly_name
) + 1 /* assembly name */ +
3453 strlen (class_name
) + 1 /* class name */ +
3454 LEB128_SIZE
/* no. methods */ +
3455 LEB128_SIZE
/* fully covered */ +
3456 LEB128_SIZE
/* partially covered */
3459 ENTER_LOG (logbuffer
, "coverage-class");
3460 emit_byte (logbuffer
, TYPE_COVERAGE_CLASS
| TYPE_COVERAGE
);
3461 emit_string (logbuffer
, assembly_name
, strlen (assembly_name
) + 1);
3462 emit_string (logbuffer
, class_name
, strlen (class_name
) + 1);
3463 emit_uvalue (logbuffer
, number_of_methods
);
3464 emit_uvalue (logbuffer
, fully_covered
);
3465 emit_uvalue (logbuffer
, partially_covered
);
3466 EXIT_LOG (logbuffer
);
3468 safe_send (prof
, logbuffer
);
3470 g_free (class_name
);
3474 get_coverage_for_image (MonoImage
*image
, int *number_of_methods
, guint
*fully_covered
, int *partially_covered
)
3476 MonoLockFreeQueue
*image_methods
= (MonoLockFreeQueue
*)mono_conc_hashtable_lookup (image_to_methods
, image
);
3478 *number_of_methods
= mono_image_get_table_rows (image
, MONO_TABLE_METHOD
);
3480 *fully_covered
= count_queue (image_methods
);
3484 // FIXME: We don't handle partially covered yet.
3485 *partially_covered
= 0;
3489 build_assembly_buffer (gpointer key
, gpointer value
, gpointer userdata
)
3491 MonoAssembly
*assembly
= (MonoAssembly
*)value
;
3492 MonoProfiler
*prof
= (MonoProfiler
*)userdata
;
3493 MonoImage
*image
= mono_assembly_get_image (assembly
);
3494 LogBuffer
*logbuffer
;
3495 const char *name
, *guid
, *filename
;
3496 int number_of_methods
= 0, partially_covered
= 0;
3497 guint fully_covered
= 0;
3499 name
= mono_image_get_name (image
);
3500 guid
= mono_image_get_guid (image
);
3501 filename
= mono_image_get_filename (image
);
3503 name
= name
? name
: "";
3504 guid
= guid
? guid
: "";
3505 filename
= filename
? filename
: "";
3507 get_coverage_for_image (image
, &number_of_methods
, &fully_covered
, &partially_covered
);
3509 logbuffer
= ensure_logbuf (
3510 EVENT_SIZE
/* event */ +
3511 strlen (name
) + 1 /* name */ +
3512 strlen (guid
) + 1 /* guid */ +
3513 strlen (filename
) + 1 /* file name */ +
3514 LEB128_SIZE
/* no. methods */ +
3515 LEB128_SIZE
/* fully covered */ +
3516 LEB128_SIZE
/* partially covered */
3519 ENTER_LOG (logbuffer
, "coverage-assemblies");
3520 emit_byte (logbuffer
, TYPE_COVERAGE_ASSEMBLY
| TYPE_COVERAGE
);
3521 emit_string (logbuffer
, name
, strlen (name
) + 1);
3522 emit_string (logbuffer
, guid
, strlen (guid
) + 1);
3523 emit_string (logbuffer
, filename
, strlen (filename
) + 1);
3524 emit_uvalue (logbuffer
, number_of_methods
);
3525 emit_uvalue (logbuffer
, fully_covered
);
3526 emit_uvalue (logbuffer
, partially_covered
);
3527 EXIT_LOG (logbuffer
);
3529 safe_send (prof
, logbuffer
);
3533 dump_coverage (MonoProfiler
*prof
)
3535 if (!coverage_initialized
)
3538 COVERAGE_DEBUG(fprintf (stderr
, "Coverage: Started dump\n");)
3541 mono_os_mutex_lock (&coverage_mutex
);
3542 mono_conc_hashtable_foreach (coverage_assemblies
, build_assembly_buffer
, prof
);
3543 mono_conc_hashtable_foreach (coverage_classes
, build_class_buffer
, prof
);
3544 mono_conc_hashtable_foreach (coverage_methods
, build_method_buffer
, prof
);
3545 mono_os_mutex_unlock (&coverage_mutex
);
3547 COVERAGE_DEBUG(fprintf (stderr
, "Coverage: Finished dump\n");)
3551 process_method_enter_coverage (MonoProfiler
*prof
, MonoMethod
*method
)
3556 if (!coverage_initialized
)
3559 klass
= mono_method_get_class (method
);
3560 image
= mono_class_get_image (klass
);
3562 if (mono_conc_hashtable_lookup (suppressed_assemblies
, (gpointer
) mono_image_get_name (image
)))
3565 mono_os_mutex_lock (&coverage_mutex
);
3566 mono_conc_hashtable_insert (entered_methods
, method
, method
);
3567 mono_os_mutex_unlock (&coverage_mutex
);
3570 static MonoLockFreeQueueNode
*
3571 create_method_node (MonoMethod
*method
)
3573 MethodNode
*node
= (MethodNode
*)g_malloc (sizeof (MethodNode
));
3574 mono_lock_free_queue_node_init ((MonoLockFreeQueueNode
*) node
, FALSE
);
3575 node
->method
= method
;
3577 return (MonoLockFreeQueueNode
*) node
;
3581 coverage_filter (MonoProfiler
*prof
, MonoMethod
*method
)
3585 MonoAssembly
*assembly
;
3586 MonoMethodHeader
*header
;
3587 guint32 iflags
, flags
, code_size
;
3588 char *fqn
, *classname
;
3589 gboolean has_positive
, found
;
3590 MonoLockFreeQueue
*image_methods
, *class_methods
;
3591 MonoLockFreeQueueNode
*node
;
3593 if (!coverage_initialized
)
3596 COVERAGE_DEBUG(fprintf (stderr
, "Coverage filter for %s\n", mono_method_get_name (method
));)
3598 flags
= mono_method_get_flags (method
, &iflags
);
3599 if ((iflags
& 0x1000 /*METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL*/) ||
3600 (flags
& 0x2000 /*METHOD_ATTRIBUTE_PINVOKE_IMPL*/)) {
3601 COVERAGE_DEBUG(fprintf (stderr
, " Internal call or pinvoke - ignoring\n");)
3605 // Don't need to do anything else if we're already tracking this method
3606 if (mono_conc_hashtable_lookup (coverage_methods
, method
)) {
3607 COVERAGE_DEBUG(fprintf (stderr
, " Already tracking\n");)
3611 klass
= mono_method_get_class (method
);
3612 image
= mono_class_get_image (klass
);
3614 // Don't handle coverage for the core assemblies
3615 if (mono_conc_hashtable_lookup (suppressed_assemblies
, (gpointer
) mono_image_get_name (image
)) != NULL
)
3618 if (prof
->coverage_filters
) {
3619 /* Check already filtered classes first */
3620 if (mono_conc_hashtable_lookup (filtered_classes
, klass
)) {
3621 COVERAGE_DEBUG(fprintf (stderr
, " Already filtered\n");)
3625 classname
= mono_type_get_name (mono_class_get_type (klass
));
3627 fqn
= g_strdup_printf ("[%s]%s", mono_image_get_name (image
), classname
);
3629 COVERAGE_DEBUG(fprintf (stderr
, " Looking for %s in filter\n", fqn
);)
3630 // Check positive filters first
3631 has_positive
= FALSE
;
3633 for (guint i
= 0; i
< prof
->coverage_filters
->len
; ++i
) {
3634 char *filter
= (char *)g_ptr_array_index (prof
->coverage_filters
, i
);
3636 if (filter
[0] == '+') {
3637 filter
= &filter
[1];
3639 COVERAGE_DEBUG(fprintf (stderr
, " Checking against +%s ...", filter
);)
3641 if (strstr (fqn
, filter
) != NULL
) {
3642 COVERAGE_DEBUG(fprintf (stderr
, "matched\n");)
3645 COVERAGE_DEBUG(fprintf (stderr
, "no match\n");)
3647 has_positive
= TRUE
;
3651 if (has_positive
&& !found
) {
3652 COVERAGE_DEBUG(fprintf (stderr
, " Positive match was not found\n");)
3654 mono_os_mutex_lock (&coverage_mutex
);
3655 mono_conc_hashtable_insert (filtered_classes
, klass
, klass
);
3656 mono_os_mutex_unlock (&coverage_mutex
);
3663 for (guint i
= 0; i
< prof
->coverage_filters
->len
; ++i
) {
3664 // FIXME: Is substring search sufficient?
3665 char *filter
= (char *)g_ptr_array_index (prof
->coverage_filters
, i
);
3666 if (filter
[0] == '+')
3670 filter
= &filter
[1];
3671 COVERAGE_DEBUG(fprintf (stderr
, " Checking against -%s ...", filter
);)
3673 if (strstr (fqn
, filter
) != NULL
) {
3674 COVERAGE_DEBUG(fprintf (stderr
, "matched\n");)
3676 mono_os_mutex_lock (&coverage_mutex
);
3677 mono_conc_hashtable_insert (filtered_classes
, klass
, klass
);
3678 mono_os_mutex_unlock (&coverage_mutex
);
3684 COVERAGE_DEBUG(fprintf (stderr
, "no match\n");)
3692 COVERAGE_DEBUG(fprintf (stderr
, " Handling coverage for %s\n", mono_method_get_name (method
));)
3693 header
= mono_method_get_header (method
);
3695 mono_method_header_get_code (header
, &code_size
, NULL
);
3697 assembly
= mono_image_get_assembly (image
);
3699 mono_os_mutex_lock (&coverage_mutex
);
3700 mono_conc_hashtable_insert (coverage_methods
, method
, method
);
3701 mono_conc_hashtable_insert (coverage_assemblies
, assembly
, assembly
);
3702 mono_os_mutex_unlock (&coverage_mutex
);
3704 image_methods
= (MonoLockFreeQueue
*)mono_conc_hashtable_lookup (image_to_methods
, image
);
3706 if (image_methods
== NULL
) {
3707 image_methods
= (MonoLockFreeQueue
*)g_malloc (sizeof (MonoLockFreeQueue
));
3708 mono_lock_free_queue_init (image_methods
);
3709 mono_os_mutex_lock (&coverage_mutex
);
3710 mono_conc_hashtable_insert (image_to_methods
, image
, image_methods
);
3711 mono_os_mutex_unlock (&coverage_mutex
);
3714 node
= create_method_node (method
);
3715 mono_lock_free_queue_enqueue (image_methods
, node
);
3717 class_methods
= (MonoLockFreeQueue
*)mono_conc_hashtable_lookup (coverage_classes
, klass
);
3719 if (class_methods
== NULL
) {
3720 class_methods
= (MonoLockFreeQueue
*)g_malloc (sizeof (MonoLockFreeQueue
));
3721 mono_lock_free_queue_init (class_methods
);
3722 mono_os_mutex_lock (&coverage_mutex
);
3723 mono_conc_hashtable_insert (coverage_classes
, klass
, class_methods
);
3724 mono_os_mutex_unlock (&coverage_mutex
);
3727 node
= create_method_node (method
);
3728 mono_lock_free_queue_enqueue (class_methods
, node
);
3733 #define LINE_BUFFER_SIZE 4096
3734 /* Max file limit of 128KB */
3735 #define MAX_FILE_SIZE 128 * 1024
3737 get_file_content (FILE *stream
)
3742 int res
, offset
= 0;
3744 res
= fseek (stream
, 0, SEEK_END
);
3748 filesize
= ftell (stream
);
3752 res
= fseek (stream
, 0, SEEK_SET
);
3756 if (filesize
> MAX_FILE_SIZE
)
3759 buffer
= (char *)g_malloc ((filesize
+ 1) * sizeof (char));
3760 while ((bytes_read
= fread (buffer
+ offset
, 1, LINE_BUFFER_SIZE
, stream
)) > 0)
3761 offset
+= bytes_read
;
3763 /* NULL terminate our buffer */
3764 buffer
[filesize
] = '\0';
3769 get_next_line (char *contents
, char **next_start
)
3773 if (p
== NULL
|| *p
== '\0') {
3778 while (*p
!= '\n' && *p
!= '\0')
3783 *next_start
= p
+ 1;
3791 init_suppressed_assemblies (void)
3797 suppressed_assemblies
= mono_conc_hashtable_new (g_str_hash
, g_str_equal
);
3798 sa_file
= fopen (SUPPRESSION_DIR
"/mono-profiler-log.suppression", "r");
3799 if (sa_file
== NULL
)
3802 /* Don't need to free @content as it is referred to by the lines stored in @suppressed_assemblies */
3803 content
= get_file_content (sa_file
);
3804 if (content
== NULL
) {
3805 g_error ("mono-profiler-log.suppression is greater than 128kb - aborting\n");
3808 while ((line
= get_next_line (content
, &content
))) {
3809 line
= g_strchomp (g_strchug (line
));
3810 /* No locking needed as we're doing initialization */
3811 mono_conc_hashtable_insert (suppressed_assemblies
, line
, line
);
3817 #endif /* DISABLE_HELPER_THREAD */
3820 coverage_init (MonoProfiler
*prof
)
3822 #ifndef DISABLE_HELPER_THREAD
3823 assert (!coverage_initialized
);
3825 COVERAGE_DEBUG(fprintf (stderr
, "Coverage initialized\n");)
3827 mono_os_mutex_init (&coverage_mutex
);
3828 coverage_methods
= mono_conc_hashtable_new (NULL
, NULL
);
3829 coverage_assemblies
= mono_conc_hashtable_new (NULL
, NULL
);
3830 coverage_classes
= mono_conc_hashtable_new (NULL
, NULL
);
3831 filtered_classes
= mono_conc_hashtable_new (NULL
, NULL
);
3832 entered_methods
= mono_conc_hashtable_new (NULL
, NULL
);
3833 image_to_methods
= mono_conc_hashtable_new (NULL
, NULL
);
3834 init_suppressed_assemblies ();
3836 coverage_initialized
= TRUE
;
3837 #endif /* DISABLE_HELPER_THREAD */
3841 log_shutdown (MonoProfiler
*prof
)
3846 #ifndef DISABLE_HELPER_THREAD
3847 counters_and_perfcounters_sample (prof
);
3849 dump_coverage (prof
);
3851 if (prof
->command_port
) {
3853 ign_res (write (prof
->pipes
[1], &c
, 1));
3854 pthread_join (prof
->helper_thread
, &res
);
3860 for (i
= 0; i
< num_perf
; ++i
)
3861 read_perf_mmap (prof
, i
);
3865 g_ptr_array_free (prof
->sorted_sample_events
, TRUE
);
3867 if (TLS_GET (LogBuffer
, tlsbuffer
))
3868 send_buffer (prof
, TLS_GET (GPtrArray
, tlsmethodlist
), TLS_GET (LogBuffer
, tlsbuffer
));
3870 TLS_SET (tlsbuffer
, NULL
);
3871 TLS_SET (tlsmethodlist
, NULL
);
3873 InterlockedWrite (&prof
->run_writer_thread
, 0);
3874 pthread_join (prof
->writer_thread
, &res
);
3876 #if defined (HAVE_SYS_ZLIB)
3878 gzclose (prof
->gzfile
);
3880 if (prof
->pipe_output
)
3881 pclose (prof
->file
);
3883 fclose (prof
->file
);
3885 mono_conc_hashtable_destroy (prof
->method_table
);
3886 mono_os_mutex_destroy (&prof
->method_table_mutex
);
3888 if (coverage_initialized
) {
3889 mono_conc_hashtable_destroy (coverage_methods
);
3890 mono_conc_hashtable_destroy (coverage_assemblies
);
3891 mono_conc_hashtable_destroy (coverage_classes
);
3892 mono_conc_hashtable_destroy (filtered_classes
);
3894 mono_conc_hashtable_destroy (entered_methods
);
3895 mono_conc_hashtable_destroy (image_to_methods
);
3896 mono_conc_hashtable_destroy (suppressed_assemblies
);
3897 mono_os_mutex_destroy (&coverage_mutex
);
3904 new_filename (const char* filename
)
3906 time_t t
= time (NULL
);
3907 int pid
= process_id ();
3912 int count_dates
= 0;
3916 for (p
= filename
; *p
; p
++) {
3927 if (!count_dates
&& !count_pids
)
3928 return pstrdup (filename
);
3929 snprintf (pid_buf
, sizeof (pid_buf
), "%d", pid
);
3931 snprintf (time_buf
, sizeof (time_buf
), "%d%02d%02d%02d%02d%02d",
3932 1900 + ts
->tm_year
, 1 + ts
->tm_mon
, ts
->tm_mday
, ts
->tm_hour
, ts
->tm_min
, ts
->tm_sec
);
3933 s_date
= strlen (time_buf
);
3934 s_pid
= strlen (pid_buf
);
3935 d
= res
= (char *)malloc (strlen (filename
) + s_date
* count_dates
+ s_pid
* count_pids
);
3936 for (p
= filename
; *p
; p
++) {
3943 strcpy (d
, time_buf
);
3946 } else if (*p
== 'p') {
3947 strcpy (d
, pid_buf
);
3950 } else if (*p
== '%') {
3962 //this is exposed by the JIT, but it's not meant to be a supported API for now.
3963 extern void mono_threads_attach_tools_thread (void);
3965 #ifndef DISABLE_HELPER_THREAD
3968 helper_thread (void* arg
)
3970 MonoProfiler
* prof
= (MonoProfiler
*)arg
;
3974 MonoThread
*thread
= NULL
;
3976 mono_threads_attach_tools_thread ();
3977 //fprintf (stderr, "Server listening\n");
3978 command_socket
= -1;
3984 FD_SET (prof
->server_socket
, &rfds
);
3985 max_fd
= prof
->server_socket
;
3986 FD_SET (prof
->pipes
[0], &rfds
);
3987 if (max_fd
< prof
->pipes
[0])
3988 max_fd
= prof
->pipes
[0];
3989 if (command_socket
>= 0) {
3990 FD_SET (command_socket
, &rfds
);
3991 if (max_fd
< command_socket
)
3992 max_fd
= command_socket
;
3997 for ( i
= 0; i
< num_perf
; ++i
) {
3998 if (perf_data
[i
].perf_fd
< 0)
4000 FD_SET (perf_data
[i
].perf_fd
, &rfds
);
4001 if (max_fd
< perf_data
[i
].perf_fd
)
4002 max_fd
= perf_data
[i
].perf_fd
;
4007 counters_and_perfcounters_sample (prof
);
4011 len
= select (max_fd
+ 1, &rfds
, NULL
, NULL
, &tv
);
4017 g_warning ("Error in proflog server: %s", strerror (errno
));
4021 if (FD_ISSET (prof
->pipes
[0], &rfds
)) {
4023 int r
= read (prof
->pipes
[0], &c
, 1);
4024 if (r
== 1 && c
== 0) {
4025 StatBuffer
*sbufbase
= prof
->stat_buffers
;
4027 if (!sbufbase
->next
)
4029 sbuf
= sbufbase
->next
->next
;
4030 sbufbase
->next
->next
= NULL
;
4032 fprintf (stderr
, "stat buffer dump\n");
4034 dump_sample_hits (prof
, sbuf
);
4035 free_buffer (sbuf
, sbuf
->size
);
4036 safe_send (prof
, ensure_logbuf (0));
4040 /* time to shut down */
4041 dump_sample_hits (prof
, prof
->stat_buffers
);
4043 mono_thread_detach (thread
);
4045 fprintf (stderr
, "helper shutdown\n");
4049 for ( i
= 0; i
< num_perf
; ++i
) {
4050 if (perf_data
[i
].perf_fd
< 0)
4052 if (FD_ISSET (perf_data
[i
].perf_fd
, &rfds
))
4053 read_perf_mmap (prof
, i
);
4057 safe_send (prof
, ensure_logbuf (0));
4063 for ( i
= 0; i
< num_perf
; ++i
) {
4064 if (perf_data
[i
].perf_fd
< 0)
4066 if (FD_ISSET (perf_data
[i
].perf_fd
, &rfds
)) {
4067 read_perf_mmap (prof
, i
);
4068 safe_send (prof
, ensure_logbuf (0));
4073 if (command_socket
>= 0 && FD_ISSET (command_socket
, &rfds
)) {
4074 len
= read (command_socket
, buf
, sizeof (buf
) - 1);
4078 close (command_socket
);
4079 command_socket
= -1;
4083 if (strcmp (buf
, "heapshot\n") == 0) {
4084 heapshot_requested
= 1;
4085 //fprintf (stderr, "perform heapshot\n");
4086 if (InterlockedRead (&runtime_inited
) && !thread
) {
4087 thread
= mono_thread_attach (mono_get_root_domain ());
4088 /*fprintf (stderr, "attached\n");*/
4091 process_requests (prof
);
4092 mono_thread_detach (thread
);
4098 if (!FD_ISSET (prof
->server_socket
, &rfds
)) {
4101 command_socket
= accept (prof
->server_socket
, NULL
, NULL
);
4102 if (command_socket
< 0)
4104 //fprintf (stderr, "Accepted connection\n");
4110 start_helper_thread (MonoProfiler
* prof
)
4112 struct sockaddr_in server_address
;
4115 if (pipe (prof
->pipes
) < 0) {
4116 fprintf (stderr
, "Cannot create pipe\n");
4119 prof
->server_socket
= socket (PF_INET
, SOCK_STREAM
, 0);
4120 if (prof
->server_socket
< 0) {
4121 fprintf (stderr
, "Cannot create server socket\n");
4124 memset (&server_address
, 0, sizeof (server_address
));
4125 server_address
.sin_family
= AF_INET
;
4126 server_address
.sin_addr
.s_addr
= INADDR_ANY
;
4127 server_address
.sin_port
= htons (prof
->command_port
);
4128 if (bind (prof
->server_socket
, (struct sockaddr
*) &server_address
, sizeof (server_address
)) < 0) {
4129 fprintf (stderr
, "Cannot bind server socket, port: %d: %s\n", prof
->command_port
, strerror (errno
));
4130 close (prof
->server_socket
);
4133 if (listen (prof
->server_socket
, 1) < 0) {
4134 fprintf (stderr
, "Cannot listen server socket\n");
4135 close (prof
->server_socket
);
4138 slen
= sizeof (server_address
);
4139 if (getsockname (prof
->server_socket
, (struct sockaddr
*)&server_address
, &slen
) == 0) {
4140 prof
->command_port
= ntohs (server_address
.sin_port
);
4141 /*fprintf (stderr, "Assigned server port: %d\n", prof->command_port);*/
4144 r
= pthread_create (&prof
->helper_thread
, NULL
, helper_thread
, prof
);
4146 close (prof
->server_socket
);
4154 writer_thread (void *arg
)
4156 MonoProfiler
*prof
= (MonoProfiler
*)arg
;
4158 mono_threads_attach_tools_thread ();
4162 while (InterlockedRead (&prof
->run_writer_thread
)) {
4163 WriterQueueEntry
*entry
;
4165 while ((entry
= (WriterQueueEntry
*) mono_lock_free_queue_dequeue (&prof
->writer_queue
))) {
4166 LogBuffer
*method_buffer
= NULL
;
4167 gboolean new_methods
= FALSE
;
4169 if (entry
->methods
->len
)
4170 method_buffer
= create_buffer ();
4173 * Encode the method events in a temporary log buffer that we
4174 * flush to disk before the main buffer, ensuring that all
4175 * methods have metadata emitted before they're referenced.
4177 for (guint i
= 0; i
< entry
->methods
->len
; i
++) {
4178 MethodInfo
*info
= (MethodInfo
*)g_ptr_array_index (entry
->methods
, i
);
4180 if (mono_conc_hashtable_lookup (prof
->method_table
, info
->method
))
4186 * Other threads use this hash table to get a general
4187 * idea of whether a method has already been emitted to
4188 * the stream. Due to the way we add to this table, it
4189 * can easily happen that multiple threads queue up the
4190 * same methods, but that's OK since eventually all
4191 * methods will be in this table and the thread-local
4192 * method lists will just be empty for the rest of the
4195 mono_os_mutex_lock (&prof
->method_table_mutex
);
4196 mono_conc_hashtable_insert (prof
->method_table
, info
->method
, info
->method
);
4197 mono_os_mutex_unlock (&prof
->method_table_mutex
);
4199 char *name
= mono_method_full_name (info
->method
, 1);
4200 int nlen
= strlen (name
) + 1;
4201 void *cstart
= info
->ji
? mono_jit_info_get_code_start (info
->ji
) : NULL
;
4202 int csize
= info
->ji
? mono_jit_info_get_code_size (info
->ji
) : 0;
4204 method_buffer
= ensure_logbuf_inner (method_buffer
,
4205 EVENT_SIZE
/* event */ +
4206 LEB128_SIZE
/* time */ +
4207 LEB128_SIZE
/* method */ +
4208 LEB128_SIZE
/* start */ +
4209 LEB128_SIZE
/* size */ +
4213 emit_byte (method_buffer
, TYPE_JIT
| TYPE_METHOD
);
4214 emit_time (method_buffer
, info
->time
);
4215 emit_method_inner (method_buffer
, info
->method
);
4216 emit_ptr (method_buffer
, cstart
);
4217 emit_value (method_buffer
, csize
);
4219 memcpy (method_buffer
->data
, name
, nlen
);
4220 method_buffer
->data
+= nlen
;
4226 g_ptr_array_free (entry
->methods
, TRUE
);
4229 dump_buffer (prof
, method_buffer
);
4230 else if (method_buffer
)
4231 free_buffer (method_buffer
, method_buffer
->size
);
4233 dump_buffer (prof
, entry
->buffer
);
4243 start_writer_thread (MonoProfiler
* prof
)
4245 InterlockedWrite (&prof
->run_writer_thread
, 1);
4247 return !pthread_create (&prof
->writer_thread
, NULL
, writer_thread
, prof
);
4251 runtime_initialized (MonoProfiler
*profiler
)
4253 #ifndef DISABLE_HELPER_THREAD
4254 if (hs_mode_ondemand
|| need_helper_thread
) {
4255 if (!start_helper_thread (profiler
))
4256 profiler
->command_port
= 0;
4260 start_writer_thread (profiler
);
4262 InterlockedWrite (&runtime_inited
, 1);
4263 #ifndef DISABLE_HELPER_THREAD
4264 counters_init (profiler
);
4265 counters_sample (profiler
, 0);
4267 /* ensure the main thread data and startup are available soon */
4268 safe_send (profiler
, ensure_logbuf (0));
4271 static MonoProfiler
*
4272 create_profiler (const char *filename
, GPtrArray
*filters
)
4276 int force_delete
= 0;
4277 prof
= (MonoProfiler
*)calloc (1, sizeof (MonoProfiler
));
4279 prof
->command_port
= command_port
;
4280 if (filename
&& *filename
== '-') {
4286 filename
= "|mprof-report -";
4288 filename
= "output.mlpd";
4289 nf
= (char*)filename
;
4291 nf
= new_filename (filename
);
4293 int s
= strlen (nf
) + 32;
4294 char *p
= (char *)malloc (s
);
4295 snprintf (p
, s
, "|mprof-report '--out=%s' -", nf
);
4301 prof
->file
= popen (nf
+ 1, "w");
4302 prof
->pipe_output
= 1;
4303 } else if (*nf
== '#') {
4304 int fd
= strtol (nf
+ 1, NULL
, 10);
4305 prof
->file
= fdopen (fd
, "a");
4309 prof
->file
= fopen (nf
, "wb");
4312 fprintf (stderr
, "Cannot create profiler output: %s\n", nf
);
4315 #if defined (HAVE_SYS_ZLIB)
4317 prof
->gzfile
= gzdopen (fileno (prof
->file
), "wb");
4320 if (sample_type
&& !do_mono_sample
)
4321 need_helper_thread
= setup_perf_event ();
4323 /* FIXME: warn if different freq or sample type */
4327 if (do_mono_sample
) {
4328 prof
->stat_buffers
= create_stat_buffer ();
4329 need_helper_thread
= 1;
4331 if (do_counters
&& !need_helper_thread
) {
4332 need_helper_thread
= 1;
4335 prof
->sorted_sample_events
= g_ptr_array_sized_new (BUFFER_SIZE
/ SAMPLE_EVENT_SIZE_IN_SLOTS (0));
4337 #ifdef DISABLE_HELPER_THREAD
4338 if (hs_mode_ondemand
)
4339 fprintf (stderr
, "Ondemand heapshot unavailable on this arch.\n");
4342 fprintf (stderr
, "Coverage unavailable on this arch.\n");
4346 mono_lock_free_queue_init (&prof
->writer_queue
);
4347 mono_os_mutex_init (&prof
->method_table_mutex
);
4348 prof
->method_table
= mono_conc_hashtable_new (NULL
, NULL
);
4351 coverage_init (prof
);
4352 prof
->coverage_filters
= filters
;
4354 prof
->startup_time
= current_time ();
4361 printf ("Log profiler version %d.%d (format: %d)\n", LOG_VERSION_MAJOR
, LOG_VERSION_MINOR
, LOG_DATA_VERSION
);
4362 printf ("Usage: mono --profile=log[:OPTION1[,OPTION2...]] program.exe\n");
4363 printf ("Options:\n");
4364 printf ("\thelp show this usage info\n");
4365 printf ("\t[no]alloc enable/disable recording allocation info\n");
4366 printf ("\t[no]calls enable/disable recording enter/leave method events\n");
4367 printf ("\theapshot[=MODE] record heap shot info (by default at each major collection)\n");
4368 printf ("\t MODE: every XXms milliseconds, every YYgc collections, ondemand\n");
4369 printf ("\tcounters sample counters every 1s\n");
4370 printf ("\tsample[=TYPE] use statistical sampling mode (by default cycles/1000)\n");
4371 printf ("\t TYPE: cycles,instr,cacherefs,cachemiss,branches,branchmiss\n");
4372 printf ("\t TYPE can be followed by /FREQUENCY\n");
4373 printf ("\ttime=fast use a faster (but more inaccurate) timer\n");
4374 printf ("\tmaxframes=NUM collect up to NUM stack frames\n");
4375 printf ("\tcalldepth=NUM ignore method events for call chain depth bigger than NUM\n");
4376 printf ("\toutput=FILENAME write the data to file FILENAME (-FILENAME to overwrite)\n");
4377 printf ("\toutput=|PROGRAM write the data to the stdin of PROGRAM\n");
4378 printf ("\t %%t is subtituted with date and time, %%p with the pid\n");
4379 printf ("\treport create a report instead of writing the raw data to a file\n");
4380 printf ("\tzip compress the output data\n");
4381 printf ("\tport=PORTNUM use PORTNUM for the listening command server\n");
4382 printf ("\tcoverage enable collection of code coverage data\n");
4383 printf ("\tcovfilter=ASSEMBLY add an assembly to the code coverage filters\n");
4384 printf ("\t add a + to include the assembly or a - to exclude it\n");
4385 printf ("\t filter=-mscorlib\n");
4386 printf ("\tcovfilter-file=FILE use FILE to generate the list of assemblies to be filtered\n");
4392 match_option (const char* p
, const char *opt
, char **rval
)
4394 int len
= strlen (opt
);
4395 if (strncmp (p
, opt
, len
) == 0) {
4397 if (p
[len
] == '=' && p
[len
+ 1]) {
4398 const char *opt
= p
+ len
+ 1;
4399 const char *end
= strchr (opt
, ',');
4407 val
= (char *)malloc (l
+ 1);
4408 memcpy (val
, opt
, l
);
4413 if (p
[len
] == 0 || p
[len
] == ',') {
4415 return p
+ len
+ (p
[len
] == ',');
4433 static const SampleMode sample_modes
[] = {
4434 {"cycles", SAMPLE_CYCLES
},
4435 {"instr", SAMPLE_INSTRUCTIONS
},
4436 {"cachemiss", SAMPLE_CACHE_MISSES
},
4437 {"cacherefs", SAMPLE_CACHE_REFS
},
4438 {"branches", SAMPLE_BRANCHES
},
4439 {"branchmiss", SAMPLE_BRANCH_MISSES
},
4444 set_sample_mode (char* val
, int allow_empty
)
4447 char *maybe_freq
= NULL
;
4449 const SampleMode
*smode
= sample_modes
;
4450 #ifndef USE_PERF_EVENTS
4453 if (allow_empty
&& !val
) {
4454 sample_type
= SAMPLE_CYCLES
;
4458 if (strcmp (val
, "mono") == 0) {
4460 sample_type
= SAMPLE_CYCLES
;
4464 for (smode
= sample_modes
; smode
->name
; smode
++) {
4465 int l
= strlen (smode
->name
);
4466 if (strncmp (val
, smode
->name
, l
) == 0) {
4467 sample_type
= smode
->sample_mode
;
4468 maybe_freq
= val
+ l
;
4474 if (*maybe_freq
== '/') {
4475 count
= strtoul (maybe_freq
+ 1, &end
, 10);
4476 if (maybe_freq
+ 1 == end
)
4478 sample_freq
= count
;
4479 } else if (*maybe_freq
!= 0) {
4488 set_hsmode (char* val
, int allow_empty
)
4492 if (allow_empty
&& !val
)
4494 if (strcmp (val
, "ondemand") == 0) {
4495 hs_mode_ondemand
= 1;
4499 count
= strtoul (val
, &end
, 10);
4502 if (strcmp (end
, "ms") == 0)
4504 else if (strcmp (end
, "gc") == 0)
4512 * declaration to silence the compiler: this is the entry point that
4513 * mono will load from the shared library and call.
4516 mono_profiler_startup (const char *desc
);
4519 mono_profiler_startup_log (const char *desc
);
4522 * this is the entry point that will be used when the profiler
4523 * is embedded inside the main executable.
4526 mono_profiler_startup_log (const char *desc
)
4528 mono_profiler_startup (desc
);
4532 mono_profiler_startup (const char *desc
)
4535 GPtrArray
*filters
= NULL
;
4536 char *filename
= NULL
;
4540 int calls_enabled
= 0;
4541 int allocs_enabled
= 0;
4542 int only_counters
= 0;
4543 int only_coverage
= 0;
4544 int events
= MONO_PROFILE_GC
|MONO_PROFILE_ALLOCATIONS
|
4545 MONO_PROFILE_GC_MOVES
|MONO_PROFILE_CLASS_EVENTS
|MONO_PROFILE_THREADS
|
4546 MONO_PROFILE_ENTER_LEAVE
|MONO_PROFILE_JIT_COMPILATION
|MONO_PROFILE_EXCEPTIONS
|
4547 MONO_PROFILE_MONITOR_EVENTS
|MONO_PROFILE_MODULE_EVENTS
|MONO_PROFILE_GC_ROOTS
|
4548 MONO_PROFILE_INS_COVERAGE
|MONO_PROFILE_APPDOMAIN_EVENTS
|MONO_PROFILE_CONTEXT_EVENTS
|
4549 MONO_PROFILE_ASSEMBLY_EVENTS
;
4552 if (strncmp (p
, "log", 3))
4557 for (; *p
; p
= opt
) {
4563 if ((opt
= match_option (p
, "help", NULL
)) != p
) {
4567 if ((opt
= match_option (p
, "calls", NULL
)) != p
) {
4571 if ((opt
= match_option (p
, "nocalls", NULL
)) != p
) {
4572 events
&= ~MONO_PROFILE_ENTER_LEAVE
;
4576 if ((opt
= match_option (p
, "alloc", NULL
)) != p
) {
4580 if ((opt
= match_option (p
, "noalloc", NULL
)) != p
) {
4581 events
&= ~MONO_PROFILE_ALLOCATIONS
;
4584 if ((opt
= match_option (p
, "time", &val
)) != p
) {
4585 if (strcmp (val
, "fast") == 0)
4587 else if (strcmp (val
, "null") == 0)
4594 if ((opt
= match_option (p
, "report", NULL
)) != p
) {
4598 if ((opt
= match_option (p
, "debug", NULL
)) != p
) {
4602 if ((opt
= match_option (p
, "sampling-real", NULL
)) != p
) {
4603 sampling_mode
= MONO_PROFILER_STAT_MODE_REAL
;
4606 if ((opt
= match_option (p
, "sampling-process", NULL
)) != p
) {
4607 sampling_mode
= MONO_PROFILER_STAT_MODE_PROCESS
;
4610 if ((opt
= match_option (p
, "heapshot", &val
)) != p
) {
4611 events
&= ~MONO_PROFILE_ALLOCATIONS
;
4612 events
&= ~MONO_PROFILE_ENTER_LEAVE
;
4615 set_hsmode (val
, 1);
4618 if ((opt
= match_option (p
, "sample", &val
)) != p
) {
4619 events
&= ~MONO_PROFILE_ALLOCATIONS
;
4620 events
&= ~MONO_PROFILE_ENTER_LEAVE
;
4622 set_sample_mode (val
, 1);
4625 if ((opt
= match_option (p
, "hsmode", &val
)) != p
) {
4626 fprintf (stderr
, "The hsmode profiler option is obsolete, use heapshot=MODE.\n");
4627 set_hsmode (val
, 0);
4630 if ((opt
= match_option (p
, "zip", NULL
)) != p
) {
4634 if ((opt
= match_option (p
, "output", &val
)) != p
) {
4638 if ((opt
= match_option (p
, "port", &val
)) != p
) {
4640 command_port
= strtoul (val
, &end
, 10);
4644 if ((opt
= match_option (p
, "maxframes", &val
)) != p
) {
4646 num_frames
= strtoul (val
, &end
, 10);
4647 if (num_frames
> MAX_FRAMES
)
4648 num_frames
= MAX_FRAMES
;
4650 notraces
= num_frames
== 0;
4653 if ((opt
= match_option (p
, "calldepth", &val
)) != p
) {
4655 max_call_depth
= strtoul (val
, &end
, 10);
4659 if ((opt
= match_option (p
, "counters", NULL
)) != p
) {
4663 if ((opt
= match_option (p
, "countersonly", NULL
)) != p
) {
4667 if ((opt
= match_option (p
, "coverage", NULL
)) != p
) {
4669 events
|= MONO_PROFILE_ENTER_LEAVE
;
4670 debug_coverage
= (g_getenv ("MONO_PROFILER_DEBUG_COVERAGE") != NULL
);
4673 if ((opt
= match_option (p
, "onlycoverage", NULL
)) != p
) {
4677 if ((opt
= match_option (p
, "covfilter-file", &val
)) != p
) {
4679 char *line
, *content
;
4681 if (filters
== NULL
)
4682 filters
= g_ptr_array_new ();
4684 filter_file
= fopen (val
, "r");
4685 if (filter_file
== NULL
) {
4686 fprintf (stderr
, "Unable to open %s\n", val
);
4690 /* Don't need to free content as it is referred to by the lines stored in @filters */
4691 content
= get_file_content (filter_file
);
4692 if (content
== NULL
)
4693 fprintf (stderr
, "WARNING: %s is greater than 128kb - ignoring\n", val
);
4695 while ((line
= get_next_line (content
, &content
)))
4696 g_ptr_array_add (filters
, g_strchug (g_strchomp (line
)));
4698 fclose (filter_file
);
4701 if ((opt
= match_option (p
, "covfilter", &val
)) != p
) {
4702 if (filters
== NULL
)
4703 filters
= g_ptr_array_new ();
4705 g_ptr_array_add (filters
, val
);
4713 if (calls_enabled
) {
4714 events
|= MONO_PROFILE_ENTER_LEAVE
;
4718 events
|= MONO_PROFILE_ALLOCATIONS
;
4722 events
= MONO_PROFILE_ENTER_LEAVE
| MONO_PROFILE_INS_COVERAGE
;
4724 utils_init (fast_time
);
4726 prof
= create_profiler (filename
, filters
);
4731 mono_profiler_install (prof
, log_shutdown
);
4732 mono_profiler_install_gc (gc_event
, gc_resize
);
4733 mono_profiler_install_allocation (gc_alloc
);
4734 mono_profiler_install_gc_moves (gc_moves
);
4735 mono_profiler_install_gc_roots (gc_handle
, gc_roots
);
4736 mono_profiler_install_appdomain (NULL
, domain_loaded
, NULL
, domain_unloaded
);
4737 mono_profiler_install_appdomain_name (domain_name
);
4738 mono_profiler_install_context (context_loaded
, context_unloaded
);
4739 mono_profiler_install_class (NULL
, class_loaded
, NULL
, class_unloaded
);
4740 mono_profiler_install_module (NULL
, image_loaded
, NULL
, image_unloaded
);
4741 mono_profiler_install_assembly (NULL
, assembly_loaded
, assembly_unloaded
, NULL
);
4742 mono_profiler_install_thread (thread_start
, thread_end
);
4743 mono_profiler_install_thread_name (thread_name
);
4744 mono_profiler_install_enter_leave (method_enter
, method_leave
);
4745 mono_profiler_install_jit_end (method_jitted
);
4746 mono_profiler_install_code_buffer_new (code_buffer_new
);
4747 mono_profiler_install_exception (throw_exc
, method_exc_leave
, clause_exc
);
4748 mono_profiler_install_monitor (monitor_event
);
4749 mono_profiler_install_runtime_initialized (runtime_initialized
);
4751 mono_profiler_install_coverage_filter (coverage_filter
);
4753 if (do_mono_sample
&& sample_type
== SAMPLE_CYCLES
&& !only_counters
) {
4754 events
|= MONO_PROFILE_STATISTICAL
;
4755 mono_profiler_set_statistical_mode (sampling_mode
, 1000000 / sample_freq
);
4756 mono_profiler_install_statistical (mono_sample_hit
);
4759 mono_profiler_set_events ((MonoProfileFlags
)events
);
4761 TLS_INIT (tlsbuffer
);
4762 TLS_INIT (tlsmethodlist
);