[profiler] Always enable the helper thread.
[mono-project.git] / mono / profiler / mono-profiler-log.c
blob4fa7bdef7eb2454245ed9cbb10cdcbfd273cf5dc
1 /*
2 * mono-profiler-log.c: mono log profiler
4 * Authors:
5 * Paolo Molaro (lupus@ximian.com)
6 * Alex Rønne Petersen (alexrp@xamarin.com)
8 * Copyright 2010 Novell, Inc (http://www.novell.com)
9 * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
10 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
13 #include <config.h>
14 #include "../mini/jit.h"
15 #include "../metadata/metadata-internals.h"
16 #include <mono/metadata/profiler.h>
17 #include <mono/metadata/threads.h>
18 #include <mono/metadata/debug-helpers.h>
19 #include <mono/metadata/mono-config.h>
20 #include <mono/metadata/mono-gc.h>
21 #include <mono/metadata/mono-perfcounters.h>
22 #include <mono/metadata/appdomain.h>
23 #include <mono/metadata/assembly.h>
24 #include <mono/metadata/tokentype.h>
25 #include <mono/metadata/tabledefs.h>
26 #include <mono/utils/atomic.h>
27 #include <mono/utils/mono-membar.h>
28 #include <mono/utils/mono-mmap.h>
29 #include <mono/utils/mono-counters.h>
30 #include <mono/utils/mono-os-mutex.h>
31 #include <mono/utils/mono-os-semaphore.h>
32 #include <mono/utils/mono-conc-hashtable.h>
33 #include <mono/utils/mono-linked-list-set.h>
34 #include <mono/utils/lock-free-alloc.h>
35 #include <mono/utils/lock-free-queue.h>
36 #include <mono/utils/hazard-pointer.h>
37 #include <mono/utils/mono-threads.h>
38 #include <mono/utils/mono-threads-api.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <assert.h>
42 #include <glib.h>
43 #ifdef HAVE_UNISTD_H
44 #include <unistd.h>
45 #endif
46 #ifdef HAVE_SCHED_GETAFFINITY
47 #include <sched.h>
48 #endif
49 #include <fcntl.h>
50 #include <errno.h>
51 #include <time.h>
52 #ifdef HAVE_SYS_TIME_H
53 #include <sys/time.h>
54 #endif
55 #if defined(__APPLE__)
56 #include <mach/mach_time.h>
57 #endif
59 #ifndef _GNU_SOURCE
60 #define _GNU_SOURCE
61 #endif
62 #ifdef HAVE_DLFCN_H
63 #include <dlfcn.h>
64 #endif
65 #ifdef HAVE_EXECINFO_H
66 #include <execinfo.h>
67 #endif
68 #ifdef HAVE_LINK_H
69 #include <link.h>
70 #endif
72 #include <sys/types.h>
73 #include <sys/socket.h>
74 #include <netinet/in.h>
75 #include <sys/select.h>
77 #ifdef HOST_WIN32
78 #include <windows.h>
79 #else
80 #include <pthread.h>
81 #endif
83 #ifdef HAVE_SYS_STAT_H
84 #include <sys/stat.h>
85 #endif
87 #include "mono-profiler-log.h"
89 #if defined (HAVE_SYS_ZLIB)
90 #include <zlib.h>
91 #endif
93 #if defined(__linux__)
95 #include <unistd.h>
96 #include <sys/syscall.h>
98 #ifdef ENABLE_PERF_EVENTS
99 #include <linux/perf_event.h>
101 #define USE_PERF_EVENTS 1
103 static int read_perf_mmap (MonoProfiler* prof, int cpu);
104 #endif
106 #endif
108 #define BUFFER_SIZE (4096 * 16)
110 /* Worst-case size in bytes of a 64-bit value encoded with LEB128. */
111 #define LEB128_SIZE 10
112 /* Size of a value encoded as a single byte. */
113 #define BYTE_SIZE 1
114 /* Size in bytes of the event prefix (ID + time). */
115 #define EVENT_SIZE (BYTE_SIZE + LEB128_SIZE)
117 static int nocalls = 0;
118 static int notraces = 0;
119 static int use_zip = 0;
120 static int do_report = 0;
121 static int do_heap_shot = 0;
122 static int max_call_depth = 100;
123 static volatile int runtime_inited = 0;
124 static int command_port = 0;
125 static int heapshot_requested = 0;
126 static int sample_type = 0;
127 static int sample_freq = 0;
128 static int do_mono_sample = 0;
129 static int in_shutdown = 0;
130 static int do_debug = 0;
131 static int do_counters = 0;
132 static int do_coverage = 0;
133 static gboolean debug_coverage = FALSE;
134 static MonoProfileSamplingMode sampling_mode = MONO_PROFILER_STAT_MODE_PROCESS;
135 static int max_allocated_sample_hits;
137 // Statistics for internal profiler data structures.
138 static gint32 sample_allocations_ctr,
139 buffer_allocations_ctr;
141 // Statistics for profiler events.
142 static gint32 sync_points_ctr,
143 heap_objects_ctr,
144 heap_starts_ctr,
145 heap_ends_ctr,
146 heap_roots_ctr,
147 gc_events_ctr,
148 gc_resizes_ctr,
149 gc_allocs_ctr,
150 gc_moves_ctr,
151 gc_handle_creations_ctr,
152 gc_handle_deletions_ctr,
153 finalize_begins_ctr,
154 finalize_ends_ctr,
155 finalize_object_begins_ctr,
156 finalize_object_ends_ctr,
157 image_loads_ctr,
158 image_unloads_ctr,
159 assembly_loads_ctr,
160 assembly_unloads_ctr,
161 class_loads_ctr,
162 class_unloads_ctr,
163 method_entries_ctr,
164 method_exits_ctr,
165 method_exception_exits_ctr,
166 method_jits_ctr,
167 code_buffers_ctr,
168 exception_throws_ctr,
169 exception_clauses_ctr,
170 monitor_contentions_ctr,
171 monitor_acquisitions_ctr,
172 monitor_failures_ctr,
173 thread_starts_ctr,
174 thread_ends_ctr,
175 thread_names_ctr,
176 domain_loads_ctr,
177 domain_unloads_ctr,
178 domain_names_ctr,
179 context_loads_ctr,
180 context_unloads_ctr,
181 sample_ubins_ctr,
182 sample_usyms_ctr,
183 sample_hits_ctr,
184 counter_descriptors_ctr,
185 counter_samples_ctr,
186 perfcounter_descriptors_ctr,
187 perfcounter_samples_ctr,
188 coverage_methods_ctr,
189 coverage_statements_ctr,
190 coverage_classes_ctr,
191 coverage_assemblies_ctr;
193 static MonoLinkedListSet profiler_thread_list;
196 * file format:
197 * [header] [buffer]*
199 * The file is composed by a header followed by 0 or more buffers.
200 * Each buffer contains events that happened on a thread: for a given thread
201 * buffers that appear later in the file are guaranteed to contain events
202 * that happened later in time. Buffers from separate threads could be interleaved,
203 * though.
204 * Buffers are not required to be aligned.
206 * header format:
207 * [id: 4 bytes] constant value: LOG_HEADER_ID
208 * [major: 1 byte] [minor: 1 byte] major and minor version of the log profiler
209 * [format: 1 byte] version of the data format for the rest of the file
210 * [ptrsize: 1 byte] size in bytes of a pointer in the profiled program
211 * [startup time: 8 bytes] time in milliseconds since the unix epoch when the program started
212 * [timer overhead: 4 bytes] approximate overhead in nanoseconds of the timer
213 * [flags: 4 bytes] file format flags, should be 0 for now
214 * [pid: 4 bytes] pid of the profiled process
215 * [port: 2 bytes] tcp port for server if != 0
216 * [args size: 4 bytes] size of args
217 * [args: string] arguments passed to the profiler
218 * [arch size: 4 bytes] size of arch
219 * [arch: string] architecture the profiler is running on
220 * [os size: 4 bytes] size of os
221 * [os: string] operating system the profiler is running on
223 * The multiple byte integers are in little-endian format.
225 * buffer format:
226 * [buffer header] [event]*
227 * Buffers have a fixed-size header followed by 0 or more bytes of event data.
228 * Timing information and other values in the event data are usually stored
229 * as uleb128 or sleb128 integers. To save space, as noted for each item below,
230 * some data is represented as a difference between the actual value and
231 * either the last value of the same type (like for timing information) or
232 * as the difference from a value stored in a buffer header.
234 * For timing information the data is stored as uleb128, since timing
235 * increases in a monotonic way in each thread: the value is the number of
236 * nanoseconds to add to the last seen timing data in a buffer. The first value
237 * in a buffer will be calculated from the time_base field in the buffer head.
239 * Object or heap sizes are stored as uleb128.
240 * Pointer differences are stored as sleb128, instead.
242 * If an unexpected value is found, the rest of the buffer should be ignored,
243 * as generally the later values need the former to be interpreted correctly.
245 * buffer header format:
246 * [bufid: 4 bytes] constant value: BUF_ID
247 * [len: 4 bytes] size of the data following the buffer header
248 * [time_base: 8 bytes] time base in nanoseconds since an unspecified epoch
249 * [ptr_base: 8 bytes] base value for pointers
250 * [obj_base: 8 bytes] base value for object addresses
251 * [thread id: 8 bytes] system-specific thread ID (pthread_t for example)
252 * [method_base: 8 bytes] base value for MonoMethod pointers
254 * event format:
255 * [extended info: upper 4 bits] [type: lower 4 bits]
256 * [time diff: uleb128] nanoseconds since last timing
257 * [data]*
258 * The data that follows depends on type and the extended info.
259 * Type is one of the enum values in mono-profiler-log.h: TYPE_ALLOC, TYPE_GC,
260 * TYPE_METADATA, TYPE_METHOD, TYPE_EXCEPTION, TYPE_MONITOR, TYPE_HEAP.
261 * The extended info bits are interpreted based on type, see
262 * each individual event description below.
263 * strings are represented as a 0-terminated utf8 sequence.
265 * backtrace format:
266 * [num: uleb128] number of frames following
267 * [frame: sleb128]* mum MonoMethod* as a pointer difference from the last such
268 * pointer or the buffer method_base
270 * type alloc format:
271 * type: TYPE_ALLOC
272 * exinfo: flags: TYPE_ALLOC_BT
273 * [ptr: sleb128] class as a byte difference from ptr_base
274 * [obj: sleb128] object address as a byte difference from obj_base
275 * [size: uleb128] size of the object in the heap
276 * If the TYPE_ALLOC_BT flag is set, a backtrace follows.
278 * type GC format:
279 * type: TYPE_GC
280 * exinfo: one of TYPE_GC_EVENT, TYPE_GC_RESIZE, TYPE_GC_MOVE, TYPE_GC_HANDLE_CREATED[_BT],
281 * TYPE_GC_HANDLE_DESTROYED[_BT], TYPE_GC_FINALIZE_START, TYPE_GC_FINALIZE_END,
282 * TYPE_GC_FINALIZE_OBJECT_START, TYPE_GC_FINALIZE_OBJECT_END
283 * if exinfo == TYPE_GC_RESIZE
284 * [heap_size: uleb128] new heap size
285 * if exinfo == TYPE_GC_EVENT
286 * [event type: byte] GC event (MONO_GC_EVENT_* from profiler.h)
287 * [generation: byte] GC generation event refers to
288 * if exinfo == TYPE_GC_MOVE
289 * [num_objects: uleb128] number of object moves that follow
290 * [objaddr: sleb128]+ num_objects object pointer differences from obj_base
291 * num is always an even number: the even items are the old
292 * addresses, the odd numbers are the respective new object addresses
293 * if exinfo == TYPE_GC_HANDLE_CREATED[_BT]
294 * [handle_type: uleb128] GC handle type (System.Runtime.InteropServices.GCHandleType)
295 * upper bits reserved as flags
296 * [handle: uleb128] GC handle value
297 * [objaddr: sleb128] object pointer differences from obj_base
298 * If exinfo == TYPE_GC_HANDLE_CREATED_BT, a backtrace follows.
299 * if exinfo == TYPE_GC_HANDLE_DESTROYED[_BT]
300 * [handle_type: uleb128] GC handle type (System.Runtime.InteropServices.GCHandleType)
301 * upper bits reserved as flags
302 * [handle: uleb128] GC handle value
303 * If exinfo == TYPE_GC_HANDLE_DESTROYED_BT, a backtrace follows.
304 * if exinfo == TYPE_GC_FINALIZE_OBJECT_{START,END}
305 * [object: sleb128] the object as a difference from obj_base
307 * type metadata format:
308 * type: TYPE_METADATA
309 * exinfo: one of: TYPE_END_LOAD, TYPE_END_UNLOAD (optional for TYPE_THREAD and TYPE_DOMAIN)
310 * [mtype: byte] metadata type, one of: TYPE_CLASS, TYPE_IMAGE, TYPE_ASSEMBLY, TYPE_DOMAIN,
311 * TYPE_THREAD, TYPE_CONTEXT
312 * [pointer: sleb128] pointer of the metadata type depending on mtype
313 * if mtype == TYPE_CLASS
314 * [image: sleb128] MonoImage* as a pointer difference from ptr_base
315 * [name: string] full class name
316 * if mtype == TYPE_IMAGE
317 * [name: string] image file name
318 * if mtype == TYPE_ASSEMBLY
319 * [name: string] assembly name
320 * if mtype == TYPE_DOMAIN && exinfo == 0
321 * [name: string] domain friendly name
322 * if mtype == TYPE_CONTEXT
323 * [domain: sleb128] domain id as pointer
324 * if mtype == TYPE_THREAD && exinfo == 0
325 * [name: string] thread name
327 * type method format:
328 * type: TYPE_METHOD
329 * exinfo: one of: TYPE_LEAVE, TYPE_ENTER, TYPE_EXC_LEAVE, TYPE_JIT
330 * [method: sleb128] MonoMethod* as a pointer difference from the last such
331 * pointer or the buffer method_base
332 * if exinfo == TYPE_JIT
333 * [code address: sleb128] pointer to the native code as a diff from ptr_base
334 * [code size: uleb128] size of the generated code
335 * [name: string] full method name
337 * type exception format:
338 * type: TYPE_EXCEPTION
339 * exinfo: TYPE_THROW_BT flag or one of: TYPE_CLAUSE
340 * if exinfo == TYPE_CLAUSE
341 * [clause type: byte] MonoExceptionEnum enum value
342 * [clause index: uleb128] index of the current clause
343 * [method: sleb128] MonoMethod* as a pointer difference from the last such
344 * pointer or the buffer method_base
345 * else
346 * [object: sleb128] the exception object as a difference from obj_base
347 * if exinfo has TYPE_THROW_BT set, a backtrace follows.
349 * type runtime format:
350 * type: TYPE_RUNTIME
351 * exinfo: one of: TYPE_JITHELPER
352 * if exinfo == TYPE_JITHELPER
353 * [type: byte] MonoProfilerCodeBufferType enum value
354 * [buffer address: sleb128] pointer to the native code as a diff from ptr_base
355 * [buffer size: uleb128] size of the generated code
356 * if type == MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE
357 * [name: string] buffer description name
359 * type monitor format:
360 * type: TYPE_MONITOR
361 * exinfo: TYPE_MONITOR_BT flag and one of: MONO_PROFILER_MONITOR_(CONTENTION|FAIL|DONE)
362 * [object: sleb128] the lock object as a difference from obj_base
363 * if exinfo.low3bits == MONO_PROFILER_MONITOR_CONTENTION
364 * If the TYPE_MONITOR_BT flag is set, a backtrace follows.
366 * type heap format
367 * type: TYPE_HEAP
368 * exinfo: one of TYPE_HEAP_START, TYPE_HEAP_END, TYPE_HEAP_OBJECT, TYPE_HEAP_ROOT
369 * if exinfo == TYPE_HEAP_OBJECT
370 * [object: sleb128] the object as a difference from obj_base
371 * [class: sleb128] the object MonoClass* as a difference from ptr_base
372 * [size: uleb128] size of the object on the heap
373 * [num_refs: uleb128] number of object references
374 * each referenced objref is preceded by a uleb128 encoded offset: the
375 * first offset is from the object address and each next offset is relative
376 * to the previous one
377 * [objrefs: sleb128]+ object referenced as a difference from obj_base
378 * The same object can appear multiple times, but only the first time
379 * with size != 0: in the other cases this data will only be used to
380 * provide additional referenced objects.
381 * if exinfo == TYPE_HEAP_ROOT
382 * [num_roots: uleb128] number of root references
383 * [num_gc: uleb128] number of major gcs
384 * [object: sleb128] the object as a difference from obj_base
385 * [root_type: byte] the root_type: MonoProfileGCRootType (profiler.h)
386 * [extra_info: uleb128] the extra_info value
387 * object, root_type and extra_info are repeated num_roots times
389 * type sample format
390 * type: TYPE_SAMPLE
391 * exinfo: one of TYPE_SAMPLE_HIT, TYPE_SAMPLE_USYM, TYPE_SAMPLE_UBIN, TYPE_SAMPLE_COUNTERS_DESC, TYPE_SAMPLE_COUNTERS
392 * if exinfo == TYPE_SAMPLE_HIT
393 * [sample_type: byte] type of sample (SAMPLE_*)
394 * [thread: sleb128] thread id as difference from ptr_base
395 * [count: uleb128] number of following instruction addresses
396 * [ip: sleb128]* instruction pointer as difference from ptr_base
397 * [mbt_count: uleb128] number of managed backtrace frames
398 * [method: sleb128]* MonoMethod* as a pointer difference from the last such
399 * pointer or the buffer method_base (the first such method can be also indentified by ip, but this is not neccessarily true)
400 * if exinfo == TYPE_SAMPLE_USYM
401 * [address: sleb128] symbol address as a difference from ptr_base
402 * [size: uleb128] symbol size (may be 0 if unknown)
403 * [name: string] symbol name
404 * if exinfo == TYPE_SAMPLE_UBIN
405 * [address: sleb128] address where binary has been loaded
406 * [offset: uleb128] file offset of mapping (the same file can be mapped multiple times)
407 * [size: uleb128] memory size
408 * [name: string] binary name
409 * if exinfo == TYPE_SAMPLE_COUNTERS_DESC
410 * [len: uleb128] number of counters
411 * for i = 0 to len
412 * [section: uleb128] section of counter
413 * if section == MONO_COUNTER_PERFCOUNTERS:
414 * [section_name: string] section name of counter
415 * [name: string] name of counter
416 * [type: byte] type of counter
417 * [unit: byte] unit of counter
418 * [variance: byte] variance of counter
419 * [index: uleb128] unique index of counter
420 * if exinfo == TYPE_SAMPLE_COUNTERS
421 * while true:
422 * [index: uleb128] unique index of counter
423 * if index == 0:
424 * break
425 * [type: byte] type of counter value
426 * if type == string:
427 * if value == null:
428 * [0: uleb128] 0 -> value is null
429 * else:
430 * [1: uleb128] 1 -> value is not null
431 * [value: string] counter value
432 * else:
433 * [value: uleb128/sleb128/double] counter value, can be sleb128, uleb128 or double (determined by using type)
435 * type coverage format
436 * type: TYPE_COVERAGE
437 * exinfo: one of TYPE_COVERAGE_METHOD, TYPE_COVERAGE_STATEMENT, TYPE_COVERAGE_ASSEMBLY, TYPE_COVERAGE_CLASS
438 * if exinfo == TYPE_COVERAGE_METHOD
439 * [assembly: string] name of assembly
440 * [class: string] name of the class
441 * [name: string] name of the method
442 * [signature: string] the signature of the method
443 * [filename: string] the file path of the file that contains this method
444 * [token: uleb128] the method token
445 * [method_id: uleb128] an ID for this data to associate with the buffers of TYPE_COVERAGE_STATEMENTS
446 * [len: uleb128] the number of TYPE_COVERAGE_BUFFERS associated with this method
447 * if exinfo == TYPE_COVERAGE_STATEMENTS
448 * [method_id: uleb128] an the TYPE_COVERAGE_METHOD buffer to associate this with
449 * [offset: uleb128] the il offset relative to the previous offset
450 * [counter: uleb128] the counter for this instruction
451 * [line: uleb128] the line of filename containing this instruction
452 * [column: uleb128] the column containing this instruction
453 * if exinfo == TYPE_COVERAGE_ASSEMBLY
454 * [name: string] assembly name
455 * [guid: string] assembly GUID
456 * [filename: string] assembly filename
457 * [number_of_methods: uleb128] the number of methods in this assembly
458 * [fully_covered: uleb128] the number of fully covered methods
459 * [partially_covered: uleb128] the number of partially covered methods
460 * currently partially_covered will always be 0, and fully_covered is the
461 * number of methods that are fully and partially covered.
462 * if exinfo == TYPE_COVERAGE_CLASS
463 * [name: string] assembly name
464 * [class: string] class name
465 * [number_of_methods: uleb128] the number of methods in this class
466 * [fully_covered: uleb128] the number of fully covered methods
467 * [partially_covered: uleb128] the number of partially covered methods
468 * currently partially_covered will always be 0, and fully_covered is the
469 * number of methods that are fully and partially covered.
471 * type meta format:
472 * type: TYPE_META
473 * exinfo: one of: TYPE_SYNC_POINT
474 * if exinfo == TYPE_SYNC_POINT
475 * [type: byte] MonoProfilerSyncPointType enum value
478 // Pending data to be written to the log, for a single thread.
479 // Threads periodically flush their own LogBuffers by calling safe_send
480 typedef struct _LogBuffer LogBuffer;
481 struct _LogBuffer {
482 // Next (older) LogBuffer in processing queue
483 LogBuffer *next;
485 uint64_t time_base;
486 uint64_t last_time;
487 uintptr_t ptr_base;
488 uintptr_t method_base;
489 uintptr_t last_method;
490 uintptr_t obj_base;
491 uintptr_t thread_id;
493 // Bytes allocated for this LogBuffer
494 int size;
496 // Start of currently unused space in buffer
497 unsigned char* cursor;
499 // Pointer to start-of-structure-plus-size (for convenience)
500 unsigned char* buf_end;
502 // Start of data in buffer. Contents follow "buffer format" described above.
503 unsigned char buf [1];
506 typedef struct {
507 MonoLinkedListSetNode node;
509 // Was this thread added to the LLS?
510 gboolean attached;
512 // The current log buffer for this thread.
513 LogBuffer *buffer;
515 // Methods referenced by events in `buffer`, see `MethodInfo`.
516 GPtrArray *methods;
518 // Current call depth for enter/leave events.
519 int call_depth;
521 // Indicates whether this thread is currently writing to its `buffer`.
522 gboolean busy;
523 } MonoProfilerThread;
525 static inline void
526 ign_res (int G_GNUC_UNUSED unused, ...)
530 static uintptr_t
531 thread_id (void)
533 return (uintptr_t) mono_native_thread_id_get ();
536 static uintptr_t
537 process_id (void)
539 #ifdef HOST_WIN32
540 return (uintptr_t) GetCurrentProcessId ();
541 #else
542 return (uintptr_t) getpid ();
543 #endif
546 #ifdef __APPLE__
547 static mach_timebase_info_data_t timebase_info;
548 #elif defined (HOST_WIN32)
549 static LARGE_INTEGER pcounter_freq;
550 #endif
552 #define TICKS_PER_SEC 1000000000LL
554 static uint64_t
555 current_time (void)
557 #ifdef __APPLE__
558 uint64_t time = mach_absolute_time ();
560 time *= timebase_info.numer;
561 time /= timebase_info.denom;
563 return time;
564 #elif defined (HOST_WIN32)
565 LARGE_INTEGER value;
567 QueryPerformanceCounter (&value);
569 return value.QuadPart * TICKS_PER_SEC / pcounter_freq.QuadPart;
570 #elif defined (CLOCK_MONOTONIC)
571 struct timespec tspec;
573 clock_gettime (CLOCK_MONOTONIC, &tspec);
575 return ((uint64_t) tspec.tv_sec * TICKS_PER_SEC + tspec.tv_nsec);
576 #else
577 struct timeval tv;
579 gettimeofday (&tv, NULL);
581 return ((uint64_t) tv.tv_sec * TICKS_PER_SEC + tv.tv_usec * 1000);
582 #endif
585 static int timer_overhead;
587 static void
588 init_time (void)
590 #ifdef __APPLE__
591 mach_timebase_info (&timebase_info);
592 #elif defined (HOST_WIN32)
593 QueryPerformanceFrequency (&pcounter_freq);
594 #endif
596 uint64_t time_start = current_time ();
598 for (int i = 0; i < 256; ++i)
599 current_time ();
601 uint64_t time_end = current_time ();
603 timer_overhead = (time_end - time_start) / 256;
607 * These macros create a scope to avoid leaking the buffer returned
608 * from ensure_logbuf () as it may have been invalidated by a GC
609 * thread during STW. If you called init_thread () with add_to_lls =
610 * FALSE, then don't use these macros.
613 #define ENTER_LOG(COUNTER, BUFFER, SIZE) \
614 do { \
615 MonoProfilerThread *thread__ = PROF_TLS_GET (); \
616 if (thread__->attached) \
617 buffer_lock (); \
618 g_assert (!thread__->busy && "Why are we trying to write a new event while already writing one?"); \
619 thread__->busy = TRUE; \
620 InterlockedIncrement ((COUNTER)); \
621 LogBuffer *BUFFER = ensure_logbuf_unsafe ((SIZE))
623 #define EXIT_LOG_EXPLICIT(PROFILER, SEND, REQUESTS) \
624 thread__->busy = FALSE; \
625 if ((SEND)) \
626 send_log_unsafe ((PROFILER), FALSE, TRUE); \
627 if (thread__->attached) \
628 buffer_unlock (); \
629 if ((REQUESTS)) \
630 process_requests ((PROFILER)); \
631 } while (0)
633 #define EXIT_LOG(PROFILER) EXIT_LOG_EXPLICIT ((PROFILER), TRUE, TRUE)
635 static volatile gint32 buffer_rwlock_count;
636 static volatile gpointer buffer_rwlock_exclusive;
638 // Can be used recursively.
639 static void
640 buffer_lock (void)
643 * If the thread holding the exclusive lock tries to modify the
644 * reader count, just make it a no-op. This way, we also avoid
645 * invoking the GC safe point macros below, which could break if
646 * done from a thread that is currently the initiator of STW.
648 * In other words, we rely on the fact that the GC thread takes
649 * the exclusive lock in the gc_event () callback when the world
650 * is about to stop.
652 if (InterlockedReadPointer (&buffer_rwlock_exclusive) != (gpointer) thread_id ()) {
653 MONO_ENTER_GC_SAFE;
655 while (InterlockedReadPointer (&buffer_rwlock_exclusive))
656 mono_thread_info_yield ();
658 InterlockedIncrement (&buffer_rwlock_count);
660 MONO_EXIT_GC_SAFE;
663 mono_memory_barrier ();
666 static void
667 buffer_unlock (void)
669 mono_memory_barrier ();
671 // See the comment in buffer_lock ().
672 if (InterlockedReadPointer (&buffer_rwlock_exclusive) == (gpointer) thread_id ())
673 return;
675 g_assert (InterlockedRead (&buffer_rwlock_count) && "Why are we trying to decrement a zero reader count?");
677 InterlockedDecrement (&buffer_rwlock_count);
680 // Cannot be used recursively.
681 static void
682 buffer_lock_excl (void)
684 gpointer tid = (gpointer) thread_id ();
686 g_assert (InterlockedReadPointer (&buffer_rwlock_exclusive) != tid && "Why are we taking the exclusive lock twice?");
688 MONO_ENTER_GC_SAFE;
690 while (InterlockedCompareExchangePointer (&buffer_rwlock_exclusive, tid, 0))
691 mono_thread_info_yield ();
693 while (InterlockedRead (&buffer_rwlock_count))
694 mono_thread_info_yield ();
696 MONO_EXIT_GC_SAFE;
698 mono_memory_barrier ();
701 static void
702 buffer_unlock_excl (void)
704 mono_memory_barrier ();
706 g_assert (InterlockedReadPointer (&buffer_rwlock_exclusive) && "Why is the exclusive lock not held?");
707 g_assert (InterlockedReadPointer (&buffer_rwlock_exclusive) == (gpointer) thread_id () && "Why does another thread hold the exclusive lock?");
708 g_assert (!InterlockedRead (&buffer_rwlock_count) && "Why are there readers when the exclusive lock is held?");
710 InterlockedWritePointer (&buffer_rwlock_exclusive, NULL);
713 typedef struct _BinaryObject BinaryObject;
714 struct _BinaryObject {
715 BinaryObject *next;
716 void *addr;
717 char *name;
720 struct _MonoProfiler {
721 FILE* file;
722 #if defined (HAVE_SYS_ZLIB)
723 gzFile gzfile;
724 #endif
725 char *args;
726 uint64_t startup_time;
727 int pipe_output;
728 int command_port;
729 int server_socket;
730 int pipes [2];
731 #ifndef HOST_WIN32
732 pthread_t helper_thread;
733 pthread_t writer_thread;
734 pthread_t dumper_thread;
735 #endif
736 volatile gint32 run_writer_thread;
737 MonoLockFreeAllocSizeClass writer_entry_size_class;
738 MonoLockFreeAllocator writer_entry_allocator;
739 MonoLockFreeQueue writer_queue;
740 MonoSemType writer_queue_sem;
741 MonoConcurrentHashTable *method_table;
742 mono_mutex_t method_table_mutex;
743 volatile gint32 run_dumper_thread;
744 MonoLockFreeQueue dumper_queue;
745 MonoSemType dumper_queue_sem;
746 MonoLockFreeAllocSizeClass sample_size_class;
747 MonoLockFreeAllocator sample_allocator;
748 MonoLockFreeQueue sample_reuse_queue;
749 BinaryObject *binary_objects;
750 GPtrArray *coverage_filters;
753 typedef struct {
754 MonoLockFreeQueueNode node;
755 GPtrArray *methods;
756 LogBuffer *buffer;
757 } WriterQueueEntry;
759 #define WRITER_ENTRY_BLOCK_SIZE (mono_pagesize ())
761 typedef struct {
762 MonoMethod *method;
763 MonoJitInfo *ji;
764 uint64_t time;
765 } MethodInfo;
767 #ifdef HOST_WIN32
769 #define PROF_TLS_SET(VAL) (TlsSetValue (profiler_tls, (VAL)))
770 #define PROF_TLS_GET() ((MonoProfilerThread *) TlsGetValue (profiler_tls))
771 #define PROF_TLS_INIT() (profiler_tls = TlsAlloc ())
772 #define PROF_TLS_FREE() (TlsFree (profiler_tls))
774 static DWORD profiler_tls;
776 #elif HAVE_KW_THREAD
778 #define PROF_TLS_SET(VAL) (profiler_tls = (VAL))
779 #define PROF_TLS_GET() (profiler_tls)
780 #define PROF_TLS_INIT()
781 #define PROF_TLS_FREE()
783 static __thread MonoProfilerThread *profiler_tls;
785 #else
787 #define PROF_TLS_SET(VAL) (pthread_setspecific (profiler_tls, (VAL)))
788 #define PROF_TLS_GET() ((MonoProfilerThread *) pthread_getspecific (profiler_tls))
789 #define PROF_TLS_INIT() (pthread_key_create (&profiler_tls, NULL))
790 #define PROF_TLS_FREE() (pthread_key_delete (&profiler_tls))
792 static pthread_key_t profiler_tls;
794 #endif
796 static char*
797 pstrdup (const char *s)
799 int len = strlen (s) + 1;
800 char *p = (char *)malloc (len);
801 memcpy (p, s, len);
802 return p;
805 static void *
806 alloc_buffer (int size)
808 return mono_valloc (NULL, size, MONO_MMAP_READ | MONO_MMAP_WRITE | MONO_MMAP_ANON | MONO_MMAP_PRIVATE, MONO_MEM_ACCOUNT_PROFILER);
811 static void
812 free_buffer (void *buf, int size)
814 mono_vfree (buf, size, MONO_MEM_ACCOUNT_PROFILER);
817 static LogBuffer*
818 create_buffer (void)
820 LogBuffer* buf = (LogBuffer *) alloc_buffer (BUFFER_SIZE);
822 InterlockedIncrement (&buffer_allocations_ctr);
824 buf->size = BUFFER_SIZE;
825 buf->time_base = current_time ();
826 buf->last_time = buf->time_base;
827 buf->buf_end = (unsigned char*)buf + buf->size;
828 buf->cursor = buf->buf;
830 return buf;
834 * Must be called with the reader lock held if thread is the current thread, or
835 * the exclusive lock if thread is a different thread. However, if thread is
836 * the current thread, and init_thread () was called with add_to_lls = FALSE,
837 * then no locking is necessary.
839 static void
840 init_buffer_state (MonoProfilerThread *thread)
842 thread->buffer = create_buffer ();
843 thread->methods = NULL;
846 static void
847 clear_hazard_pointers (MonoThreadHazardPointers *hp)
849 mono_hazard_pointer_clear (hp, 0);
850 mono_hazard_pointer_clear (hp, 1);
851 mono_hazard_pointer_clear (hp, 2);
854 static MonoProfilerThread *
855 init_thread (gboolean add_to_lls)
857 MonoProfilerThread *thread = PROF_TLS_GET ();
860 * Sometimes we may try to initialize a thread twice. One example is the
861 * main thread: We initialize it when setting up the profiler, but we will
862 * also get a thread_start () callback for it. Another example is when
863 * attaching new threads to the runtime: We may get a gc_alloc () callback
864 * for that thread's thread object (where we initialize it), soon followed
865 * by a thread_start () callback.
867 * These cases are harmless anyhow. Just return if we've already done the
868 * initialization work.
870 if (thread)
871 return thread;
873 thread = malloc (sizeof (MonoProfilerThread));
874 thread->node.key = thread_id ();
875 thread->attached = add_to_lls;
876 thread->call_depth = 0;
877 thread->busy = 0;
879 init_buffer_state (thread);
882 * Some internal profiler threads don't need to be cleaned up
883 * by the main thread on shutdown.
885 if (add_to_lls) {
886 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
887 g_assert (mono_lls_insert (&profiler_thread_list, hp, &thread->node) && "Why can't we insert the thread in the LLS?");
888 clear_hazard_pointers (hp);
891 PROF_TLS_SET (thread);
893 return thread;
896 // Only valid if init_thread () was called with add_to_lls = FALSE.
897 static void
898 deinit_thread (MonoProfilerThread *thread)
900 g_assert (!thread->attached && "Why are we manually freeing an attached thread?");
902 free (thread);
903 PROF_TLS_SET (NULL);
906 static LogBuffer *
907 ensure_logbuf_inner (LogBuffer *old, int bytes)
909 if (old && old->cursor + bytes + 100 < old->buf_end)
910 return old;
912 LogBuffer *new_ = create_buffer ();
913 new_->next = old;
915 return new_;
918 // Only valid if init_thread () was called with add_to_lls = FALSE.
919 static LogBuffer *
920 ensure_logbuf_unsafe (int bytes)
922 MonoProfilerThread *thread = PROF_TLS_GET ();
923 LogBuffer *old = thread->buffer;
924 LogBuffer *new_ = ensure_logbuf_inner (old, bytes);
926 if (new_ == old)
927 return old; // Still enough space.
929 thread->buffer = new_;
931 return new_;
934 static void
935 encode_uleb128 (uint64_t value, uint8_t *buf, uint8_t **endbuf)
937 uint8_t *p = buf;
939 do {
940 uint8_t b = value & 0x7f;
941 value >>= 7;
943 if (value != 0) /* more bytes to come */
944 b |= 0x80;
946 *p ++ = b;
947 } while (value);
949 *endbuf = p;
952 static void
953 encode_sleb128 (intptr_t value, uint8_t *buf, uint8_t **endbuf)
955 int more = 1;
956 int negative = (value < 0);
957 unsigned int size = sizeof (intptr_t) * 8;
958 uint8_t byte;
959 uint8_t *p = buf;
961 while (more) {
962 byte = value & 0x7f;
963 value >>= 7;
965 /* the following is unnecessary if the
966 * implementation of >>= uses an arithmetic rather
967 * than logical shift for a signed left operand
969 if (negative)
970 /* sign extend */
971 value |= - ((intptr_t) 1 <<(size - 7));
973 /* sign bit of byte is second high order bit (0x40) */
974 if ((value == 0 && !(byte & 0x40)) ||
975 (value == -1 && (byte & 0x40)))
976 more = 0;
977 else
978 byte |= 0x80;
980 *p ++= byte;
983 *endbuf = p;
986 static void
987 emit_byte (LogBuffer *logbuffer, int value)
989 logbuffer->cursor [0] = value;
990 logbuffer->cursor++;
991 assert (logbuffer->cursor <= logbuffer->buf_end);
994 static void
995 emit_value (LogBuffer *logbuffer, int value)
997 encode_uleb128 (value, logbuffer->cursor, &logbuffer->cursor);
998 assert (logbuffer->cursor <= logbuffer->buf_end);
1001 static void
1002 emit_time (LogBuffer *logbuffer, uint64_t value)
1004 uint64_t tdiff = value - logbuffer->last_time;
1005 //if (value < logbuffer->last_time)
1006 // printf ("time went backwards\n");
1007 //if (tdiff > 1000000)
1008 // printf ("large time offset: %llu\n", tdiff);
1009 encode_uleb128 (tdiff, logbuffer->cursor, &logbuffer->cursor);
1010 /*if (tdiff != decode_uleb128 (p, &p))
1011 printf ("incorrect encoding: %llu\n", tdiff);*/
1012 logbuffer->last_time = value;
1013 assert (logbuffer->cursor <= logbuffer->buf_end);
1016 static void
1017 emit_event_time (LogBuffer *logbuffer, int event, uint64_t time)
1019 emit_byte (logbuffer, event);
1020 emit_time (logbuffer, time);
1023 static void
1024 emit_event (LogBuffer *logbuffer, int event)
1026 emit_event_time (logbuffer, event, current_time ());
1029 static void
1030 emit_svalue (LogBuffer *logbuffer, int64_t value)
1032 encode_sleb128 (value, logbuffer->cursor, &logbuffer->cursor);
1033 assert (logbuffer->cursor <= logbuffer->buf_end);
1036 static void
1037 emit_uvalue (LogBuffer *logbuffer, uint64_t value)
1039 encode_uleb128 (value, logbuffer->cursor, &logbuffer->cursor);
1040 assert (logbuffer->cursor <= logbuffer->buf_end);
1043 static void
1044 emit_ptr (LogBuffer *logbuffer, void *ptr)
1046 if (!logbuffer->ptr_base)
1047 logbuffer->ptr_base = (uintptr_t)ptr;
1048 emit_svalue (logbuffer, (intptr_t)ptr - logbuffer->ptr_base);
1049 assert (logbuffer->cursor <= logbuffer->buf_end);
1052 static void
1053 emit_method_inner (LogBuffer *logbuffer, void *method)
1055 if (!logbuffer->method_base) {
1056 logbuffer->method_base = (intptr_t)method;
1057 logbuffer->last_method = (intptr_t)method;
1059 encode_sleb128 ((intptr_t)((char*)method - (char*)logbuffer->last_method), logbuffer->cursor, &logbuffer->cursor);
1060 logbuffer->last_method = (intptr_t)method;
1061 assert (logbuffer->cursor <= logbuffer->buf_end);
1065 typedef struct {
1066 MonoMethod *method;
1067 MonoJitInfo *found;
1068 } MethodSearch;
1070 static void
1071 find_method (MonoDomain *domain, void *user_data)
1073 MethodSearch *search = user_data;
1075 if (search->found)
1076 return;
1078 MonoJitInfo *ji = mono_get_jit_info_from_method (domain, search->method);
1080 // It could be AOT'd, so we need to get it from the AOT runtime's cache.
1081 if (!ji) {
1082 void *ip = mono_aot_get_method (domain, search->method);
1084 // Avoid a slow path in mono_jit_info_table_find ().
1085 if (ip)
1086 ji = mono_jit_info_table_find (domain, ip);
1089 if (ji)
1090 search->found = ji;
1094 static void
1095 register_method_local (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *ji)
1097 if (!mono_conc_hashtable_lookup (prof->method_table, method)) {
1099 * FIXME: In some cases, we crash while looking up JIT info for AOT'd methods.
1100 * This usually happens for static constructors. This code is disabled for now
1101 * as we don't need this info for anything critical.
1103 * https://bugzilla.xamarin.com/show_bug.cgi?id=35171
1106 if (!ji) {
1107 MethodSearch search = { method, NULL };
1109 mono_domain_foreach (find_method, &search);
1111 ji = search.found;
1116 * FIXME: We can't always find JIT info for a generic shared method, especially
1117 * if we obtained the MonoMethod during an async stack walk. For now, we deal
1118 * with this by giving the generic shared method name and dummy code start/size
1119 * information (i.e. zeroes).
1121 //g_assert (ji);
1123 MethodInfo *info = (MethodInfo *) malloc (sizeof (MethodInfo));
1125 info->method = method;
1126 info->ji = ji;
1127 info->time = current_time ();
1129 MonoProfilerThread *thread = PROF_TLS_GET ();
1130 GPtrArray *arr = thread->methods ? thread->methods : (thread->methods = g_ptr_array_new ());
1131 g_ptr_array_add (arr, info);
1135 static void
1136 emit_method (MonoProfiler *prof, LogBuffer *logbuffer, MonoMethod *method)
1138 register_method_local (prof, method, NULL);
1139 emit_method_inner (logbuffer, method);
1142 static void
1143 emit_obj (LogBuffer *logbuffer, void *ptr)
1145 if (!logbuffer->obj_base)
1146 logbuffer->obj_base = (uintptr_t)ptr >> 3;
1147 emit_svalue (logbuffer, ((uintptr_t)ptr >> 3) - logbuffer->obj_base);
1148 assert (logbuffer->cursor <= logbuffer->buf_end);
1151 static void
1152 emit_string (LogBuffer *logbuffer, const char *str, size_t size)
1154 size_t i = 0;
1155 if (str) {
1156 for (; i < size; i++) {
1157 if (str[i] == '\0')
1158 break;
1159 emit_byte (logbuffer, str [i]);
1162 emit_byte (logbuffer, '\0');
1165 static void
1166 emit_double (LogBuffer *logbuffer, double value)
1168 int i;
1169 unsigned char buffer[8];
1170 memcpy (buffer, &value, 8);
1171 #if G_BYTE_ORDER == G_BIG_ENDIAN
1172 for (i = 7; i >= 0; i--)
1173 #else
1174 for (i = 0; i < 8; i++)
1175 #endif
1176 emit_byte (logbuffer, buffer[i]);
1179 static char*
1180 write_int16 (char *buf, int32_t value)
1182 int i;
1183 for (i = 0; i < 2; ++i) {
1184 buf [i] = value;
1185 value >>= 8;
1187 return buf + 2;
1190 static char*
1191 write_int32 (char *buf, int32_t value)
1193 int i;
1194 for (i = 0; i < 4; ++i) {
1195 buf [i] = value;
1196 value >>= 8;
1198 return buf + 4;
1201 static char*
1202 write_int64 (char *buf, int64_t value)
1204 int i;
1205 for (i = 0; i < 8; ++i) {
1206 buf [i] = value;
1207 value >>= 8;
1209 return buf + 8;
1212 static char *
1213 write_header_string (char *p, const char *str)
1215 size_t len = strlen (str) + 1;
1217 p = write_int32 (p, len);
1218 strcpy (p, str);
1220 return p + len;
1223 static void
1224 dump_header (MonoProfiler *profiler)
1226 const char *args = profiler->args;
1227 const char *arch = mono_config_get_cpu ();
1228 const char *os = mono_config_get_os ();
1230 char *hbuf = malloc (
1231 sizeof (gint32) /* header id */ +
1232 sizeof (gint8) /* major version */ +
1233 sizeof (gint8) /* minor version */ +
1234 sizeof (gint8) /* data version */ +
1235 sizeof (gint8) /* word size */ +
1236 sizeof (gint64) /* startup time */ +
1237 sizeof (gint32) /* timer overhead */ +
1238 sizeof (gint32) /* flags */ +
1239 sizeof (gint32) /* process id */ +
1240 sizeof (gint16) /* command port */ +
1241 sizeof (gint32) + strlen (args) + 1 /* arguments */ +
1242 sizeof (gint32) + strlen (arch) + 1 /* architecture */ +
1243 sizeof (gint32) + strlen (os) + 1 /* operating system */
1245 char *p = hbuf;
1247 p = write_int32 (p, LOG_HEADER_ID);
1248 *p++ = LOG_VERSION_MAJOR;
1249 *p++ = LOG_VERSION_MINOR;
1250 *p++ = LOG_DATA_VERSION;
1251 *p++ = sizeof (void *);
1252 p = write_int64 (p, ((uint64_t) time (NULL)) * 1000);
1253 p = write_int32 (p, timer_overhead);
1254 p = write_int32 (p, 0); /* flags */
1255 p = write_int32 (p, process_id ());
1256 p = write_int16 (p, profiler->command_port);
1257 p = write_header_string (p, args);
1258 p = write_header_string (p, arch);
1259 p = write_header_string (p, os);
1261 #if defined (HAVE_SYS_ZLIB)
1262 if (profiler->gzfile) {
1263 gzwrite (profiler->gzfile, hbuf, p - hbuf);
1264 } else
1265 #endif
1267 fwrite (hbuf, p - hbuf, 1, profiler->file);
1268 fflush (profiler->file);
1271 free (hbuf);
1275 * Must be called with the reader lock held if thread is the current thread, or
1276 * the exclusive lock if thread is a different thread. However, if thread is
1277 * the current thread, and init_thread () was called with add_to_lls = FALSE,
1278 * then no locking is necessary.
1280 static void
1281 send_buffer (MonoProfiler *prof, MonoProfilerThread *thread)
1283 WriterQueueEntry *entry = mono_lock_free_alloc (&prof->writer_entry_allocator);
1284 entry->methods = thread->methods;
1285 entry->buffer = thread->buffer;
1287 mono_lock_free_queue_node_init (&entry->node, FALSE);
1289 mono_lock_free_queue_enqueue (&prof->writer_queue, &entry->node);
1290 mono_os_sem_post (&prof->writer_queue_sem);
1293 static void
1294 remove_thread (MonoProfiler *prof, MonoProfilerThread *thread, gboolean from_callback)
1296 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
1298 if (mono_lls_remove (&profiler_thread_list, hp, &thread->node)) {
1300 * No need to take the buffer lock here as no other threads can
1301 * be accessing this buffer anymore.
1304 if (!from_callback) {
1306 * The thread is being cleaned up by the main thread during
1307 * shutdown. This typically happens for internal runtime
1308 * threads. We need to synthesize a thread end event.
1311 InterlockedIncrement (&thread_ends_ctr);
1313 thread->buffer = ensure_logbuf_inner (thread->buffer,
1314 EVENT_SIZE /* event */ +
1315 BYTE_SIZE /* type */ +
1316 LEB128_SIZE /* tid */
1319 emit_event (thread->buffer, TYPE_END_UNLOAD | TYPE_METADATA);
1320 emit_byte (thread->buffer, TYPE_THREAD);
1321 emit_ptr (thread->buffer, (void *) thread->node.key);
1324 send_buffer (prof, thread);
1326 mono_thread_hazardous_try_free (thread, free);
1329 clear_hazard_pointers (hp);
1331 if (from_callback)
1332 PROF_TLS_SET (NULL);
1335 static void
1336 dump_buffer (MonoProfiler *profiler, LogBuffer *buf)
1338 char hbuf [128];
1339 char *p = hbuf;
1341 if (buf->next)
1342 dump_buffer (profiler, buf->next);
1344 if (buf->cursor - buf->buf) {
1345 p = write_int32 (p, BUF_ID);
1346 p = write_int32 (p, buf->cursor - buf->buf);
1347 p = write_int64 (p, buf->time_base);
1348 p = write_int64 (p, buf->ptr_base);
1349 p = write_int64 (p, buf->obj_base);
1350 p = write_int64 (p, buf->thread_id);
1351 p = write_int64 (p, buf->method_base);
1353 #if defined (HAVE_SYS_ZLIB)
1354 if (profiler->gzfile) {
1355 gzwrite (profiler->gzfile, hbuf, p - hbuf);
1356 gzwrite (profiler->gzfile, buf->buf, buf->cursor - buf->buf);
1357 } else
1358 #endif
1360 fwrite (hbuf, p - hbuf, 1, profiler->file);
1361 fwrite (buf->buf, buf->cursor - buf->buf, 1, profiler->file);
1362 fflush (profiler->file);
1366 free_buffer (buf, buf->size);
1369 static void
1370 dump_buffer_threadless (MonoProfiler *profiler, LogBuffer *buf)
1372 for (LogBuffer *iter = buf; iter; iter = iter->next)
1373 iter->thread_id = 0;
1375 dump_buffer (profiler, buf);
1378 static void
1379 process_requests (MonoProfiler *profiler)
1381 if (heapshot_requested)
1382 mono_gc_collect (mono_gc_max_generation ());
1385 // Avoid calling this directly if possible. Use the functions below.
1386 static void
1387 send_log_unsafe (MonoProfiler *profiler, gboolean lock, gboolean if_needed)
1389 MonoProfilerThread *thread = PROF_TLS_GET ();
1391 if (lock)
1392 buffer_lock ();
1394 if (!if_needed || (if_needed && thread->buffer->next)) {
1395 if (!thread->attached)
1396 for (LogBuffer *iter = thread->buffer; iter; iter = iter->next)
1397 iter->thread_id = 0;
1399 send_buffer (profiler, thread);
1400 init_buffer_state (thread);
1403 if (lock)
1404 buffer_unlock ();
1407 // Assumes that the exclusive lock is held.
1408 static void
1409 sync_point_flush (MonoProfiler *prof)
1411 g_assert (InterlockedReadPointer (&buffer_rwlock_exclusive) == (gpointer) thread_id () && "Why don't we hold the exclusive lock?");
1413 MONO_LLS_FOREACH_SAFE (&profiler_thread_list, MonoProfilerThread, thread) {
1414 send_buffer (prof, thread);
1415 init_buffer_state (thread);
1416 } MONO_LLS_FOREACH_SAFE_END
1419 // Assumes that the exclusive lock is held.
1420 static void
1421 sync_point_mark (MonoProfiler *prof, MonoProfilerSyncPointType type)
1423 g_assert (InterlockedReadPointer (&buffer_rwlock_exclusive) == (gpointer) thread_id () && "Why don't we hold the exclusive lock?");
1425 ENTER_LOG (&sync_points_ctr, logbuffer,
1426 EVENT_SIZE /* event */ +
1427 LEB128_SIZE /* type */
1430 emit_event (logbuffer, TYPE_META | TYPE_SYNC_POINT);
1431 emit_byte (logbuffer, type);
1433 EXIT_LOG_EXPLICIT (prof, FALSE, FALSE);
1435 send_log_unsafe (prof, FALSE, FALSE);
1438 // Assumes that the exclusive lock is held.
1439 static void
1440 sync_point (MonoProfiler *prof, MonoProfilerSyncPointType type)
1442 sync_point_flush (prof);
1443 sync_point_mark (prof, type);
1446 static int
1447 gc_reference (MonoObject *obj, MonoClass *klass, uintptr_t size, uintptr_t num, MonoObject **refs, uintptr_t *offsets, void *data)
1449 MonoProfiler *prof = data;
1451 /* account for object alignment in the heap */
1452 size += 7;
1453 size &= ~7;
1455 ENTER_LOG (&heap_objects_ctr, logbuffer,
1456 EVENT_SIZE /* event */ +
1457 LEB128_SIZE /* obj */ +
1458 LEB128_SIZE /* klass */ +
1459 LEB128_SIZE /* size */ +
1460 LEB128_SIZE /* num */ +
1461 num * (
1462 LEB128_SIZE /* offset */ +
1463 LEB128_SIZE /* ref */
1467 emit_event (logbuffer, TYPE_HEAP_OBJECT | TYPE_HEAP);
1468 emit_obj (logbuffer, obj);
1469 emit_ptr (logbuffer, klass);
1470 emit_value (logbuffer, size);
1471 emit_value (logbuffer, num);
1473 uintptr_t last_offset = 0;
1475 for (int i = 0; i < num; ++i) {
1476 emit_value (logbuffer, offsets [i] - last_offset);
1477 last_offset = offsets [i];
1478 emit_obj (logbuffer, refs [i]);
1481 EXIT_LOG_EXPLICIT (prof, TRUE, FALSE);
1483 return 0;
1486 static unsigned int hs_mode_ms = 0;
1487 static unsigned int hs_mode_gc = 0;
1488 static unsigned int hs_mode_ondemand = 0;
1489 static unsigned int gc_count = 0;
1490 static uint64_t last_hs_time = 0;
1491 static gboolean do_heap_walk = FALSE;
1493 static void
1494 heap_walk (MonoProfiler *profiler)
1496 ENTER_LOG (&heap_starts_ctr, logbuffer,
1497 EVENT_SIZE /* event */
1500 emit_event (logbuffer, TYPE_HEAP_START | TYPE_HEAP);
1502 EXIT_LOG_EXPLICIT (profiler, TRUE, FALSE);
1504 mono_gc_walk_heap (0, gc_reference, profiler);
1506 ENTER_LOG (&heap_ends_ctr, logbuffer,
1507 EVENT_SIZE /* event */
1510 emit_event (logbuffer, TYPE_HEAP_END | TYPE_HEAP);
1512 EXIT_LOG_EXPLICIT (profiler, TRUE, FALSE);
1515 static void
1516 gc_roots (MonoProfiler *prof, int num, void **objects, int *root_types, uintptr_t *extra_info)
1518 ENTER_LOG (&heap_roots_ctr, logbuffer,
1519 EVENT_SIZE /* event */ +
1520 LEB128_SIZE /* num */ +
1521 LEB128_SIZE /* collections */ +
1522 num * (
1523 LEB128_SIZE /* object */ +
1524 LEB128_SIZE /* root type */ +
1525 LEB128_SIZE /* extra info */
1529 emit_event (logbuffer, TYPE_HEAP_ROOT | TYPE_HEAP);
1530 emit_value (logbuffer, num);
1531 emit_value (logbuffer, mono_gc_collection_count (mono_gc_max_generation ()));
1533 for (int i = 0; i < num; ++i) {
1534 emit_obj (logbuffer, objects [i]);
1535 emit_byte (logbuffer, root_types [i]);
1536 emit_value (logbuffer, extra_info [i]);
1539 EXIT_LOG_EXPLICIT (prof, TRUE, FALSE);
1542 static void
1543 gc_event (MonoProfiler *profiler, MonoGCEvent ev, int generation)
1545 ENTER_LOG (&gc_events_ctr, logbuffer,
1546 EVENT_SIZE /* event */ +
1547 BYTE_SIZE /* gc event */ +
1548 BYTE_SIZE /* generation */
1551 emit_event (logbuffer, TYPE_GC_EVENT | TYPE_GC);
1552 emit_byte (logbuffer, ev);
1553 emit_byte (logbuffer, generation);
1555 EXIT_LOG_EXPLICIT (profiler, FALSE, FALSE);
1557 switch (ev) {
1558 case MONO_GC_EVENT_START:
1559 if (generation == mono_gc_max_generation ())
1560 gc_count++;
1562 uint64_t now = current_time ();
1564 if (hs_mode_ms && (now - last_hs_time) / 1000 * 1000 >= hs_mode_ms)
1565 do_heap_walk = TRUE;
1566 else if (hs_mode_gc && !(gc_count % hs_mode_gc))
1567 do_heap_walk = TRUE;
1568 else if (hs_mode_ondemand)
1569 do_heap_walk = heapshot_requested;
1570 else if (!hs_mode_ms && !hs_mode_gc && generation == mono_gc_max_generation ())
1571 do_heap_walk = TRUE;
1572 break;
1573 case MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED:
1575 * Ensure that no thread can be in the middle of writing to
1576 * a buffer when the world stops...
1578 buffer_lock_excl ();
1579 break;
1580 case MONO_GC_EVENT_POST_STOP_WORLD:
1582 * ... So that we now have a consistent view of all buffers.
1583 * This allows us to flush them. We need to do this because
1584 * they may contain object allocation events that need to be
1585 * committed to the log file before any object move events
1586 * that will be produced during this GC.
1588 sync_point (profiler, SYNC_POINT_WORLD_STOP);
1589 break;
1590 case MONO_GC_EVENT_PRE_START_WORLD:
1591 if (do_heap_shot && do_heap_walk) {
1592 heap_walk (profiler);
1594 do_heap_walk = FALSE;
1595 heapshot_requested = 0;
1596 last_hs_time = current_time ();
1598 break;
1599 case MONO_GC_EVENT_POST_START_WORLD_UNLOCKED:
1601 * Similarly, we must now make sure that any object moves
1602 * written to the GC thread's buffer are flushed. Otherwise,
1603 * object allocation events for certain addresses could come
1604 * after the move events that made those addresses available.
1606 sync_point_mark (profiler, SYNC_POINT_WORLD_START);
1609 * Finally, it is safe to allow other threads to write to
1610 * their buffers again.
1612 buffer_unlock_excl ();
1613 break;
1614 default:
1615 break;
1619 static void
1620 gc_resize (MonoProfiler *profiler, int64_t new_size)
1622 ENTER_LOG (&gc_resizes_ctr, logbuffer,
1623 EVENT_SIZE /* event */ +
1624 LEB128_SIZE /* new size */
1627 emit_event (logbuffer, TYPE_GC_RESIZE | TYPE_GC);
1628 emit_value (logbuffer, new_size);
1630 EXIT_LOG_EXPLICIT (profiler, TRUE, FALSE);
1633 // If you alter MAX_FRAMES, you may need to alter SAMPLE_BLOCK_SIZE too.
1634 #define MAX_FRAMES 32
1636 typedef struct {
1637 int count;
1638 MonoMethod* methods [MAX_FRAMES];
1639 int32_t il_offsets [MAX_FRAMES];
1640 int32_t native_offsets [MAX_FRAMES];
1641 } FrameData;
1643 static int num_frames = MAX_FRAMES;
1645 static mono_bool
1646 walk_stack (MonoMethod *method, int32_t native_offset, int32_t il_offset, mono_bool managed, void* data)
1648 FrameData *frame = (FrameData *)data;
1649 if (method && frame->count < num_frames) {
1650 frame->il_offsets [frame->count] = il_offset;
1651 frame->native_offsets [frame->count] = native_offset;
1652 frame->methods [frame->count++] = method;
1653 //printf ("In %d %s at %d (native: %d)\n", frame->count, mono_method_get_name (method), il_offset, native_offset);
1655 return frame->count == num_frames;
1659 * a note about stack walks: they can cause more profiler events to fire,
1660 * so we need to make sure they don't happen after we started emitting an
1661 * event, hence the collect_bt/emit_bt split.
1663 static void
1664 collect_bt (FrameData *data)
1666 data->count = 0;
1667 mono_stack_walk_no_il (walk_stack, data);
1670 static void
1671 emit_bt (MonoProfiler *prof, LogBuffer *logbuffer, FrameData *data)
1673 /* FIXME: this is actually tons of data and we should
1674 * just output it the first time and use an id the next
1676 if (data->count > num_frames)
1677 printf ("bad num frames: %d\n", data->count);
1678 emit_value (logbuffer, data->count);
1679 //if (*p != data.count) {
1680 // printf ("bad num frames enc at %d: %d -> %d\n", count, data.count, *p); printf ("frames end: %p->%p\n", p, logbuffer->cursor); exit(0);}
1681 while (data->count) {
1682 emit_method (prof, logbuffer, data->methods [--data->count]);
1686 static void
1687 gc_alloc (MonoProfiler *prof, MonoObject *obj, MonoClass *klass)
1689 init_thread (TRUE);
1691 int do_bt = (nocalls && InterlockedRead (&runtime_inited) && !notraces) ? TYPE_ALLOC_BT : 0;
1692 FrameData data;
1693 uintptr_t len = mono_object_get_size (obj);
1694 /* account for object alignment in the heap */
1695 len += 7;
1696 len &= ~7;
1698 if (do_bt)
1699 collect_bt (&data);
1701 ENTER_LOG (&gc_allocs_ctr, logbuffer,
1702 EVENT_SIZE /* event */ +
1703 LEB128_SIZE /* klass */ +
1704 LEB128_SIZE /* obj */ +
1705 LEB128_SIZE /* size */ +
1706 (do_bt ? (
1707 LEB128_SIZE /* count */ +
1708 data.count * (
1709 LEB128_SIZE /* method */
1711 ) : 0)
1714 emit_event (logbuffer, do_bt | TYPE_ALLOC);
1715 emit_ptr (logbuffer, klass);
1716 emit_obj (logbuffer, obj);
1717 emit_value (logbuffer, len);
1719 if (do_bt)
1720 emit_bt (prof, logbuffer, &data);
1722 EXIT_LOG (prof);
1725 static void
1726 gc_moves (MonoProfiler *prof, void **objects, int num)
1728 ENTER_LOG (&gc_moves_ctr, logbuffer,
1729 EVENT_SIZE /* event */ +
1730 LEB128_SIZE /* num */ +
1731 num * (
1732 LEB128_SIZE /* object */
1736 emit_event (logbuffer, TYPE_GC_MOVE | TYPE_GC);
1737 emit_value (logbuffer, num);
1739 for (int i = 0; i < num; ++i)
1740 emit_obj (logbuffer, objects [i]);
1742 EXIT_LOG_EXPLICIT (prof, TRUE, FALSE);
1745 static void
1746 gc_handle (MonoProfiler *prof, int op, int type, uintptr_t handle, MonoObject *obj)
1748 int do_bt = nocalls && InterlockedRead (&runtime_inited) && !notraces;
1749 FrameData data;
1751 if (do_bt)
1752 collect_bt (&data);
1754 gint32 *ctr = op == MONO_PROFILER_GC_HANDLE_CREATED ? &gc_handle_creations_ctr : &gc_handle_deletions_ctr;
1756 ENTER_LOG (ctr, logbuffer,
1757 EVENT_SIZE /* event */ +
1758 LEB128_SIZE /* type */ +
1759 LEB128_SIZE /* handle */ +
1760 (op == MONO_PROFILER_GC_HANDLE_CREATED ? (
1761 LEB128_SIZE /* obj */
1762 ) : 0) +
1763 (do_bt ? (
1764 LEB128_SIZE /* count */ +
1765 data.count * (
1766 LEB128_SIZE /* method */
1768 ) : 0)
1771 if (op == MONO_PROFILER_GC_HANDLE_CREATED)
1772 emit_event (logbuffer, (do_bt ? TYPE_GC_HANDLE_CREATED_BT : TYPE_GC_HANDLE_CREATED) | TYPE_GC);
1773 else if (op == MONO_PROFILER_GC_HANDLE_DESTROYED)
1774 emit_event (logbuffer, (do_bt ? TYPE_GC_HANDLE_DESTROYED_BT : TYPE_GC_HANDLE_DESTROYED) | TYPE_GC);
1775 else
1776 g_assert_not_reached ();
1778 emit_value (logbuffer, type);
1779 emit_value (logbuffer, handle);
1781 if (op == MONO_PROFILER_GC_HANDLE_CREATED)
1782 emit_obj (logbuffer, obj);
1784 if (do_bt)
1785 emit_bt (prof, logbuffer, &data);
1787 EXIT_LOG (prof);
1790 static void
1791 finalize_begin (MonoProfiler *prof)
1793 ENTER_LOG (&finalize_begins_ctr, buf,
1794 EVENT_SIZE /* event */
1797 emit_event (buf, TYPE_GC_FINALIZE_START | TYPE_GC);
1799 EXIT_LOG (prof);
1802 static void
1803 finalize_end (MonoProfiler *prof)
1805 ENTER_LOG (&finalize_ends_ctr, buf,
1806 EVENT_SIZE /* event */
1809 emit_event (buf, TYPE_GC_FINALIZE_END | TYPE_GC);
1811 EXIT_LOG (prof);
1814 static void
1815 finalize_object_begin (MonoProfiler *prof, MonoObject *obj)
1817 ENTER_LOG (&finalize_object_begins_ctr, buf,
1818 EVENT_SIZE /* event */ +
1819 LEB128_SIZE /* obj */
1822 emit_event (buf, TYPE_GC_FINALIZE_OBJECT_START | TYPE_GC);
1823 emit_obj (buf, obj);
1825 EXIT_LOG (prof);
1828 static void
1829 finalize_object_end (MonoProfiler *prof, MonoObject *obj)
1831 ENTER_LOG (&finalize_object_ends_ctr, buf,
1832 EVENT_SIZE /* event */ +
1833 LEB128_SIZE /* obj */
1836 emit_event (buf, TYPE_GC_FINALIZE_OBJECT_END | TYPE_GC);
1837 emit_obj (buf, obj);
1839 EXIT_LOG (prof);
1842 static char*
1843 push_nesting (char *p, MonoClass *klass)
1845 MonoClass *nesting;
1846 const char *name;
1847 const char *nspace;
1848 nesting = mono_class_get_nesting_type (klass);
1849 if (nesting) {
1850 p = push_nesting (p, nesting);
1851 *p++ = '/';
1852 *p = 0;
1854 name = mono_class_get_name (klass);
1855 nspace = mono_class_get_namespace (klass);
1856 if (*nspace) {
1857 strcpy (p, nspace);
1858 p += strlen (nspace);
1859 *p++ = '.';
1860 *p = 0;
1862 strcpy (p, name);
1863 p += strlen (name);
1864 return p;
1867 static char*
1868 type_name (MonoClass *klass)
1870 char buf [1024];
1871 char *p;
1872 push_nesting (buf, klass);
1873 p = (char *)malloc (strlen (buf) + 1);
1874 strcpy (p, buf);
1875 return p;
1878 static void
1879 image_loaded (MonoProfiler *prof, MonoImage *image, int result)
1881 if (result != MONO_PROFILE_OK)
1882 return;
1884 const char *name = mono_image_get_filename (image);
1885 int nlen = strlen (name) + 1;
1887 ENTER_LOG (&image_loads_ctr, logbuffer,
1888 EVENT_SIZE /* event */ +
1889 BYTE_SIZE /* type */ +
1890 LEB128_SIZE /* image */ +
1891 nlen /* name */
1894 emit_event (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
1895 emit_byte (logbuffer, TYPE_IMAGE);
1896 emit_ptr (logbuffer, image);
1897 memcpy (logbuffer->cursor, name, nlen);
1898 logbuffer->cursor += nlen;
1900 EXIT_LOG (prof);
1903 static void
1904 image_unloaded (MonoProfiler *prof, MonoImage *image)
1906 const char *name = mono_image_get_filename (image);
1907 int nlen = strlen (name) + 1;
1909 ENTER_LOG (&image_unloads_ctr, logbuffer,
1910 EVENT_SIZE /* event */ +
1911 BYTE_SIZE /* type */ +
1912 LEB128_SIZE /* image */ +
1913 nlen /* name */
1916 emit_event (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
1917 emit_byte (logbuffer, TYPE_IMAGE);
1918 emit_ptr (logbuffer, image);
1919 memcpy (logbuffer->cursor, name, nlen);
1920 logbuffer->cursor += nlen;
1922 EXIT_LOG (prof);
1925 static void
1926 assembly_loaded (MonoProfiler *prof, MonoAssembly *assembly, int result)
1928 if (result != MONO_PROFILE_OK)
1929 return;
1931 char *name = mono_stringify_assembly_name (mono_assembly_get_name (assembly));
1932 int nlen = strlen (name) + 1;
1934 ENTER_LOG (&assembly_loads_ctr, logbuffer,
1935 EVENT_SIZE /* event */ +
1936 BYTE_SIZE /* type */ +
1937 LEB128_SIZE /* assembly */ +
1938 nlen /* name */
1941 emit_event (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
1942 emit_byte (logbuffer, TYPE_ASSEMBLY);
1943 emit_ptr (logbuffer, assembly);
1944 memcpy (logbuffer->cursor, name, nlen);
1945 logbuffer->cursor += nlen;
1947 EXIT_LOG (prof);
1949 mono_free (name);
1952 static void
1953 assembly_unloaded (MonoProfiler *prof, MonoAssembly *assembly)
1955 char *name = mono_stringify_assembly_name (mono_assembly_get_name (assembly));
1956 int nlen = strlen (name) + 1;
1958 ENTER_LOG (&assembly_unloads_ctr, logbuffer,
1959 EVENT_SIZE /* event */ +
1960 BYTE_SIZE /* type */ +
1961 LEB128_SIZE /* assembly */ +
1962 nlen /* name */
1965 emit_event (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
1966 emit_byte (logbuffer, TYPE_ASSEMBLY);
1967 emit_ptr (logbuffer, assembly);
1968 memcpy (logbuffer->cursor, name, nlen);
1969 logbuffer->cursor += nlen;
1971 EXIT_LOG (prof);
1973 mono_free (name);
1976 static void
1977 class_loaded (MonoProfiler *prof, MonoClass *klass, int result)
1979 if (result != MONO_PROFILE_OK)
1980 return;
1982 char *name;
1984 if (InterlockedRead (&runtime_inited))
1985 name = mono_type_get_name (mono_class_get_type (klass));
1986 else
1987 name = type_name (klass);
1989 int nlen = strlen (name) + 1;
1990 MonoImage *image = mono_class_get_image (klass);
1992 ENTER_LOG (&class_loads_ctr, logbuffer,
1993 EVENT_SIZE /* event */ +
1994 BYTE_SIZE /* type */ +
1995 LEB128_SIZE /* klass */ +
1996 LEB128_SIZE /* image */ +
1997 nlen /* name */
2000 emit_event (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
2001 emit_byte (logbuffer, TYPE_CLASS);
2002 emit_ptr (logbuffer, klass);
2003 emit_ptr (logbuffer, image);
2004 memcpy (logbuffer->cursor, name, nlen);
2005 logbuffer->cursor += nlen;
2007 EXIT_LOG (prof);
2009 if (runtime_inited)
2010 mono_free (name);
2011 else
2012 g_free (name);
2015 static void
2016 class_unloaded (MonoProfiler *prof, MonoClass *klass)
2018 char *name;
2020 if (InterlockedRead (&runtime_inited))
2021 name = mono_type_get_name (mono_class_get_type (klass));
2022 else
2023 name = type_name (klass);
2025 int nlen = strlen (name) + 1;
2026 MonoImage *image = mono_class_get_image (klass);
2028 ENTER_LOG (&class_unloads_ctr, logbuffer,
2029 EVENT_SIZE /* event */ +
2030 BYTE_SIZE /* type */ +
2031 LEB128_SIZE /* klass */ +
2032 LEB128_SIZE /* image */ +
2033 nlen /* name */
2036 emit_event (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
2037 emit_byte (logbuffer, TYPE_CLASS);
2038 emit_ptr (logbuffer, klass);
2039 emit_ptr (logbuffer, image);
2040 memcpy (logbuffer->cursor, name, nlen);
2041 logbuffer->cursor += nlen;
2043 EXIT_LOG (prof);
2045 if (runtime_inited)
2046 mono_free (name);
2047 else
2048 g_free (name);
2051 static void process_method_enter_coverage (MonoProfiler *prof, MonoMethod *method);
2053 static void
2054 method_enter (MonoProfiler *prof, MonoMethod *method)
2056 process_method_enter_coverage (prof, method);
2058 if (PROF_TLS_GET ()->call_depth++ <= max_call_depth) {
2059 ENTER_LOG (&method_entries_ctr, logbuffer,
2060 EVENT_SIZE /* event */ +
2061 LEB128_SIZE /* method */
2064 emit_event (logbuffer, TYPE_ENTER | TYPE_METHOD);
2065 emit_method (prof, logbuffer, method);
2067 EXIT_LOG (prof);
2071 static void
2072 method_leave (MonoProfiler *prof, MonoMethod *method)
2074 if (--PROF_TLS_GET ()->call_depth <= max_call_depth) {
2075 ENTER_LOG (&method_exits_ctr, logbuffer,
2076 EVENT_SIZE /* event */ +
2077 LEB128_SIZE /* method */
2080 emit_event (logbuffer, TYPE_LEAVE | TYPE_METHOD);
2081 emit_method (prof, logbuffer, method);
2083 EXIT_LOG (prof);
2087 static void
2088 method_exc_leave (MonoProfiler *prof, MonoMethod *method)
2090 if (!nocalls && --PROF_TLS_GET ()->call_depth <= max_call_depth) {
2091 ENTER_LOG (&method_exception_exits_ctr, logbuffer,
2092 EVENT_SIZE /* event */ +
2093 LEB128_SIZE /* method */
2096 emit_event (logbuffer, TYPE_EXC_LEAVE | TYPE_METHOD);
2097 emit_method (prof, logbuffer, method);
2099 EXIT_LOG (prof);
2103 static void
2104 method_jitted (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *ji, int result)
2106 if (result != MONO_PROFILE_OK)
2107 return;
2109 register_method_local (prof, method, ji);
2112 static void
2113 code_buffer_new (MonoProfiler *prof, void *buffer, int size, MonoProfilerCodeBufferType type, void *data)
2115 char *name;
2116 int nlen;
2118 if (type == MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE) {
2119 name = (char *) data;
2120 nlen = strlen (name) + 1;
2121 } else {
2122 name = NULL;
2123 nlen = 0;
2126 ENTER_LOG (&code_buffers_ctr, logbuffer,
2127 EVENT_SIZE /* event */ +
2128 BYTE_SIZE /* type */ +
2129 LEB128_SIZE /* buffer */ +
2130 LEB128_SIZE /* size */ +
2131 (name ? (
2132 nlen /* name */
2133 ) : 0)
2136 emit_event (logbuffer, TYPE_JITHELPER | TYPE_RUNTIME);
2137 emit_byte (logbuffer, type);
2138 emit_ptr (logbuffer, buffer);
2139 emit_value (logbuffer, size);
2141 if (name) {
2142 memcpy (logbuffer->cursor, name, nlen);
2143 logbuffer->cursor += nlen;
2146 EXIT_LOG (prof);
2149 static void
2150 throw_exc (MonoProfiler *prof, MonoObject *object)
2152 int do_bt = (nocalls && InterlockedRead (&runtime_inited) && !notraces) ? TYPE_THROW_BT : 0;
2153 FrameData data;
2155 if (do_bt)
2156 collect_bt (&data);
2158 ENTER_LOG (&exception_throws_ctr, logbuffer,
2159 EVENT_SIZE /* event */ +
2160 LEB128_SIZE /* object */ +
2161 (do_bt ? (
2162 LEB128_SIZE /* count */ +
2163 data.count * (
2164 LEB128_SIZE /* method */
2166 ) : 0)
2169 emit_event (logbuffer, do_bt | TYPE_EXCEPTION);
2170 emit_obj (logbuffer, object);
2172 if (do_bt)
2173 emit_bt (prof, logbuffer, &data);
2175 EXIT_LOG (prof);
2178 static void
2179 clause_exc (MonoProfiler *prof, MonoMethod *method, int clause_type, int clause_num)
2181 ENTER_LOG (&exception_clauses_ctr, logbuffer,
2182 EVENT_SIZE /* event */ +
2183 BYTE_SIZE /* clause type */ +
2184 LEB128_SIZE /* clause num */ +
2185 LEB128_SIZE /* method */
2188 emit_event (logbuffer, TYPE_EXCEPTION | TYPE_CLAUSE);
2189 emit_byte (logbuffer, clause_type);
2190 emit_value (logbuffer, clause_num);
2191 emit_method (prof, logbuffer, method);
2193 EXIT_LOG (prof);
2196 static void
2197 monitor_event (MonoProfiler *profiler, MonoObject *object, MonoProfilerMonitorEvent event)
2199 int do_bt = (nocalls && InterlockedRead (&runtime_inited) && !notraces && event == MONO_PROFILER_MONITOR_CONTENTION) ? TYPE_MONITOR_BT : 0;
2200 FrameData data;
2202 if (do_bt)
2203 collect_bt (&data);
2205 gint32 *ctr;
2207 switch (event) {
2208 case MONO_PROFILER_MONITOR_CONTENTION:
2209 ctr = &monitor_contentions_ctr;
2210 break;
2211 case MONO_PROFILER_MONITOR_DONE:
2212 ctr = &monitor_acquisitions_ctr;
2213 break;
2214 case MONO_PROFILER_MONITOR_FAIL:
2215 ctr = &monitor_failures_ctr;
2216 break;
2217 default:
2218 g_assert_not_reached ();
2219 break;
2222 ENTER_LOG (ctr, logbuffer,
2223 EVENT_SIZE /* event */ +
2224 LEB128_SIZE /* object */ +
2225 (do_bt ? (
2226 LEB128_SIZE /* count */ +
2227 data.count * (
2228 LEB128_SIZE /* method */
2230 ) : 0)
2233 emit_event (logbuffer, (event << 4) | do_bt | TYPE_MONITOR);
2234 emit_obj (logbuffer, object);
2236 if (do_bt)
2237 emit_bt (profiler, logbuffer, &data);
2239 EXIT_LOG (profiler);
2242 static void
2243 thread_start (MonoProfiler *prof, uintptr_t tid)
2245 init_thread (TRUE);
2247 ENTER_LOG (&thread_starts_ctr, logbuffer,
2248 EVENT_SIZE /* event */ +
2249 BYTE_SIZE /* type */ +
2250 LEB128_SIZE /* tid */
2253 emit_event (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
2254 emit_byte (logbuffer, TYPE_THREAD);
2255 emit_ptr (logbuffer, (void*) tid);
2257 EXIT_LOG (prof);
2260 static void
2261 thread_end (MonoProfiler *prof, uintptr_t tid)
2263 ENTER_LOG (&thread_ends_ctr, logbuffer,
2264 EVENT_SIZE /* event */ +
2265 BYTE_SIZE /* type */ +
2266 LEB128_SIZE /* tid */
2269 emit_event (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
2270 emit_byte (logbuffer, TYPE_THREAD);
2271 emit_ptr (logbuffer, (void*) tid);
2273 EXIT_LOG_EXPLICIT (prof, FALSE, FALSE);
2275 remove_thread (prof, PROF_TLS_GET (), TRUE);
2278 static void
2279 thread_name (MonoProfiler *prof, uintptr_t tid, const char *name)
2281 int len = strlen (name) + 1;
2283 ENTER_LOG (&thread_names_ctr, logbuffer,
2284 EVENT_SIZE /* event */ +
2285 BYTE_SIZE /* type */ +
2286 LEB128_SIZE /* tid */ +
2287 len /* name */
2290 emit_event (logbuffer, TYPE_METADATA);
2291 emit_byte (logbuffer, TYPE_THREAD);
2292 emit_ptr (logbuffer, (void*)tid);
2293 memcpy (logbuffer->cursor, name, len);
2294 logbuffer->cursor += len;
2296 EXIT_LOG (prof);
2299 static void
2300 domain_loaded (MonoProfiler *prof, MonoDomain *domain, int result)
2302 if (result != MONO_PROFILE_OK)
2303 return;
2305 ENTER_LOG (&domain_loads_ctr, logbuffer,
2306 EVENT_SIZE /* event */ +
2307 BYTE_SIZE /* type */ +
2308 LEB128_SIZE /* domain id */
2311 emit_event (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
2312 emit_byte (logbuffer, TYPE_DOMAIN);
2313 emit_ptr (logbuffer, (void*)(uintptr_t) mono_domain_get_id (domain));
2315 EXIT_LOG (prof);
2318 static void
2319 domain_unloaded (MonoProfiler *prof, MonoDomain *domain)
2321 ENTER_LOG (&domain_unloads_ctr, logbuffer,
2322 EVENT_SIZE /* event */ +
2323 BYTE_SIZE /* type */ +
2324 LEB128_SIZE /* domain id */
2327 emit_event (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
2328 emit_byte (logbuffer, TYPE_DOMAIN);
2329 emit_ptr (logbuffer, (void*)(uintptr_t) mono_domain_get_id (domain));
2331 EXIT_LOG (prof);
2334 static void
2335 domain_name (MonoProfiler *prof, MonoDomain *domain, const char *name)
2337 int nlen = strlen (name) + 1;
2339 ENTER_LOG (&domain_names_ctr, logbuffer,
2340 EVENT_SIZE /* event */ +
2341 BYTE_SIZE /* type */ +
2342 LEB128_SIZE /* domain id */ +
2343 nlen /* name */
2346 emit_event (logbuffer, TYPE_METADATA);
2347 emit_byte (logbuffer, TYPE_DOMAIN);
2348 emit_ptr (logbuffer, (void*)(uintptr_t) mono_domain_get_id (domain));
2349 memcpy (logbuffer->cursor, name, nlen);
2350 logbuffer->cursor += nlen;
2352 EXIT_LOG (prof);
2355 static void
2356 context_loaded (MonoProfiler *prof, MonoAppContext *context)
2358 ENTER_LOG (&context_loads_ctr, logbuffer,
2359 EVENT_SIZE /* event */ +
2360 BYTE_SIZE /* type */ +
2361 LEB128_SIZE /* context id */ +
2362 LEB128_SIZE /* domain id */
2365 emit_event (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
2366 emit_byte (logbuffer, TYPE_CONTEXT);
2367 emit_ptr (logbuffer, (void*)(uintptr_t) mono_context_get_id (context));
2368 emit_ptr (logbuffer, (void*)(uintptr_t) mono_context_get_domain_id (context));
2370 EXIT_LOG (prof);
2373 static void
2374 context_unloaded (MonoProfiler *prof, MonoAppContext *context)
2376 ENTER_LOG (&context_unloads_ctr, logbuffer,
2377 EVENT_SIZE /* event */ +
2378 BYTE_SIZE /* type */ +
2379 LEB128_SIZE /* context id */ +
2380 LEB128_SIZE /* domain id */
2383 emit_event (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
2384 emit_byte (logbuffer, TYPE_CONTEXT);
2385 emit_ptr (logbuffer, (void*)(uintptr_t) mono_context_get_id (context));
2386 emit_ptr (logbuffer, (void*)(uintptr_t) mono_context_get_domain_id (context));
2388 EXIT_LOG (prof);
2391 typedef struct {
2392 MonoMethod *method;
2393 MonoDomain *domain;
2394 void *base_address;
2395 int offset;
2396 } AsyncFrameInfo;
2398 typedef struct {
2399 MonoLockFreeQueueNode node;
2400 MonoProfiler *prof;
2401 uint64_t time;
2402 uintptr_t tid;
2403 void *ip;
2404 int count;
2405 AsyncFrameInfo frames [MONO_ZERO_LEN_ARRAY];
2406 } SampleHit;
2408 static mono_bool
2409 async_walk_stack (MonoMethod *method, MonoDomain *domain, void *base_address, int offset, void *data)
2411 SampleHit *sample = (SampleHit *) data;
2413 if (sample->count < num_frames) {
2414 int i = sample->count;
2416 sample->frames [i].method = method;
2417 sample->frames [i].domain = domain;
2418 sample->frames [i].base_address = base_address;
2419 sample->frames [i].offset = offset;
2421 sample->count++;
2424 return sample->count == num_frames;
2427 #define SAMPLE_SLOT_SIZE(FRAMES) (sizeof (SampleHit) + sizeof (AsyncFrameInfo) * (FRAMES - MONO_ZERO_LEN_ARRAY))
2428 #define SAMPLE_BLOCK_SIZE (mono_pagesize ())
2430 static void
2431 enqueue_sample_hit (gpointer p)
2433 SampleHit *sample = p;
2435 mono_lock_free_queue_node_unpoison (&sample->node);
2436 mono_lock_free_queue_enqueue (&sample->prof->dumper_queue, &sample->node);
2437 mono_os_sem_post (&sample->prof->dumper_queue_sem);
2440 static void
2441 mono_sample_hit (MonoProfiler *profiler, unsigned char *ip, void *context)
2444 * Please note: We rely on the runtime loading the profiler with
2445 * MONO_DL_EAGER (RTLD_NOW) so that references to runtime functions within
2446 * this function (and its siblings) are resolved when the profiler is
2447 * loaded. Otherwise, we would potentially invoke the dynamic linker when
2448 * invoking runtime functions, which is not async-signal-safe.
2451 if (in_shutdown)
2452 return;
2454 SampleHit *sample = (SampleHit *) mono_lock_free_queue_dequeue (&profiler->sample_reuse_queue);
2456 if (!sample) {
2458 * If we're out of reusable sample events and we're not allowed to
2459 * allocate more, we have no choice but to drop the event.
2461 if (InterlockedRead (&sample_allocations_ctr) >= max_allocated_sample_hits)
2462 return;
2464 sample = mono_lock_free_alloc (&profiler->sample_allocator);
2465 sample->prof = profiler;
2466 mono_lock_free_queue_node_init (&sample->node, TRUE);
2468 InterlockedIncrement (&sample_allocations_ctr);
2471 sample->count = 0;
2472 mono_stack_walk_async_safe (&async_walk_stack, context, sample);
2474 sample->time = current_time ();
2475 sample->tid = thread_id ();
2476 sample->ip = ip;
2478 if (do_debug) {
2479 int len;
2480 char buf [256];
2481 snprintf (buf, sizeof (buf), "hit at %p in thread %p after %llu ms\n", ip, (void *) sample->tid, (unsigned long long int) ((sample->time - profiler->startup_time) / 10000 / 100));
2482 len = strlen (buf);
2483 ign_res (write (2, buf, len));
2486 mono_thread_hazardous_try_free (sample, enqueue_sample_hit);
2489 static uintptr_t *code_pages = 0;
2490 static int num_code_pages = 0;
2491 static int size_code_pages = 0;
2492 #define CPAGE_SHIFT (9)
2493 #define CPAGE_SIZE (1 << CPAGE_SHIFT)
2494 #define CPAGE_MASK (~(CPAGE_SIZE - 1))
2495 #define CPAGE_ADDR(p) ((p) & CPAGE_MASK)
2497 static uintptr_t
2498 add_code_page (uintptr_t *hash, uintptr_t hsize, uintptr_t page)
2500 uintptr_t i;
2501 uintptr_t start_pos;
2502 start_pos = (page >> CPAGE_SHIFT) % hsize;
2503 i = start_pos;
2504 do {
2505 if (hash [i] && CPAGE_ADDR (hash [i]) == CPAGE_ADDR (page)) {
2506 return 0;
2507 } else if (!hash [i]) {
2508 hash [i] = page;
2509 return 1;
2511 /* wrap around */
2512 if (++i == hsize)
2513 i = 0;
2514 } while (i != start_pos);
2515 /* should not happen */
2516 printf ("failed code page store\n");
2517 return 0;
2520 static void
2521 add_code_pointer (uintptr_t ip)
2523 uintptr_t i;
2524 if (num_code_pages * 2 >= size_code_pages) {
2525 uintptr_t *n;
2526 uintptr_t old_size = size_code_pages;
2527 size_code_pages *= 2;
2528 if (size_code_pages == 0)
2529 size_code_pages = 16;
2530 n = (uintptr_t *)calloc (sizeof (uintptr_t) * size_code_pages, 1);
2531 for (i = 0; i < old_size; ++i) {
2532 if (code_pages [i])
2533 add_code_page (n, size_code_pages, code_pages [i]);
2535 if (code_pages)
2536 g_free (code_pages);
2537 code_pages = n;
2539 num_code_pages += add_code_page (code_pages, size_code_pages, ip & CPAGE_MASK);
2542 /* ELF code crashes on some systems. */
2543 //#if defined(HAVE_DL_ITERATE_PHDR) && defined(ELFMAG0)
2544 #if 0
2545 static void
2546 dump_ubin (MonoProfiler *prof, const char *filename, uintptr_t load_addr, uint64_t offset, uintptr_t size)
2548 int len = strlen (filename) + 1;
2550 ENTER_LOG (&sample_ubins_ctr, logbuffer,
2551 EVENT_SIZE /* event */ +
2552 LEB128_SIZE /* load address */ +
2553 LEB128_SIZE /* offset */ +
2554 LEB128_SIZE /* size */ +
2555 nlen /* file name */
2558 emit_event (logbuffer, TYPE_SAMPLE | TYPE_SAMPLE_UBIN);
2559 emit_svalue (logbuffer, load_addr);
2560 emit_uvalue (logbuffer, offset);
2561 emit_uvalue (logbuffer, size);
2562 memcpy (logbuffer->cursor, filename, len);
2563 logbuffer->cursor += len;
2565 EXIT_LOG_EXPLICIT (prof, TRUE, FALSE);
2567 #endif
2569 static void
2570 dump_usym (MonoProfiler *prof, const char *name, uintptr_t value, uintptr_t size)
2572 int len = strlen (name) + 1;
2574 ENTER_LOG (&sample_usyms_ctr, logbuffer,
2575 EVENT_SIZE /* event */ +
2576 LEB128_SIZE /* value */ +
2577 LEB128_SIZE /* size */ +
2578 len /* name */
2581 emit_event (logbuffer, TYPE_SAMPLE | TYPE_SAMPLE_USYM);
2582 emit_ptr (logbuffer, (void*)value);
2583 emit_value (logbuffer, size);
2584 memcpy (logbuffer->cursor, name, len);
2585 logbuffer->cursor += len;
2587 EXIT_LOG_EXPLICIT (prof, TRUE, FALSE);
2590 /* ELF code crashes on some systems. */
2591 //#if defined(ELFMAG0)
2592 #if 0
2594 #if SIZEOF_VOID_P == 4
2595 #define ELF_WSIZE 32
2596 #else
2597 #define ELF_WSIZE 64
2598 #endif
2599 #ifndef ElfW
2600 #define ElfW(type) _ElfW (Elf, ELF_WSIZE, type)
2601 #define _ElfW(e,w,t) _ElfW_1 (e, w, _##t)
2602 #define _ElfW_1(e,w,t) e##w##t
2603 #endif
2605 static void
2606 dump_elf_symbols (MonoProfiler *prof, ElfW(Sym) *symbols, int num_symbols, const char *strtab, void *load_addr)
2608 int i;
2609 for (i = 0; i < num_symbols; ++i) {
2610 const char* sym;
2611 sym = strtab + symbols [i].st_name;
2612 if (!symbols [i].st_name || !symbols [i].st_size || (symbols [i].st_info & 0xf) != STT_FUNC)
2613 continue;
2614 //printf ("symbol %s at %d\n", sym, symbols [i].st_value);
2615 dump_usym (sym, (uintptr_t)load_addr + symbols [i].st_value, symbols [i].st_size);
2619 static int
2620 read_elf_symbols (MonoProfiler *prof, const char *filename, void *load_addr)
2622 int fd, i;
2623 void *data;
2624 struct stat statb;
2625 uint64_t file_size;
2626 ElfW(Ehdr) *header;
2627 ElfW(Shdr) *sheader;
2628 ElfW(Shdr) *shstrtabh;
2629 ElfW(Shdr) *symtabh = NULL;
2630 ElfW(Shdr) *strtabh = NULL;
2631 ElfW(Sym) *symbols = NULL;
2632 const char *strtab;
2633 int num_symbols;
2635 fd = open (filename, O_RDONLY);
2636 if (fd < 0)
2637 return 0;
2638 if (fstat (fd, &statb) != 0) {
2639 close (fd);
2640 return 0;
2642 file_size = statb.st_size;
2643 data = mmap (NULL, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
2644 close (fd);
2645 if (data == MAP_FAILED)
2646 return 0;
2647 header = data;
2648 if (header->e_ident [EI_MAG0] != ELFMAG0 ||
2649 header->e_ident [EI_MAG1] != ELFMAG1 ||
2650 header->e_ident [EI_MAG2] != ELFMAG2 ||
2651 header->e_ident [EI_MAG3] != ELFMAG3 ) {
2652 munmap (data, file_size);
2653 return 0;
2655 sheader = (void*)((char*)data + header->e_shoff);
2656 shstrtabh = (void*)((char*)sheader + (header->e_shentsize * header->e_shstrndx));
2657 strtab = (const char*)data + shstrtabh->sh_offset;
2658 for (i = 0; i < header->e_shnum; ++i) {
2659 //printf ("section header: %d\n", sheader->sh_type);
2660 if (sheader->sh_type == SHT_SYMTAB) {
2661 symtabh = sheader;
2662 strtabh = (void*)((char*)data + header->e_shoff + sheader->sh_link * header->e_shentsize);
2663 /*printf ("symtab section header: %d, .strstr: %d\n", i, sheader->sh_link);*/
2664 break;
2666 sheader = (void*)((char*)sheader + header->e_shentsize);
2668 if (!symtabh || !strtabh) {
2669 munmap (data, file_size);
2670 return 0;
2672 strtab = (const char*)data + strtabh->sh_offset;
2673 num_symbols = symtabh->sh_size / symtabh->sh_entsize;
2674 symbols = (void*)((char*)data + symtabh->sh_offset);
2675 dump_elf_symbols (symbols, num_symbols, strtab, load_addr);
2676 munmap (data, file_size);
2677 return 1;
2679 #endif
2681 /* ELF code crashes on some systems. */
2682 //#if defined(HAVE_DL_ITERATE_PHDR) && defined(ELFMAG0)
2683 #if 0
2684 static int
2685 elf_dl_callback (struct dl_phdr_info *info, size_t size, void *data)
2687 MonoProfiler *prof = data;
2688 char buf [256];
2689 const char *filename;
2690 BinaryObject *obj;
2691 char *a = (void*)info->dlpi_addr;
2692 int i, num_sym;
2693 ElfW(Dyn) *dyn = NULL;
2694 ElfW(Sym) *symtab = NULL;
2695 ElfW(Word) *hash_table = NULL;
2696 ElfW(Ehdr) *header = NULL;
2697 const char* strtab = NULL;
2698 for (obj = prof->binary_objects; obj; obj = obj->next) {
2699 if (obj->addr == a)
2700 return 0;
2702 filename = info->dlpi_name;
2703 if (!filename)
2704 return 0;
2705 if (!info->dlpi_addr && !filename [0]) {
2706 int l = readlink ("/proc/self/exe", buf, sizeof (buf) - 1);
2707 if (l > 0) {
2708 buf [l] = 0;
2709 filename = buf;
2712 obj = g_calloc (sizeof (BinaryObject), 1);
2713 obj->addr = (void*)info->dlpi_addr;
2714 obj->name = pstrdup (filename);
2715 obj->next = prof->binary_objects;
2716 prof->binary_objects = obj;
2717 //printf ("loaded file: %s at %p, segments: %d\n", filename, (void*)info->dlpi_addr, info->dlpi_phnum);
2718 a = NULL;
2719 for (i = 0; i < info->dlpi_phnum; ++i) {
2720 //printf ("segment type %d file offset: %d, size: %d\n", info->dlpi_phdr[i].p_type, info->dlpi_phdr[i].p_offset, info->dlpi_phdr[i].p_memsz);
2721 if (info->dlpi_phdr[i].p_type == PT_LOAD && !header) {
2722 header = (ElfW(Ehdr)*)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr);
2723 if (header->e_ident [EI_MAG0] != ELFMAG0 ||
2724 header->e_ident [EI_MAG1] != ELFMAG1 ||
2725 header->e_ident [EI_MAG2] != ELFMAG2 ||
2726 header->e_ident [EI_MAG3] != ELFMAG3 ) {
2727 header = NULL;
2729 dump_ubin (prof, filename, info->dlpi_addr + info->dlpi_phdr[i].p_vaddr, info->dlpi_phdr[i].p_offset, info->dlpi_phdr[i].p_memsz);
2730 } else if (info->dlpi_phdr[i].p_type == PT_DYNAMIC) {
2731 dyn = (ElfW(Dyn) *)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr);
2734 if (read_elf_symbols (prof, filename, (void*)info->dlpi_addr))
2735 return 0;
2736 if (!info->dlpi_name || !info->dlpi_name[0])
2737 return 0;
2738 if (!dyn)
2739 return 0;
2740 for (i = 0; dyn [i].d_tag != DT_NULL; ++i) {
2741 if (dyn [i].d_tag == DT_SYMTAB) {
2742 if (symtab && do_debug)
2743 printf ("multiple symtabs: %d\n", i);
2744 symtab = (ElfW(Sym) *)(a + dyn [i].d_un.d_ptr);
2745 } else if (dyn [i].d_tag == DT_HASH) {
2746 hash_table = (ElfW(Word) *)(a + dyn [i].d_un.d_ptr);
2747 } else if (dyn [i].d_tag == DT_STRTAB) {
2748 strtab = (const char*)(a + dyn [i].d_un.d_ptr);
2751 if (!hash_table)
2752 return 0;
2753 num_sym = hash_table [1];
2754 dump_elf_symbols (prof, symtab, num_sym, strtab, (void*)info->dlpi_addr);
2755 return 0;
2758 static int
2759 load_binaries (MonoProfiler *prof)
2761 dl_iterate_phdr (elf_dl_callback, prof);
2762 return 1;
2764 #else
2765 static int
2766 load_binaries (MonoProfiler *prof)
2768 return 0;
2770 #endif
2772 static const char*
2773 symbol_for (uintptr_t code)
2775 #ifdef HAVE_DLADDR
2776 void *ip = (void*)code;
2777 Dl_info di;
2778 if (dladdr (ip, &di)) {
2779 if (di.dli_sname)
2780 return di.dli_sname;
2781 } else {
2782 /* char **names;
2783 names = backtrace_symbols (&ip, 1);
2784 if (names) {
2785 const char* p = names [0];
2786 g_free (names);
2787 return p;
2791 #endif
2792 return NULL;
2795 static void
2796 dump_unmanaged_coderefs (MonoProfiler *prof)
2798 int i;
2799 const char* last_symbol;
2800 uintptr_t addr, page_end;
2802 if (load_binaries (prof))
2803 return;
2804 for (i = 0; i < size_code_pages; ++i) {
2805 const char* sym;
2806 if (!code_pages [i] || code_pages [i] & 1)
2807 continue;
2808 last_symbol = NULL;
2809 addr = CPAGE_ADDR (code_pages [i]);
2810 page_end = addr + CPAGE_SIZE;
2811 code_pages [i] |= 1;
2812 /* we dump the symbols for the whole page */
2813 for (; addr < page_end; addr += 16) {
2814 sym = symbol_for (addr);
2815 if (sym && sym == last_symbol)
2816 continue;
2817 last_symbol = sym;
2818 if (!sym)
2819 continue;
2820 dump_usym (prof, sym, addr, 0); /* let's not guess the size */
2821 //printf ("found symbol at %p: %s\n", (void*)addr, sym);
2826 static int
2827 mono_cpu_count (void)
2829 #ifdef PLATFORM_ANDROID
2830 /* Android tries really hard to save power by powering off CPUs on SMP phones which
2831 * means the normal way to query cpu count returns a wrong value with userspace API.
2832 * Instead we use /sys entries to query the actual hardware CPU count.
2834 int count = 0;
2835 char buffer[8] = {'\0'};
2836 int present = open ("/sys/devices/system/cpu/present", O_RDONLY);
2837 /* Format of the /sys entry is a cpulist of indexes which in the case
2838 * of present is always of the form "0-(n-1)" when there is more than
2839 * 1 core, n being the number of CPU cores in the system. Otherwise
2840 * the value is simply 0
2842 if (present != -1 && read (present, (char*)buffer, sizeof (buffer)) > 3)
2843 count = strtol (((char*)buffer) + 2, NULL, 10);
2844 if (present != -1)
2845 close (present);
2846 if (count > 0)
2847 return count + 1;
2848 #endif
2850 #if defined(HOST_ARM) || defined (HOST_ARM64)
2852 /* ARM platforms tries really hard to save power by powering off CPUs on SMP phones which
2853 * means the normal way to query cpu count returns a wrong value with userspace API. */
2855 #ifdef _SC_NPROCESSORS_CONF
2857 int count = sysconf (_SC_NPROCESSORS_CONF);
2858 if (count > 0)
2859 return count;
2861 #endif
2863 #else
2865 #ifdef HAVE_SCHED_GETAFFINITY
2867 cpu_set_t set;
2868 if (sched_getaffinity (getpid (), sizeof (set), &set) == 0)
2869 return CPU_COUNT (&set);
2871 #endif
2872 #ifdef _SC_NPROCESSORS_ONLN
2874 int count = sysconf (_SC_NPROCESSORS_ONLN);
2875 if (count > 0)
2876 return count;
2878 #endif
2880 #endif /* defined(HOST_ARM) || defined (HOST_ARM64) */
2882 #ifdef USE_SYSCTL
2884 int count;
2885 int mib [2];
2886 size_t len = sizeof (int);
2887 mib [0] = CTL_HW;
2888 mib [1] = HW_NCPU;
2889 if (sysctl (mib, 2, &count, &len, NULL, 0) == 0)
2890 return count;
2892 #endif
2893 #ifdef HOST_WIN32
2895 SYSTEM_INFO info;
2896 GetSystemInfo (&info);
2897 return info.dwNumberOfProcessors;
2899 #endif
2900 /* FIXME: warn */
2901 return 1;
2904 #if USE_PERF_EVENTS
2906 typedef struct {
2907 int perf_fd;
2908 unsigned int prev_pos;
2909 void *mmap_base;
2910 struct perf_event_mmap_page *page_desc;
2911 } PerfData ;
2913 static PerfData *perf_data = NULL;
2914 static int num_perf;
2915 #define PERF_PAGES_SHIFT 4
2916 static int num_pages = 1 << PERF_PAGES_SHIFT;
2917 static unsigned int mmap_mask;
2919 typedef struct {
2920 struct perf_event_header h;
2921 uint64_t ip;
2922 uint32_t pid;
2923 uint32_t tid;
2924 uint64_t timestamp;
2925 uint64_t period;
2926 uint64_t nframes;
2927 } PSample;
2929 static int
2930 perf_event_syscall (struct perf_event_attr *attr, pid_t pid, int cpu, int group_fd, unsigned long flags)
2932 attr->size = PERF_ATTR_SIZE_VER0;
2933 //printf ("perf attr size: %d\n", attr->size);
2934 #if defined(__x86_64__)
2935 return syscall(/*__NR_perf_event_open*/ 298, attr, pid, cpu, group_fd, flags);
2936 #elif defined(__i386__)
2937 return syscall(/*__NR_perf_event_open*/ 336, attr, pid, cpu, group_fd, flags);
2938 #elif defined(__arm__) || defined (__aarch64__)
2939 return syscall(/*__NR_perf_event_open*/ 364, attr, pid, cpu, group_fd, flags);
2940 #else
2941 return -1;
2942 #endif
2945 static int
2946 setup_perf_map (PerfData *perf)
2948 perf->mmap_base = mmap (NULL, (num_pages + 1) * getpagesize (), PROT_READ|PROT_WRITE, MAP_SHARED, perf->perf_fd, 0);
2949 if (perf->mmap_base == MAP_FAILED) {
2950 if (do_debug)
2951 printf ("failed mmap\n");
2952 return 0;
2954 perf->page_desc = perf->mmap_base;
2955 if (do_debug)
2956 printf ("mmap version: %d\n", perf->page_desc->version);
2957 return 1;
2960 static void
2961 dump_perf_hits (MonoProfiler *prof, void *buf, int size)
2963 int count = 1;
2964 int mbt_count = 0;
2965 void *end = (char*)buf + size;
2966 int samples = 0;
2967 int pid = getpid ();
2969 while (buf < end) {
2970 PSample *s = buf;
2971 if (s->h.size == 0)
2972 break;
2973 if (pid != s->pid) {
2974 if (do_debug)
2975 printf ("event for different pid: %d\n", s->pid);
2976 buf = (char*)buf + s->h.size;
2977 continue;
2979 /*ip = (void*)s->ip;
2980 printf ("sample: %d, size: %d, ip: %p (%s), timestamp: %llu, nframes: %llu\n",
2981 s->h.type, s->h.size, ip, symbol_for (ip), s->timestamp, s->nframes);*/
2983 InterlockedIncrement (&sample_hits_ctr);
2985 ENTER_LOG (&sample_hits_ctr, logbuffer,
2986 EVENT_SIZE /* event */ +
2987 BYTE_SIZE /* type */ +
2988 LEB128_SIZE /* tid */ +
2989 LEB128_SIZE /* count */ +
2990 count * (
2991 LEB128_SIZE /* ip */
2993 LEB128_SIZE /* managed count */ +
2994 mbt_count * (
2995 LEB128_SIZE /* method */
2999 emit_event (logbuffer, TYPE_SAMPLE | TYPE_SAMPLE_HIT);
3000 emit_byte (logbuffer, sample_type);
3002 * No useful thread ID to write here, since throughout the
3003 * profiler we use pthread_self () but the ID we get from
3004 * perf is the kernel's thread ID.
3006 emit_ptr (logbuffer, 0);
3007 emit_value (logbuffer, count);
3008 emit_ptr (logbuffer, (void*)(uintptr_t)s->ip);
3009 /* no support here yet for the managed backtrace */
3010 emit_uvalue (logbuffer, mbt_count);
3012 EXIT_LOG_EXPLICIT (prof, TRUE, FALSE);
3014 add_code_pointer (s->ip);
3015 buf = (char*)buf + s->h.size;
3016 samples++;
3018 if (do_debug)
3019 printf ("dumped %d samples\n", samples);
3020 dump_unmanaged_coderefs (prof);
3023 /* read events from the ring buffer */
3024 static int
3025 read_perf_mmap (MonoProfiler* prof, int cpu)
3027 PerfData *perf = perf_data + cpu;
3028 unsigned char *buf;
3029 unsigned char *data = (unsigned char*)perf->mmap_base + getpagesize ();
3030 unsigned int head = perf->page_desc->data_head;
3031 int diff, size;
3032 unsigned int old;
3034 mono_memory_read_barrier ();
3036 old = perf->prev_pos;
3037 diff = head - old;
3038 if (diff < 0) {
3039 if (do_debug)
3040 printf ("lost mmap events: old: %d, head: %d\n", old, head);
3041 old = head;
3043 size = head - old;
3044 if ((old & mmap_mask) + size != (head & mmap_mask)) {
3045 buf = data + (old & mmap_mask);
3046 size = mmap_mask + 1 - (old & mmap_mask);
3047 old += size;
3048 /* size bytes at buf */
3049 if (do_debug)
3050 printf ("found1 bytes of events: %d\n", size);
3051 dump_perf_hits (prof, buf, size);
3053 buf = data + (old & mmap_mask);
3054 size = head - old;
3055 /* size bytes at buf */
3056 if (do_debug)
3057 printf ("found bytes of events: %d\n", size);
3058 dump_perf_hits (prof, buf, size);
3059 old += size;
3060 perf->prev_pos = old;
3061 perf->page_desc->data_tail = old;
3062 return 0;
3065 static int
3066 setup_perf_event_for_cpu (PerfData *perf, int cpu)
3068 struct perf_event_attr attr;
3069 memset (&attr, 0, sizeof (attr));
3070 attr.type = PERF_TYPE_HARDWARE;
3071 switch (sample_type) {
3072 case SAMPLE_CYCLES: attr.config = PERF_COUNT_HW_CPU_CYCLES; break;
3073 case SAMPLE_INSTRUCTIONS: attr.config = PERF_COUNT_HW_INSTRUCTIONS; break;
3074 case SAMPLE_CACHE_MISSES: attr.config = PERF_COUNT_HW_CACHE_MISSES; break;
3075 case SAMPLE_CACHE_REFS: attr.config = PERF_COUNT_HW_CACHE_REFERENCES; break;
3076 case SAMPLE_BRANCHES: attr.config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS; break;
3077 case SAMPLE_BRANCH_MISSES: attr.config = PERF_COUNT_HW_BRANCH_MISSES; break;
3078 default: attr.config = PERF_COUNT_HW_CPU_CYCLES; break;
3080 attr.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_PERIOD | PERF_SAMPLE_TIME;
3081 // attr.sample_type |= PERF_SAMPLE_CALLCHAIN;
3082 attr.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING | PERF_FORMAT_ID;
3083 attr.inherit = 1;
3084 attr.freq = 1;
3085 attr.sample_freq = sample_freq;
3087 perf->perf_fd = perf_event_syscall (&attr, getpid (), cpu, -1, 0);
3088 if (do_debug)
3089 printf ("perf fd: %d, freq: %d, event: %llu\n", perf->perf_fd, sample_freq, attr.config);
3090 if (perf->perf_fd < 0) {
3091 if (perf->perf_fd == -EPERM) {
3092 fprintf (stderr, "Perf syscall denied, do \"echo 1 > /proc/sys/kernel/perf_event_paranoid\" as root to enable.\n");
3093 } else {
3094 if (do_debug)
3095 perror ("open perf event");
3097 return 0;
3099 if (!setup_perf_map (perf)) {
3100 close (perf->perf_fd);
3101 perf->perf_fd = -1;
3102 return 0;
3104 return 1;
3107 static int
3108 setup_perf_event (void)
3110 int i, count = 0;
3111 mmap_mask = num_pages * getpagesize () - 1;
3112 num_perf = mono_cpu_count ();
3113 perf_data = g_calloc (num_perf, sizeof (PerfData));
3114 for (i = 0; i < num_perf; ++i) {
3115 count += setup_perf_event_for_cpu (perf_data + i, i);
3117 if (count)
3118 return 1;
3119 g_free (perf_data);
3120 perf_data = NULL;
3121 return 0;
3124 #endif /* USE_PERF_EVENTS */
3126 typedef struct MonoCounterAgent {
3127 MonoCounter *counter;
3128 // MonoCounterAgent specific data :
3129 void *value;
3130 size_t value_size;
3131 short index;
3132 short emitted;
3133 struct MonoCounterAgent *next;
3134 } MonoCounterAgent;
3136 static MonoCounterAgent* counters;
3137 static int counters_index = 1;
3138 static mono_mutex_t counters_mutex;
3140 static void
3141 counters_add_agent (MonoCounter *counter)
3143 if (in_shutdown)
3144 return;
3146 MonoCounterAgent *agent, *item;
3148 mono_os_mutex_lock (&counters_mutex);
3150 for (agent = counters; agent; agent = agent->next) {
3151 if (agent->counter == counter) {
3152 agent->value_size = 0;
3153 if (agent->value) {
3154 g_free (agent->value);
3155 agent->value = NULL;
3157 goto done;
3161 agent = (MonoCounterAgent *)malloc (sizeof (MonoCounterAgent));
3162 agent->counter = counter;
3163 agent->value = NULL;
3164 agent->value_size = 0;
3165 agent->index = counters_index++;
3166 agent->emitted = 0;
3167 agent->next = NULL;
3169 if (!counters) {
3170 counters = agent;
3171 } else {
3172 item = counters;
3173 while (item->next)
3174 item = item->next;
3175 item->next = agent;
3178 done:
3179 mono_os_mutex_unlock (&counters_mutex);
3182 static mono_bool
3183 counters_init_foreach_callback (MonoCounter *counter, gpointer data)
3185 counters_add_agent (counter);
3186 return TRUE;
3189 static void
3190 counters_init (MonoProfiler *profiler)
3192 mono_os_mutex_init (&counters_mutex);
3194 mono_counters_on_register (&counters_add_agent);
3195 mono_counters_foreach (counters_init_foreach_callback, NULL);
3198 static void
3199 counters_emit (MonoProfiler *profiler)
3201 MonoCounterAgent *agent;
3202 int len = 0;
3203 int size =
3204 EVENT_SIZE /* event */ +
3205 LEB128_SIZE /* len */
3208 mono_os_mutex_lock (&counters_mutex);
3210 for (agent = counters; agent; agent = agent->next) {
3211 if (agent->emitted)
3212 continue;
3214 size +=
3215 LEB128_SIZE /* section */ +
3216 strlen (mono_counter_get_name (agent->counter)) + 1 /* name */ +
3217 BYTE_SIZE /* type */ +
3218 BYTE_SIZE /* unit */ +
3219 BYTE_SIZE /* variance */ +
3220 LEB128_SIZE /* index */
3223 len++;
3226 if (!len)
3227 goto done;
3229 ENTER_LOG (&counter_descriptors_ctr, logbuffer, size);
3231 emit_event (logbuffer, TYPE_SAMPLE_COUNTERS_DESC | TYPE_SAMPLE);
3232 emit_value (logbuffer, len);
3234 for (agent = counters; agent; agent = agent->next) {
3235 const char *name;
3237 if (agent->emitted)
3238 continue;
3240 name = mono_counter_get_name (agent->counter);
3241 emit_value (logbuffer, mono_counter_get_section (agent->counter));
3242 emit_string (logbuffer, name, strlen (name) + 1);
3243 emit_byte (logbuffer, mono_counter_get_type (agent->counter));
3244 emit_byte (logbuffer, mono_counter_get_unit (agent->counter));
3245 emit_byte (logbuffer, mono_counter_get_variance (agent->counter));
3246 emit_value (logbuffer, agent->index);
3248 agent->emitted = 1;
3251 EXIT_LOG_EXPLICIT (profiler, TRUE, FALSE);
3253 done:
3254 mono_os_mutex_unlock (&counters_mutex);
3257 static void
3258 counters_sample (MonoProfiler *profiler, uint64_t timestamp)
3260 MonoCounterAgent *agent;
3261 MonoCounter *counter;
3262 int type;
3263 int buffer_size;
3264 void *buffer;
3265 int size;
3267 counters_emit (profiler);
3269 buffer_size = 8;
3270 buffer = g_calloc (1, buffer_size);
3272 mono_os_mutex_lock (&counters_mutex);
3274 size =
3275 EVENT_SIZE /* event */
3278 for (agent = counters; agent; agent = agent->next) {
3279 size +=
3280 LEB128_SIZE /* index */ +
3281 BYTE_SIZE /* type */ +
3282 mono_counter_get_size (agent->counter) /* value */
3286 size +=
3287 LEB128_SIZE /* stop marker */
3290 ENTER_LOG (&counter_samples_ctr, logbuffer, size);
3292 emit_event_time (logbuffer, TYPE_SAMPLE_COUNTERS | TYPE_SAMPLE, timestamp);
3294 for (agent = counters; agent; agent = agent->next) {
3295 size_t size;
3297 counter = agent->counter;
3299 size = mono_counter_get_size (counter);
3300 if (size < 0) {
3301 continue; // FIXME error
3302 } else if (size > buffer_size) {
3303 buffer_size = size;
3304 buffer = g_realloc (buffer, buffer_size);
3307 memset (buffer, 0, buffer_size);
3309 if (mono_counters_sample (counter, buffer, size) < 0)
3310 continue; // FIXME error
3312 type = mono_counter_get_type (counter);
3314 if (!agent->value) {
3315 agent->value = g_calloc (1, size);
3316 agent->value_size = size;
3317 } else {
3318 if (type == MONO_COUNTER_STRING) {
3319 if (strcmp (agent->value, buffer) == 0)
3320 continue;
3321 } else {
3322 if (agent->value_size == size && memcmp (agent->value, buffer, size) == 0)
3323 continue;
3327 emit_uvalue (logbuffer, agent->index);
3328 emit_byte (logbuffer, type);
3329 switch (type) {
3330 case MONO_COUNTER_INT:
3331 #if SIZEOF_VOID_P == 4
3332 case MONO_COUNTER_WORD:
3333 #endif
3334 emit_svalue (logbuffer, *(int*)buffer - *(int*)agent->value);
3335 break;
3336 case MONO_COUNTER_UINT:
3337 emit_uvalue (logbuffer, *(guint*)buffer - *(guint*)agent->value);
3338 break;
3339 case MONO_COUNTER_TIME_INTERVAL:
3340 case MONO_COUNTER_LONG:
3341 #if SIZEOF_VOID_P == 8
3342 case MONO_COUNTER_WORD:
3343 #endif
3344 emit_svalue (logbuffer, *(gint64*)buffer - *(gint64*)agent->value);
3345 break;
3346 case MONO_COUNTER_ULONG:
3347 emit_uvalue (logbuffer, *(guint64*)buffer - *(guint64*)agent->value);
3348 break;
3349 case MONO_COUNTER_DOUBLE:
3350 emit_double (logbuffer, *(double*)buffer);
3351 break;
3352 case MONO_COUNTER_STRING:
3353 if (size == 0) {
3354 emit_byte (logbuffer, 0);
3355 } else {
3356 emit_byte (logbuffer, 1);
3357 emit_string (logbuffer, (char*)buffer, size);
3359 break;
3360 default:
3361 assert (0);
3364 if (type == MONO_COUNTER_STRING && size > agent->value_size) {
3365 agent->value = g_realloc (agent->value, size);
3366 agent->value_size = size;
3369 if (size > 0)
3370 memcpy (agent->value, buffer, size);
3372 g_free (buffer);
3374 emit_value (logbuffer, 0);
3376 EXIT_LOG_EXPLICIT (profiler, TRUE, FALSE);
3378 mono_os_mutex_unlock (&counters_mutex);
3381 typedef struct _PerfCounterAgent PerfCounterAgent;
3382 struct _PerfCounterAgent {
3383 PerfCounterAgent *next;
3384 int index;
3385 char *category_name;
3386 char *name;
3387 int type;
3388 gint64 value;
3389 guint8 emitted;
3390 guint8 updated;
3391 guint8 deleted;
3394 static PerfCounterAgent *perfcounters = NULL;
3396 static void
3397 perfcounters_emit (MonoProfiler *profiler)
3399 PerfCounterAgent *pcagent;
3400 int len = 0;
3401 int size =
3402 EVENT_SIZE /* event */ +
3403 LEB128_SIZE /* len */
3406 for (pcagent = perfcounters; pcagent; pcagent = pcagent->next) {
3407 if (pcagent->emitted)
3408 continue;
3410 size +=
3411 LEB128_SIZE /* section */ +
3412 strlen (pcagent->category_name) + 1 /* category name */ +
3413 strlen (pcagent->name) + 1 /* name */ +
3414 BYTE_SIZE /* type */ +
3415 BYTE_SIZE /* unit */ +
3416 BYTE_SIZE /* variance */ +
3417 LEB128_SIZE /* index */
3420 len++;
3423 if (!len)
3424 return;
3426 ENTER_LOG (&perfcounter_descriptors_ctr, logbuffer, size);
3428 emit_event (logbuffer, TYPE_SAMPLE_COUNTERS_DESC | TYPE_SAMPLE);
3429 emit_value (logbuffer, len);
3431 for (pcagent = perfcounters; pcagent; pcagent = pcagent->next) {
3432 if (pcagent->emitted)
3433 continue;
3435 emit_value (logbuffer, MONO_COUNTER_PERFCOUNTERS);
3436 emit_string (logbuffer, pcagent->category_name, strlen (pcagent->category_name) + 1);
3437 emit_string (logbuffer, pcagent->name, strlen (pcagent->name) + 1);
3438 emit_byte (logbuffer, MONO_COUNTER_LONG);
3439 emit_byte (logbuffer, MONO_COUNTER_RAW);
3440 emit_byte (logbuffer, MONO_COUNTER_VARIABLE);
3441 emit_value (logbuffer, pcagent->index);
3443 pcagent->emitted = 1;
3446 EXIT_LOG_EXPLICIT (profiler, TRUE, FALSE);
3449 static gboolean
3450 perfcounters_foreach (char *category_name, char *name, unsigned char type, gint64 value, gpointer user_data)
3452 PerfCounterAgent *pcagent;
3454 for (pcagent = perfcounters; pcagent; pcagent = pcagent->next) {
3455 if (strcmp (pcagent->category_name, category_name) != 0 || strcmp (pcagent->name, name) != 0)
3456 continue;
3457 if (pcagent->value == value)
3458 return TRUE;
3460 pcagent->value = value;
3461 pcagent->updated = 1;
3462 pcagent->deleted = 0;
3463 return TRUE;
3466 pcagent = g_new0 (PerfCounterAgent, 1);
3467 pcagent->next = perfcounters;
3468 pcagent->index = counters_index++;
3469 pcagent->category_name = g_strdup (category_name);
3470 pcagent->name = g_strdup (name);
3471 pcagent->type = (int) type;
3472 pcagent->value = value;
3473 pcagent->emitted = 0;
3474 pcagent->updated = 1;
3475 pcagent->deleted = 0;
3477 perfcounters = pcagent;
3479 return TRUE;
3482 static void
3483 perfcounters_sample (MonoProfiler *profiler, uint64_t timestamp)
3485 PerfCounterAgent *pcagent;
3486 int len = 0;
3487 int size;
3489 mono_os_mutex_lock (&counters_mutex);
3491 /* mark all perfcounters as deleted, foreach will unmark them as necessary */
3492 for (pcagent = perfcounters; pcagent; pcagent = pcagent->next)
3493 pcagent->deleted = 1;
3495 mono_perfcounter_foreach (perfcounters_foreach, perfcounters);
3497 perfcounters_emit (profiler);
3499 size =
3500 EVENT_SIZE /* event */
3503 for (pcagent = perfcounters; pcagent; pcagent = pcagent->next) {
3504 if (pcagent->deleted || !pcagent->updated)
3505 continue;
3507 size +=
3508 LEB128_SIZE /* index */ +
3509 BYTE_SIZE /* type */ +
3510 LEB128_SIZE /* value */
3513 len++;
3516 if (!len)
3517 goto done;
3519 size +=
3520 LEB128_SIZE /* stop marker */
3523 ENTER_LOG (&perfcounter_samples_ctr, logbuffer, size);
3525 emit_event_time (logbuffer, TYPE_SAMPLE_COUNTERS | TYPE_SAMPLE, timestamp);
3527 for (pcagent = perfcounters; pcagent; pcagent = pcagent->next) {
3528 if (pcagent->deleted || !pcagent->updated)
3529 continue;
3530 emit_uvalue (logbuffer, pcagent->index);
3531 emit_byte (logbuffer, MONO_COUNTER_LONG);
3532 emit_svalue (logbuffer, pcagent->value);
3534 pcagent->updated = 0;
3537 emit_value (logbuffer, 0);
3539 EXIT_LOG_EXPLICIT (profiler, TRUE, FALSE);
3541 done:
3542 mono_os_mutex_unlock (&counters_mutex);
3545 static void
3546 counters_and_perfcounters_sample (MonoProfiler *prof)
3548 uint64_t now = current_time ();
3550 counters_sample (prof, now);
3551 perfcounters_sample (prof, now);
3554 #define COVERAGE_DEBUG(x) if (debug_coverage) {x}
3555 static mono_mutex_t coverage_mutex;
3556 static MonoConcurrentHashTable *coverage_methods = NULL;
3557 static MonoConcurrentHashTable *coverage_assemblies = NULL;
3558 static MonoConcurrentHashTable *coverage_classes = NULL;
3560 static MonoConcurrentHashTable *filtered_classes = NULL;
3561 static MonoConcurrentHashTable *entered_methods = NULL;
3562 static MonoConcurrentHashTable *image_to_methods = NULL;
3563 static MonoConcurrentHashTable *suppressed_assemblies = NULL;
3564 static gboolean coverage_initialized = FALSE;
3566 static GPtrArray *coverage_data = NULL;
3567 static int previous_offset = 0;
3569 typedef struct {
3570 MonoLockFreeQueueNode node;
3571 MonoMethod *method;
3572 } MethodNode;
3574 typedef struct {
3575 int offset;
3576 int counter;
3577 char *filename;
3578 int line;
3579 int column;
3580 } CoverageEntry;
3582 static void
3583 free_coverage_entry (gpointer data, gpointer userdata)
3585 CoverageEntry *entry = (CoverageEntry *)data;
3586 g_free (entry->filename);
3587 g_free (entry);
3590 static void
3591 obtain_coverage_for_method (MonoProfiler *prof, const MonoProfileCoverageEntry *entry)
3593 int offset = entry->iloffset - previous_offset;
3594 CoverageEntry *e = g_new (CoverageEntry, 1);
3596 previous_offset = entry->iloffset;
3598 e->offset = offset;
3599 e->counter = entry->counter;
3600 e->filename = g_strdup(entry->filename ? entry->filename : "");
3601 e->line = entry->line;
3602 e->column = entry->col;
3604 g_ptr_array_add (coverage_data, e);
3607 static char *
3608 parse_generic_type_names(char *name)
3610 char *new_name, *ret;
3611 int within_generic_declaration = 0, generic_members = 1;
3613 if (name == NULL || *name == '\0')
3614 return g_strdup ("");
3616 if (!(ret = new_name = (char *)calloc (strlen (name) * 4 + 1, sizeof (char))))
3617 return NULL;
3619 do {
3620 switch (*name) {
3621 case '<':
3622 within_generic_declaration = 1;
3623 break;
3625 case '>':
3626 within_generic_declaration = 0;
3628 if (*(name - 1) != '<') {
3629 *new_name++ = '`';
3630 *new_name++ = '0' + generic_members;
3631 } else {
3632 memcpy (new_name, "&lt;&gt;", 8);
3633 new_name += 8;
3636 generic_members = 0;
3637 break;
3639 case ',':
3640 generic_members++;
3641 break;
3643 default:
3644 if (!within_generic_declaration)
3645 *new_name++ = *name;
3647 break;
3649 } while (*name++);
3651 return ret;
3654 static int method_id;
3655 static void
3656 build_method_buffer (gpointer key, gpointer value, gpointer userdata)
3658 MonoMethod *method = (MonoMethod *)value;
3659 MonoProfiler *prof = (MonoProfiler *)userdata;
3660 MonoClass *klass;
3661 MonoImage *image;
3662 char *class_name;
3663 const char *image_name, *method_name, *sig, *first_filename;
3664 guint i;
3666 previous_offset = 0;
3667 coverage_data = g_ptr_array_new ();
3669 mono_profiler_coverage_get (prof, method, obtain_coverage_for_method);
3671 klass = mono_method_get_class (method);
3672 image = mono_class_get_image (klass);
3673 image_name = mono_image_get_name (image);
3675 sig = mono_signature_get_desc (mono_method_signature (method), TRUE);
3676 class_name = parse_generic_type_names (mono_type_get_name (mono_class_get_type (klass)));
3677 method_name = mono_method_get_name (method);
3679 if (coverage_data->len != 0) {
3680 CoverageEntry *entry = (CoverageEntry *)coverage_data->pdata[0];
3681 first_filename = entry->filename ? entry->filename : "";
3682 } else
3683 first_filename = "";
3685 image_name = image_name ? image_name : "";
3686 sig = sig ? sig : "";
3687 method_name = method_name ? method_name : "";
3689 ENTER_LOG (&coverage_methods_ctr, logbuffer,
3690 EVENT_SIZE /* event */ +
3691 strlen (image_name) + 1 /* image name */ +
3692 strlen (class_name) + 1 /* class name */ +
3693 strlen (method_name) + 1 /* method name */ +
3694 strlen (sig) + 1 /* signature */ +
3695 strlen (first_filename) + 1 /* first file name */ +
3696 LEB128_SIZE /* token */ +
3697 LEB128_SIZE /* method id */ +
3698 LEB128_SIZE /* entries */
3701 emit_event (logbuffer, TYPE_COVERAGE_METHOD | TYPE_COVERAGE);
3702 emit_string (logbuffer, image_name, strlen (image_name) + 1);
3703 emit_string (logbuffer, class_name, strlen (class_name) + 1);
3704 emit_string (logbuffer, method_name, strlen (method_name) + 1);
3705 emit_string (logbuffer, sig, strlen (sig) + 1);
3706 emit_string (logbuffer, first_filename, strlen (first_filename) + 1);
3708 emit_uvalue (logbuffer, mono_method_get_token (method));
3709 emit_uvalue (logbuffer, method_id);
3710 emit_value (logbuffer, coverage_data->len);
3712 EXIT_LOG_EXPLICIT (prof, TRUE, FALSE);
3714 for (i = 0; i < coverage_data->len; i++) {
3715 CoverageEntry *entry = (CoverageEntry *)coverage_data->pdata[i];
3717 ENTER_LOG (&coverage_statements_ctr, logbuffer,
3718 EVENT_SIZE /* event */ +
3719 LEB128_SIZE /* method id */ +
3720 LEB128_SIZE /* offset */ +
3721 LEB128_SIZE /* counter */ +
3722 LEB128_SIZE /* line */ +
3723 LEB128_SIZE /* column */
3726 emit_event (logbuffer, TYPE_COVERAGE_STATEMENT | TYPE_COVERAGE);
3727 emit_uvalue (logbuffer, method_id);
3728 emit_uvalue (logbuffer, entry->offset);
3729 emit_uvalue (logbuffer, entry->counter);
3730 emit_uvalue (logbuffer, entry->line);
3731 emit_uvalue (logbuffer, entry->column);
3733 EXIT_LOG_EXPLICIT (prof, TRUE, FALSE);
3736 method_id++;
3738 g_free (class_name);
3740 g_ptr_array_foreach (coverage_data, free_coverage_entry, NULL);
3741 g_ptr_array_free (coverage_data, TRUE);
3742 coverage_data = NULL;
3745 /* This empties the queue */
3746 static guint
3747 count_queue (MonoLockFreeQueue *queue)
3749 MonoLockFreeQueueNode *node;
3750 guint count = 0;
3752 while ((node = mono_lock_free_queue_dequeue (queue))) {
3753 count++;
3754 mono_thread_hazardous_try_free (node, free);
3757 return count;
3760 static void
3761 build_class_buffer (gpointer key, gpointer value, gpointer userdata)
3763 MonoClass *klass = (MonoClass *)key;
3764 MonoLockFreeQueue *class_methods = (MonoLockFreeQueue *)value;
3765 MonoProfiler *prof = (MonoProfiler *)userdata;
3766 MonoImage *image;
3767 char *class_name;
3768 const char *assembly_name;
3769 int number_of_methods, partially_covered;
3770 guint fully_covered;
3772 image = mono_class_get_image (klass);
3773 assembly_name = mono_image_get_name (image);
3774 class_name = mono_type_get_name (mono_class_get_type (klass));
3776 assembly_name = assembly_name ? assembly_name : "";
3777 number_of_methods = mono_class_num_methods (klass);
3778 fully_covered = count_queue (class_methods);
3779 /* We don't handle partial covered yet */
3780 partially_covered = 0;
3782 ENTER_LOG (&coverage_classes_ctr, logbuffer,
3783 EVENT_SIZE /* event */ +
3784 strlen (assembly_name) + 1 /* assembly name */ +
3785 strlen (class_name) + 1 /* class name */ +
3786 LEB128_SIZE /* no. methods */ +
3787 LEB128_SIZE /* fully covered */ +
3788 LEB128_SIZE /* partially covered */
3791 emit_event (logbuffer, TYPE_COVERAGE_CLASS | TYPE_COVERAGE);
3792 emit_string (logbuffer, assembly_name, strlen (assembly_name) + 1);
3793 emit_string (logbuffer, class_name, strlen (class_name) + 1);
3794 emit_uvalue (logbuffer, number_of_methods);
3795 emit_uvalue (logbuffer, fully_covered);
3796 emit_uvalue (logbuffer, partially_covered);
3798 EXIT_LOG_EXPLICIT (prof, TRUE, FALSE);
3800 g_free (class_name);
3803 static void
3804 get_coverage_for_image (MonoImage *image, int *number_of_methods, guint *fully_covered, int *partially_covered)
3806 MonoLockFreeQueue *image_methods = (MonoLockFreeQueue *)mono_conc_hashtable_lookup (image_to_methods, image);
3808 *number_of_methods = mono_image_get_table_rows (image, MONO_TABLE_METHOD);
3809 if (image_methods)
3810 *fully_covered = count_queue (image_methods);
3811 else
3812 *fully_covered = 0;
3814 // FIXME: We don't handle partially covered yet.
3815 *partially_covered = 0;
3818 static void
3819 build_assembly_buffer (gpointer key, gpointer value, gpointer userdata)
3821 MonoAssembly *assembly = (MonoAssembly *)value;
3822 MonoProfiler *prof = (MonoProfiler *)userdata;
3823 MonoImage *image = mono_assembly_get_image (assembly);
3824 const char *name, *guid, *filename;
3825 int number_of_methods = 0, partially_covered = 0;
3826 guint fully_covered = 0;
3828 name = mono_image_get_name (image);
3829 guid = mono_image_get_guid (image);
3830 filename = mono_image_get_filename (image);
3832 name = name ? name : "";
3833 guid = guid ? guid : "";
3834 filename = filename ? filename : "";
3836 get_coverage_for_image (image, &number_of_methods, &fully_covered, &partially_covered);
3838 ENTER_LOG (&coverage_assemblies_ctr, logbuffer,
3839 EVENT_SIZE /* event */ +
3840 strlen (name) + 1 /* name */ +
3841 strlen (guid) + 1 /* guid */ +
3842 strlen (filename) + 1 /* file name */ +
3843 LEB128_SIZE /* no. methods */ +
3844 LEB128_SIZE /* fully covered */ +
3845 LEB128_SIZE /* partially covered */
3848 emit_event (logbuffer, TYPE_COVERAGE_ASSEMBLY | TYPE_COVERAGE);
3849 emit_string (logbuffer, name, strlen (name) + 1);
3850 emit_string (logbuffer, guid, strlen (guid) + 1);
3851 emit_string (logbuffer, filename, strlen (filename) + 1);
3852 emit_uvalue (logbuffer, number_of_methods);
3853 emit_uvalue (logbuffer, fully_covered);
3854 emit_uvalue (logbuffer, partially_covered);
3856 EXIT_LOG_EXPLICIT (prof, TRUE, FALSE);
3859 static void
3860 dump_coverage (MonoProfiler *prof)
3862 if (!coverage_initialized)
3863 return;
3865 COVERAGE_DEBUG(fprintf (stderr, "Coverage: Started dump\n");)
3866 method_id = 0;
3868 mono_os_mutex_lock (&coverage_mutex);
3869 mono_conc_hashtable_foreach (coverage_assemblies, build_assembly_buffer, prof);
3870 mono_conc_hashtable_foreach (coverage_classes, build_class_buffer, prof);
3871 mono_conc_hashtable_foreach (coverage_methods, build_method_buffer, prof);
3872 mono_os_mutex_unlock (&coverage_mutex);
3874 COVERAGE_DEBUG(fprintf (stderr, "Coverage: Finished dump\n");)
3877 static void
3878 process_method_enter_coverage (MonoProfiler *prof, MonoMethod *method)
3880 MonoClass *klass;
3881 MonoImage *image;
3883 if (!coverage_initialized)
3884 return;
3886 klass = mono_method_get_class (method);
3887 image = mono_class_get_image (klass);
3889 if (mono_conc_hashtable_lookup (suppressed_assemblies, (gpointer) mono_image_get_name (image)))
3890 return;
3892 mono_os_mutex_lock (&coverage_mutex);
3893 mono_conc_hashtable_insert (entered_methods, method, method);
3894 mono_os_mutex_unlock (&coverage_mutex);
3897 static MonoLockFreeQueueNode *
3898 create_method_node (MonoMethod *method)
3900 MethodNode *node = (MethodNode *)g_malloc (sizeof (MethodNode));
3901 mono_lock_free_queue_node_init ((MonoLockFreeQueueNode *) node, FALSE);
3902 node->method = method;
3904 return (MonoLockFreeQueueNode *) node;
3907 static gboolean
3908 coverage_filter (MonoProfiler *prof, MonoMethod *method)
3910 MonoError error;
3911 MonoClass *klass;
3912 MonoImage *image;
3913 MonoAssembly *assembly;
3914 MonoMethodHeader *header;
3915 guint32 iflags, flags, code_size;
3916 char *fqn, *classname;
3917 gboolean has_positive, found;
3918 MonoLockFreeQueue *image_methods, *class_methods;
3919 MonoLockFreeQueueNode *node;
3921 if (!coverage_initialized)
3922 return FALSE;
3924 COVERAGE_DEBUG(fprintf (stderr, "Coverage filter for %s\n", mono_method_get_name (method));)
3926 flags = mono_method_get_flags (method, &iflags);
3927 if ((iflags & 0x1000 /*METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL*/) ||
3928 (flags & 0x2000 /*METHOD_ATTRIBUTE_PINVOKE_IMPL*/)) {
3929 COVERAGE_DEBUG(fprintf (stderr, " Internal call or pinvoke - ignoring\n");)
3930 return FALSE;
3933 // Don't need to do anything else if we're already tracking this method
3934 if (mono_conc_hashtable_lookup (coverage_methods, method)) {
3935 COVERAGE_DEBUG(fprintf (stderr, " Already tracking\n");)
3936 return TRUE;
3939 klass = mono_method_get_class (method);
3940 image = mono_class_get_image (klass);
3942 // Don't handle coverage for the core assemblies
3943 if (mono_conc_hashtable_lookup (suppressed_assemblies, (gpointer) mono_image_get_name (image)) != NULL)
3944 return FALSE;
3946 if (prof->coverage_filters) {
3947 /* Check already filtered classes first */
3948 if (mono_conc_hashtable_lookup (filtered_classes, klass)) {
3949 COVERAGE_DEBUG(fprintf (stderr, " Already filtered\n");)
3950 return FALSE;
3953 classname = mono_type_get_name (mono_class_get_type (klass));
3955 fqn = g_strdup_printf ("[%s]%s", mono_image_get_name (image), classname);
3957 COVERAGE_DEBUG(fprintf (stderr, " Looking for %s in filter\n", fqn);)
3958 // Check positive filters first
3959 has_positive = FALSE;
3960 found = FALSE;
3961 for (guint i = 0; i < prof->coverage_filters->len; ++i) {
3962 char *filter = (char *)g_ptr_array_index (prof->coverage_filters, i);
3964 if (filter [0] == '+') {
3965 filter = &filter [1];
3967 COVERAGE_DEBUG(fprintf (stderr, " Checking against +%s ...", filter);)
3969 if (strstr (fqn, filter) != NULL) {
3970 COVERAGE_DEBUG(fprintf (stderr, "matched\n");)
3971 found = TRUE;
3972 } else
3973 COVERAGE_DEBUG(fprintf (stderr, "no match\n");)
3975 has_positive = TRUE;
3979 if (has_positive && !found) {
3980 COVERAGE_DEBUG(fprintf (stderr, " Positive match was not found\n");)
3982 mono_os_mutex_lock (&coverage_mutex);
3983 mono_conc_hashtable_insert (filtered_classes, klass, klass);
3984 mono_os_mutex_unlock (&coverage_mutex);
3985 g_free (fqn);
3986 g_free (classname);
3988 return FALSE;
3991 for (guint i = 0; i < prof->coverage_filters->len; ++i) {
3992 // FIXME: Is substring search sufficient?
3993 char *filter = (char *)g_ptr_array_index (prof->coverage_filters, i);
3994 if (filter [0] == '+')
3995 continue;
3997 // Skip '-'
3998 filter = &filter [1];
3999 COVERAGE_DEBUG(fprintf (stderr, " Checking against -%s ...", filter);)
4001 if (strstr (fqn, filter) != NULL) {
4002 COVERAGE_DEBUG(fprintf (stderr, "matched\n");)
4004 mono_os_mutex_lock (&coverage_mutex);
4005 mono_conc_hashtable_insert (filtered_classes, klass, klass);
4006 mono_os_mutex_unlock (&coverage_mutex);
4007 g_free (fqn);
4008 g_free (classname);
4010 return FALSE;
4011 } else
4012 COVERAGE_DEBUG(fprintf (stderr, "no match\n");)
4016 g_free (fqn);
4017 g_free (classname);
4020 COVERAGE_DEBUG(fprintf (stderr, " Handling coverage for %s\n", mono_method_get_name (method));)
4021 header = mono_method_get_header_checked (method, &error);
4022 mono_error_cleanup (&error);
4024 mono_method_header_get_code (header, &code_size, NULL);
4026 assembly = mono_image_get_assembly (image);
4028 // Need to keep the assemblies around for as long as they are kept in the hashtable
4029 // Nunit, for example, has a habit of unloading them before the coverage statistics are
4030 // generated causing a crash. See https://bugzilla.xamarin.com/show_bug.cgi?id=39325
4031 mono_assembly_addref (assembly);
4033 mono_os_mutex_lock (&coverage_mutex);
4034 mono_conc_hashtable_insert (coverage_methods, method, method);
4035 mono_conc_hashtable_insert (coverage_assemblies, assembly, assembly);
4036 mono_os_mutex_unlock (&coverage_mutex);
4038 image_methods = (MonoLockFreeQueue *)mono_conc_hashtable_lookup (image_to_methods, image);
4040 if (image_methods == NULL) {
4041 image_methods = (MonoLockFreeQueue *)g_malloc (sizeof (MonoLockFreeQueue));
4042 mono_lock_free_queue_init (image_methods);
4043 mono_os_mutex_lock (&coverage_mutex);
4044 mono_conc_hashtable_insert (image_to_methods, image, image_methods);
4045 mono_os_mutex_unlock (&coverage_mutex);
4048 node = create_method_node (method);
4049 mono_lock_free_queue_enqueue (image_methods, node);
4051 class_methods = (MonoLockFreeQueue *)mono_conc_hashtable_lookup (coverage_classes, klass);
4053 if (class_methods == NULL) {
4054 class_methods = (MonoLockFreeQueue *)g_malloc (sizeof (MonoLockFreeQueue));
4055 mono_lock_free_queue_init (class_methods);
4056 mono_os_mutex_lock (&coverage_mutex);
4057 mono_conc_hashtable_insert (coverage_classes, klass, class_methods);
4058 mono_os_mutex_unlock (&coverage_mutex);
4061 node = create_method_node (method);
4062 mono_lock_free_queue_enqueue (class_methods, node);
4064 return TRUE;
4067 #define LINE_BUFFER_SIZE 4096
4068 /* Max file limit of 128KB */
4069 #define MAX_FILE_SIZE 128 * 1024
4070 static char *
4071 get_file_content (FILE *stream)
4073 char *buffer;
4074 ssize_t bytes_read;
4075 long filesize;
4076 int res, offset = 0;
4078 res = fseek (stream, 0, SEEK_END);
4079 if (res < 0)
4080 return NULL;
4082 filesize = ftell (stream);
4083 if (filesize < 0)
4084 return NULL;
4086 res = fseek (stream, 0, SEEK_SET);
4087 if (res < 0)
4088 return NULL;
4090 if (filesize > MAX_FILE_SIZE)
4091 return NULL;
4093 buffer = (char *)g_malloc ((filesize + 1) * sizeof (char));
4094 while ((bytes_read = fread (buffer + offset, 1, LINE_BUFFER_SIZE, stream)) > 0)
4095 offset += bytes_read;
4097 /* NULL terminate our buffer */
4098 buffer[filesize] = '\0';
4099 return buffer;
4102 static char *
4103 get_next_line (char *contents, char **next_start)
4105 char *p = contents;
4107 if (p == NULL || *p == '\0') {
4108 *next_start = NULL;
4109 return NULL;
4112 while (*p != '\n' && *p != '\0')
4113 p++;
4115 if (*p == '\n') {
4116 *p = '\0';
4117 *next_start = p + 1;
4118 } else
4119 *next_start = NULL;
4121 return contents;
4124 static void
4125 init_suppressed_assemblies (void)
4127 char *content;
4128 char *line;
4129 FILE *sa_file;
4131 suppressed_assemblies = mono_conc_hashtable_new (g_str_hash, g_str_equal);
4132 sa_file = fopen (SUPPRESSION_DIR "/mono-profiler-log.suppression", "r");
4133 if (sa_file == NULL)
4134 return;
4136 /* Don't need to free @content as it is referred to by the lines stored in @suppressed_assemblies */
4137 content = get_file_content (sa_file);
4138 if (content == NULL) {
4139 g_error ("mono-profiler-log.suppression is greater than 128kb - aborting\n");
4142 while ((line = get_next_line (content, &content))) {
4143 line = g_strchomp (g_strchug (line));
4144 /* No locking needed as we're doing initialization */
4145 mono_conc_hashtable_insert (suppressed_assemblies, line, line);
4148 fclose (sa_file);
4151 static void
4152 coverage_init (MonoProfiler *prof)
4154 assert (!coverage_initialized);
4156 COVERAGE_DEBUG(fprintf (stderr, "Coverage initialized\n");)
4158 mono_os_mutex_init (&coverage_mutex);
4159 coverage_methods = mono_conc_hashtable_new (NULL, NULL);
4160 coverage_assemblies = mono_conc_hashtable_new (NULL, NULL);
4161 coverage_classes = mono_conc_hashtable_new (NULL, NULL);
4162 filtered_classes = mono_conc_hashtable_new (NULL, NULL);
4163 entered_methods = mono_conc_hashtable_new (NULL, NULL);
4164 image_to_methods = mono_conc_hashtable_new (NULL, NULL);
4165 init_suppressed_assemblies ();
4167 coverage_initialized = TRUE;
4170 static void
4171 unref_coverage_assemblies (gpointer key, gpointer value, gpointer userdata)
4173 MonoAssembly *assembly = (MonoAssembly *)value;
4174 mono_assembly_close (assembly);
4177 static void
4178 free_sample_hit (gpointer p)
4180 mono_lock_free_free (p, SAMPLE_BLOCK_SIZE);
4183 static void
4184 cleanup_reusable_samples (MonoProfiler *prof)
4186 SampleHit *sample;
4188 while ((sample = (SampleHit *) mono_lock_free_queue_dequeue (&prof->sample_reuse_queue)))
4189 mono_thread_hazardous_try_free (sample, free_sample_hit);
4192 static void
4193 log_shutdown (MonoProfiler *prof)
4195 void *res;
4197 in_shutdown = 1;
4199 counters_and_perfcounters_sample (prof);
4201 dump_coverage (prof);
4203 char c = 1;
4204 ign_res (write (prof->pipes [1], &c, 1));
4205 pthread_join (prof->helper_thread, &res);
4207 mono_os_mutex_destroy (&counters_mutex);
4209 MonoCounterAgent *mc_next;
4211 for (MonoCounterAgent *cur = counters; cur; cur = mc_next) {
4212 mc_next = cur->next;
4213 g_free (cur);
4216 PerfCounterAgent *pc_next;
4218 for (PerfCounterAgent *cur = perfcounters; cur; cur = pc_next) {
4219 pc_next = cur->next;
4220 g_free (cur);
4223 #if USE_PERF_EVENTS
4224 if (perf_data) {
4225 int i;
4226 for (i = 0; i < num_perf; ++i)
4227 read_perf_mmap (prof, i);
4229 #endif
4232 * Ensure that we empty the LLS completely, even if some nodes are
4233 * not immediately removed upon calling mono_lls_remove (), by
4234 * iterating until the head is NULL.
4236 while (profiler_thread_list.head) {
4237 MONO_LLS_FOREACH_SAFE (&profiler_thread_list, MonoProfilerThread, thread) {
4238 remove_thread (prof, thread, FALSE);
4239 } MONO_LLS_FOREACH_SAFE_END
4242 InterlockedWrite (&prof->run_dumper_thread, 0);
4243 mono_os_sem_post (&prof->dumper_queue_sem);
4244 pthread_join (prof->dumper_thread, &res);
4245 mono_os_sem_destroy (&prof->dumper_queue_sem);
4247 InterlockedWrite (&prof->run_writer_thread, 0);
4248 mono_os_sem_post (&prof->writer_queue_sem);
4249 pthread_join (prof->writer_thread, &res);
4250 mono_os_sem_destroy (&prof->writer_queue_sem);
4252 cleanup_reusable_samples (prof);
4255 * Pump the entire hazard free queue to make sure that anything we allocated
4256 * in the profiler will be freed. If we don't do this, the runtime could get
4257 * around to freeing some items after the profiler has been unloaded, which
4258 * would mean calling into functions in the profiler library, leading to a
4259 * crash.
4261 mono_thread_hazardous_try_free_all ();
4263 g_assert (!InterlockedRead (&buffer_rwlock_count) && "Why is the reader count still non-zero?");
4264 g_assert (!InterlockedReadPointer (&buffer_rwlock_exclusive) && "Why does someone still hold the exclusive lock?");
4266 #if defined (HAVE_SYS_ZLIB)
4267 if (prof->gzfile)
4268 gzclose (prof->gzfile);
4269 #endif
4270 if (prof->pipe_output)
4271 pclose (prof->file);
4272 else
4273 fclose (prof->file);
4275 mono_conc_hashtable_destroy (prof->method_table);
4276 mono_os_mutex_destroy (&prof->method_table_mutex);
4278 if (coverage_initialized) {
4279 mono_os_mutex_lock (&coverage_mutex);
4280 mono_conc_hashtable_foreach (coverage_assemblies, unref_coverage_assemblies, prof);
4281 mono_os_mutex_unlock (&coverage_mutex);
4283 mono_conc_hashtable_destroy (coverage_methods);
4284 mono_conc_hashtable_destroy (coverage_assemblies);
4285 mono_conc_hashtable_destroy (coverage_classes);
4286 mono_conc_hashtable_destroy (filtered_classes);
4288 mono_conc_hashtable_destroy (entered_methods);
4289 mono_conc_hashtable_destroy (image_to_methods);
4290 mono_conc_hashtable_destroy (suppressed_assemblies);
4291 mono_os_mutex_destroy (&coverage_mutex);
4294 PROF_TLS_FREE ();
4296 g_free (prof->args);
4297 g_free (prof);
4300 static char*
4301 new_filename (const char* filename)
4303 time_t t = time (NULL);
4304 int pid = process_id ();
4305 char pid_buf [16];
4306 char time_buf [16];
4307 char *res, *d;
4308 const char *p;
4309 int count_dates = 0;
4310 int count_pids = 0;
4311 int s_date, s_pid;
4312 struct tm *ts;
4313 for (p = filename; *p; p++) {
4314 if (*p != '%')
4315 continue;
4316 p++;
4317 if (*p == 't')
4318 count_dates++;
4319 else if (*p == 'p')
4320 count_pids++;
4321 else if (*p == 0)
4322 break;
4324 if (!count_dates && !count_pids)
4325 return pstrdup (filename);
4326 snprintf (pid_buf, sizeof (pid_buf), "%d", pid);
4327 ts = gmtime (&t);
4328 snprintf (time_buf, sizeof (time_buf), "%d%02d%02d%02d%02d%02d",
4329 1900 + ts->tm_year, 1 + ts->tm_mon, ts->tm_mday, ts->tm_hour, ts->tm_min, ts->tm_sec);
4330 s_date = strlen (time_buf);
4331 s_pid = strlen (pid_buf);
4332 d = res = (char *)malloc (strlen (filename) + s_date * count_dates + s_pid * count_pids);
4333 for (p = filename; *p; p++) {
4334 if (*p != '%') {
4335 *d++ = *p;
4336 continue;
4338 p++;
4339 if (*p == 't') {
4340 strcpy (d, time_buf);
4341 d += s_date;
4342 continue;
4343 } else if (*p == 'p') {
4344 strcpy (d, pid_buf);
4345 d += s_pid;
4346 continue;
4347 } else if (*p == '%') {
4348 *d++ = '%';
4349 continue;
4350 } else if (*p == 0)
4351 break;
4352 *d++ = '%';
4353 *d++ = *p;
4355 *d = 0;
4356 return res;
4359 //this is exposed by the JIT, but it's not meant to be a supported API for now.
4360 extern void mono_threads_attach_tools_thread (void);
4362 static void*
4363 helper_thread (void* arg)
4365 MonoProfiler* prof = (MonoProfiler *)arg;
4366 int command_socket;
4367 int len;
4368 char buf [64];
4370 mono_threads_attach_tools_thread ();
4371 mono_native_thread_set_name (mono_native_thread_id_get (), "Profiler helper");
4373 MonoProfilerThread *thread = init_thread (FALSE);
4375 //fprintf (stderr, "Server listening\n");
4376 command_socket = -1;
4377 while (1) {
4378 fd_set rfds;
4379 struct timeval tv;
4380 int max_fd = -1;
4381 FD_ZERO (&rfds);
4382 FD_SET (prof->server_socket, &rfds);
4383 max_fd = prof->server_socket;
4384 FD_SET (prof->pipes [0], &rfds);
4385 if (max_fd < prof->pipes [0])
4386 max_fd = prof->pipes [0];
4387 if (command_socket >= 0) {
4388 FD_SET (command_socket, &rfds);
4389 if (max_fd < command_socket)
4390 max_fd = command_socket;
4392 #if USE_PERF_EVENTS
4393 if (perf_data) {
4394 int i;
4395 for ( i = 0; i < num_perf; ++i) {
4396 if (perf_data [i].perf_fd < 0)
4397 continue;
4398 FD_SET (perf_data [i].perf_fd, &rfds);
4399 if (max_fd < perf_data [i].perf_fd)
4400 max_fd = perf_data [i].perf_fd;
4403 #endif
4405 counters_and_perfcounters_sample (prof);
4407 buffer_lock_excl ();
4409 sync_point (prof, SYNC_POINT_PERIODIC);
4411 buffer_unlock_excl ();
4413 tv.tv_sec = 1;
4414 tv.tv_usec = 0;
4415 len = select (max_fd + 1, &rfds, NULL, NULL, &tv);
4417 if (len < 0) {
4418 if (errno == EINTR)
4419 continue;
4421 g_warning ("Error in mono-profiler-log server: %s", strerror (errno));
4422 return NULL;
4425 if (FD_ISSET (prof->pipes [0], &rfds)) {
4426 char c;
4427 read (prof->pipes [0], &c, 1);
4428 if (do_debug)
4429 fprintf (stderr, "helper shutdown\n");
4430 #if USE_PERF_EVENTS
4431 if (perf_data) {
4432 int i;
4433 for ( i = 0; i < num_perf; ++i) {
4434 if (perf_data [i].perf_fd < 0)
4435 continue;
4436 if (FD_ISSET (perf_data [i].perf_fd, &rfds))
4437 read_perf_mmap (prof, i);
4440 #endif
4441 send_log_unsafe (prof, FALSE, FALSE);
4442 return NULL;
4444 #if USE_PERF_EVENTS
4445 if (perf_data) {
4446 int i;
4447 for ( i = 0; i < num_perf; ++i) {
4448 if (perf_data [i].perf_fd < 0)
4449 continue;
4450 if (FD_ISSET (perf_data [i].perf_fd, &rfds))
4451 read_perf_mmap (prof, i);
4454 #endif
4455 if (command_socket >= 0 && FD_ISSET (command_socket, &rfds)) {
4456 len = read (command_socket, buf, sizeof (buf) - 1);
4457 if (len < 0)
4458 continue;
4459 if (len == 0) {
4460 close (command_socket);
4461 command_socket = -1;
4462 continue;
4464 buf [len] = 0;
4465 if (strcmp (buf, "heapshot\n") == 0 && hs_mode_ondemand) {
4466 // Rely on the finalization callbacks invoking process_requests ().
4467 heapshot_requested = 1;
4468 mono_gc_finalize_notify ();
4470 continue;
4472 if (!FD_ISSET (prof->server_socket, &rfds)) {
4473 continue;
4475 command_socket = accept (prof->server_socket, NULL, NULL);
4476 if (command_socket < 0)
4477 continue;
4478 //fprintf (stderr, "Accepted connection\n");
4481 deinit_thread (thread);
4483 mono_thread_info_detach ();
4485 return NULL;
4488 static void
4489 start_helper_thread (MonoProfiler* prof)
4491 if (pipe (prof->pipes) == -1) {
4492 fprintf (stderr, "Cannot create pipe: %s\n", strerror (errno));
4493 exit (1);
4496 prof->server_socket = socket (PF_INET, SOCK_STREAM, 0);
4498 if (prof->server_socket == -1) {
4499 fprintf (stderr, "Cannot create server socket: %s\n", strerror (errno));
4500 exit (1);
4503 struct sockaddr_in server_address;
4505 memset (&server_address, 0, sizeof (server_address));
4506 server_address.sin_family = AF_INET;
4507 server_address.sin_addr.s_addr = INADDR_ANY;
4508 server_address.sin_port = htons (prof->command_port);
4510 if (bind (prof->server_socket, (struct sockaddr *) &server_address, sizeof (server_address)) == -1) {
4511 fprintf (stderr, "Cannot bind server socket on port %d: %s\n", prof->command_port, strerror (errno));
4512 close (prof->server_socket);
4513 exit (1);
4516 if (listen (prof->server_socket, 1) == -1) {
4517 fprintf (stderr, "Cannot listen on server socket: %s\n", strerror (errno));
4518 close (prof->server_socket);
4519 exit (1);
4522 socklen_t slen = sizeof (server_address);
4524 if (getsockname (prof->server_socket, (struct sockaddr *)&server_address, &slen)) {
4525 fprintf (stderr, "Could not get assigned port: %s\n", strerror (errno));
4526 close (prof->server_socket);
4527 exit (1);
4530 prof->command_port = ntohs (server_address.sin_port);
4532 int r;
4534 if ((r = pthread_create (&prof->helper_thread, NULL, helper_thread, prof))) {
4535 fprintf (stderr, "Could not start helper thread: %s\n", strerror (r));
4536 close (prof->server_socket);
4537 exit (1);
4541 static void
4542 free_writer_entry (gpointer p)
4544 mono_lock_free_free (p, WRITER_ENTRY_BLOCK_SIZE);
4547 static gboolean
4548 handle_writer_queue_entry (MonoProfiler *prof)
4550 WriterQueueEntry *entry;
4552 if ((entry = (WriterQueueEntry *) mono_lock_free_queue_dequeue (&prof->writer_queue))) {
4553 if (!entry->methods)
4554 goto no_methods;
4556 gboolean wrote_methods = FALSE;
4559 * Encode the method events in a temporary log buffer that we
4560 * flush to disk before the main buffer, ensuring that all
4561 * methods have metadata emitted before they're referenced.
4563 * We use a 'proper' thread-local buffer for this as opposed
4564 * to allocating and freeing a buffer by hand because the call
4565 * to mono_method_full_name () below may trigger class load
4566 * events when it retrieves the signature of the method. So a
4567 * thread-local buffer needs to exist when such events occur.
4569 for (guint i = 0; i < entry->methods->len; i++) {
4570 MethodInfo *info = (MethodInfo *) g_ptr_array_index (entry->methods, i);
4572 if (mono_conc_hashtable_lookup (prof->method_table, info->method))
4573 goto free_info; // This method already has metadata emitted.
4576 * Other threads use this hash table to get a general
4577 * idea of whether a method has already been emitted to
4578 * the stream. Due to the way we add to this table, it
4579 * can easily happen that multiple threads queue up the
4580 * same methods, but that's OK since eventually all
4581 * methods will be in this table and the thread-local
4582 * method lists will just be empty for the rest of the
4583 * app's lifetime.
4585 mono_os_mutex_lock (&prof->method_table_mutex);
4586 mono_conc_hashtable_insert (prof->method_table, info->method, info->method);
4587 mono_os_mutex_unlock (&prof->method_table_mutex);
4589 char *name = mono_method_full_name (info->method, 1);
4590 int nlen = strlen (name) + 1;
4591 void *cstart = info->ji ? mono_jit_info_get_code_start (info->ji) : NULL;
4592 int csize = info->ji ? mono_jit_info_get_code_size (info->ji) : 0;
4594 ENTER_LOG (&method_jits_ctr, logbuffer,
4595 EVENT_SIZE /* event */ +
4596 LEB128_SIZE /* method */ +
4597 LEB128_SIZE /* start */ +
4598 LEB128_SIZE /* size */ +
4599 nlen /* name */
4602 emit_event_time (logbuffer, TYPE_JIT | TYPE_METHOD, info->time);
4603 emit_method_inner (logbuffer, info->method);
4604 emit_ptr (logbuffer, cstart);
4605 emit_value (logbuffer, csize);
4607 memcpy (logbuffer->cursor, name, nlen);
4608 logbuffer->cursor += nlen;
4610 EXIT_LOG_EXPLICIT (prof, FALSE, FALSE);
4612 mono_free (name);
4614 wrote_methods = TRUE;
4616 free_info:
4617 g_free (info);
4620 g_ptr_array_free (entry->methods, TRUE);
4622 if (wrote_methods) {
4623 dump_buffer_threadless (prof, PROF_TLS_GET ()->buffer);
4624 init_buffer_state (PROF_TLS_GET ());
4627 no_methods:
4628 dump_buffer (prof, entry->buffer);
4630 mono_thread_hazardous_try_free (entry, free_writer_entry);
4632 return TRUE;
4635 return FALSE;
4638 static void *
4639 writer_thread (void *arg)
4641 MonoProfiler *prof = (MonoProfiler *)arg;
4643 mono_threads_attach_tools_thread ();
4644 mono_native_thread_set_name (mono_native_thread_id_get (), "Profiler writer");
4646 dump_header (prof);
4648 MonoProfilerThread *thread = init_thread (FALSE);
4650 while (InterlockedRead (&prof->run_writer_thread)) {
4651 mono_os_sem_wait (&prof->writer_queue_sem, MONO_SEM_FLAGS_NONE);
4652 handle_writer_queue_entry (prof);
4655 /* Drain any remaining entries on shutdown. */
4656 while (handle_writer_queue_entry (prof));
4658 free_buffer (thread->buffer, thread->buffer->size);
4659 deinit_thread (thread);
4661 mono_thread_info_detach ();
4663 return NULL;
4666 static void
4667 start_writer_thread (MonoProfiler* prof)
4669 InterlockedWrite (&prof->run_writer_thread, 1);
4671 int r;
4673 if ((r = pthread_create (&prof->writer_thread, NULL, writer_thread, prof))) {
4674 fprintf (stderr, "Could not start writer thread: %s\n", strerror (r));
4675 exit (1);
4679 static void
4680 reuse_sample_hit (gpointer p)
4682 SampleHit *sample = p;
4684 mono_lock_free_queue_node_unpoison (&sample->node);
4685 mono_lock_free_queue_enqueue (&sample->prof->sample_reuse_queue, &sample->node);
4688 static gboolean
4689 handle_dumper_queue_entry (MonoProfiler *prof)
4691 SampleHit *sample;
4693 if ((sample = (SampleHit *) mono_lock_free_queue_dequeue (&prof->dumper_queue))) {
4694 for (int i = 0; i < sample->count; ++i) {
4695 MonoMethod *method = sample->frames [i].method;
4696 MonoDomain *domain = sample->frames [i].domain;
4697 void *address = sample->frames [i].base_address;
4699 if (!method) {
4700 g_assert (domain && "What happened to the domain pointer?");
4701 g_assert (address && "What happened to the instruction pointer?");
4703 MonoJitInfo *ji = mono_jit_info_table_find (domain, (char *) address);
4705 if (ji)
4706 sample->frames [i].method = mono_jit_info_get_method (ji);
4710 ENTER_LOG (&sample_hits_ctr, logbuffer,
4711 EVENT_SIZE /* event */ +
4712 BYTE_SIZE /* type */ +
4713 LEB128_SIZE /* tid */ +
4714 LEB128_SIZE /* count */ +
4715 1 * (
4716 LEB128_SIZE /* ip */
4718 LEB128_SIZE /* managed count */ +
4719 sample->count * (
4720 LEB128_SIZE /* method */
4724 emit_event_time (logbuffer, TYPE_SAMPLE | TYPE_SAMPLE_HIT, sample->time);
4725 emit_byte (logbuffer, sample_type);
4726 emit_ptr (logbuffer, (void *) sample->tid);
4727 emit_value (logbuffer, 1);
4729 // TODO: Actual native unwinding.
4730 for (int i = 0; i < 1; ++i) {
4731 emit_ptr (logbuffer, sample->ip);
4732 add_code_pointer ((uintptr_t) sample->ip);
4735 /* new in data version 6 */
4736 emit_uvalue (logbuffer, sample->count);
4738 for (int i = 0; i < sample->count; ++i)
4739 emit_method (prof, logbuffer, sample->frames [i].method);
4741 EXIT_LOG_EXPLICIT (prof, TRUE, FALSE);
4743 mono_thread_hazardous_try_free (sample, reuse_sample_hit);
4745 dump_unmanaged_coderefs (prof);
4748 return FALSE;
4751 static void *
4752 dumper_thread (void *arg)
4754 MonoProfiler *prof = (MonoProfiler *)arg;
4756 mono_threads_attach_tools_thread ();
4757 mono_native_thread_set_name (mono_native_thread_id_get (), "Profiler dumper");
4759 MonoProfilerThread *thread = init_thread (FALSE);
4761 while (InterlockedRead (&prof->run_dumper_thread)) {
4762 mono_os_sem_wait (&prof->dumper_queue_sem, MONO_SEM_FLAGS_NONE);
4763 handle_dumper_queue_entry (prof);
4766 /* Drain any remaining entries on shutdown. */
4767 while (handle_dumper_queue_entry (prof));
4769 send_log_unsafe (prof, FALSE, FALSE);
4770 deinit_thread (thread);
4772 mono_thread_info_detach ();
4774 return NULL;
4777 static void
4778 start_dumper_thread (MonoProfiler* prof)
4780 InterlockedWrite (&prof->run_dumper_thread, 1);
4782 int r;
4784 if ((r = pthread_create (&prof->dumper_thread, NULL, dumper_thread, prof))) {
4785 fprintf (stderr, "Could not start dumper thread: %s\n", strerror (r));
4786 exit (1);
4790 static void
4791 register_counter (const char *name, gint32 *counter)
4793 mono_counters_register (name, MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, counter);
4796 static void
4797 runtime_initialized (MonoProfiler *profiler)
4799 InterlockedWrite (&runtime_inited, 1);
4801 start_writer_thread (profiler);
4802 start_dumper_thread (profiler);
4804 register_counter ("Sample events allocated", &sample_allocations_ctr);
4805 register_counter ("Log buffers allocated", &buffer_allocations_ctr);
4807 register_counter ("Event: Sync points", &sync_points_ctr);
4808 register_counter ("Event: Heap objects", &heap_objects_ctr);
4809 register_counter ("Event: Heap starts", &heap_starts_ctr);
4810 register_counter ("Event: Heap ends", &heap_ends_ctr);
4811 register_counter ("Event: Heap roots", &heap_roots_ctr);
4812 register_counter ("Event: GC events", &gc_events_ctr);
4813 register_counter ("Event: GC resizes", &gc_resizes_ctr);
4814 register_counter ("Event: GC allocations", &gc_allocs_ctr);
4815 register_counter ("Event: GC moves", &gc_moves_ctr);
4816 register_counter ("Event: GC handle creations", &gc_handle_creations_ctr);
4817 register_counter ("Event: GC handle deletions", &gc_handle_deletions_ctr);
4818 register_counter ("Event: GC finalize starts", &finalize_begins_ctr);
4819 register_counter ("Event: GC finalize ends", &finalize_ends_ctr);
4820 register_counter ("Event: GC finalize object starts", &finalize_object_begins_ctr);
4821 register_counter ("Event: GC finalize object ends", &finalize_object_ends_ctr);
4822 register_counter ("Event: Image loads", &image_loads_ctr);
4823 register_counter ("Event: Image unloads", &image_unloads_ctr);
4824 register_counter ("Event: Assembly loads", &assembly_loads_ctr);
4825 register_counter ("Event: Assembly unloads", &assembly_unloads_ctr);
4826 register_counter ("Event: Class loads", &class_loads_ctr);
4827 register_counter ("Event: Class unloads", &class_unloads_ctr);
4828 register_counter ("Event: Method entries", &method_entries_ctr);
4829 register_counter ("Event: Method exits", &method_exits_ctr);
4830 register_counter ("Event: Method exception leaves", &method_exception_exits_ctr);
4831 register_counter ("Event: Method JITs", &method_jits_ctr);
4832 register_counter ("Event: Code buffers", &code_buffers_ctr);
4833 register_counter ("Event: Exception throws", &exception_throws_ctr);
4834 register_counter ("Event: Exception clauses", &exception_clauses_ctr);
4835 register_counter ("Event: Monitor contentions", &monitor_contentions_ctr);
4836 register_counter ("Event: Monitor acquisitions", &monitor_acquisitions_ctr);
4837 register_counter ("Event: Monitor failures", &monitor_failures_ctr);
4838 register_counter ("Event: Thread starts", &thread_starts_ctr);
4839 register_counter ("Event: Thread ends", &thread_ends_ctr);
4840 register_counter ("Event: Thread names", &thread_names_ctr);
4841 register_counter ("Event: Domain loads", &domain_loads_ctr);
4842 register_counter ("Event: Domain unloads", &domain_unloads_ctr);
4843 register_counter ("Event: Domain names", &domain_names_ctr);
4844 register_counter ("Event: Context loads", &context_loads_ctr);
4845 register_counter ("Event: Context unloads", &context_unloads_ctr);
4846 register_counter ("Event: Sample binaries", &sample_ubins_ctr);
4847 register_counter ("Event: Sample symbols", &sample_usyms_ctr);
4848 register_counter ("Event: Sample hits", &sample_hits_ctr);
4849 register_counter ("Event: Counter descriptors", &counter_descriptors_ctr);
4850 register_counter ("Event: Counter samples", &counter_samples_ctr);
4851 register_counter ("Event: Performance counter descriptors", &perfcounter_descriptors_ctr);
4852 register_counter ("Event: Performance counter samples", &perfcounter_samples_ctr);
4853 register_counter ("Event: Coverage methods", &coverage_methods_ctr);
4854 register_counter ("Event: Coverage statements", &coverage_statements_ctr);
4855 register_counter ("Event: Coverage classes", &coverage_classes_ctr);
4856 register_counter ("Event: Coverage assemblies", &coverage_assemblies_ctr);
4858 counters_init (profiler);
4860 start_helper_thread (profiler);
4863 static MonoProfiler*
4864 create_profiler (const char *args, const char *filename, GPtrArray *filters)
4866 MonoProfiler *prof;
4867 char *nf;
4868 int force_delete = 0;
4869 prof = (MonoProfiler *)calloc (1, sizeof (MonoProfiler));
4871 prof->args = pstrdup (args);
4872 prof->command_port = command_port;
4873 if (filename && *filename == '-') {
4874 force_delete = 1;
4875 filename++;
4877 if (!filename) {
4878 if (do_report)
4879 filename = "|mprof-report -";
4880 else
4881 filename = "output.mlpd";
4882 nf = (char*)filename;
4883 } else {
4884 nf = new_filename (filename);
4885 if (do_report) {
4886 int s = strlen (nf) + 32;
4887 char *p = (char *)malloc (s);
4888 snprintf (p, s, "|mprof-report '--out=%s' -", nf);
4889 g_free (nf);
4890 nf = p;
4893 if (*nf == '|') {
4894 prof->file = popen (nf + 1, "w");
4895 prof->pipe_output = 1;
4896 } else if (*nf == '#') {
4897 int fd = strtol (nf + 1, NULL, 10);
4898 prof->file = fdopen (fd, "a");
4899 } else {
4900 if (force_delete)
4901 unlink (nf);
4902 prof->file = fopen (nf, "wb");
4904 if (!prof->file) {
4905 fprintf (stderr, "Cannot create profiler output: %s\n", nf);
4906 exit (1);
4909 #if defined (HAVE_SYS_ZLIB)
4910 if (use_zip)
4911 prof->gzfile = gzdopen (fileno (prof->file), "wb");
4912 #endif
4914 #if USE_PERF_EVENTS
4915 setup_perf_event ();
4917 if (!perf_data) {
4918 /* FIXME: warn if different freq or sample type */
4919 do_mono_sample = 1;
4921 #endif
4924 * If you hit this assert while increasing MAX_FRAMES, you need to increase
4925 * SAMPLE_BLOCK_SIZE as well.
4927 g_assert (SAMPLE_SLOT_SIZE (MAX_FRAMES) * 2 < LOCK_FREE_ALLOC_SB_USABLE_SIZE (SAMPLE_BLOCK_SIZE));
4929 // FIXME: We should free this stuff too.
4930 mono_lock_free_allocator_init_size_class (&prof->sample_size_class, SAMPLE_SLOT_SIZE (num_frames), SAMPLE_BLOCK_SIZE);
4931 mono_lock_free_allocator_init_allocator (&prof->sample_allocator, &prof->sample_size_class);
4933 mono_lock_free_queue_init (&prof->sample_reuse_queue);
4935 g_assert (sizeof (WriterQueueEntry) * 2 < LOCK_FREE_ALLOC_SB_USABLE_SIZE (WRITER_ENTRY_BLOCK_SIZE));
4937 // FIXME: We should free this stuff too.
4938 mono_lock_free_allocator_init_size_class (&prof->writer_entry_size_class, sizeof (WriterQueueEntry), WRITER_ENTRY_BLOCK_SIZE);
4939 mono_lock_free_allocator_init_allocator (&prof->writer_entry_allocator, &prof->writer_entry_size_class);
4941 mono_lock_free_queue_init (&prof->writer_queue);
4942 mono_os_sem_init (&prof->writer_queue_sem, 0);
4944 mono_lock_free_queue_init (&prof->dumper_queue);
4945 mono_os_sem_init (&prof->dumper_queue_sem, 0);
4947 mono_os_mutex_init (&prof->method_table_mutex);
4948 prof->method_table = mono_conc_hashtable_new (NULL, NULL);
4950 if (do_coverage)
4951 coverage_init (prof);
4952 prof->coverage_filters = filters;
4954 prof->startup_time = current_time ();
4955 return prof;
4958 static void
4959 usage (int do_exit)
4961 printf ("Log profiler version %d.%d (format: %d)\n", LOG_VERSION_MAJOR, LOG_VERSION_MINOR, LOG_DATA_VERSION);
4962 printf ("Usage: mono --profile=log[:OPTION1[,OPTION2...]] program.exe\n");
4963 printf ("Options:\n");
4964 printf ("\thelp show this usage info\n");
4965 printf ("\t[no]alloc enable/disable recording allocation info\n");
4966 printf ("\t[no]calls enable/disable recording enter/leave method events\n");
4967 printf ("\theapshot[=MODE] record heap shot info (by default at each major collection)\n");
4968 printf ("\t MODE: every XXms milliseconds, every YYgc collections, ondemand\n");
4969 printf ("\tcounters sample counters every 1s\n");
4970 printf ("\tsample[=TYPE] use statistical sampling mode (by default cycles/100)\n");
4971 printf ("\t TYPE: cycles,instr,cacherefs,cachemiss,branches,branchmiss\n");
4972 printf ("\t TYPE can be followed by /FREQUENCY\n");
4973 printf ("\tmaxframes=NUM collect up to NUM stack frames\n");
4974 printf ("\tcalldepth=NUM ignore method events for call chain depth bigger than NUM\n");
4975 printf ("\toutput=FILENAME write the data to file FILENAME (-FILENAME to overwrite)\n");
4976 printf ("\toutput=|PROGRAM write the data to the stdin of PROGRAM\n");
4977 printf ("\t %%t is subtituted with date and time, %%p with the pid\n");
4978 printf ("\treport create a report instead of writing the raw data to a file\n");
4979 printf ("\tzip compress the output data\n");
4980 printf ("\tport=PORTNUM use PORTNUM for the listening command server\n");
4981 printf ("\tcoverage enable collection of code coverage data\n");
4982 printf ("\tcovfilter=ASSEMBLY add an assembly to the code coverage filters\n");
4983 printf ("\t add a + to include the assembly or a - to exclude it\n");
4984 printf ("\t filter=-mscorlib\n");
4985 printf ("\tcovfilter-file=FILE use FILE to generate the list of assemblies to be filtered\n");
4986 if (do_exit)
4987 exit (1);
4990 static const char*
4991 match_option (const char* p, const char *opt, char **rval)
4993 int len = strlen (opt);
4994 if (strncmp (p, opt, len) == 0) {
4995 if (rval) {
4996 if (p [len] == '=' && p [len + 1]) {
4997 const char *opt = p + len + 1;
4998 const char *end = strchr (opt, ',');
4999 char *val;
5000 int l;
5001 if (end == NULL) {
5002 l = strlen (opt);
5003 } else {
5004 l = end - opt;
5006 val = (char *)malloc (l + 1);
5007 memcpy (val, opt, l);
5008 val [l] = 0;
5009 *rval = val;
5010 return opt + l;
5012 if (p [len] == 0 || p [len] == ',') {
5013 *rval = NULL;
5014 return p + len + (p [len] == ',');
5016 usage (1);
5017 } else {
5018 if (p [len] == 0)
5019 return p + len;
5020 if (p [len] == ',')
5021 return p + len + 1;
5024 return p;
5027 typedef struct {
5028 const char *name;
5029 int sample_mode;
5030 } SampleMode;
5032 static const SampleMode sample_modes [] = {
5033 {"cycles", SAMPLE_CYCLES},
5034 {"instr", SAMPLE_INSTRUCTIONS},
5035 {"cachemiss", SAMPLE_CACHE_MISSES},
5036 {"cacherefs", SAMPLE_CACHE_REFS},
5037 {"branches", SAMPLE_BRANCHES},
5038 {"branchmiss", SAMPLE_BRANCH_MISSES},
5039 {NULL, 0}
5042 static void
5043 set_sample_mode (char* val, int allow_empty)
5045 char *end;
5046 char *maybe_freq = NULL;
5047 unsigned int count;
5048 const SampleMode *smode = sample_modes;
5049 #ifndef USE_PERF_EVENTS
5050 do_mono_sample = 1;
5051 #endif
5052 if (allow_empty && !val) {
5053 sample_type = SAMPLE_CYCLES;
5054 sample_freq = 100;
5055 return;
5057 if (strcmp (val, "mono") == 0) {
5058 do_mono_sample = 1;
5059 sample_type = SAMPLE_CYCLES;
5060 g_free (val);
5061 return;
5063 for (smode = sample_modes; smode->name; smode++) {
5064 int l = strlen (smode->name);
5065 if (strncmp (val, smode->name, l) == 0) {
5066 sample_type = smode->sample_mode;
5067 maybe_freq = val + l;
5068 break;
5071 if (!smode->name)
5072 usage (1);
5073 if (*maybe_freq == '/') {
5074 count = strtoul (maybe_freq + 1, &end, 10);
5075 if (maybe_freq + 1 == end)
5076 usage (1);
5077 sample_freq = count;
5078 } else if (*maybe_freq != 0) {
5079 usage (1);
5080 } else {
5081 sample_freq = 100;
5083 g_free (val);
5086 static void
5087 set_hsmode (char* val, int allow_empty)
5089 char *end;
5090 unsigned int count;
5091 if (allow_empty && !val)
5092 return;
5093 if (strcmp (val, "ondemand") == 0) {
5094 hs_mode_ondemand = 1;
5095 g_free (val);
5096 return;
5098 count = strtoul (val, &end, 10);
5099 if (val == end)
5100 usage (1);
5101 if (strcmp (end, "ms") == 0)
5102 hs_mode_ms = count;
5103 else if (strcmp (end, "gc") == 0)
5104 hs_mode_gc = count;
5105 else
5106 usage (1);
5107 g_free (val);
5111 * declaration to silence the compiler: this is the entry point that
5112 * mono will load from the shared library and call.
5114 extern void
5115 mono_profiler_startup (const char *desc);
5117 extern void
5118 mono_profiler_startup_log (const char *desc);
5121 * this is the entry point that will be used when the profiler
5122 * is embedded inside the main executable.
5124 void
5125 mono_profiler_startup_log (const char *desc)
5127 mono_profiler_startup (desc);
5130 void
5131 mono_profiler_startup (const char *desc)
5133 MonoProfiler *prof;
5134 GPtrArray *filters = NULL;
5135 char *filename = NULL;
5136 const char *p;
5137 const char *opt;
5138 int calls_enabled = 0;
5139 int allocs_enabled = 0;
5140 int only_coverage = 0;
5141 int events = MONO_PROFILE_GC|MONO_PROFILE_ALLOCATIONS|
5142 MONO_PROFILE_GC_MOVES|MONO_PROFILE_CLASS_EVENTS|MONO_PROFILE_THREADS|
5143 MONO_PROFILE_ENTER_LEAVE|MONO_PROFILE_JIT_COMPILATION|MONO_PROFILE_EXCEPTIONS|
5144 MONO_PROFILE_MONITOR_EVENTS|MONO_PROFILE_MODULE_EVENTS|MONO_PROFILE_GC_ROOTS|
5145 MONO_PROFILE_INS_COVERAGE|MONO_PROFILE_APPDOMAIN_EVENTS|MONO_PROFILE_CONTEXT_EVENTS|
5146 MONO_PROFILE_ASSEMBLY_EVENTS|MONO_PROFILE_GC_FINALIZATION;
5148 max_allocated_sample_hits = mono_cpu_count () * 1000;
5150 p = desc;
5151 if (strncmp (p, "log", 3))
5152 usage (1);
5153 p += 3;
5154 if (*p == ':')
5155 p++;
5156 for (; *p; p = opt) {
5157 char *val;
5158 if (*p == ',') {
5159 opt = p + 1;
5160 continue;
5162 if ((opt = match_option (p, "help", NULL)) != p) {
5163 usage (0);
5164 continue;
5166 if ((opt = match_option (p, "calls", NULL)) != p) {
5167 calls_enabled = 1;
5168 continue;
5170 if ((opt = match_option (p, "nocalls", NULL)) != p) {
5171 events &= ~MONO_PROFILE_ENTER_LEAVE;
5172 nocalls = 1;
5173 continue;
5175 if ((opt = match_option (p, "alloc", NULL)) != p) {
5176 allocs_enabled = 1;
5177 continue;
5179 if ((opt = match_option (p, "noalloc", NULL)) != p) {
5180 events &= ~MONO_PROFILE_ALLOCATIONS;
5181 continue;
5183 if ((opt = match_option (p, "time", &val)) != p) {
5184 if (strcmp (val, "fast") && strcmp (val, "null"))
5185 usage (1);
5186 g_free (val);
5187 continue;
5189 if ((opt = match_option (p, "report", NULL)) != p) {
5190 do_report = 1;
5191 continue;
5193 if ((opt = match_option (p, "debug", NULL)) != p) {
5194 do_debug = 1;
5195 continue;
5197 if ((opt = match_option (p, "sampling-real", NULL)) != p) {
5198 sampling_mode = MONO_PROFILER_STAT_MODE_REAL;
5199 continue;
5201 if ((opt = match_option (p, "sampling-process", NULL)) != p) {
5202 sampling_mode = MONO_PROFILER_STAT_MODE_PROCESS;
5203 continue;
5205 if ((opt = match_option (p, "heapshot", &val)) != p) {
5206 events &= ~MONO_PROFILE_ALLOCATIONS;
5207 events &= ~MONO_PROFILE_ENTER_LEAVE;
5208 nocalls = 1;
5209 do_heap_shot = 1;
5210 set_hsmode (val, 1);
5211 continue;
5213 if ((opt = match_option (p, "sample", &val)) != p) {
5214 events &= ~MONO_PROFILE_ALLOCATIONS;
5215 events &= ~MONO_PROFILE_ENTER_LEAVE;
5216 nocalls = 1;
5217 set_sample_mode (val, 1);
5218 continue;
5220 if ((opt = match_option (p, "zip", NULL)) != p) {
5221 use_zip = 1;
5222 continue;
5224 if ((opt = match_option (p, "output", &val)) != p) {
5225 filename = val;
5226 continue;
5228 if ((opt = match_option (p, "port", &val)) != p) {
5229 char *end;
5230 command_port = strtoul (val, &end, 10);
5231 g_free (val);
5232 continue;
5234 if ((opt = match_option (p, "maxframes", &val)) != p) {
5235 char *end;
5236 num_frames = strtoul (val, &end, 10);
5237 if (num_frames > MAX_FRAMES)
5238 num_frames = MAX_FRAMES;
5239 g_free (val);
5240 notraces = num_frames == 0;
5241 continue;
5243 if ((opt = match_option (p, "maxsamples", &val)) != p) {
5244 char *end;
5245 max_allocated_sample_hits = strtoul (val, &end, 10);
5246 if (!max_allocated_sample_hits)
5247 max_allocated_sample_hits = G_MAXINT32;
5248 g_free (val);
5249 continue;
5251 if ((opt = match_option (p, "calldepth", &val)) != p) {
5252 char *end;
5253 max_call_depth = strtoul (val, &end, 10);
5254 g_free (val);
5255 continue;
5257 if ((opt = match_option (p, "counters", NULL)) != p) {
5258 do_counters = 1;
5259 continue;
5261 if ((opt = match_option (p, "coverage", NULL)) != p) {
5262 do_coverage = 1;
5263 events |= MONO_PROFILE_ENTER_LEAVE;
5264 debug_coverage = (g_getenv ("MONO_PROFILER_DEBUG_COVERAGE") != NULL);
5265 continue;
5267 if ((opt = match_option (p, "onlycoverage", NULL)) != p) {
5268 only_coverage = 1;
5269 continue;
5271 if ((opt = match_option (p, "covfilter-file", &val)) != p) {
5272 FILE *filter_file;
5273 char *line, *content;
5275 if (filters == NULL)
5276 filters = g_ptr_array_new ();
5278 filter_file = fopen (val, "r");
5279 if (filter_file == NULL) {
5280 fprintf (stderr, "Unable to open %s\n", val);
5281 exit (0);
5284 /* Don't need to free content as it is referred to by the lines stored in @filters */
5285 content = get_file_content (filter_file);
5286 if (content == NULL)
5287 fprintf (stderr, "WARNING: %s is greater than 128kb - ignoring\n", val);
5289 while ((line = get_next_line (content, &content)))
5290 g_ptr_array_add (filters, g_strchug (g_strchomp (line)));
5292 fclose (filter_file);
5293 continue;
5295 if ((opt = match_option (p, "covfilter", &val)) != p) {
5296 if (filters == NULL)
5297 filters = g_ptr_array_new ();
5299 g_ptr_array_add (filters, val);
5300 continue;
5302 if (opt == p) {
5303 usage (0);
5304 exit (0);
5307 if (calls_enabled) {
5308 events |= MONO_PROFILE_ENTER_LEAVE;
5309 nocalls = 0;
5311 if (allocs_enabled)
5312 events |= MONO_PROFILE_ALLOCATIONS;
5314 // Only activate the bare minimum events the profiler needs to function.
5315 if (only_coverage) {
5316 if (!do_coverage) {
5317 fprintf (stderr, "The onlycoverage option is only valid when paired with the coverage option\n");
5318 exit (1);
5321 events = MONO_PROFILE_GC | MONO_PROFILE_THREADS | MONO_PROFILE_ENTER_LEAVE | MONO_PROFILE_INS_COVERAGE;
5324 init_time ();
5326 PROF_TLS_INIT ();
5328 prof = create_profiler (desc, filename, filters);
5329 if (!prof) {
5330 PROF_TLS_FREE ();
5331 return;
5334 mono_lls_init (&profiler_thread_list, NULL);
5336 init_thread (TRUE);
5338 mono_profiler_install (prof, log_shutdown);
5339 mono_profiler_install_gc (gc_event, gc_resize);
5340 mono_profiler_install_allocation (gc_alloc);
5341 mono_profiler_install_gc_moves (gc_moves);
5342 mono_profiler_install_gc_roots (gc_handle, gc_roots);
5343 mono_profiler_install_gc_finalize (finalize_begin, finalize_object_begin, finalize_object_end, finalize_end);
5344 mono_profiler_install_appdomain (NULL, domain_loaded, domain_unloaded, NULL);
5345 mono_profiler_install_appdomain_name (domain_name);
5346 mono_profiler_install_context (context_loaded, context_unloaded);
5347 mono_profiler_install_class (NULL, class_loaded, class_unloaded, NULL);
5348 mono_profiler_install_module (NULL, image_loaded, image_unloaded, NULL);
5349 mono_profiler_install_assembly (NULL, assembly_loaded, assembly_unloaded, NULL);
5350 mono_profiler_install_thread (thread_start, thread_end);
5351 mono_profiler_install_thread_name (thread_name);
5352 mono_profiler_install_enter_leave (method_enter, method_leave);
5353 mono_profiler_install_jit_end (method_jitted);
5354 mono_profiler_install_code_buffer_new (code_buffer_new);
5355 mono_profiler_install_exception (throw_exc, method_exc_leave, clause_exc);
5356 mono_profiler_install_monitor (monitor_event);
5357 mono_profiler_install_runtime_initialized (runtime_initialized);
5358 if (do_coverage)
5359 mono_profiler_install_coverage_filter (coverage_filter);
5361 if (do_mono_sample && sample_type == SAMPLE_CYCLES && sample_freq) {
5362 events |= MONO_PROFILE_STATISTICAL;
5363 mono_profiler_set_statistical_mode (sampling_mode, sample_freq);
5364 mono_profiler_install_statistical (mono_sample_hit);
5367 mono_profiler_set_events ((MonoProfileFlags)events);