X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fprofiler%2Flog.c;h=8a677c2720d48bba530592351a5f6283d87f970b;hb=bfc76bafbf72f8e10c4d2cc7b12e779715c527fa;hp=d84ad572ade2d08054aa26d751a3c2685d978dea;hpb=501ded4a5e8ff38c73f10c7e0276160e8ed9e75e;p=mono.git diff --git a/mono/profiler/log.c b/mono/profiler/log.c index d84ad572ade..8a677c2720d 100644 --- a/mono/profiler/log.c +++ b/mono/profiler/log.c @@ -12,16 +12,20 @@ #include #include +#include #include -#include "../metadata/metadata-internals.h" +#include +#include #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -55,291 +59,6 @@ #include #endif -/* - * file format: - * [header] [buffer]* - * - * The file is composed by a header followed by 0 or more buffers. - * Each buffer contains events that happened on a thread: for a given thread - * buffers that appear later in the file are guaranteed to contain events - * that happened later in time. Buffers from separate threads could be interleaved, - * though. - * Buffers are not required to be aligned. - * - * header format: - * [id: 4 bytes] constant value: LOG_HEADER_ID - * [major: 1 byte] [minor: 1 byte] major and minor version of the log profiler - * [format: 1 byte] version of the data format for the rest of the file - * [ptrsize: 1 byte] size in bytes of a pointer in the profiled program - * [startup time: 8 bytes] time in milliseconds since the unix epoch when the program started - * [timer overhead: 4 bytes] approximate overhead in nanoseconds of the timer - * [flags: 4 bytes] file format flags, should be 0 for now - * [pid: 4 bytes] pid of the profiled process - * [port: 2 bytes] tcp port for server if != 0 - * [args size: 4 bytes] size of args - * [args: string] arguments passed to the profiler - * [arch size: 4 bytes] size of arch - * [arch: string] architecture the profiler is running on - * [os size: 4 bytes] size of os - * [os: string] operating system the profiler is running on - * - * The multiple byte integers are in little-endian format. - * - * buffer format: - * [buffer header] [event]* - * Buffers have a fixed-size header followed by 0 or more bytes of event data. - * Timing information and other values in the event data are usually stored - * as uleb128 or sleb128 integers. To save space, as noted for each item below, - * some data is represented as a difference between the actual value and - * either the last value of the same type (like for timing information) or - * as the difference from a value stored in a buffer header. - * - * For timing information the data is stored as uleb128, since timing - * increases in a monotonic way in each thread: the value is the number of - * nanoseconds to add to the last seen timing data in a buffer. The first value - * in a buffer will be calculated from the time_base field in the buffer head. - * - * Object or heap sizes are stored as uleb128. - * Pointer differences are stored as sleb128, instead. - * - * If an unexpected value is found, the rest of the buffer should be ignored, - * as generally the later values need the former to be interpreted correctly. - * - * buffer header format: - * [bufid: 4 bytes] constant value: BUF_ID - * [len: 4 bytes] size of the data following the buffer header - * [time_base: 8 bytes] time base in nanoseconds since an unspecified epoch - * [ptr_base: 8 bytes] base value for pointers - * [obj_base: 8 bytes] base value for object addresses - * [thread id: 8 bytes] system-specific thread ID (pthread_t for example) - * [method_base: 8 bytes] base value for MonoMethod pointers - * - * event format: - * [extended info: upper 4 bits] [type: lower 4 bits] - * [time diff: uleb128] nanoseconds since last timing - * [data]* - * The data that follows depends on type and the extended info. - * Type is one of the enum values in mono-profiler-log.h: TYPE_ALLOC, TYPE_GC, - * TYPE_METADATA, TYPE_METHOD, TYPE_EXCEPTION, TYPE_MONITOR, TYPE_HEAP. - * The extended info bits are interpreted based on type, see - * each individual event description below. - * strings are represented as a 0-terminated utf8 sequence. - * - * backtrace format: - * [num: uleb128] number of frames following - * [frame: sleb128]* mum MonoMethod* as a pointer difference from the last such - * pointer or the buffer method_base - * - * type alloc format: - * type: TYPE_ALLOC - * exinfo: zero or TYPE_ALLOC_BT - * [ptr: sleb128] class as a byte difference from ptr_base - * [obj: sleb128] object address as a byte difference from obj_base - * [size: uleb128] size of the object in the heap - * If exinfo == TYPE_ALLOC_BT, a backtrace follows. - * - * type GC format: - * type: TYPE_GC - * exinfo: one of TYPE_GC_EVENT, TYPE_GC_RESIZE, TYPE_GC_MOVE, TYPE_GC_HANDLE_CREATED[_BT], - * TYPE_GC_HANDLE_DESTROYED[_BT], TYPE_GC_FINALIZE_START, TYPE_GC_FINALIZE_END, - * TYPE_GC_FINALIZE_OBJECT_START, TYPE_GC_FINALIZE_OBJECT_END - * if exinfo == TYPE_GC_RESIZE - * [heap_size: uleb128] new heap size - * if exinfo == TYPE_GC_EVENT - * [event type: byte] GC event (MONO_GC_EVENT_* from profiler.h) - * [generation: byte] GC generation event refers to - * if exinfo == TYPE_GC_MOVE - * [num_objects: uleb128] number of object moves that follow - * [objaddr: sleb128]+ num_objects object pointer differences from obj_base - * num is always an even number: the even items are the old - * addresses, the odd numbers are the respective new object addresses - * if exinfo == TYPE_GC_HANDLE_CREATED[_BT] - * [handle_type: uleb128] MonoGCHandleType enum value - * upper bits reserved as flags - * [handle: uleb128] GC handle value - * [objaddr: sleb128] object pointer differences from obj_base - * If exinfo == TYPE_GC_HANDLE_CREATED_BT, a backtrace follows. - * if exinfo == TYPE_GC_HANDLE_DESTROYED[_BT] - * [handle_type: uleb128] MonoGCHandleType enum value - * upper bits reserved as flags - * [handle: uleb128] GC handle value - * If exinfo == TYPE_GC_HANDLE_DESTROYED_BT, a backtrace follows. - * if exinfo == TYPE_GC_FINALIZE_OBJECT_{START,END} - * [object: sleb128] the object as a difference from obj_base - * - * type metadata format: - * type: TYPE_METADATA - * exinfo: one of: TYPE_END_LOAD, TYPE_END_UNLOAD (optional for TYPE_THREAD and TYPE_DOMAIN, - * doesn't occur for TYPE_CLASS) - * [mtype: byte] metadata type, one of: TYPE_CLASS, TYPE_IMAGE, TYPE_ASSEMBLY, TYPE_DOMAIN, - * TYPE_THREAD, TYPE_CONTEXT - * [pointer: sleb128] pointer of the metadata type depending on mtype - * if mtype == TYPE_CLASS - * [image: sleb128] MonoImage* as a pointer difference from ptr_base - * [name: string] full class name - * if mtype == TYPE_IMAGE - * [name: string] image file name - * if mtype == TYPE_ASSEMBLY - * [image: sleb128] MonoImage* as a pointer difference from ptr_base - * [name: string] assembly name - * if mtype == TYPE_DOMAIN && exinfo == 0 - * [name: string] domain friendly name - * if mtype == TYPE_CONTEXT - * [domain: sleb128] domain id as pointer - * if mtype == TYPE_THREAD && exinfo == 0 - * [name: string] thread name - * - * type method format: - * type: TYPE_METHOD - * exinfo: one of: TYPE_LEAVE, TYPE_ENTER, TYPE_EXC_LEAVE, TYPE_JIT - * [method: sleb128] MonoMethod* as a pointer difference from the last such - * pointer or the buffer method_base - * if exinfo == TYPE_JIT - * [code address: sleb128] pointer to the native code as a diff from ptr_base - * [code size: uleb128] size of the generated code - * [name: string] full method name - * - * type exception format: - * type: TYPE_EXCEPTION - * exinfo: zero, TYPE_CLAUSE, or TYPE_THROW_BT - * if exinfo == TYPE_CLAUSE - * [clause type: byte] MonoExceptionEnum enum value - * [clause index: uleb128] index of the current clause - * [method: sleb128] MonoMethod* as a pointer difference from the last such - * pointer or the buffer method_base - * [object: sleb128] the exception object as a difference from obj_base - * else - * [object: sleb128] the exception object as a difference from obj_base - * If exinfo == TYPE_THROW_BT, a backtrace follows. - * - * type runtime format: - * type: TYPE_RUNTIME - * exinfo: one of: TYPE_JITHELPER - * if exinfo == TYPE_JITHELPER - * [type: byte] MonoProfilerCodeBufferType enum value - * [buffer address: sleb128] pointer to the native code as a diff from ptr_base - * [buffer size: uleb128] size of the generated code - * if type == MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE - * [name: string] buffer description name - * - * type monitor format: - * type: TYPE_MONITOR - * exinfo: zero or TYPE_MONITOR_BT - * [type: byte] MonoProfilerMonitorEvent enum value - * [object: sleb128] the lock object as a difference from obj_base - * If exinfo == TYPE_MONITOR_BT, a backtrace follows. - * - * type heap format - * type: TYPE_HEAP - * exinfo: one of TYPE_HEAP_START, TYPE_HEAP_END, TYPE_HEAP_OBJECT, TYPE_HEAP_ROOT - * if exinfo == TYPE_HEAP_OBJECT - * [object: sleb128] the object as a difference from obj_base - * [class: sleb128] the object MonoClass* as a difference from ptr_base - * [size: uleb128] size of the object on the heap - * [num_refs: uleb128] number of object references - * each referenced objref is preceded by a uleb128 encoded offset: the - * first offset is from the object address and each next offset is relative - * to the previous one - * [objrefs: sleb128]+ object referenced as a difference from obj_base - * The same object can appear multiple times, but only the first time - * with size != 0: in the other cases this data will only be used to - * provide additional referenced objects. - * if exinfo == TYPE_HEAP_ROOT - * [num_roots: uleb128] number of root references - * [num_gc: uleb128] number of major gcs - * [object: sleb128] the object as a difference from obj_base - * [root_type: byte] the root_type: MonoProfileGCRootType (profiler.h) - * [extra_info: uleb128] the extra_info value - * object, root_type and extra_info are repeated num_roots times - * - * type sample format - * type: TYPE_SAMPLE - * exinfo: one of TYPE_SAMPLE_HIT, TYPE_SAMPLE_USYM, TYPE_SAMPLE_UBIN, TYPE_SAMPLE_COUNTERS_DESC, TYPE_SAMPLE_COUNTERS - * if exinfo == TYPE_SAMPLE_HIT - * [thread: sleb128] thread id as difference from ptr_base - * [count: uleb128] number of following instruction addresses - * [ip: sleb128]* instruction pointer as difference from ptr_base - * [mbt_count: uleb128] number of managed backtrace frames - * [method: sleb128]* MonoMethod* as a pointer difference from the last such - * pointer or the buffer method_base (the first such method can be also indentified by ip, but this is not neccessarily true) - * if exinfo == TYPE_SAMPLE_USYM - * [address: sleb128] symbol address as a difference from ptr_base - * [size: uleb128] symbol size (may be 0 if unknown) - * [name: string] symbol name - * if exinfo == TYPE_SAMPLE_UBIN - * [address: sleb128] address where binary has been loaded as a difference from ptr_base - * [offset: uleb128] file offset of mapping (the same file can be mapped multiple times) - * [size: uleb128] memory size - * [name: string] binary name - * if exinfo == TYPE_SAMPLE_COUNTERS_DESC - * [len: uleb128] number of counters - * for i = 0 to len - * [section: uleb128] section of counter - * if section == MONO_COUNTER_PERFCOUNTERS: - * [section_name: string] section name of counter - * [name: string] name of counter - * [type: byte] type of counter - * [unit: byte] unit of counter - * [variance: byte] variance of counter - * [index: uleb128] unique index of counter - * if exinfo == TYPE_SAMPLE_COUNTERS - * while true: - * [index: uleb128] unique index of counter - * if index == 0: - * break - * [type: byte] type of counter value - * if type == string: - * if value == null: - * [0: byte] 0 -> value is null - * else: - * [1: byte] 1 -> value is not null - * [value: string] counter value - * else: - * [value: uleb128/sleb128/double] counter value, can be sleb128, uleb128 or double (determined by using type) - * - * type coverage format - * type: TYPE_COVERAGE - * exinfo: one of TYPE_COVERAGE_METHOD, TYPE_COVERAGE_STATEMENT, TYPE_COVERAGE_ASSEMBLY, TYPE_COVERAGE_CLASS - * if exinfo == TYPE_COVERAGE_METHOD - * [assembly: string] name of assembly - * [class: string] name of the class - * [name: string] name of the method - * [signature: string] the signature of the method - * [filename: string] the file path of the file that contains this method - * [token: uleb128] the method token - * [method_id: uleb128] an ID for this data to associate with the buffers of TYPE_COVERAGE_STATEMENTS - * [len: uleb128] the number of TYPE_COVERAGE_BUFFERS associated with this method - * if exinfo == TYPE_COVERAGE_STATEMENTS - * [method_id: uleb128] an the TYPE_COVERAGE_METHOD buffer to associate this with - * [offset: uleb128] the il offset relative to the previous offset - * [counter: uleb128] the counter for this instruction - * [line: uleb128] the line of filename containing this instruction - * [column: uleb128] the column containing this instruction - * if exinfo == TYPE_COVERAGE_ASSEMBLY - * [name: string] assembly name - * [guid: string] assembly GUID - * [filename: string] assembly filename - * [number_of_methods: uleb128] the number of methods in this assembly - * [fully_covered: uleb128] the number of fully covered methods - * [partially_covered: uleb128] the number of partially covered methods - * currently partially_covered will always be 0, and fully_covered is the - * number of methods that are fully and partially covered. - * if exinfo == TYPE_COVERAGE_CLASS - * [name: string] assembly name - * [class: string] class name - * [number_of_methods: uleb128] the number of methods in this class - * [fully_covered: uleb128] the number of fully covered methods - * [partially_covered: uleb128] the number of partially covered methods - * currently partially_covered will always be 0, and fully_covered is the - * number of methods that are fully and partially covered. - * - * type meta format: - * type: TYPE_META - * exinfo: one of: TYPE_SYNC_POINT - * if exinfo == TYPE_SYNC_POINT - * [type: byte] MonoProfilerSyncPointType enum value - */ - // Statistics for internal profiler data structures. static gint32 sample_allocations_ctr, buffer_allocations_ctr; @@ -494,7 +213,9 @@ process_id (void) #endif } -#define ENABLED(EVT) (log_config.effective_mask & (EVT)) +#define ENABLED(EVT) (!!(log_config.effective_mask & (EVT))) +#define ENABLE(EVT) do { log_config.effective_mask |= (EVT); } while (0) +#define DISABLE(EVT) do { log_config.effective_mask &= ~(EVT); } while (0) /* * These macros should be used when writing an event to a log buffer. They @@ -617,11 +338,10 @@ struct _MonoProfiler { BinaryObject *binary_objects; - gboolean heapshot_requested; + volatile gint32 heapshot_requested; guint64 gc_count; guint64 last_hs_time; gboolean do_heap_walk; - gboolean ignore_heap_events; mono_mutex_t counters_mutex; MonoCounterAgent *counters; @@ -643,6 +363,8 @@ struct _MonoProfiler { guint32 coverage_previous_offset; guint32 coverage_method_id; + + MonoCoopMutex api_mutex; }; static ProfilerConfig log_config; @@ -1312,17 +1034,15 @@ free_thread (gpointer p) InterlockedIncrement (&thread_ends_ctr); - if (ENABLED (PROFLOG_THREAD_EVENTS)) { - LogBuffer *buf = ensure_logbuf_unsafe (thread, - EVENT_SIZE /* event */ + - BYTE_SIZE /* type */ + - LEB128_SIZE /* tid */ - ); + LogBuffer *buf = ensure_logbuf_unsafe (thread, + EVENT_SIZE /* event */ + + BYTE_SIZE /* type */ + + LEB128_SIZE /* tid */ + ); - emit_event (buf, TYPE_END_UNLOAD | TYPE_METADATA); - emit_byte (buf, TYPE_THREAD); - emit_ptr (buf, (void *) thread->node.key); - } + emit_event (buf, TYPE_END_UNLOAD | TYPE_METADATA); + emit_byte (buf, TYPE_THREAD); + emit_ptr (buf, (void *) thread->node.key); } send_buffer (thread); @@ -1422,7 +1142,7 @@ sync_point_mark (MonoProfilerSyncPointType type) ENTER_LOG (&sync_points_ctr, logbuffer, EVENT_SIZE /* event */ + - LEB128_SIZE /* type */ + BYTE_SIZE /* type */ ); emit_event (logbuffer, TYPE_META | TYPE_SYNC_POINT); @@ -1482,9 +1202,6 @@ gc_reference (MonoObject *obj, MonoClass *klass, uintptr_t size, uintptr_t num, static void gc_roots (MonoProfiler *prof, MonoObject *const *objects, const MonoProfilerGCRootType *root_types, const uintptr_t *extra_info, uint64_t num) { - if (log_profiler.ignore_heap_events) - return; - ENTER_LOG (&heap_roots_ctr, logbuffer, EVENT_SIZE /* event */ + LEB128_SIZE /* num */ + @@ -1502,7 +1219,7 @@ gc_roots (MonoProfiler *prof, MonoObject *const *objects, const MonoProfilerGCRo for (int i = 0; i < num; ++i) { emit_obj (logbuffer, objects [i]); - emit_byte (logbuffer, root_types [i]); + emit_value (logbuffer, root_types [i]); emit_value (logbuffer, extra_info [i]); } @@ -1513,35 +1230,15 @@ gc_roots (MonoProfiler *prof, MonoObject *const *objects, const MonoProfilerGCRo static void trigger_on_demand_heapshot (void) { - if (log_profiler.heapshot_requested) + if (InterlockedRead (&log_profiler.heapshot_requested)) mono_gc_collect (mono_gc_max_generation ()); } -#define ALL_GC_EVENTS_MASK (PROFLOG_GC_MOVES_EVENTS | PROFLOG_GC_ROOT_EVENTS | PROFLOG_GC_EVENTS | PROFLOG_HEAPSHOT_FEATURE) +#define ALL_GC_EVENTS_MASK (PROFLOG_GC_EVENTS | PROFLOG_GC_MOVE_EVENTS | PROFLOG_GC_ROOT_EVENTS) static void gc_event (MonoProfiler *profiler, MonoProfilerGCEvent ev, uint32_t generation) { - if (ev == MONO_GC_EVENT_START) { - uint64_t now = current_time (); - - if (log_config.hs_mode_ms && (now - log_profiler.last_hs_time) / 1000 * 1000 >= log_config.hs_mode_ms) - log_profiler.do_heap_walk = TRUE; - else if (log_config.hs_mode_gc && !(log_profiler.gc_count % log_config.hs_mode_gc)) - log_profiler.do_heap_walk = TRUE; - else if (log_config.hs_mode_ondemand) - log_profiler.do_heap_walk = log_profiler.heapshot_requested; - else if (!log_config.hs_mode_ms && !log_config.hs_mode_gc && generation == mono_gc_max_generation ()) - log_profiler.do_heap_walk = TRUE; - - //If using heapshot, ignore events for collections we don't care - if (ENABLED (PROFLOG_HEAPSHOT_FEATURE)) { - // Ignore events generated during the collection itself (IE GC ROOTS) - log_profiler.ignore_heap_events = !log_profiler.do_heap_walk; - } - } - - if (ENABLED (PROFLOG_GC_EVENTS)) { ENTER_LOG (&gc_events_ctr, logbuffer, EVENT_SIZE /* event */ + @@ -1557,17 +1254,46 @@ gc_event (MonoProfiler *profiler, MonoProfilerGCEvent ev, uint32_t generation) } switch (ev) { - case MONO_GC_EVENT_START: - if (generation == mono_gc_max_generation ()) - log_profiler.gc_count++; - - break; case MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED: + switch (log_config.hs_mode) { + case MONO_PROFILER_HEAPSHOT_NONE: + log_profiler.do_heap_walk = FALSE; + break; + case MONO_PROFILER_HEAPSHOT_MAJOR: + log_profiler.do_heap_walk = generation == mono_gc_max_generation (); + break; + case MONO_PROFILER_HEAPSHOT_ON_DEMAND: + log_profiler.do_heap_walk = InterlockedRead (&log_profiler.heapshot_requested); + break; + case MONO_PROFILER_HEAPSHOT_X_GC: + log_profiler.do_heap_walk = !(log_profiler.gc_count % log_config.hs_freq_gc); + break; + case MONO_PROFILER_HEAPSHOT_X_MS: + log_profiler.do_heap_walk = (current_time () - log_profiler.last_hs_time) / 1000 * 1000 >= log_config.hs_freq_ms; + break; + default: + g_assert_not_reached (); + } + + /* + * heapshot_requested is set either because on-demand heapshot is + * enabled and a heapshot was triggered, or because we're doing a + * shutdown heapshot. In the latter case, we won't check it in the + * switch above, so check it here and override any decision we made + * above. + */ + if (InterlockedRead (&log_profiler.heapshot_requested)) + log_profiler.do_heap_walk = TRUE; + + if (ENABLED (PROFLOG_GC_ROOT_EVENTS) && log_profiler.do_heap_walk) + mono_profiler_set_gc_roots_callback (log_profiler.handle, gc_roots); + /* * Ensure that no thread can be in the middle of writing to * a buffer when the world stops... */ buffer_lock_excl (); + break; case MONO_GC_EVENT_POST_STOP_WORLD: /* @@ -1580,11 +1306,8 @@ gc_event (MonoProfiler *profiler, MonoProfilerGCEvent ev, uint32_t generation) if (ENABLED (ALL_GC_EVENTS_MASK)) sync_point (SYNC_POINT_WORLD_STOP); - /* - * All heap events are surrounded by a HEAP_START and a HEAP_ENV event. - * Right now, that's the case for GC Moves, GC Roots or heapshots. - */ - if (ENABLED (PROFLOG_GC_MOVES_EVENTS | PROFLOG_GC_ROOT_EVENTS) || log_profiler.do_heap_walk) { + // Surround heapshots with HEAP_START/HEAP_END events. + if (log_profiler.do_heap_walk) { ENTER_LOG (&heap_starts_ctr, logbuffer, EVENT_SIZE /* event */ ); @@ -1594,13 +1317,18 @@ gc_event (MonoProfiler *profiler, MonoProfilerGCEvent ev, uint32_t generation) EXIT_LOG; } + break; + case MONO_GC_EVENT_START: + if (generation == mono_gc_max_generation ()) + log_profiler.gc_count++; + break; case MONO_GC_EVENT_PRE_START_WORLD: - if (ENABLED (PROFLOG_HEAPSHOT_FEATURE) && log_profiler.do_heap_walk) + mono_profiler_set_gc_roots_callback (log_profiler.handle, NULL); + + if (log_profiler.do_heap_walk) { mono_gc_walk_heap (0, gc_reference, NULL); - /* Matching HEAP_END to the HEAP_START from above */ - if (ENABLED (PROFLOG_GC_MOVES_EVENTS | PROFLOG_GC_ROOT_EVENTS) || log_profiler.do_heap_walk) { ENTER_LOG (&heap_ends_ctr, logbuffer, EVENT_SIZE /* event */ ); @@ -1608,12 +1336,11 @@ gc_event (MonoProfiler *profiler, MonoProfilerGCEvent ev, uint32_t generation) emit_event (logbuffer, TYPE_HEAP_END | TYPE_HEAP); EXIT_LOG; - } - if (ENABLED (PROFLOG_HEAPSHOT_FEATURE) && log_profiler.do_heap_walk) { log_profiler.do_heap_walk = FALSE; - log_profiler.heapshot_requested = FALSE; log_profiler.last_hs_time = current_time (); + + InterlockedWrite (&log_profiler.heapshot_requested, 0); } /* @@ -1624,6 +1351,7 @@ gc_event (MonoProfiler *profiler, MonoProfilerGCEvent ev, uint32_t generation) */ if (ENABLED (ALL_GC_EVENTS_MASK)) sync_point_mark (SYNC_POINT_WORLD_START); + break; case MONO_GC_EVENT_POST_START_WORLD_UNLOCKED: /* @@ -1631,6 +1359,7 @@ gc_event (MonoProfiler *profiler, MonoProfilerGCEvent ev, uint32_t generation) * their buffers again. */ buffer_unlock_excl (); + break; default: break; @@ -1694,7 +1423,7 @@ emit_bt (LogBuffer *logbuffer, FrameData *data) static void gc_alloc (MonoProfiler *prof, MonoObject *obj) { - int do_bt = (!ENABLED (PROFLOG_CALL_EVENTS) && InterlockedRead (&log_profiler.runtime_inited) && !log_config.notraces) ? TYPE_ALLOC_BT : 0; + int do_bt = (!log_config.enter_leave && InterlockedRead (&log_profiler.runtime_inited) && log_config.num_frames) ? TYPE_ALLOC_BT : 0; FrameData data; uintptr_t len = mono_object_get_size (obj); /* account for object alignment in the heap */ @@ -1751,7 +1480,7 @@ gc_moves (MonoProfiler *prof, MonoObject *const *objects, uint64_t num) static void gc_handle (MonoProfiler *prof, int op, MonoGCHandleType type, uint32_t handle, MonoObject *obj) { - int do_bt = !ENABLED (PROFLOG_CALL_EVENTS) && InterlockedRead (&log_profiler.runtime_inited) && !log_config.notraces; + int do_bt = !log_config.enter_leave && InterlockedRead (&log_profiler.runtime_inited) && log_config.num_frames; FrameData data; if (do_bt) @@ -1821,7 +1550,7 @@ static void finalize_end (MonoProfiler *prof) { trigger_on_demand_heapshot (); - if (ENABLED (PROFLOG_FINALIZATION_EVENTS)) { + if (ENABLED (PROFLOG_GC_FINALIZATION_EVENTS)) { ENTER_LOG (&finalize_ends_ctr, buf, EVENT_SIZE /* event */ ); @@ -2031,7 +1760,7 @@ class_loaded (MonoProfiler *prof, MonoClass *klass) } static void -method_enter (MonoProfiler *prof, MonoMethod *method) +method_enter (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallContext *ctx) { if (get_thread ()->call_depth++ <= log_config.max_call_depth) { ENTER_LOG (&method_entries_ctr, logbuffer, @@ -2047,7 +1776,7 @@ method_enter (MonoProfiler *prof, MonoMethod *method) } static void -method_leave (MonoProfiler *prof, MonoMethod *method) +method_leave (MonoProfiler *prof, MonoMethod *method, MonoProfilerCallContext *ctx) { if (--get_thread ()->call_depth <= log_config.max_call_depth) { ENTER_LOG (&method_exits_ctr, logbuffer, @@ -2062,6 +1791,12 @@ method_leave (MonoProfiler *prof, MonoMethod *method) } } +static void +tail_call (MonoProfiler *prof, MonoMethod *method, MonoMethod *target) +{ + method_leave (prof, method, NULL); +} + static void method_exc_leave (MonoProfiler *prof, MonoMethod *method, MonoObject *exc) { @@ -2081,7 +1816,14 @@ method_exc_leave (MonoProfiler *prof, MonoMethod *method, MonoObject *exc) static MonoProfilerCallInstrumentationFlags method_filter (MonoProfiler *prof, MonoMethod *method) { - return MONO_PROFILER_CALL_INSTRUMENTATION_PROLOGUE | MONO_PROFILER_CALL_INSTRUMENTATION_EPILOGUE; + if (log_config.callspec.len > 0 && + !mono_callspec_eval (method, &log_config.callspec)) + return MONO_PROFILER_CALL_INSTRUMENTATION_NONE; + + return MONO_PROFILER_CALL_INSTRUMENTATION_ENTER | + MONO_PROFILER_CALL_INSTRUMENTATION_LEAVE | + MONO_PROFILER_CALL_INSTRUMENTATION_TAIL_CALL | + MONO_PROFILER_CALL_INSTRUMENTATION_EXCEPTION_LEAVE; } static void @@ -2134,7 +1876,7 @@ code_buffer_new (MonoProfiler *prof, const mono_byte *buffer, uint64_t size, Mon static void throw_exc (MonoProfiler *prof, MonoObject *object) { - int do_bt = (!ENABLED (PROFLOG_CALL_EVENTS) && InterlockedRead (&log_profiler.runtime_inited) && !log_config.notraces) ? TYPE_THROW_BT : 0; + int do_bt = (!log_config.enter_leave && InterlockedRead (&log_profiler.runtime_inited) && log_config.num_frames) ? TYPE_THROW_BT : 0; FrameData data; if (do_bt) @@ -2167,7 +1909,8 @@ clause_exc (MonoProfiler *prof, MonoMethod *method, uint32_t clause_num, MonoExc EVENT_SIZE /* event */ + BYTE_SIZE /* clause type */ + LEB128_SIZE /* clause num */ + - LEB128_SIZE /* method */ + LEB128_SIZE /* method */ + + LEB128_SIZE /* exc */ ); emit_event (logbuffer, TYPE_EXCEPTION | TYPE_CLAUSE); @@ -2182,7 +1925,7 @@ clause_exc (MonoProfiler *prof, MonoMethod *method, uint32_t clause_num, MonoExc static void monitor_event (MonoProfiler *profiler, MonoObject *object, MonoProfilerMonitorEvent ev) { - int do_bt = (!ENABLED (PROFLOG_CALL_EVENTS) && InterlockedRead (&log_profiler.runtime_inited) && !log_config.notraces) ? TYPE_MONITOR_BT : 0; + int do_bt = (!log_config.enter_leave && InterlockedRead (&log_profiler.runtime_inited) && log_config.num_frames) ? TYPE_MONITOR_BT : 0; FrameData data; if (do_bt) @@ -2231,37 +1974,33 @@ monitor_failed (MonoProfiler *prof, MonoObject *object) static void thread_start (MonoProfiler *prof, uintptr_t tid) { - if (ENABLED (PROFLOG_THREAD_EVENTS)) { - ENTER_LOG (&thread_starts_ctr, logbuffer, - EVENT_SIZE /* event */ + - BYTE_SIZE /* type */ + - LEB128_SIZE /* tid */ - ); + ENTER_LOG (&thread_starts_ctr, logbuffer, + EVENT_SIZE /* event */ + + BYTE_SIZE /* type */ + + LEB128_SIZE /* tid */ + ); - emit_event (logbuffer, TYPE_END_LOAD | TYPE_METADATA); - emit_byte (logbuffer, TYPE_THREAD); - emit_ptr (logbuffer, (void*) tid); + emit_event (logbuffer, TYPE_END_LOAD | TYPE_METADATA); + emit_byte (logbuffer, TYPE_THREAD); + emit_ptr (logbuffer, (void*) tid); - EXIT_LOG; - } + EXIT_LOG; } static void thread_end (MonoProfiler *prof, uintptr_t tid) { - if (ENABLED (PROFLOG_THREAD_EVENTS)) { - ENTER_LOG (&thread_ends_ctr, logbuffer, - EVENT_SIZE /* event */ + - BYTE_SIZE /* type */ + - LEB128_SIZE /* tid */ - ); + ENTER_LOG (&thread_ends_ctr, logbuffer, + EVENT_SIZE /* event */ + + BYTE_SIZE /* type */ + + LEB128_SIZE /* tid */ + ); - emit_event (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA); - emit_byte (logbuffer, TYPE_THREAD); - emit_ptr (logbuffer, (void*) tid); + emit_event (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA); + emit_byte (logbuffer, TYPE_THREAD); + emit_ptr (logbuffer, (void*) tid); - EXIT_LOG_EXPLICIT (NO_SEND); - } + EXIT_LOG_EXPLICIT (NO_SEND); MonoProfilerThread *thread = get_thread (); @@ -2276,22 +2015,20 @@ thread_name (MonoProfiler *prof, uintptr_t tid, const char *name) { int len = strlen (name) + 1; - if (ENABLED (PROFLOG_THREAD_EVENTS)) { - ENTER_LOG (&thread_names_ctr, logbuffer, - EVENT_SIZE /* event */ + - BYTE_SIZE /* type */ + - LEB128_SIZE /* tid */ + - len /* name */ - ); + ENTER_LOG (&thread_names_ctr, logbuffer, + EVENT_SIZE /* event */ + + BYTE_SIZE /* type */ + + LEB128_SIZE /* tid */ + + len /* name */ + ); - emit_event (logbuffer, TYPE_METADATA); - emit_byte (logbuffer, TYPE_THREAD); - emit_ptr (logbuffer, (void*)tid); - memcpy (logbuffer->cursor, name, len); - logbuffer->cursor += len; + emit_event (logbuffer, TYPE_METADATA); + emit_byte (logbuffer, TYPE_THREAD); + emit_ptr (logbuffer, (void*)tid); + memcpy (logbuffer->cursor, name, len); + logbuffer->cursor += len; - EXIT_LOG; - } + EXIT_LOG; } static void @@ -2536,7 +2273,7 @@ dump_ubin (const char *filename, uintptr_t load_addr, uint64_t offset, uintptr_t LEB128_SIZE /* load address */ + LEB128_SIZE /* offset */ + LEB128_SIZE /* size */ + - nlen /* file name */ + len /* file name */ ); emit_event (logbuffer, TYPE_SAMPLE | TYPE_SAMPLE_UBIN); @@ -2939,6 +2676,12 @@ counters_sample (uint64_t timestamp) ; for (agent = log_profiler.counters; agent; agent = agent->next) { + /* + * FIXME: This calculation is incorrect for string counters since + * mono_counter_get_size () just returns 0 in that case. We should + * address this if we ever actually add any string counters to Mono. + */ + size += LEB128_SIZE /* index */ + BYTE_SIZE /* type */ + @@ -3516,8 +3259,8 @@ coverage_filter (MonoProfiler *prof, MonoMethod *method) MonoLockFreeQueueNode *node; flags = mono_method_get_flags (method, &iflags); - if ((iflags & 0x1000 /*METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL*/) || - (flags & 0x2000 /*METHOD_ATTRIBUTE_PINVOKE_IMPL*/)) + if ((iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) || + (flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) return FALSE; // Don't need to do anything else if we're already tracking this method @@ -3777,6 +3520,15 @@ cleanup_reusable_samples (void) mono_thread_hazardous_try_free (sample, free_sample_hit); } +static void +log_early_shutdown (MonoProfiler *prof) +{ + if (log_config.hs_on_shutdown) { + InterlockedWrite (&log_profiler.heapshot_requested, 1); + mono_gc_collect (mono_gc_max_generation ()); + } +} + static void log_shutdown (MonoProfiler *prof) { @@ -3785,13 +3537,13 @@ log_shutdown (MonoProfiler *prof) if (ENABLED (PROFLOG_COUNTER_EVENTS)) counters_and_perfcounters_sample (); - if (ENABLED (PROFLOG_CODE_COV_FEATURE)) + if (log_config.collect_coverage) dump_coverage (); char c = 1; if (write (prof->pipes [1], &c, 1) != 1) { - mono_profiler_printf_err ("Could not write to log profiler pipe: %s", strerror (errno)); + mono_profiler_printf_err ("Could not write to log profiler pipe: %s", g_strerror (errno)); exit (1); } @@ -3875,7 +3627,7 @@ log_shutdown (MonoProfiler *prof) mono_conc_hashtable_destroy (prof->method_table); mono_os_mutex_destroy (&prof->method_table_mutex); - if (ENABLED (PROFLOG_CODE_COV_FEATURE)) { + if (log_config.collect_coverage) { mono_os_mutex_lock (&log_profiler.coverage_mutex); mono_conc_hashtable_foreach (log_profiler.coverage_assemblies, unref_coverage_assemblies, NULL); mono_os_mutex_unlock (&log_profiler.coverage_mutex); @@ -3890,6 +3642,8 @@ log_shutdown (MonoProfiler *prof) mono_os_mutex_destroy (&log_profiler.coverage_mutex); } + mono_coop_mutex_destroy (&log_profiler.api_mutex); + PROF_TLS_FREE (); g_free (prof->args); @@ -4003,7 +3757,7 @@ helper_thread (void *arg) if (errno == EINTR) continue; - mono_profiler_printf_err ("Could not poll in log profiler helper thread: %s", strerror (errno)); + mono_profiler_printf_err ("Could not poll in log profiler helper thread: %s", g_strerror (errno)); exit (1); } @@ -4045,9 +3799,9 @@ helper_thread (void *arg) buf [len] = 0; - if (!strcmp (buf, "heapshot\n") && log_config.hs_mode_ondemand) { + if (log_config.hs_mode == MONO_PROFILER_HEAPSHOT_ON_DEMAND && !strcmp (buf, "heapshot\n")) { // Rely on the finalization callback triggering a GC. - log_profiler.heapshot_requested = TRUE; + InterlockedWrite (&log_profiler.heapshot_requested, 1); mono_gc_finalize_notify (); } } @@ -4081,14 +3835,14 @@ static void start_helper_thread (void) { if (pipe (log_profiler.pipes) == -1) { - mono_profiler_printf_err ("Could not create log profiler pipe: %s", strerror (errno)); + mono_profiler_printf_err ("Could not create log profiler pipe: %s", g_strerror (errno)); exit (1); } log_profiler.server_socket = socket (PF_INET, SOCK_STREAM, 0); if (log_profiler.server_socket == -1) { - mono_profiler_printf_err ("Could not create log profiler server socket: %s", strerror (errno)); + mono_profiler_printf_err ("Could not create log profiler server socket: %s", g_strerror (errno)); exit (1); } @@ -4100,13 +3854,13 @@ start_helper_thread (void) server_address.sin_port = htons (log_profiler.command_port); if (bind (log_profiler.server_socket, (struct sockaddr *) &server_address, sizeof (server_address)) == -1) { - mono_profiler_printf_err ("Could not bind log profiler server socket on port %d: %s", log_profiler.command_port, strerror (errno)); + mono_profiler_printf_err ("Could not bind log profiler server socket on port %d: %s", log_profiler.command_port, g_strerror (errno)); close (log_profiler.server_socket); exit (1); } if (listen (log_profiler.server_socket, 1) == -1) { - mono_profiler_printf_err ("Could not listen on log profiler server socket: %s", strerror (errno)); + mono_profiler_printf_err ("Could not listen on log profiler server socket: %s", g_strerror (errno)); close (log_profiler.server_socket); exit (1); } @@ -4114,7 +3868,7 @@ start_helper_thread (void) socklen_t slen = sizeof (server_address); if (getsockname (log_profiler.server_socket, (struct sockaddr *) &server_address, &slen)) { - mono_profiler_printf_err ("Could not retrieve assigned port for log profiler server socket: %s", strerror (errno)); + mono_profiler_printf_err ("Could not retrieve assigned port for log profiler server socket: %s", g_strerror (errno)); close (log_profiler.server_socket); exit (1); } @@ -4381,6 +4135,325 @@ register_counter (const char *name, gint32 *counter) mono_counters_register (name, MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, counter); } +ICALL_EXPORT gint32 +proflog_icall_GetMaxStackTraceFrames (void) +{ + return MAX_FRAMES; +} + +ICALL_EXPORT gint32 +proflog_icall_GetStackTraceFrames (void) +{ + return log_config.num_frames; +} + +ICALL_EXPORT void +proflog_icall_SetStackTraceFrames (gint32 value) +{ + log_config.num_frames = value; +} + +ICALL_EXPORT MonoProfilerHeapshotMode +proflog_icall_GetHeapshotMode (void) +{ + return log_config.hs_mode; +} + +ICALL_EXPORT void +proflog_icall_SetHeapshotMode (MonoProfilerHeapshotMode value) +{ + log_config.hs_mode = value; +} + +ICALL_EXPORT gint32 +proflog_icall_GetHeapshotMillisecondsFrequency (void) +{ + return log_config.hs_freq_ms; +} + +ICALL_EXPORT void +proflog_icall_SetHeapshotMillisecondsFrequency (gint32 value) +{ + log_config.hs_freq_ms = value; +} + +ICALL_EXPORT gint32 +proflog_icall_GetHeapshotCollectionsFrequency (void) +{ + return log_config.hs_freq_gc; +} + +ICALL_EXPORT void +proflog_icall_SetHeapshotCollectionsFrequency (gint32 value) +{ + log_config.hs_freq_gc = value; +} + +ICALL_EXPORT gint32 +proflog_icall_GetCallDepth (void) +{ + return log_config.max_call_depth; +} + +ICALL_EXPORT void +proflog_icall_SetCallDepth (gint32 value) +{ + log_config.max_call_depth = value; +} + +ICALL_EXPORT void +proflog_icall_GetSampleMode (MonoProfilerSampleMode *mode, gint32 *frequency) +{ + uint32_t freq; + + mono_profiler_get_sample_mode (log_profiler.handle, mode, &freq); + + *frequency = freq; +} + +ICALL_EXPORT MonoBoolean +proflog_icall_SetSampleMode (MonoProfilerSampleMode mode, gint32 frequency) +{ + mono_coop_mutex_lock (&log_profiler.api_mutex); + + mono_bool result = mono_profiler_set_sample_mode (log_profiler.handle, mode, frequency); + + if (mode != MONO_PROFILER_SAMPLE_MODE_NONE) { + ENABLE (PROFLOG_SAMPLE_EVENTS); + mono_profiler_set_sample_hit_callback (log_profiler.handle, mono_sample_hit); + } else { + DISABLE (PROFLOG_SAMPLE_EVENTS); + mono_profiler_set_sample_hit_callback (log_profiler.handle, NULL); + } + + mono_coop_mutex_unlock (&log_profiler.api_mutex); + + return result; +} + +ICALL_EXPORT MonoBoolean +proflog_icall_GetExceptionEvents (void) +{ + return ENABLED (PROFLOG_EXCEPTION_EVENTS); +} + +ICALL_EXPORT void +proflog_icall_SetExceptionEvents (MonoBoolean value) +{ + mono_coop_mutex_lock (&log_profiler.api_mutex); + + if (value) { + ENABLE (PROFLOG_EXCEPTION_EVENTS); + mono_profiler_set_exception_throw_callback (log_profiler.handle, throw_exc); + mono_profiler_set_exception_clause_callback (log_profiler.handle, clause_exc); + } else { + DISABLE (PROFLOG_EXCEPTION_EVENTS); + mono_profiler_set_exception_throw_callback (log_profiler.handle, NULL); + mono_profiler_set_exception_clause_callback (log_profiler.handle, NULL); + } + + mono_coop_mutex_unlock (&log_profiler.api_mutex); +} + +ICALL_EXPORT MonoBoolean +proflog_icall_GetMonitorEvents (void) +{ + return ENABLED (PROFLOG_MONITOR_EVENTS); +} + +ICALL_EXPORT void +proflog_icall_SetMonitorEvents (MonoBoolean value) +{ + mono_coop_mutex_lock (&log_profiler.api_mutex); + + if (value) { + ENABLE (PROFLOG_MONITOR_EVENTS); + mono_profiler_set_monitor_contention_callback (log_profiler.handle, monitor_contention); + mono_profiler_set_monitor_acquired_callback (log_profiler.handle, monitor_acquired); + mono_profiler_set_monitor_failed_callback (log_profiler.handle, monitor_failed); + } else { + DISABLE (PROFLOG_MONITOR_EVENTS); + mono_profiler_set_monitor_contention_callback (log_profiler.handle, NULL); + mono_profiler_set_monitor_acquired_callback (log_profiler.handle, NULL); + mono_profiler_set_monitor_failed_callback (log_profiler.handle, NULL); + } + + mono_coop_mutex_unlock (&log_profiler.api_mutex); +} + +ICALL_EXPORT MonoBoolean +proflog_icall_GetGCEvents (void) +{ + return ENABLED (PROFLOG_GC_EVENTS); +} + +ICALL_EXPORT void +proflog_icall_SetGCEvents (MonoBoolean value) +{ + mono_coop_mutex_lock (&log_profiler.api_mutex); + + if (value) + ENABLE (PROFLOG_GC_EVENTS); + else + DISABLE (PROFLOG_GC_EVENTS); + + mono_coop_mutex_unlock (&log_profiler.api_mutex); +} + +ICALL_EXPORT MonoBoolean +proflog_icall_GetGCAllocationEvents (void) +{ + return ENABLED (PROFLOG_GC_ALLOCATION_EVENTS); +} + +ICALL_EXPORT void +proflog_icall_SetGCAllocationEvents (MonoBoolean value) +{ + mono_coop_mutex_lock (&log_profiler.api_mutex); + + if (value) { + ENABLE (PROFLOG_GC_ALLOCATION_EVENTS); + mono_profiler_set_gc_allocation_callback (log_profiler.handle, gc_alloc); + } else { + DISABLE (PROFLOG_GC_ALLOCATION_EVENTS); + mono_profiler_set_gc_allocation_callback (log_profiler.handle, NULL); + } + + mono_coop_mutex_unlock (&log_profiler.api_mutex); +} + +ICALL_EXPORT MonoBoolean +proflog_icall_GetGCMoveEvents (void) +{ + return ENABLED (PROFLOG_GC_MOVE_EVENTS); +} + +ICALL_EXPORT void +proflog_icall_SetGCMoveEvents (MonoBoolean value) +{ + mono_coop_mutex_lock (&log_profiler.api_mutex); + + if (value) { + ENABLE (PROFLOG_GC_MOVE_EVENTS); + mono_profiler_set_gc_moves_callback (log_profiler.handle, gc_moves); + } else { + DISABLE (PROFLOG_GC_MOVE_EVENTS); + mono_profiler_set_gc_moves_callback (log_profiler.handle, NULL); + } + + mono_coop_mutex_unlock (&log_profiler.api_mutex); +} + +ICALL_EXPORT MonoBoolean +proflog_icall_GetGCRootEvents (void) +{ + return ENABLED (PROFLOG_GC_ROOT_EVENTS); +} + +ICALL_EXPORT void +proflog_icall_SetGCRootEvents (MonoBoolean value) +{ + mono_coop_mutex_lock (&log_profiler.api_mutex); + + if (value) + ENABLE (PROFLOG_GC_ROOT_EVENTS); + else + DISABLE (PROFLOG_GC_ROOT_EVENTS); + + mono_coop_mutex_unlock (&log_profiler.api_mutex); +} + +ICALL_EXPORT MonoBoolean +proflog_icall_GetGCHandleEvents (void) +{ + return ENABLED (PROFLOG_GC_HANDLE_EVENTS); +} + +ICALL_EXPORT void +proflog_icall_SetGCHandleEvents (MonoBoolean value) +{ + mono_coop_mutex_lock (&log_profiler.api_mutex); + + if (value) { + ENABLE (PROFLOG_GC_HANDLE_EVENTS); + mono_profiler_set_gc_handle_created_callback (log_profiler.handle, gc_handle_created); + mono_profiler_set_gc_handle_deleted_callback (log_profiler.handle, gc_handle_deleted); + } else { + DISABLE (PROFLOG_GC_HANDLE_EVENTS); + mono_profiler_set_gc_handle_created_callback (log_profiler.handle, NULL); + mono_profiler_set_gc_handle_deleted_callback (log_profiler.handle, NULL); + } + + mono_coop_mutex_unlock (&log_profiler.api_mutex); +} + +ICALL_EXPORT MonoBoolean +proflog_icall_GetGCFinalizationEvents (void) +{ + return ENABLED (PROFLOG_GC_FINALIZATION_EVENTS); +} + +ICALL_EXPORT void +proflog_icall_SetGCFinalizationEvents (MonoBoolean value) +{ + mono_coop_mutex_lock (&log_profiler.api_mutex); + + if (value) { + ENABLE (PROFLOG_GC_FINALIZATION_EVENTS); + mono_profiler_set_gc_finalizing_callback (log_profiler.handle, finalize_begin); + mono_profiler_set_gc_finalizing_object_callback (log_profiler.handle, finalize_object_begin); + mono_profiler_set_gc_finalized_object_callback (log_profiler.handle, finalize_object_end); + } else { + DISABLE (PROFLOG_GC_FINALIZATION_EVENTS); + mono_profiler_set_gc_finalizing_callback (log_profiler.handle, NULL); + mono_profiler_set_gc_finalizing_object_callback (log_profiler.handle, NULL); + mono_profiler_set_gc_finalized_object_callback (log_profiler.handle, NULL); + } + + mono_coop_mutex_unlock (&log_profiler.api_mutex); +} + +ICALL_EXPORT MonoBoolean +proflog_icall_GetCounterEvents (void) +{ + return ENABLED (PROFLOG_COUNTER_EVENTS); +} + +ICALL_EXPORT void +proflog_icall_SetCounterEvents (MonoBoolean value) +{ + mono_coop_mutex_lock (&log_profiler.api_mutex); + + if (value) + ENABLE (PROFLOG_COUNTER_EVENTS); + else + DISABLE (PROFLOG_COUNTER_EVENTS); + + mono_coop_mutex_unlock (&log_profiler.api_mutex); +} + +ICALL_EXPORT MonoBoolean +proflog_icall_GetJitEvents (void) +{ + return ENABLED (PROFLOG_JIT_EVENTS); +} + +ICALL_EXPORT void +proflog_icall_SetJitEvents (MonoBoolean value) +{ + mono_coop_mutex_lock (&log_profiler.api_mutex); + + if (value) { + ENABLE (PROFLOG_JIT_EVENTS); + mono_profiler_set_jit_code_buffer_callback (log_profiler.handle, code_buffer_new); + } else { + DISABLE (PROFLOG_JIT_EVENTS); + mono_profiler_set_jit_code_buffer_callback (log_profiler.handle, NULL); + } + + mono_coop_mutex_unlock (&log_profiler.api_mutex); +} + static void runtime_initialized (MonoProfiler *profiler) { @@ -4448,6 +4521,47 @@ runtime_initialized (MonoProfiler *profiler) start_helper_thread (); start_writer_thread (); start_dumper_thread (); + + mono_coop_mutex_init (&log_profiler.api_mutex); + +#define ADD_ICALL(NAME) \ + mono_add_internal_call ("Mono.Profiler.Log.LogProfiler::" EGLIB_STRINGIFY (NAME), proflog_icall_ ## NAME); + + ADD_ICALL (GetMaxStackTraceFrames); + ADD_ICALL (GetStackTraceFrames); + ADD_ICALL (SetStackTraceFrames); + ADD_ICALL (GetHeapshotMode); + ADD_ICALL (SetHeapshotMode); + ADD_ICALL (GetHeapshotMillisecondsFrequency); + ADD_ICALL (SetHeapshotMillisecondsFrequency); + ADD_ICALL (GetHeapshotCollectionsFrequency); + ADD_ICALL (SetHeapshotCollectionsFrequency); + ADD_ICALL (GetCallDepth); + ADD_ICALL (SetCallDepth); + ADD_ICALL (GetSampleMode); + ADD_ICALL (SetSampleMode); + ADD_ICALL (GetExceptionEvents); + ADD_ICALL (SetExceptionEvents); + ADD_ICALL (GetMonitorEvents); + ADD_ICALL (SetMonitorEvents); + ADD_ICALL (GetGCEvents); + ADD_ICALL (SetGCEvents); + ADD_ICALL (GetGCAllocationEvents); + ADD_ICALL (SetGCAllocationEvents); + ADD_ICALL (GetGCMoveEvents); + ADD_ICALL (SetGCMoveEvents); + ADD_ICALL (GetGCRootEvents); + ADD_ICALL (SetGCRootEvents); + ADD_ICALL (GetGCHandleEvents); + ADD_ICALL (SetGCHandleEvents); + ADD_ICALL (GetGCFinalizationEvents); + ADD_ICALL (SetGCFinalizationEvents); + ADD_ICALL (GetCounterEvents); + ADD_ICALL (SetCounterEvents); + ADD_ICALL (GetJitEvents); + ADD_ICALL (SetJitEvents); + +#undef ADD_ICALL } static void @@ -4524,7 +4638,7 @@ create_profiler (const char *args, const char *filename, GPtrArray *filters) mono_os_mutex_init (&log_profiler.method_table_mutex); log_profiler.method_table = mono_conc_hashtable_new (NULL, NULL); - if (ENABLED (PROFLOG_CODE_COV_FEATURE)) + if (log_config.collect_coverage) coverage_init (); log_profiler.coverage_filters = filters; @@ -4532,28 +4646,11 @@ create_profiler (const char *args, const char *filename, GPtrArray *filters) log_profiler.startup_time = current_time (); } -/* - * declaration to silence the compiler: this is the entry point that - * mono will load from the shared library and call. - */ -extern void -mono_profiler_init (const char *desc); - -extern void +MONO_API void mono_profiler_init_log (const char *desc); -/* - * this is the entry point that will be used when the profiler - * is embedded inside the main executable. - */ void mono_profiler_init_log (const char *desc) -{ - mono_profiler_init (desc); -} - -void -mono_profiler_init (const char *desc) { GPtrArray *filters = NULL; @@ -4576,107 +4673,105 @@ mono_profiler_init (const char *desc) mono_lls_init (&log_profiler.profiler_thread_list, NULL); - MonoProfilerHandle handle = log_profiler.handle = mono_profiler_install (&log_profiler); + MonoProfilerHandle handle = log_profiler.handle = mono_profiler_create (&log_profiler); - //Required callbacks + /* + * Required callbacks. These are either necessary for the profiler itself + * to function, or provide metadata that's needed if other events (e.g. + * allocations, exceptions) are dynamically enabled/disabled. + */ + + mono_profiler_set_runtime_shutdown_begin_callback (handle, log_early_shutdown); mono_profiler_set_runtime_shutdown_end_callback (handle, log_shutdown); mono_profiler_set_runtime_initialized_callback (handle, runtime_initialized); mono_profiler_set_gc_event_callback (handle, gc_event); - mono_profiler_set_gc_resize_callback (handle, gc_resize); + mono_profiler_set_thread_started_callback (handle, thread_start); mono_profiler_set_thread_stopped_callback (handle, thread_end); - - //It's questionable whether we actually want this to be mandatory, maybe put it behind the actual event? mono_profiler_set_thread_name_callback (handle, thread_name); - if (log_config.effective_mask & PROFLOG_DOMAIN_EVENTS) { - mono_profiler_set_domain_loaded_callback (handle, domain_loaded); - mono_profiler_set_domain_unloading_callback (handle, domain_unloaded); - mono_profiler_set_domain_name_callback (handle, domain_name); - } + mono_profiler_set_domain_loaded_callback (handle, domain_loaded); + mono_profiler_set_domain_unloading_callback (handle, domain_unloaded); + mono_profiler_set_domain_name_callback (handle, domain_name); - if (log_config.effective_mask & PROFLOG_ASSEMBLY_EVENTS) { - mono_profiler_set_assembly_loaded_callback (handle, assembly_loaded); - mono_profiler_set_assembly_unloading_callback (handle, assembly_unloaded); - } + mono_profiler_set_context_loaded_callback (handle, context_loaded); + mono_profiler_set_context_unloaded_callback (handle, context_unloaded); - if (log_config.effective_mask & PROFLOG_MODULE_EVENTS) { - mono_profiler_set_image_loaded_callback (handle, image_loaded); - mono_profiler_set_image_unloading_callback (handle, image_unloaded); - } + mono_profiler_set_assembly_loaded_callback (handle, assembly_loaded); + mono_profiler_set_assembly_unloading_callback (handle, assembly_unloaded); - if (log_config.effective_mask & PROFLOG_CLASS_EVENTS) - mono_profiler_set_class_loaded_callback (handle, class_loaded); + mono_profiler_set_image_loaded_callback (handle, image_loaded); + mono_profiler_set_image_unloading_callback (handle, image_unloaded); - if (log_config.effective_mask & PROFLOG_JIT_COMPILATION_EVENTS) { - mono_profiler_set_jit_done_callback (handle, method_jitted); - mono_profiler_set_jit_code_buffer_callback (handle, code_buffer_new); - } + mono_profiler_set_class_loaded_callback (handle, class_loaded); - if (log_config.effective_mask & PROFLOG_EXCEPTION_EVENTS) { + mono_profiler_set_jit_done_callback (handle, method_jitted); + + if (ENABLED (PROFLOG_EXCEPTION_EVENTS)) { mono_profiler_set_exception_throw_callback (handle, throw_exc); mono_profiler_set_exception_clause_callback (handle, clause_exc); } - if (log_config.effective_mask & PROFLOG_ALLOCATION_EVENTS) { - mono_profiler_enable_allocations (); - mono_profiler_set_gc_allocation_callback (handle, gc_alloc); - } - - //PROFLOG_GC_EVENTS is mandatory - //PROFLOG_THREAD_EVENTS is mandatory - - if (log_config.effective_mask & PROFLOG_CALL_EVENTS) { - mono_profiler_set_call_instrumentation_filter_callback (handle, method_filter); - mono_profiler_set_method_enter_callback (handle, method_enter); - mono_profiler_set_method_leave_callback (handle, method_leave); - mono_profiler_set_method_exception_leave_callback (handle, method_exc_leave); - } - - if (log_config.effective_mask & PROFLOG_INS_COVERAGE_EVENTS) - mono_profiler_set_coverage_filter_callback (handle, coverage_filter); - - if (log_config.effective_mask & PROFLOG_SAMPLING_EVENTS) { - mono_profiler_enable_sampling (handle); - - if (!mono_profiler_set_sample_mode (handle, log_config.sampling_mode, log_config.sample_freq)) - mono_profiler_printf_err ("Another profiler controls sampling parameters; the log profiler will not be able to modify them."); - - mono_profiler_set_sample_hit_callback (handle, mono_sample_hit); - } - - if (log_config.effective_mask & PROFLOG_MONITOR_EVENTS) { + if (ENABLED (PROFLOG_MONITOR_EVENTS)) { mono_profiler_set_monitor_contention_callback (handle, monitor_contention); mono_profiler_set_monitor_acquired_callback (handle, monitor_acquired); mono_profiler_set_monitor_failed_callback (handle, monitor_failed); } - if (log_config.effective_mask & PROFLOG_GC_MOVES_EVENTS) + if (ENABLED (PROFLOG_GC_EVENTS)) + mono_profiler_set_gc_resize_callback (handle, gc_resize); + + if (ENABLED (PROFLOG_GC_ALLOCATION_EVENTS)) + mono_profiler_set_gc_allocation_callback (handle, gc_alloc); + + if (ENABLED (PROFLOG_GC_MOVE_EVENTS)) mono_profiler_set_gc_moves_callback (handle, gc_moves); - if (log_config.effective_mask & PROFLOG_GC_ROOT_EVENTS) + if (ENABLED (PROFLOG_GC_ROOT_EVENTS)) mono_profiler_set_gc_roots_callback (handle, gc_roots); - if (log_config.effective_mask & PROFLOG_CONTEXT_EVENTS) { - mono_profiler_set_context_loaded_callback (handle, context_loaded); - mono_profiler_set_context_unloaded_callback (handle, context_unloaded); + if (ENABLED (PROFLOG_GC_HANDLE_EVENTS)) { + mono_profiler_set_gc_handle_created_callback (handle, gc_handle_created); + mono_profiler_set_gc_handle_deleted_callback (handle, gc_handle_deleted); } - if (log_config.effective_mask & PROFLOG_FINALIZATION_EVENTS) { + if (ENABLED (PROFLOG_GC_FINALIZATION_EVENTS)) { mono_profiler_set_gc_finalizing_callback (handle, finalize_begin); mono_profiler_set_gc_finalized_callback (handle, finalize_end); mono_profiler_set_gc_finalizing_object_callback (handle, finalize_object_begin); - mono_profiler_set_gc_finalized_object_callback (handle, finalize_object_end); - } else if (ENABLED (PROFLOG_HEAPSHOT_FEATURE) && log_config.hs_mode_ondemand) { - //On Demand heapshot uses the finalizer thread to force a collection and thus a heapshot - mono_profiler_set_gc_finalized_callback (handle, finalize_end); } - //PROFLOG_COUNTER_EVENTS is a pseudo event controled by the no_counters global var + //On Demand heapshot uses the finalizer thread to force a collection and thus a heapshot + mono_profiler_set_gc_finalized_callback (handle, finalize_end); - if (log_config.effective_mask & PROFLOG_GC_HANDLE_EVENTS) { - mono_profiler_set_gc_handle_created_callback (handle, gc_handle_created); - mono_profiler_set_gc_handle_deleted_callback (handle, gc_handle_deleted); + if (ENABLED (PROFLOG_SAMPLE_EVENTS)) + mono_profiler_set_sample_hit_callback (handle, mono_sample_hit); + + if (ENABLED (PROFLOG_JIT_EVENTS)) + mono_profiler_set_jit_code_buffer_callback (handle, code_buffer_new); + + if (log_config.enter_leave) { + mono_profiler_set_call_instrumentation_filter_callback (handle, method_filter); + mono_profiler_set_method_enter_callback (handle, method_enter); + mono_profiler_set_method_leave_callback (handle, method_leave); + mono_profiler_set_method_tail_call_callback (handle, tail_call); + mono_profiler_set_method_exception_leave_callback (handle, method_exc_leave); + } + + if (log_config.collect_coverage) { + mono_profiler_enable_coverage (); + mono_profiler_set_coverage_filter_callback (handle, coverage_filter); } + + mono_profiler_enable_allocations (); + mono_profiler_enable_sampling (handle); + + /* + * If no sample option was given by the user, this just leaves the sampling + * thread in idle mode. We do this even if no option was given so that we + * can warn if another profiler controls sampling parameters. + */ + if (!mono_profiler_set_sample_mode (handle, log_config.sampling_mode, log_config.sample_freq)) + mono_profiler_printf_err ("Another profiler controls sampling parameters; the log profiler will not be able to modify them."); }