/*
* proflog.c: mono log profiler
*
- * Author:
+ * Authors:
* Paolo Molaro (lupus@ximian.com)
+ * Alex Rønne Petersen (alexrp@xamarin.com)
*
* Copyright 2010 Novell, Inc (http://www.novell.com)
* Copyright 2011 Xamarin Inc (http://www.xamarin.com)
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
*/
#include <config.h>
#include <mono/metadata/debug-helpers.h>
#include <mono/metadata/mono-perfcounters.h>
#include <mono/metadata/appdomain.h>
+#include <mono/metadata/assembly.h>
+#include <mono/metadata/tokentype.h>
+#include <mono/metadata/tabledefs.h>
#include <mono/utils/atomic.h>
#include <mono/utils/mono-membar.h>
#include <mono/utils/mono-counters.h>
-#include <mono/utils/mono-mutex.h>
+#include <mono/utils/mono-os-mutex.h>
#include <mono/utils/mono-conc-hashtable.h>
#include <mono/utils/lock-free-queue.h>
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
+#ifdef HAVE_SCHED_GETAFFINITY
+#include <sched.h>
+#endif
#include <fcntl.h>
#include <errno.h>
#if defined(HOST_WIN32) || defined(DISABLE_SOCKETS)
#include <unistd.h>
#include <sys/syscall.h>
-#include "perf_event.h"
#ifdef ENABLE_PERF_EVENTS
+#include <linux/perf_event.h>
+
#define USE_PERF_EVENTS 1
static int read_perf_mmap (MonoProfiler* prof, int cpu);
#endif
#define BUFFER_SIZE (4096 * 16)
+
+/* Worst-case size in bytes of a 64-bit value encoded with LEB128. */
+#define LEB128_SIZE 10
+/* Size in bytes of the event ID prefix. */
+#define EVENT_SIZE 1
+
static int nocalls = 0;
static int notraces = 0;
static int use_zip = 0;
static int do_report = 0;
static int do_heap_shot = 0;
static int max_call_depth = 100;
-static int runtime_inited = 0;
+static volatile int runtime_inited = 0;
+static int need_helper_thread = 0;
static int command_port = 0;
static int heapshot_requested = 0;
static int sample_type = 0;
static int in_shutdown = 0;
static int do_debug = 0;
static int do_counters = 0;
+static int do_coverage = 0;
+static gboolean debug_coverage = FALSE;
static MonoProfileSamplingMode sampling_mode = MONO_PROFILER_STAT_MODE_PROCESS;
-/* For linux compile with:
- * gcc -fPIC -shared -o libmono-profiler-log.so proflog.c utils.c -Wall -g -lz `pkg-config --cflags --libs mono-2`
- * gcc -o mprof-report decode.c utils.c -Wall -g -lz -lrt -lpthread `pkg-config --cflags mono-2`
- *
- * For osx compile with:
- * gcc -m32 -Dmono_free=free shared -o libmono-profiler-log.dylib proflog.c utils.c -Wall -g -lz `pkg-config --cflags mono-2` -undefined suppress -flat_namespace
- * gcc -m32 -o mprof-report decode.c utils.c -Wall -g -lz -lrt -lpthread `pkg-config --cflags mono-2`
- *
- * Install with:
- * sudo cp mprof-report /usr/local/bin
- * sudo cp libmono-profiler-log.so /usr/local/lib
- * sudo ldconfig
- */
-
typedef struct _LogBuffer LogBuffer;
/*
*
* type GC format:
* type: TYPE_GC
- * exinfo: one of TYPE_GC_EVENT, TYPE_GC_RESIZE, TYPE_GC_MOVE, TYPE_GC_HANDLE_CREATED,
- * TYPE_GC_HANDLE_DESTROYED
+ * exinfo: one of TYPE_GC_EVENT, TYPE_GC_RESIZE, TYPE_GC_MOVE, TYPE_GC_HANDLE_CREATED[_BT],
+ * TYPE_GC_HANDLE_DESTROYED[_BT]
* [time diff: uleb128] nanoseconds since last timing
* if exinfo == TYPE_GC_RESIZE
* [heap_size: uleb128] new heap size
* [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
+ * if exinfo == TYPE_GC_HANDLE_CREATED[_BT]
* [handle_type: uleb128] GC handle type (System.Runtime.InteropServices.GCHandleType)
* upper bits reserved as flags
* [handle: uleb128] GC handle value
* [objaddr: sleb128] object pointer differences from obj_base
- * if exinfo == TYPE_GC_HANDLE_DESTROYED
+ * If exinfo == TYPE_GC_HANDLE_CREATED_BT, a backtrace follows.
+ * if exinfo == TYPE_GC_HANDLE_DESTROYED[_BT]
* [handle_type: uleb128] GC handle type (System.Runtime.InteropServices.GCHandleType)
* upper bits reserved as flags
* [handle: uleb128] GC handle value
+ * If exinfo == TYPE_GC_HANDLE_DESTROYED_BT, a backtrace follows.
*
* type metadata format:
* type: TYPE_METADATA
- * exinfo: flags: TYPE_LOAD_ERR
+ * exinfo: one of: TYPE_END_LOAD, TYPE_END_UNLOAD (optional for TYPE_THREAD and TYPE_DOMAIN)
* [time diff: uleb128] nanoseconds since last timing
* [mtype: byte] metadata type, one of: TYPE_CLASS, TYPE_IMAGE, TYPE_ASSEMBLY, TYPE_DOMAIN,
- * TYPE_THREAD
+ * 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
* if mtype == TYPE_IMAGE
* [flags: uleb128] must be 0
* [name: string] image file name
- * if mtype == TYPE_THREAD
+ * if mtype == TYPE_ASSEMBLY
+ * [flags: uleb128] must be 0
+ * [name: string] assembly name
+ * if mtype == TYPE_DOMAIN
+ * [flags: uleb128] must be 0
+ * if mtype == TYPE_DOMAIN && exinfo == 0
+ * [name: string] domain friendly name
+ * if mtype == TYPE_CONTEXT
+ * [flags: uleb128] must be 0
+ * [domain: sleb128] domain id as pointer
+ * if mtype == TYPE_THREAD && (format_version < 11 || (format_version > 10 && exinfo == 0))
* [flags: uleb128] must be 0
* [name: string] thread name
*
* if exinfo == TYPE_SAMPLE_HIT
* [sample_type: uleb128] type of sample (SAMPLE_*)
* [timestamp: uleb128] nanoseconds since startup (note: different from other timestamps!)
+ * if (format_version > 10)
+ * [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
* if (format_version > 5)
* 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.
+ */
+
+/*
+ * Format oddities that we ought to fix:
+ *
+ * - Methods written in emit_bt () should be based on the buffer's base
+ * method instead of the base pointer.
+ * - The TYPE_SAMPLE_HIT event contains (currently) pointless data like
+ * always-one unmanaged frame count and always-zero IL offsets.
+ *
+ * These are mostly small things and are not worth a format change by
+ * themselves. They should be done when some other major change has to
+ * be done to the format.
*/
+
+// Pending data to be written to the log, for a single thread.
+// Threads periodically flush their own LogBuffers by calling safe_send
struct _LogBuffer {
+ // Next (older) LogBuffer in processing queue
LogBuffer *next;
+
uint64_t time_base;
uint64_t last_time;
uintptr_t ptr_base;
uintptr_t last_method;
uintptr_t obj_base;
uintptr_t thread_id;
- unsigned char* data_end;
- unsigned char* data;
int locked;
- int size;
int call_depth;
+
+ // Bytes allocated for this LogBuffer
+ int size;
+
+ // Start of currently unused space in buffer
+ unsigned char* cursor;
+
+ // Pointer to start-of-structure-plus-size (for convenience)
+ unsigned char* buf_end;
+
+ // Start of data in buffer. Contents follow "buffer format" described above.
unsigned char buf [1];
};
#define ENTER_LOG(lb,str) if ((lb)->locked) {ign_res (write(2, str, strlen(str))); ign_res (write(2, "\n", 1));return;} else {(lb)->locked++;}
#define EXIT_LOG(lb) (lb)->locked--;
+// Shared queue of sample snapshots taken at signal time.
+// The queue is written into by signal handlers for all threads;
+// the helper thread later unqueues and writes into its own LogBuffer.
typedef struct _StatBuffer StatBuffer;
struct _StatBuffer {
+ // Next (older) StatBuffer in processing queue
StatBuffer *next;
+
+ // Bytes allocated for this StatBuffer
uintptr_t size;
- uintptr_t *data_end;
- uintptr_t *data;
+
+ // Start of currently unused space in buffer
+ uintptr_t *cursor;
+
+ // Pointer to start-of-structure-plus-size (for convenience)
+ uintptr_t *buf_end;
+
+ // Start of data in buffer.
+ // Data consists of a series of sample packets consisting of:
+ // 1 ptrword: Metadata
+ // Low 8 bits: COUNT, the count of native stack frames in this sample (currently always 1)
+ // Next 8 bits: MBT_COUNT, the count of managed stacks in this sample
+ // Next 8 bits: TYPE. See "sampling sources" enum in proflog.h. Usually SAMPLE_CYCLES (1)
+ // 1 ptrword: Thread ID
+ // 1 ptrword: Timestamp
+ // COUNT ptrwords: Native stack frames
+ // Each word is an IP (first is IP where the signal did the interruption)
+ // MBT_COUNT * 4 ptrwords: Managed stack frames (AsyncFrameInfo, repacked)
+ // Word 1: MonoMethod ptr
+ // Word 2: MonoDomain ptr
+ // Word 3: Base address of method
+ // Word 4: Offset within method
uintptr_t buf [1];
};
MonoConcurrentHashTable *method_table;
mono_mutex_t method_table_mutex;
BinaryObject *binary_objects;
+ GPtrArray *coverage_filters;
+ GPtrArray *sorted_sample_events;
};
typedef struct _WriterQueueEntry WriterQueueEntry;
struct _MethodInfo {
MonoMethod *method;
MonoJitInfo *ji;
+ uint64_t time;
};
#ifdef TLS_INIT
pstrdup (const char *s)
{
int len = strlen (s) + 1;
- char *p = malloc (len);
+ char *p = (char *)malloc (len);
memcpy (p, s, len);
return p;
}
static StatBuffer*
create_stat_buffer (void)
{
- StatBuffer* buf = alloc_buffer (BUFFER_SIZE);
+ StatBuffer* buf = (StatBuffer *)alloc_buffer (BUFFER_SIZE);
buf->size = BUFFER_SIZE;
- buf->data_end = (uintptr_t*)((unsigned char*)buf + buf->size);
- buf->data = buf->buf;
+ buf->buf_end = (uintptr_t*)((unsigned char*)buf + buf->size);
+ buf->cursor = buf->buf;
return buf;
}
static LogBuffer*
create_buffer (void)
{
- LogBuffer* buf = alloc_buffer (BUFFER_SIZE);
+ LogBuffer* buf = (LogBuffer *)alloc_buffer (BUFFER_SIZE);
buf->size = BUFFER_SIZE;
buf->time_base = current_time ();
buf->last_time = buf->time_base;
- buf->data_end = (unsigned char*)buf + buf->size;
- buf->data = buf->buf;
+ buf->buf_end = (unsigned char*)buf + buf->size;
+ buf->cursor = buf->buf;
return buf;
}
static LogBuffer *
ensure_logbuf_inner (LogBuffer *old, int bytes)
{
- if (old && old->data + bytes + 100 < old->data_end)
+ if (old && old->cursor + bytes + 100 < old->buf_end)
return old;
- LogBuffer *new = create_buffer ();
- new->thread_id = thread_id ();
- new->next = old;
+ LogBuffer *new_ = (LogBuffer *)create_buffer ();
+ new_->thread_id = thread_id ();
+ new_->next = old;
if (old)
- new->call_depth = old->call_depth;
+ new_->call_depth = old->call_depth;
- return new;
+ return new_;
}
static LogBuffer*
ensure_logbuf (int bytes)
{
LogBuffer *old = TLS_GET (LogBuffer, tlsbuffer);
- LogBuffer *new = ensure_logbuf_inner (old, bytes);
+ LogBuffer *new_ = ensure_logbuf_inner (old, bytes);
- if (new == old)
+ if (new_ == old)
return old; // Still enough space.
- TLS_SET (tlsbuffer, new);
+ TLS_SET (tlsbuffer, new_);
init_thread ();
- return new;
+ return new_;
}
static void
emit_byte (LogBuffer *logbuffer, int value)
{
- logbuffer->data [0] = value;
- logbuffer->data++;
- assert (logbuffer->data <= logbuffer->data_end);
+ logbuffer->cursor [0] = value;
+ logbuffer->cursor++;
+ assert (logbuffer->cursor <= logbuffer->buf_end);
}
static void
emit_value (LogBuffer *logbuffer, int value)
{
- encode_uleb128 (value, logbuffer->data, &logbuffer->data);
- assert (logbuffer->data <= logbuffer->data_end);
+ encode_uleb128 (value, logbuffer->cursor, &logbuffer->cursor);
+ assert (logbuffer->cursor <= logbuffer->buf_end);
}
static void
emit_time (LogBuffer *logbuffer, uint64_t value)
{
uint64_t tdiff = value - logbuffer->last_time;
- if (value < logbuffer->last_time)
- printf ("time went backwards\n");
+ //if (value < logbuffer->last_time)
+ // printf ("time went backwards\n");
//if (tdiff > 1000000)
// printf ("large time offset: %llu\n", tdiff);
- encode_uleb128 (tdiff, logbuffer->data, &logbuffer->data);
+ encode_uleb128 (tdiff, logbuffer->cursor, &logbuffer->cursor);
/*if (tdiff != decode_uleb128 (p, &p))
printf ("incorrect encoding: %llu\n", tdiff);*/
logbuffer->last_time = value;
- assert (logbuffer->data <= logbuffer->data_end);
+ assert (logbuffer->cursor <= logbuffer->buf_end);
}
static void
emit_svalue (LogBuffer *logbuffer, int64_t value)
{
- encode_sleb128 (value, logbuffer->data, &logbuffer->data);
- assert (logbuffer->data <= logbuffer->data_end);
+ encode_sleb128 (value, logbuffer->cursor, &logbuffer->cursor);
+ assert (logbuffer->cursor <= logbuffer->buf_end);
}
static void
emit_uvalue (LogBuffer *logbuffer, uint64_t value)
{
- encode_uleb128 (value, logbuffer->data, &logbuffer->data);
- assert (logbuffer->data <= logbuffer->data_end);
+ encode_uleb128 (value, logbuffer->cursor, &logbuffer->cursor);
+ assert (logbuffer->cursor <= logbuffer->buf_end);
}
static void
if (!logbuffer->ptr_base)
logbuffer->ptr_base = (uintptr_t)ptr;
emit_svalue (logbuffer, (intptr_t)ptr - logbuffer->ptr_base);
- assert (logbuffer->data <= logbuffer->data_end);
+ assert (logbuffer->cursor <= logbuffer->buf_end);
}
static void
logbuffer->method_base = (intptr_t)method;
logbuffer->last_method = (intptr_t)method;
}
- encode_sleb128 ((intptr_t)((char*)method - (char*)logbuffer->last_method), logbuffer->data, &logbuffer->data);
+ encode_sleb128 ((intptr_t)((char*)method - (char*)logbuffer->last_method), logbuffer->cursor, &logbuffer->cursor);
logbuffer->last_method = (intptr_t)method;
- assert (logbuffer->data <= logbuffer->data_end);
+ assert (logbuffer->cursor <= logbuffer->buf_end);
}
+/*
typedef struct {
MonoMethod *method;
MonoJitInfo *found;
if (ji)
search->found = ji;
}
+*/
static void
-register_method_local (MonoProfiler *prof, MonoDomain *domain, MonoMethod *method, MonoJitInfo *ji)
+register_method_local (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *ji)
{
- if (!domain)
- g_assert (ji);
-
if (!mono_conc_hashtable_lookup (prof->method_table, method)) {
+ /*
+ * FIXME: In some cases, we crash while looking up JIT info for AOT'd methods.
+ * This usually happens for static constructors. This code is disabled for now
+ * as we don't need this info for anything critical.
+ *
+ * https://bugzilla.xamarin.com/show_bug.cgi?id=35171
+ */
+ /*
if (!ji) {
MethodSearch search = { method, NULL };
ji = search.found;
}
+ */
- g_assert (ji);
+ /*
+ * FIXME: We can't always find JIT info for a generic shared method, especially
+ * if we obtained the MonoMethod during an async stack walk. For now, we deal
+ * with this by giving the generic shared method name and dummy code start/size
+ * information (i.e. zeroes).
+ */
+ //g_assert (ji);
- MethodInfo *info = malloc (sizeof (MethodInfo));
+ MethodInfo *info = (MethodInfo *)malloc (sizeof (MethodInfo));
info->method = method;
info->ji = ji;
+ info->time = current_time ();
g_ptr_array_add (TLS_GET (GPtrArray, tlsmethodlist), info);
}
}
static void
-emit_method (MonoProfiler *prof, LogBuffer *logbuffer, MonoDomain *domain, MonoMethod *method)
+emit_method (MonoProfiler *prof, LogBuffer *logbuffer, MonoMethod *method)
{
- register_method_local (prof, domain, method, NULL);
+ register_method_local (prof, method, NULL);
emit_method_inner (logbuffer, method);
}
+static void
+emit_method_as_ptr (MonoProfiler *prof, LogBuffer *logbuffer, MonoMethod *method)
+{
+ register_method_local (prof, method, NULL);
+ emit_ptr (logbuffer, method);
+}
+
static void
emit_obj (LogBuffer *logbuffer, void *ptr)
{
if (!logbuffer->obj_base)
logbuffer->obj_base = (uintptr_t)ptr >> 3;
emit_svalue (logbuffer, ((uintptr_t)ptr >> 3) - logbuffer->obj_base);
- assert (logbuffer->data <= logbuffer->data_end);
+ assert (logbuffer->cursor <= logbuffer->buf_end);
}
static void
static void
send_buffer (MonoProfiler *prof, GPtrArray *methods, LogBuffer *buffer)
{
- WriterQueueEntry *entry = calloc (1, sizeof (WriterQueueEntry));
+ WriterQueueEntry *entry = (WriterQueueEntry *)calloc (1, sizeof (WriterQueueEntry));
mono_lock_free_queue_node_init (&entry->node, FALSE);
entry->methods = methods;
entry->buffer = buffer;
if (buf->next)
dump_buffer (profiler, buf->next);
p = write_int32 (p, BUF_ID);
- p = write_int32 (p, buf->data - buf->buf);
+ p = write_int32 (p, buf->cursor - buf->buf);
p = write_int64 (p, buf->time_base);
p = write_int64 (p, buf->ptr_base);
p = write_int64 (p, buf->obj_base);
#if defined (HAVE_SYS_ZLIB)
if (profiler->gzfile) {
gzwrite (profiler->gzfile, hbuf, p - hbuf);
- gzwrite (profiler->gzfile, buf->buf, buf->data - buf->buf);
+ gzwrite (profiler->gzfile, buf->buf, buf->cursor - buf->buf);
} else {
#endif
fwrite (hbuf, p - hbuf, 1, profiler->file);
- fwrite (buf->buf, buf->data - buf->buf, 1, profiler->file);
+ fwrite (buf->buf, buf->cursor - buf->buf, 1, profiler->file);
fflush (profiler->file);
#if defined (HAVE_SYS_ZLIB)
}
static void counters_init (MonoProfiler *profiler);
static void counters_sample (MonoProfiler *profiler, uint64_t timestamp);
-static void
-runtime_initialized (MonoProfiler *profiler)
-{
- runtime_inited = 1;
-#ifndef DISABLE_HELPER_THREAD
- counters_init (profiler);
- counters_sample (profiler, 0);
-#endif
- /* ensure the main thread data and startup are available soon */
- safe_send (profiler, ensure_logbuf (0));
-}
-
/*
* Can be called only at safe callback locations.
*/
static void
safe_send (MonoProfiler *profiler, LogBuffer *logbuffer)
{
+ /* We need the runtime initialized so that we have threads and hazard
+ * pointers available. Otherwise, the lock free queue will not work and
+ * there won't be a thread to process the data.
+ *
+ * While the runtime isn't initialized, we just accumulate data in the
+ * thread local buffer list.
+ */
+ if (!InterlockedRead (&runtime_inited))
+ return;
+
int cd = logbuffer->call_depth;
send_buffer (profiler, TLS_GET (GPtrArray, tlsmethodlist), TLS_GET (LogBuffer, tlsbuffer));
int i;
uintptr_t last_offset = 0;
//const char *name = mono_class_get_name (klass);
- LogBuffer *logbuffer = ensure_logbuf (20 + num * 8);
+ LogBuffer *logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* obj */ +
+ LEB128_SIZE /* klass */ +
+ LEB128_SIZE /* size */ +
+ LEB128_SIZE /* num */ +
+ num * (
+ LEB128_SIZE /* offset */ +
+ LEB128_SIZE /* ref */
+ )
+ );
emit_byte (logbuffer, TYPE_HEAP_OBJECT | TYPE_HEAP);
emit_obj (logbuffer, obj);
emit_ptr (logbuffer, klass);
LogBuffer *logbuffer;
if (!do_heap_shot)
return;
- logbuffer = ensure_logbuf (10);
+ logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* time */
+ );
now = current_time ();
if (hs_mode_ms && (now - last_hs_time)/1000000 >= hs_mode_ms)
do_walk = 1;
emit_byte (logbuffer, TYPE_HEAP_START | TYPE_HEAP);
emit_time (logbuffer, now);
mono_gc_walk_heap (0, gc_reference, NULL);
- logbuffer = ensure_logbuf (10);
+ logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* time */
+ );
now = current_time ();
emit_byte (logbuffer, TYPE_HEAP_END | TYPE_HEAP);
emit_time (logbuffer, now);
static void
gc_event (MonoProfiler *profiler, MonoGCEvent ev, int generation) {
uint64_t now;
- LogBuffer *logbuffer = ensure_logbuf (10);
+ LogBuffer *logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* time */ +
+ LEB128_SIZE /* gc event */ +
+ LEB128_SIZE /* generation */
+ );
now = current_time ();
ENTER_LOG (logbuffer, "gcevent");
emit_byte (logbuffer, TYPE_GC_EVENT | TYPE_GC);
static void
gc_resize (MonoProfiler *profiler, int64_t new_size) {
uint64_t now;
- LogBuffer *logbuffer = ensure_logbuf (10);
+ LogBuffer *logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* time */ +
+ LEB128_SIZE /* new size */
+ );
now = current_time ();
ENTER_LOG (logbuffer, "gcresize");
emit_byte (logbuffer, TYPE_GC_RESIZE | TYPE_GC);
static mono_bool
walk_stack (MonoMethod *method, int32_t native_offset, int32_t il_offset, mono_bool managed, void* data)
{
- FrameData *frame = data;
+ FrameData *frame = (FrameData *)data;
if (method && frame->count < num_frames) {
frame->il_offsets [frame->count] = il_offset;
frame->native_offsets [frame->count] = native_offset;
}
static void
-emit_bt (LogBuffer *logbuffer, FrameData *data)
+emit_bt (MonoProfiler *prof, LogBuffer *logbuffer, FrameData *data)
{
/* FIXME: this is actually tons of data and we should
* just output it the first time and use an id the next
emit_value (logbuffer, 0); /* flags */
emit_value (logbuffer, data->count);
//if (*p != data.count) {
- // printf ("bad num frames enc at %d: %d -> %d\n", count, data.count, *p); printf ("frames end: %p->%p\n", p, logbuffer->data); exit(0);}
+ // 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);}
while (data->count) {
- emit_ptr (logbuffer, data->methods [--data->count]);
+ emit_method_as_ptr (prof, logbuffer, data->methods [--data->count]);
}
}
{
uint64_t now;
uintptr_t len;
- int do_bt = (nocalls && runtime_inited && !notraces)? TYPE_ALLOC_BT: 0;
+ int do_bt = (nocalls && InterlockedRead (&runtime_inited) && !notraces)? TYPE_ALLOC_BT: 0;
FrameData data;
LogBuffer *logbuffer;
len = mono_object_get_size (obj);
len &= ~7;
if (do_bt)
collect_bt (&data);
- logbuffer = ensure_logbuf (32 + MAX_FRAMES * 8);
+ logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* time */ +
+ LEB128_SIZE /* klass */ +
+ LEB128_SIZE /* obj */ +
+ LEB128_SIZE /* size */ +
+ (do_bt ? (
+ LEB128_SIZE /* flags */ +
+ LEB128_SIZE /* count */ +
+ data.count * (
+ LEB128_SIZE /* method */
+ )
+ ) : 0)
+ );
now = current_time ();
ENTER_LOG (logbuffer, "gcalloc");
emit_byte (logbuffer, do_bt | TYPE_ALLOC);
emit_obj (logbuffer, obj);
emit_value (logbuffer, len);
if (do_bt)
- emit_bt (logbuffer, &data);
+ emit_bt (prof, logbuffer, &data);
EXIT_LOG (logbuffer);
if (logbuffer->next)
safe_send (prof, logbuffer);
{
int i;
uint64_t now;
- LogBuffer *logbuffer = ensure_logbuf (10 + num * 8);
+ LogBuffer *logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* time */ +
+ LEB128_SIZE /* num */ +
+ num * (
+ LEB128_SIZE /* object */
+ )
+ );
now = current_time ();
ENTER_LOG (logbuffer, "gcmove");
emit_byte (logbuffer, TYPE_GC_MOVE | TYPE_GC);
gc_roots (MonoProfiler *prof, int num, void **objects, int *root_types, uintptr_t *extra_info)
{
int i;
- LogBuffer *logbuffer = ensure_logbuf (5 + num * 18);
+ LogBuffer *logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* num */ +
+ LEB128_SIZE /* collections */ +
+ num * (
+ LEB128_SIZE /* object */ +
+ LEB128_SIZE /* root type */ +
+ LEB128_SIZE /* extra info */
+ )
+ );
ENTER_LOG (logbuffer, "gcroots");
emit_byte (logbuffer, TYPE_HEAP_ROOT | TYPE_HEAP);
emit_value (logbuffer, num);
static void
gc_handle (MonoProfiler *prof, int op, int type, uintptr_t handle, MonoObject *obj)
{
+ int do_bt = nocalls && InterlockedRead (&runtime_inited) && !notraces;
uint64_t now;
- LogBuffer *logbuffer = ensure_logbuf (16);
+ FrameData data;
+
+ if (do_bt)
+ collect_bt (&data);
+
+ LogBuffer *logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* time */ +
+ LEB128_SIZE /* type */ +
+ LEB128_SIZE /* handle */ +
+ (op == MONO_PROFILER_GC_HANDLE_CREATED ? (
+ LEB128_SIZE /* obj */
+ ) : 0) +
+ (do_bt ? (
+ LEB128_SIZE /* flags */ +
+ LEB128_SIZE /* count */ +
+ data.count * (
+ LEB128_SIZE /* method */
+ )
+ ) : 0)
+ );
+
now = current_time ();
ENTER_LOG (logbuffer, "gchandle");
+
if (op == MONO_PROFILER_GC_HANDLE_CREATED)
- emit_byte (logbuffer, TYPE_GC_HANDLE_CREATED | TYPE_GC);
+ emit_byte (logbuffer, (do_bt ? TYPE_GC_HANDLE_CREATED_BT : TYPE_GC_HANDLE_CREATED) | TYPE_GC);
else if (op == MONO_PROFILER_GC_HANDLE_DESTROYED)
- emit_byte (logbuffer, TYPE_GC_HANDLE_DESTROYED | TYPE_GC);
+ emit_byte (logbuffer, (do_bt ? TYPE_GC_HANDLE_DESTROYED_BT : TYPE_GC_HANDLE_DESTROYED) | TYPE_GC);
else
- return;
+ g_assert_not_reached ();
+
emit_time (logbuffer, now);
emit_value (logbuffer, type);
emit_value (logbuffer, handle);
+
if (op == MONO_PROFILER_GC_HANDLE_CREATED)
emit_obj (logbuffer, obj);
+
+ if (do_bt)
+ emit_bt (prof, logbuffer, &data);
+
EXIT_LOG (logbuffer);
process_requests (prof);
}
char buf [1024];
char *p;
push_nesting (buf, klass);
- p = malloc (strlen (buf) + 1);
+ p = (char *)malloc (strlen (buf) + 1);
strcpy (p, buf);
return p;
}
return;
name = mono_image_get_filename (image);
nlen = strlen (name) + 1;
- logbuffer = ensure_logbuf (16 + nlen);
+ logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* time */ +
+ EVENT_SIZE /* type */ +
+ LEB128_SIZE /* image */ +
+ LEB128_SIZE /* flags */ +
+ nlen /* name */
+ );
now = current_time ();
ENTER_LOG (logbuffer, "image");
emit_byte (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
emit_byte (logbuffer, TYPE_IMAGE);
emit_ptr (logbuffer, image);
emit_value (logbuffer, 0); /* flags */
- memcpy (logbuffer->data, name, nlen);
- logbuffer->data += nlen;
+ memcpy (logbuffer->cursor, name, nlen);
+ logbuffer->cursor += nlen;
//printf ("loaded image %p (%s)\n", image, name);
EXIT_LOG (logbuffer);
if (logbuffer->next)
process_requests (prof);
}
+static void
+image_unloaded (MonoProfiler *prof, MonoImage *image)
+{
+ const char *name = mono_image_get_filename (image);
+ int nlen = strlen (name) + 1;
+ LogBuffer *logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* time */ +
+ EVENT_SIZE /* type */ +
+ LEB128_SIZE /* image */ +
+ LEB128_SIZE /* flags */ +
+ nlen /* name */
+ );
+ uint64_t now = current_time ();
+
+ ENTER_LOG (logbuffer, "image-unload");
+ emit_byte (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
+ emit_time (logbuffer, now);
+ emit_byte (logbuffer, TYPE_IMAGE);
+ emit_ptr (logbuffer, image);
+ emit_value (logbuffer, 0); /* flags */
+ memcpy (logbuffer->cursor, name, nlen);
+ logbuffer->cursor += nlen;
+ EXIT_LOG (logbuffer);
+
+ if (logbuffer->next)
+ safe_send (prof, logbuffer);
+
+ process_requests (prof);
+}
+
+static void
+assembly_loaded (MonoProfiler *prof, MonoAssembly *assembly, int result)
+{
+ if (result != MONO_PROFILE_OK)
+ return;
+
+ char *name = mono_stringify_assembly_name (mono_assembly_get_name (assembly));
+ int nlen = strlen (name) + 1;
+ LogBuffer *logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* time */ +
+ EVENT_SIZE /* type */ +
+ LEB128_SIZE /* assembly */ +
+ LEB128_SIZE /* flags */ +
+ nlen /* name */
+ );
+ uint64_t now = current_time ();
+
+ ENTER_LOG (logbuffer, "assembly-load");
+ emit_byte (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
+ emit_time (logbuffer, now);
+ emit_byte (logbuffer, TYPE_ASSEMBLY);
+ emit_ptr (logbuffer, assembly);
+ emit_value (logbuffer, 0); /* flags */
+ memcpy (logbuffer->cursor, name, nlen);
+ logbuffer->cursor += nlen;
+ EXIT_LOG (logbuffer);
+
+ mono_free (name);
+
+ if (logbuffer->next)
+ safe_send (prof, logbuffer);
+
+ process_requests (prof);
+}
+
+static void
+assembly_unloaded (MonoProfiler *prof, MonoAssembly *assembly)
+{
+ char *name = mono_stringify_assembly_name (mono_assembly_get_name (assembly));
+ int nlen = strlen (name) + 1;
+ LogBuffer *logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* time */ +
+ EVENT_SIZE /* type */ +
+ LEB128_SIZE /* assembly */ +
+ LEB128_SIZE /* flags */ +
+ nlen /* name */
+ );
+ uint64_t now = current_time ();
+
+ ENTER_LOG (logbuffer, "assembly-unload");
+ emit_byte (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
+ emit_time (logbuffer, now);
+ emit_byte (logbuffer, TYPE_ASSEMBLY);
+ emit_ptr (logbuffer, assembly);
+ emit_value (logbuffer, 0); /* flags */
+ memcpy (logbuffer->cursor, name, nlen);
+ logbuffer->cursor += nlen;
+ EXIT_LOG (logbuffer);
+
+ mono_free (name);
+
+ if (logbuffer->next)
+ safe_send (prof, logbuffer);
+
+ process_requests (prof);
+}
+
static void
class_loaded (MonoProfiler *prof, MonoClass *klass, int result)
{
LogBuffer *logbuffer;
if (result != MONO_PROFILE_OK)
return;
- if (runtime_inited)
+ if (InterlockedRead (&runtime_inited))
name = mono_type_get_name (mono_class_get_type (klass));
else
name = type_name (klass);
nlen = strlen (name) + 1;
image = mono_class_get_image (klass);
- logbuffer = ensure_logbuf (24 + nlen);
+ logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* time */ +
+ EVENT_SIZE /* type */ +
+ LEB128_SIZE /* klass */ +
+ LEB128_SIZE /* image */ +
+ LEB128_SIZE /* flags */ +
+ nlen /* name */
+ );
now = current_time ();
ENTER_LOG (logbuffer, "class");
emit_byte (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
emit_ptr (logbuffer, klass);
emit_ptr (logbuffer, image);
emit_value (logbuffer, 0); /* flags */
- memcpy (logbuffer->data, name, nlen);
- logbuffer->data += nlen;
+ memcpy (logbuffer->cursor, name, nlen);
+ logbuffer->cursor += nlen;
//printf ("loaded class %p (%s)\n", klass, name);
if (runtime_inited)
mono_free (name);
process_requests (prof);
}
+static void
+class_unloaded (MonoProfiler *prof, MonoClass *klass)
+{
+ char *name;
+
+ if (InterlockedRead (&runtime_inited))
+ name = mono_type_get_name (mono_class_get_type (klass));
+ else
+ name = type_name (klass);
+
+ int nlen = strlen (name) + 1;
+ MonoImage *image = mono_class_get_image (klass);
+ LogBuffer *logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* time */ +
+ EVENT_SIZE /* type */ +
+ LEB128_SIZE /* klass */ +
+ LEB128_SIZE /* image */ +
+ LEB128_SIZE /* flags */ +
+ nlen /* name */
+ );
+ uint64_t now = current_time ();
+
+ ENTER_LOG (logbuffer, "class-unload");
+ emit_byte (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
+ emit_time (logbuffer, now);
+ emit_byte (logbuffer, TYPE_CLASS);
+ emit_ptr (logbuffer, klass);
+ emit_ptr (logbuffer, image);
+ emit_value (logbuffer, 0); /* flags */
+ memcpy (logbuffer->cursor, name, nlen);
+ logbuffer->cursor += nlen;
+ EXIT_LOG (logbuffer);
+
+ if (runtime_inited)
+ mono_free (name);
+ else
+ free (name);
+
+ if (logbuffer->next)
+ safe_send (prof, logbuffer);
+
+ process_requests (prof);
+}
+
+#ifndef DISABLE_HELPER_THREAD
+static void process_method_enter_coverage (MonoProfiler *prof, MonoMethod *method);
+#endif /* DISABLE_HELPER_THREAD */
+
static void
method_enter (MonoProfiler *prof, MonoMethod *method)
{
- uint64_t now;
- LogBuffer *logbuffer = ensure_logbuf (16);
+ uint64_t now = current_time ();
+
+#ifndef DISABLE_HELPER_THREAD
+ process_method_enter_coverage (prof, method);
+#endif /* DISABLE_HELPER_THREAD */
+
+ LogBuffer *logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* time */ +
+ LEB128_SIZE /* method */
+ );
if (logbuffer->call_depth++ > max_call_depth)
return;
- now = current_time ();
ENTER_LOG (logbuffer, "enter");
emit_byte (logbuffer, TYPE_ENTER | TYPE_METHOD);
emit_time (logbuffer, now);
- emit_method (prof, logbuffer, mono_domain_get (), method);
+ emit_method (prof, logbuffer, method);
EXIT_LOG (logbuffer);
+
process_requests (prof);
}
method_leave (MonoProfiler *prof, MonoMethod *method)
{
uint64_t now;
- LogBuffer *logbuffer = ensure_logbuf (16);
+ LogBuffer *logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* time */ +
+ LEB128_SIZE /* method */
+ );
if (--logbuffer->call_depth > max_call_depth)
return;
now = current_time ();
ENTER_LOG (logbuffer, "leave");
emit_byte (logbuffer, TYPE_LEAVE | TYPE_METHOD);
emit_time (logbuffer, now);
- emit_method (prof, logbuffer, mono_domain_get (), method);
+ emit_method (prof, logbuffer, method);
EXIT_LOG (logbuffer);
if (logbuffer->next)
safe_send (prof, logbuffer);
LogBuffer *logbuffer;
if (nocalls)
return;
- logbuffer = ensure_logbuf (16);
+ logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* time */ +
+ LEB128_SIZE /* method */
+ );
if (--logbuffer->call_depth > max_call_depth)
return;
now = current_time ();
ENTER_LOG (logbuffer, "eleave");
emit_byte (logbuffer, TYPE_EXC_LEAVE | TYPE_METHOD);
emit_time (logbuffer, now);
- emit_method (prof, logbuffer, mono_domain_get (), method);
+ emit_method (prof, logbuffer, method);
EXIT_LOG (logbuffer);
process_requests (prof);
}
if (result != MONO_PROFILE_OK)
return;
- register_method_local (prof, NULL, method, ji);
+ register_method_local (prof, method, ji);
+
+ process_requests (prof);
}
static void
char *name;
LogBuffer *logbuffer;
if (type == MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE) {
- name = data;
+ name = (char *)data;
nlen = strlen (name) + 1;
} else {
name = NULL;
nlen = 0;
}
- logbuffer = ensure_logbuf (32 + nlen);
+ logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* time */ +
+ LEB128_SIZE /* type */ +
+ LEB128_SIZE /* buffer */ +
+ LEB128_SIZE /* size */ +
+ (name ? (
+ nlen /* name */
+ ) : 0)
+ );
now = current_time ();
ENTER_LOG (logbuffer, "code buffer");
emit_byte (logbuffer, TYPE_JITHELPER | TYPE_RUNTIME);
emit_ptr (logbuffer, buffer);
emit_value (logbuffer, size);
if (name) {
- memcpy (logbuffer->data, name, nlen);
- logbuffer->data += nlen;
+ memcpy (logbuffer->cursor, name, nlen);
+ logbuffer->cursor += nlen;
}
EXIT_LOG (logbuffer);
process_requests (prof);
static void
throw_exc (MonoProfiler *prof, MonoObject *object)
{
- int do_bt = (nocalls && runtime_inited && !notraces)? TYPE_EXCEPTION_BT: 0;
+ int do_bt = (nocalls && InterlockedRead (&runtime_inited) && !notraces)? TYPE_EXCEPTION_BT: 0;
uint64_t now;
FrameData data;
LogBuffer *logbuffer;
if (do_bt)
collect_bt (&data);
- logbuffer = ensure_logbuf (16 + MAX_FRAMES * 8);
+ logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* time */ +
+ LEB128_SIZE /* object */ +
+ (do_bt ? (
+ LEB128_SIZE /* flags */ +
+ LEB128_SIZE /* count */ +
+ data.count * (
+ LEB128_SIZE /* method */
+ )
+ ) : 0)
+ );
now = current_time ();
ENTER_LOG (logbuffer, "throw");
emit_byte (logbuffer, do_bt | TYPE_EXCEPTION);
emit_time (logbuffer, now);
emit_obj (logbuffer, object);
if (do_bt)
- emit_bt (logbuffer, &data);
+ emit_bt (prof, logbuffer, &data);
EXIT_LOG (logbuffer);
process_requests (prof);
}
clause_exc (MonoProfiler *prof, MonoMethod *method, int clause_type, int clause_num)
{
uint64_t now;
- LogBuffer *logbuffer = ensure_logbuf (16);
+ LogBuffer *logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* time */ +
+ LEB128_SIZE /* clause type */ +
+ LEB128_SIZE /* clause num */ +
+ LEB128_SIZE /* method */
+ );
now = current_time ();
ENTER_LOG (logbuffer, "clause");
emit_byte (logbuffer, TYPE_EXCEPTION | TYPE_CLAUSE);
emit_time (logbuffer, now);
emit_value (logbuffer, clause_type);
emit_value (logbuffer, clause_num);
- emit_method (prof, logbuffer, mono_domain_get (), method);
+ emit_method (prof, logbuffer, method);
EXIT_LOG (logbuffer);
+
+ process_requests (prof);
}
static void
monitor_event (MonoProfiler *profiler, MonoObject *object, MonoProfilerMonitorEvent event)
{
- int do_bt = (nocalls && runtime_inited && !notraces && event == MONO_PROFILER_MONITOR_CONTENTION)? TYPE_MONITOR_BT: 0;
+ int do_bt = (nocalls && InterlockedRead (&runtime_inited) && !notraces && event == MONO_PROFILER_MONITOR_CONTENTION)? TYPE_MONITOR_BT: 0;
uint64_t now;
FrameData data;
LogBuffer *logbuffer;
if (do_bt)
collect_bt (&data);
- logbuffer = ensure_logbuf (16 + MAX_FRAMES * 8);
+ logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* time */ +
+ LEB128_SIZE /* object */ +
+ (do_bt ? (
+ LEB128_SIZE /* flags */ +
+ LEB128_SIZE /* count */ +
+ data.count * (
+ LEB128_SIZE /* method */
+ )
+ ) : 0)
+ );
now = current_time ();
ENTER_LOG (logbuffer, "monitor");
emit_byte (logbuffer, (event << 4) | do_bt | TYPE_MONITOR);
emit_time (logbuffer, now);
emit_obj (logbuffer, object);
if (do_bt)
- emit_bt (logbuffer, &data);
+ emit_bt (profiler, logbuffer, &data);
EXIT_LOG (logbuffer);
process_requests (profiler);
}
{
//printf ("thread start %p\n", (void*)tid);
init_thread ();
+
+ LogBuffer *logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* time */ +
+ EVENT_SIZE /* type */ +
+ LEB128_SIZE /* tid */ +
+ LEB128_SIZE /* flags */
+ );
+ uint64_t now = current_time ();
+
+ ENTER_LOG (logbuffer, "thread-start");
+ emit_byte (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
+ emit_time (logbuffer, now);
+ emit_byte (logbuffer, TYPE_THREAD);
+ emit_ptr (logbuffer, (void*) tid);
+ emit_value (logbuffer, 0); /* flags */
+ EXIT_LOG (logbuffer);
+
+ if (logbuffer->next)
+ safe_send (prof, logbuffer);
+
+ process_requests (prof);
}
static void
thread_end (MonoProfiler *prof, uintptr_t tid)
{
- if (TLS_GET (LogBuffer, tlsbuffer))
- send_buffer (prof, TLS_GET (GPtrArray, tlsmethodlist), TLS_GET (LogBuffer, tlsbuffer));
+ if (TLS_GET (LogBuffer, tlsbuffer)) {
+ LogBuffer *logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* time */ +
+ EVENT_SIZE /* type */ +
+ LEB128_SIZE /* tid */ +
+ LEB128_SIZE /* flags */
+ );
+ uint64_t now = current_time ();
+
+ ENTER_LOG (logbuffer, "thread-end");
+ emit_byte (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
+ emit_time (logbuffer, now);
+ emit_byte (logbuffer, TYPE_THREAD);
+ emit_ptr (logbuffer, (void*) tid);
+ emit_value (logbuffer, 0); /* flags */
+ EXIT_LOG (logbuffer);
+
+ send_buffer (prof, TLS_GET (GPtrArray, tlsmethodlist), logbuffer);
+
+ /* Don't process requests as the thread is detached from the runtime. */
+ }
TLS_SET (tlsbuffer, NULL);
TLS_SET (tlsmethodlist, NULL);
}
+static void
+domain_loaded (MonoProfiler *prof, MonoDomain *domain, int result)
+{
+ if (result != MONO_PROFILE_OK)
+ return;
+
+ LogBuffer *logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* time */ +
+ EVENT_SIZE /* type */ +
+ LEB128_SIZE /* domain id */ +
+ LEB128_SIZE /* flags */
+ );
+ uint64_t now = current_time ();
+
+ ENTER_LOG (logbuffer, "domain-start");
+ emit_byte (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
+ emit_time (logbuffer, now);
+ emit_byte (logbuffer, TYPE_DOMAIN);
+ emit_ptr (logbuffer, (void*)(uintptr_t) mono_domain_get_id (domain));
+ emit_value (logbuffer, 0); /* flags */
+ EXIT_LOG (logbuffer);
+
+ if (logbuffer->next)
+ safe_send (prof, logbuffer);
+
+ process_requests (prof);
+}
+
+static void
+domain_unloaded (MonoProfiler *prof, MonoDomain *domain)
+{
+ LogBuffer *logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* time */ +
+ EVENT_SIZE /* type */ +
+ LEB128_SIZE /* domain id */ +
+ LEB128_SIZE /* flags */
+ );
+ uint64_t now = current_time ();
+
+ ENTER_LOG (logbuffer, "domain-end");
+ emit_byte (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
+ emit_time (logbuffer, now);
+ emit_byte (logbuffer, TYPE_DOMAIN);
+ emit_ptr (logbuffer, (void*)(uintptr_t) mono_domain_get_id (domain));
+ emit_value (logbuffer, 0); /* flags */
+ EXIT_LOG (logbuffer);
+
+ if (logbuffer->next)
+ safe_send (prof, logbuffer);
+
+ process_requests (prof);
+}
+
+static void
+domain_name (MonoProfiler *prof, MonoDomain *domain, const char *name)
+{
+ int nlen = strlen (name) + 1;
+ LogBuffer *logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* time */ +
+ EVENT_SIZE /* type */ +
+ LEB128_SIZE /* domain id */ +
+ LEB128_SIZE /* flags */ +
+ nlen /* name */
+ );
+ uint64_t now = current_time ();
+
+ ENTER_LOG (logbuffer, "domain-name");
+ emit_byte (logbuffer, TYPE_METADATA);
+ emit_time (logbuffer, now);
+ emit_byte (logbuffer, TYPE_DOMAIN);
+ emit_ptr (logbuffer, (void*)(uintptr_t) mono_domain_get_id (domain));
+ emit_value (logbuffer, 0); /* flags */
+ memcpy (logbuffer->cursor, name, nlen);
+ logbuffer->cursor += nlen;
+ EXIT_LOG (logbuffer);
+
+ if (logbuffer->next)
+ safe_send (prof, logbuffer);
+
+ process_requests (prof);
+}
+
+static void
+context_loaded (MonoProfiler *prof, MonoAppContext *context)
+{
+ LogBuffer *logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* time */ +
+ EVENT_SIZE /* type */ +
+ LEB128_SIZE /* context id */ +
+ LEB128_SIZE /* flags */ +
+ LEB128_SIZE /* domain id */
+ );
+ uint64_t now = current_time ();
+
+ ENTER_LOG (logbuffer, "context-start");
+ emit_byte (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
+ emit_time (logbuffer, now);
+ emit_byte (logbuffer, TYPE_CONTEXT);
+ emit_ptr (logbuffer, (void*)(uintptr_t) mono_context_get_id (context));
+ emit_value (logbuffer, 0); /* flags */
+ emit_ptr (logbuffer, (void*)(uintptr_t) mono_context_get_domain_id (context));
+ EXIT_LOG (logbuffer);
+
+ if (logbuffer->next)
+ safe_send (prof, logbuffer);
+
+ process_requests (prof);
+}
+
+static void
+context_unloaded (MonoProfiler *prof, MonoAppContext *context)
+{
+ LogBuffer *logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* time */ +
+ EVENT_SIZE /* type */ +
+ LEB128_SIZE /* context id */ +
+ LEB128_SIZE /* flags */ +
+ LEB128_SIZE /* domain id */
+ );
+ uint64_t now = current_time ();
+
+ ENTER_LOG (logbuffer, "context-end");
+ emit_byte (logbuffer, TYPE_END_UNLOAD | TYPE_METADATA);
+ emit_time (logbuffer, now);
+ emit_byte (logbuffer, TYPE_CONTEXT);
+ emit_ptr (logbuffer, (void*)(uintptr_t) mono_context_get_id (context));
+ emit_value (logbuffer, 0); /* flags */
+ emit_ptr (logbuffer, (void*)(uintptr_t) mono_context_get_domain_id (context));
+ EXIT_LOG (logbuffer);
+
+ if (logbuffer->next)
+ safe_send (prof, logbuffer);
+
+ process_requests (prof);
+}
+
static void
thread_name (MonoProfiler *prof, uintptr_t tid, const char *name)
{
int len = strlen (name) + 1;
uint64_t now;
LogBuffer *logbuffer;
- logbuffer = ensure_logbuf (10 + len);
+ logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* time */ +
+ EVENT_SIZE /* type */ +
+ LEB128_SIZE /* tid */ +
+ LEB128_SIZE /* flags */ +
+ len /* name */
+ );
now = current_time ();
ENTER_LOG (logbuffer, "tname");
emit_byte (logbuffer, TYPE_METADATA);
emit_byte (logbuffer, TYPE_THREAD);
emit_ptr (logbuffer, (void*)tid);
emit_value (logbuffer, 0); /* flags */
- memcpy (logbuffer->data, name, len);
- logbuffer->data += len;
+ memcpy (logbuffer->cursor, name, len);
+ logbuffer->cursor += len;
EXIT_LOG (logbuffer);
+
+ if (logbuffer->next)
+ safe_send (prof, logbuffer);
+
+ process_requests (prof);
}
typedef struct {
static mono_bool
async_walk_stack (MonoMethod *method, MonoDomain *domain, void *base_address, int offset, void *data)
{
- AsyncFrameData *frame = data;
+ AsyncFrameData *frame = (AsyncFrameData *)data;
if (frame->count < num_frames) {
frame->data [frame->count].method = method;
frame->data [frame->count].domain = domain;
if (!sbuf)
return;
/* flush the buffer at 1 second intervals */
- if (sbuf->data > sbuf->buf && (elapsed - sbuf->buf [2]) > 100000) {
+ if (sbuf->cursor > sbuf->buf && (elapsed - sbuf->buf [2]) > 100000) {
timedout = 1;
}
/* overflow: 400 slots is a big enough number to reduce the chance of losing this event if many
* threads hit this same spot at the same time
*/
- if (timedout || (sbuf->data + 400 >= sbuf->data_end)) {
+ if (timedout || (sbuf->cursor + 400 >= sbuf->buf_end)) {
StatBuffer *oldsb, *foundsb;
sbuf = create_stat_buffer ();
do {
oldsb = profiler->stat_buffers;
sbuf->next = oldsb;
- foundsb = InterlockedCompareExchangePointer ((void * volatile*)&profiler->stat_buffers, sbuf, oldsb);
+ foundsb = (StatBuffer *)InterlockedCompareExchangePointer ((void * volatile*)&profiler->stat_buffers, sbuf, oldsb);
} while (foundsb != oldsb);
if (do_debug)
ign_res (write (2, "overflow\n", 9));
}
}
do {
- old_data = sbuf->data;
+ old_data = sbuf->cursor;
new_data = old_data + SAMPLE_EVENT_SIZE_IN_SLOTS (bt_data.count);
- data = InterlockedCompareExchangePointer ((void * volatile*)&sbuf->data, new_data, old_data);
+ if (new_data > sbuf->buf_end)
+ return; /* Not enough room in buf to hold this event-- lost event */
+ data = (uintptr_t *)InterlockedCompareExchangePointer ((void * volatile*)&sbuf->cursor, new_data, old_data);
} while (data != old_data);
- if (old_data >= sbuf->data_end)
- return; /* lost event */
+
old_data [0] = 1 | (sample_type << 16) | (bt_data.count << 8);
old_data [1] = thread_id ();
old_data [2] = elapsed;
size_code_pages *= 2;
if (size_code_pages == 0)
size_code_pages = 16;
- n = calloc (sizeof (uintptr_t) * size_code_pages, 1);
+ n = (uintptr_t *)calloc (sizeof (uintptr_t) * size_code_pages, 1);
for (i = 0; i < old_size; ++i) {
if (code_pages [i])
add_code_page (n, size_code_pages, code_pages [i]);
num_code_pages += add_code_page (code_pages, size_code_pages, ip & CPAGE_MASK);
}
-#if defined(HAVE_DL_ITERATE_PHDR) && defined(ELFMAG0)
+/* ELF code crashes on some systems. */
+//#if defined(HAVE_DL_ITERATE_PHDR) && defined(ELFMAG0)
+#if 0
static void
dump_ubin (const char *filename, uintptr_t load_addr, uint64_t offset, uintptr_t size)
{
int len;
len = strlen (filename) + 1;
now = current_time ();
- logbuffer = ensure_logbuf (20 + len);
+ logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* time */ +
+ LEB128_SIZE /* load address */ +
+ LEB128_SIZE /* offset */ +
+ LEB128_SIZE /* size */ +
+ nlen /* file name */
+ );
emit_byte (logbuffer, TYPE_SAMPLE | TYPE_SAMPLE_UBIN);
emit_time (logbuffer, now);
emit_svalue (logbuffer, load_addr);
emit_uvalue (logbuffer, offset);
emit_uvalue (logbuffer, size);
- memcpy (logbuffer->data, filename, len);
- logbuffer->data += len;
+ memcpy (logbuffer->cursor, filename, len);
+ logbuffer->cursor += len;
}
#endif
LogBuffer *logbuffer;
int len;
len = strlen (name) + 1;
- logbuffer = ensure_logbuf (20 + len);
+ logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* value */ +
+ LEB128_SIZE /* size */ +
+ len /* name */
+ );
emit_byte (logbuffer, TYPE_SAMPLE | TYPE_SAMPLE_USYM);
emit_ptr (logbuffer, (void*)value);
emit_value (logbuffer, size);
- memcpy (logbuffer->data, name, len);
- logbuffer->data += len;
+ memcpy (logbuffer->cursor, name, len);
+ logbuffer->cursor += len;
}
-#ifdef ELFMAG0
+/* ELF code crashes on some systems. */
+//#if defined(ELFMAG0)
+#if 0
#if SIZEOF_VOID_P == 4
#define ELF_WSIZE 32
}
#endif
-#if defined(HAVE_DL_ITERATE_PHDR) && defined(ELFMAG0)
+/* ELF code crashes on some systems. */
+//#if defined(HAVE_DL_ITERATE_PHDR) && defined(ELFMAG0)
+#if 0
static int
elf_dl_callback (struct dl_phdr_info *info, size_t size, void *data)
{
}
}
+static gint
+compare_sample_events (gconstpointer a, gconstpointer b)
+{
+ uintptr_t tid1 = (*(uintptr_t **) a) [1];
+ uintptr_t tid2 = (*(uintptr_t **) b) [1];
+
+ return tid1 > tid2 ? 1 :
+ tid1 < tid2 ? -1 :
+ 0;
+}
+
static void
dump_sample_hits (MonoProfiler *prof, StatBuffer *sbuf)
{
- uintptr_t *sample;
LogBuffer *logbuffer;
if (!sbuf)
return;
free_buffer (sbuf->next, sbuf->next->size);
sbuf->next = NULL;
}
- for (sample = sbuf->buf; sample < sbuf->data;) {
- int i;
+
+ g_ptr_array_set_size (prof->sorted_sample_events, 0);
+
+ for (uintptr_t *sample = sbuf->buf; sample < sbuf->cursor;) {
int count = sample [0] & 0xff;
int mbt_count = (sample [0] & 0xff00) >> 8;
- int type = sample [0] >> 16;
- uintptr_t *managed_sample_base = sample + count + 3;
- if (sample + SAMPLE_EVENT_SIZE_IN_SLOTS (mbt_count) > sbuf->data)
+ if (sample + SAMPLE_EVENT_SIZE_IN_SLOTS (mbt_count) > sbuf->cursor)
break;
- for (i = 0; i < mbt_count; ++i) {
+ g_ptr_array_add (prof->sorted_sample_events, sample);
+
+ sample += count + 3 + 4 * mbt_count;
+ }
+
+ g_ptr_array_sort (prof->sorted_sample_events, compare_sample_events);
+
+ for (guint sidx = 0; sidx < prof->sorted_sample_events->len; sidx++) {
+ uintptr_t *sample = (uintptr_t *)g_ptr_array_index (prof->sorted_sample_events, sidx);
+ int count = sample [0] & 0xff;
+ int mbt_count = (sample [0] & 0xff00) >> 8;
+ int type = sample [0] >> 16;
+ uintptr_t *managed_sample_base = sample + count + 3;
+ uintptr_t thread_id = sample [1];
+
+ for (int i = 0; i < mbt_count; ++i) {
MonoMethod *method = (MonoMethod*)managed_sample_base [i * 4 + 0];
MonoDomain *domain = (MonoDomain*)managed_sample_base [i * 4 + 1];
void *address = (void*)managed_sample_base [i * 4 + 2];
if (!method) {
- MonoJitInfo *ji = mono_jit_info_table_find (domain, address);
+ g_assert (domain);
+ MonoJitInfo *ji = mono_jit_info_table_find (domain, (char *)address);
if (ji)
managed_sample_base [i * 4 + 0] = (uintptr_t)mono_jit_info_get_method (ji);
}
}
- logbuffer = ensure_logbuf (20 + count * 8);
+
+ logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* type */ +
+ LEB128_SIZE /* time */ +
+ LEB128_SIZE /* tid */ +
+ LEB128_SIZE /* count */ +
+ count * (
+ LEB128_SIZE /* ip */
+ ) +
+ LEB128_SIZE /* managed count */ +
+ mbt_count * (
+ LEB128_SIZE /* method */ +
+ LEB128_SIZE /* il offset */ +
+ LEB128_SIZE /* native offset */
+ )
+ );
emit_byte (logbuffer, TYPE_SAMPLE | TYPE_SAMPLE_HIT);
emit_value (logbuffer, type);
emit_uvalue (logbuffer, prof->startup_time + (uint64_t)sample [2] * (uint64_t)10000);
+ emit_ptr (logbuffer, (void *) thread_id);
emit_value (logbuffer, count);
- for (i = 0; i < count; ++i) {
+ for (int i = 0; i < count; ++i) {
emit_ptr (logbuffer, (void*)sample [i + 3]);
add_code_pointer (sample [i + 3]);
}
sample += count + 3;
/* new in data version 6 */
emit_uvalue (logbuffer, mbt_count);
- for (i = 0; i < mbt_count; ++i) {
+ for (int i = 0; i < mbt_count; ++i) {
MonoMethod *method = (MonoMethod *) sample [i * 4 + 0];
- MonoDomain *domain = (MonoDomain *) sample [i * 4 + 1];
uintptr_t native_offset = sample [i * 4 + 3];
- emit_method (prof, logbuffer, domain, method);
+ emit_method (prof, logbuffer, method);
emit_svalue (logbuffer, 0); /* il offset will always be 0 from now on */
emit_svalue (logbuffer, native_offset);
}
- sample += 4 * mbt_count;
}
+
dump_unmanaged_coderefs (prof);
}
static int
mono_cpu_count (void)
{
- int count = 0;
#ifdef PLATFORM_ANDROID
/* Android tries really hard to save power by powering off CPUs on SMP phones which
* means the normal way to query cpu count returns a wrong value with userspace API.
* Instead we use /sys entries to query the actual hardware CPU count.
*/
+ int count = 0;
char buffer[8] = {'\0'};
int present = open ("/sys/devices/system/cpu/present", O_RDONLY);
/* Format of the /sys entry is a cpulist of indexes which in the case
if (count > 0)
return count + 1;
#endif
+
+#if defined(HOST_ARM) || defined (HOST_ARM64)
+
+ /* ARM platforms tries really hard to save power by powering off CPUs on SMP phones which
+ * means the normal way to query cpu count returns a wrong value with userspace API. */
+
+#ifdef _SC_NPROCESSORS_CONF
+ {
+ int count = sysconf (_SC_NPROCESSORS_CONF);
+ if (count > 0)
+ return count;
+ }
+#endif
+
+#else
+
+#ifdef HAVE_SCHED_GETAFFINITY
+ {
+ cpu_set_t set;
+ if (sched_getaffinity (getpid (), sizeof (set), &set) == 0)
+ return CPU_COUNT (&set);
+ }
+#endif
#ifdef _SC_NPROCESSORS_ONLN
- count = sysconf (_SC_NPROCESSORS_ONLN);
- if (count > 0)
- return count;
+ {
+ int count = sysconf (_SC_NPROCESSORS_ONLN);
+ if (count > 0)
+ return count;
+ }
#endif
+
+#endif /* defined(HOST_ARM) || defined (HOST_ARM64) */
+
#ifdef USE_SYSCTL
{
+ int count;
int mib [2];
size_t len = sizeof (int);
mib [0] = CTL_HW;
dump_perf_hits (MonoProfiler *prof, void *buf, int size)
{
LogBuffer *logbuffer;
+ int count = 1;
+ int mbt_count = 0;
void *end = (char*)buf + size;
int samples = 0;
int pid = getpid ();
/*ip = (void*)s->ip;
printf ("sample: %d, size: %d, ip: %p (%s), timestamp: %llu, nframes: %llu\n",
s->h.type, s->h.size, ip, symbol_for (ip), s->timestamp, s->nframes);*/
- logbuffer = ensure_logbuf (20 + s->nframes * 8);
+ logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* type */ +
+ LEB128_SIZE /* time */ +
+ LEB128_SIZE /* tid */ +
+ LEB128_SIZE /* count */ +
+ count * (
+ LEB128_SIZE /* ip */
+ ) +
+ LEB128_SIZE /* managed count */ +
+ mbt_count * (
+ LEB128_SIZE /* method */ +
+ LEB128_SIZE /* il offset */ +
+ LEB128_SIZE /* native offset */
+ )
+ );
emit_byte (logbuffer, TYPE_SAMPLE | TYPE_SAMPLE_HIT);
emit_value (logbuffer, sample_type);
emit_uvalue (logbuffer, s->timestamp - prof->startup_time);
- emit_value (logbuffer, 1); /* count */
+ /*
+ * No useful thread ID to write here, since throughout the
+ * profiler we use pthread_self () but the ID we get from
+ * perf is the kernel's thread ID.
+ */
+ emit_ptr (logbuffer, 0);
+ emit_value (logbuffer, count);
emit_ptr (logbuffer, (void*)(uintptr_t)s->ip);
- /* no support here yet for the managed backtrace */
- emit_uvalue (logbuffer, 0);
add_code_pointer (s->ip);
+ /* no support here yet for the managed backtrace */
+ emit_uvalue (logbuffer, mbt_count);
buf = (char*)buf + s->h.size;
samples++;
}
if (!counters_initialized)
return;
- mono_mutex_lock (&counters_mutex);
+ mono_os_mutex_lock (&counters_mutex);
for (agent = counters; agent; agent = agent->next) {
if (agent->counter == counter) {
free (agent->value);
agent->value = NULL;
}
- mono_mutex_unlock (&counters_mutex);
+ mono_os_mutex_unlock (&counters_mutex);
return;
}
}
- agent = malloc (sizeof (MonoCounterAgent));
+ agent = (MonoCounterAgent *)malloc (sizeof (MonoCounterAgent));
agent->counter = counter;
agent->value = NULL;
agent->value_size = 0;
item->next = agent;
}
- mono_mutex_unlock (&counters_mutex);
+ mono_os_mutex_unlock (&counters_mutex);
}
static mono_bool
{
assert (!counters_initialized);
- mono_mutex_init (&counters_mutex);
+ mono_os_mutex_init (&counters_mutex);
counters_initialized = TRUE;
{
MonoCounterAgent *agent;
LogBuffer *logbuffer;
- int size = 1 + 5, len = 0;
+ int len = 0;
+ int size =
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* len */
+ ;
if (!counters_initialized)
return;
- mono_mutex_lock (&counters_mutex);
+ mono_os_mutex_lock (&counters_mutex);
for (agent = counters; agent; agent = agent->next) {
if (agent->emitted)
continue;
- size += strlen (mono_counter_get_name (agent->counter)) + 1 + 5 * 5;
+ size +=
+ LEB128_SIZE /* section */ +
+ strlen (mono_counter_get_name (agent->counter)) + 1 /* name */ +
+ LEB128_SIZE /* type */ +
+ LEB128_SIZE /* unit */ +
+ LEB128_SIZE /* variance */ +
+ LEB128_SIZE /* index */
+ ;
+
len += 1;
}
if (!len) {
- mono_mutex_unlock (&counters_mutex);
+ mono_os_mutex_unlock (&counters_mutex);
return;
}
}
EXIT_LOG (logbuffer);
- safe_send (profiler, ensure_logbuf (0));
+ safe_send (profiler, logbuffer);
- mono_mutex_unlock (&counters_mutex);
+ mono_os_mutex_unlock (&counters_mutex);
}
static void
buffer_size = 8;
buffer = calloc (1, buffer_size);
- mono_mutex_lock (&counters_mutex);
+ mono_os_mutex_lock (&counters_mutex);
+
+ size =
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* time */
+ ;
+
+ for (agent = counters; agent; agent = agent->next) {
+ size +=
+ LEB128_SIZE /* index */ +
+ LEB128_SIZE /* type */ +
+ mono_counter_get_size (agent->counter) /* value */
+ ;
+ }
- size = 1 + 10 + 5;
- for (agent = counters; agent; agent = agent->next)
- size += 10 * 2 + mono_counter_get_size (agent->counter);
+ size +=
+ LEB128_SIZE /* stop marker */
+ ;
logbuffer = ensure_logbuf (size);
emit_value (logbuffer, 0);
EXIT_LOG (logbuffer);
- safe_send (profiler, ensure_logbuf (0));
+ safe_send (profiler, logbuffer);
- mono_mutex_unlock (&counters_mutex);
+ mono_os_mutex_unlock (&counters_mutex);
}
typedef struct _PerfCounterAgent PerfCounterAgent;
{
PerfCounterAgent *pcagent;
LogBuffer *logbuffer;
- int size = 1 + 5, len = 0;
+ int len = 0;
+ int size =
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* len */
+ ;
for (pcagent = perfcounters; pcagent; pcagent = pcagent->next) {
if (pcagent->emitted)
continue;
- size += strlen (pcagent->name) + 1 + 5 * 5;
+ size +=
+ LEB128_SIZE /* section */ +
+ strlen (pcagent->category_name) + 1 /* category name */ +
+ strlen (pcagent->name) + 1 /* name */ +
+ LEB128_SIZE /* type */ +
+ LEB128_SIZE /* unit */ +
+ LEB128_SIZE /* variance */ +
+ LEB128_SIZE /* index */
+ ;
+
len += 1;
}
}
EXIT_LOG (logbuffer);
- safe_send (profiler, ensure_logbuf (0));
+ safe_send (profiler, logbuffer);
}
static gboolean
if (!counters_initialized)
return;
- mono_mutex_lock (&counters_mutex);
+ mono_os_mutex_lock (&counters_mutex);
/* mark all perfcounters as deleted, foreach will unmark them as necessary */
for (pcagent = perfcounters; pcagent; pcagent = pcagent->next)
perfcounters_emit (profiler);
+ size =
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* time */
+ ;
- size = 1 + 10 + 5;
for (pcagent = perfcounters; pcagent; pcagent = pcagent->next) {
if (pcagent->deleted || !pcagent->updated)
continue;
- size += 10 * 2 + sizeof (gint64);
+
+ size +=
+ LEB128_SIZE /* index */ +
+ LEB128_SIZE /* type */ +
+ LEB128_SIZE /* value */
+ ;
}
+ size +=
+ LEB128_SIZE /* stop marker */
+ ;
+
logbuffer = ensure_logbuf (size);
ENTER_LOG (logbuffer, "perfcounters");
emit_value (logbuffer, 0);
EXIT_LOG (logbuffer);
- safe_send (profiler, ensure_logbuf (0));
+ safe_send (profiler, logbuffer);
- mono_mutex_unlock (&counters_mutex);
+ mono_os_mutex_unlock (&counters_mutex);
}
static void
perfcounters_sample (prof, (now - start) / 1000/ 1000);
}
+#define COVERAGE_DEBUG(x) if (debug_coverage) {x}
+static mono_mutex_t coverage_mutex;
+static MonoConcurrentHashTable *coverage_methods = NULL;
+static MonoConcurrentHashTable *coverage_assemblies = NULL;
+static MonoConcurrentHashTable *coverage_classes = NULL;
+
+static MonoConcurrentHashTable *filtered_classes = NULL;
+static MonoConcurrentHashTable *entered_methods = NULL;
+static MonoConcurrentHashTable *image_to_methods = NULL;
+static MonoConcurrentHashTable *suppressed_assemblies = NULL;
+static gboolean coverage_initialized = FALSE;
+
+static GPtrArray *coverage_data = NULL;
+static int previous_offset = 0;
+
+typedef struct _MethodNode MethodNode;
+struct _MethodNode {
+ MonoLockFreeQueueNode node;
+ MonoMethod *method;
+};
+
+typedef struct _CoverageEntry CoverageEntry;
+struct _CoverageEntry {
+ int offset;
+ int counter;
+ char *filename;
+ int line;
+ int column;
+};
+
+static void
+free_coverage_entry (gpointer data, gpointer userdata)
+{
+ CoverageEntry *entry = (CoverageEntry *)data;
+ g_free (entry->filename);
+ g_free (entry);
+}
+
+static void
+obtain_coverage_for_method (MonoProfiler *prof, const MonoProfileCoverageEntry *entry)
+{
+ int offset = entry->iloffset - previous_offset;
+ CoverageEntry *e = g_new (CoverageEntry, 1);
+
+ previous_offset = entry->iloffset;
+
+ e->offset = offset;
+ e->counter = entry->counter;
+ e->filename = g_strdup(entry->filename ? entry->filename : "");
+ e->line = entry->line;
+ e->column = entry->col;
+
+ g_ptr_array_add (coverage_data, e);
+}
+
+static char *
+parse_generic_type_names(char *name)
+{
+ char *new_name, *ret;
+ int within_generic_declaration = 0, generic_members = 1;
+
+ if (name == NULL || *name == '\0')
+ return g_strdup ("");
+
+ if (!(ret = new_name = (char *)calloc (strlen (name) * 4 + 1, sizeof (char))))
+ return NULL;
+
+ do {
+ switch (*name) {
+ case '<':
+ within_generic_declaration = 1;
+ break;
+
+ case '>':
+ within_generic_declaration = 0;
+
+ if (*(name - 1) != '<') {
+ *new_name++ = '`';
+ *new_name++ = '0' + generic_members;
+ } else {
+ memcpy (new_name, "<>", 8);
+ new_name += 8;
+ }
+
+ generic_members = 0;
+ break;
+
+ case ',':
+ generic_members++;
+ break;
+
+ default:
+ if (!within_generic_declaration)
+ *new_name++ = *name;
+
+ break;
+ }
+ } while (*name++);
+
+ return ret;
+}
+
+static int method_id;
+static void
+build_method_buffer (gpointer key, gpointer value, gpointer userdata)
+{
+ MonoMethod *method = (MonoMethod *)value;
+ MonoProfiler *prof = (MonoProfiler *)userdata;
+ MonoClass *klass;
+ MonoImage *image;
+ char *class_name;
+ const char *image_name, *method_name, *sig, *first_filename;
+ LogBuffer *logbuffer;
+ guint i;
+
+ previous_offset = 0;
+ coverage_data = g_ptr_array_new ();
+
+ mono_profiler_coverage_get (prof, method, obtain_coverage_for_method);
+
+ klass = mono_method_get_class (method);
+ image = mono_class_get_image (klass);
+ image_name = mono_image_get_name (image);
+
+ sig = mono_signature_get_desc (mono_method_signature (method), TRUE);
+ class_name = parse_generic_type_names (mono_type_get_name (mono_class_get_type (klass)));
+ method_name = mono_method_get_name (method);
+
+ if (coverage_data->len != 0) {
+ CoverageEntry *entry = (CoverageEntry *)coverage_data->pdata[0];
+ first_filename = entry->filename ? entry->filename : "";
+ } else
+ first_filename = "";
+
+ image_name = image_name ? image_name : "";
+ sig = sig ? sig : "";
+ method_name = method_name ? method_name : "";
+
+ logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ strlen (image_name) + 1 /* image name */ +
+ strlen (class_name) + 1 /* class name */ +
+ strlen (method_name) + 1 /* method name */ +
+ strlen (sig) + 1 /* signature */ +
+ strlen (first_filename) + 1 /* first file name */ +
+ LEB128_SIZE /* token */ +
+ LEB128_SIZE /* method id */ +
+ LEB128_SIZE /* entries */
+ );
+ ENTER_LOG (logbuffer, "coverage-methods");
+
+ emit_byte (logbuffer, TYPE_COVERAGE_METHOD | TYPE_COVERAGE);
+ emit_string (logbuffer, image_name, strlen (image_name) + 1);
+ emit_string (logbuffer, class_name, strlen (class_name) + 1);
+ emit_string (logbuffer, method_name, strlen (method_name) + 1);
+ emit_string (logbuffer, sig, strlen (sig) + 1);
+ emit_string (logbuffer, first_filename, strlen (first_filename) + 1);
+
+ emit_uvalue (logbuffer, mono_method_get_token (method));
+ emit_uvalue (logbuffer, method_id);
+ emit_value (logbuffer, coverage_data->len);
+
+ EXIT_LOG (logbuffer);
+ safe_send (prof, logbuffer);
+
+ for (i = 0; i < coverage_data->len; i++) {
+ CoverageEntry *entry = (CoverageEntry *)coverage_data->pdata[i];
+
+ logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* method id */ +
+ LEB128_SIZE /* offset */ +
+ LEB128_SIZE /* counter */ +
+ LEB128_SIZE /* line */ +
+ LEB128_SIZE /* column */
+ );
+ ENTER_LOG (logbuffer, "coverage-statement");
+
+ emit_byte (logbuffer, TYPE_COVERAGE_STATEMENT | TYPE_COVERAGE);
+ emit_uvalue (logbuffer, method_id);
+ emit_uvalue (logbuffer, entry->offset);
+ emit_uvalue (logbuffer, entry->counter);
+ emit_uvalue (logbuffer, entry->line);
+ emit_uvalue (logbuffer, entry->column);
+
+ EXIT_LOG (logbuffer);
+ safe_send (prof, logbuffer);
+ }
+
+ method_id++;
+
+ g_free (class_name);
+
+ g_ptr_array_foreach (coverage_data, free_coverage_entry, NULL);
+ g_ptr_array_free (coverage_data, TRUE);
+ coverage_data = NULL;
+}
+
+/* This empties the queue */
+static guint
+count_queue (MonoLockFreeQueue *queue)
+{
+ MonoLockFreeQueueNode *node;
+ guint count = 0;
+
+ while ((node = mono_lock_free_queue_dequeue (queue))) {
+ count++;
+ mono_lock_free_queue_node_free (node);
+ }
+
+ return count;
+}
+
+static void
+build_class_buffer (gpointer key, gpointer value, gpointer userdata)
+{
+ MonoClass *klass = (MonoClass *)key;
+ MonoLockFreeQueue *class_methods = (MonoLockFreeQueue *)value;
+ MonoProfiler *prof = (MonoProfiler *)userdata;
+ MonoImage *image;
+ char *class_name;
+ const char *assembly_name;
+ int number_of_methods, partially_covered;
+ guint fully_covered;
+ LogBuffer *logbuffer;
+
+ image = mono_class_get_image (klass);
+ assembly_name = mono_image_get_name (image);
+ class_name = mono_type_get_name (mono_class_get_type (klass));
+
+ assembly_name = assembly_name ? assembly_name : "";
+ number_of_methods = mono_class_num_methods (klass);
+ fully_covered = count_queue (class_methods);
+ /* We don't handle partial covered yet */
+ partially_covered = 0;
+
+ logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ strlen (assembly_name) + 1 /* assembly name */ +
+ strlen (class_name) + 1 /* class name */ +
+ LEB128_SIZE /* no. methods */ +
+ LEB128_SIZE /* fully covered */ +
+ LEB128_SIZE /* partially covered */
+ );
+
+ ENTER_LOG (logbuffer, "coverage-class");
+ emit_byte (logbuffer, TYPE_COVERAGE_CLASS | TYPE_COVERAGE);
+ emit_string (logbuffer, assembly_name, strlen (assembly_name) + 1);
+ emit_string (logbuffer, class_name, strlen (class_name) + 1);
+ emit_uvalue (logbuffer, number_of_methods);
+ emit_uvalue (logbuffer, fully_covered);
+ emit_uvalue (logbuffer, partially_covered);
+ EXIT_LOG (logbuffer);
+
+ safe_send (prof, logbuffer);
+
+ g_free (class_name);
+}
+
+static void
+get_coverage_for_image (MonoImage *image, int *number_of_methods, guint *fully_covered, int *partially_covered)
+{
+ MonoLockFreeQueue *image_methods = (MonoLockFreeQueue *)mono_conc_hashtable_lookup (image_to_methods, image);
+
+ *number_of_methods = mono_image_get_table_rows (image, MONO_TABLE_METHOD);
+ if (image_methods)
+ *fully_covered = count_queue (image_methods);
+ else
+ *fully_covered = 0;
+
+ // FIXME: We don't handle partially covered yet.
+ *partially_covered = 0;
+}
+
+static void
+build_assembly_buffer (gpointer key, gpointer value, gpointer userdata)
+{
+ MonoAssembly *assembly = (MonoAssembly *)value;
+ MonoProfiler *prof = (MonoProfiler *)userdata;
+ MonoImage *image = mono_assembly_get_image (assembly);
+ LogBuffer *logbuffer;
+ const char *name, *guid, *filename;
+ int number_of_methods = 0, partially_covered = 0;
+ guint fully_covered = 0;
+
+ name = mono_image_get_name (image);
+ guid = mono_image_get_guid (image);
+ filename = mono_image_get_filename (image);
+
+ name = name ? name : "";
+ guid = guid ? guid : "";
+ filename = filename ? filename : "";
+
+ get_coverage_for_image (image, &number_of_methods, &fully_covered, &partially_covered);
+
+ logbuffer = ensure_logbuf (
+ EVENT_SIZE /* event */ +
+ strlen (name) + 1 /* name */ +
+ strlen (guid) + 1 /* guid */ +
+ strlen (filename) + 1 /* file name */ +
+ LEB128_SIZE /* no. methods */ +
+ LEB128_SIZE /* fully covered */ +
+ LEB128_SIZE /* partially covered */
+ );
+
+ ENTER_LOG (logbuffer, "coverage-assemblies");
+ emit_byte (logbuffer, TYPE_COVERAGE_ASSEMBLY | TYPE_COVERAGE);
+ emit_string (logbuffer, name, strlen (name) + 1);
+ emit_string (logbuffer, guid, strlen (guid) + 1);
+ emit_string (logbuffer, filename, strlen (filename) + 1);
+ emit_uvalue (logbuffer, number_of_methods);
+ emit_uvalue (logbuffer, fully_covered);
+ emit_uvalue (logbuffer, partially_covered);
+ EXIT_LOG (logbuffer);
+
+ safe_send (prof, logbuffer);
+}
+
+static void
+dump_coverage (MonoProfiler *prof)
+{
+ if (!coverage_initialized)
+ return;
+
+ COVERAGE_DEBUG(fprintf (stderr, "Coverage: Started dump\n");)
+ method_id = 0;
+
+ mono_os_mutex_lock (&coverage_mutex);
+ mono_conc_hashtable_foreach (coverage_assemblies, build_assembly_buffer, prof);
+ mono_conc_hashtable_foreach (coverage_classes, build_class_buffer, prof);
+ mono_conc_hashtable_foreach (coverage_methods, build_method_buffer, prof);
+ mono_os_mutex_unlock (&coverage_mutex);
+
+ COVERAGE_DEBUG(fprintf (stderr, "Coverage: Finished dump\n");)
+}
+
+static void
+process_method_enter_coverage (MonoProfiler *prof, MonoMethod *method)
+{
+ MonoClass *klass;
+ MonoImage *image;
+
+ if (!coverage_initialized)
+ return;
+
+ klass = mono_method_get_class (method);
+ image = mono_class_get_image (klass);
+
+ if (mono_conc_hashtable_lookup (suppressed_assemblies, (gpointer) mono_image_get_name (image)))
+ return;
+
+ mono_os_mutex_lock (&coverage_mutex);
+ mono_conc_hashtable_insert (entered_methods, method, method);
+ mono_os_mutex_unlock (&coverage_mutex);
+}
+
+static MonoLockFreeQueueNode *
+create_method_node (MonoMethod *method)
+{
+ MethodNode *node = (MethodNode *)g_malloc (sizeof (MethodNode));
+ mono_lock_free_queue_node_init ((MonoLockFreeQueueNode *) node, FALSE);
+ node->method = method;
+
+ return (MonoLockFreeQueueNode *) node;
+}
+
+static gboolean
+coverage_filter (MonoProfiler *prof, MonoMethod *method)
+{
+ MonoError error;
+ MonoClass *klass;
+ MonoImage *image;
+ MonoAssembly *assembly;
+ MonoMethodHeader *header;
+ guint32 iflags, flags, code_size;
+ char *fqn, *classname;
+ gboolean has_positive, found;
+ MonoLockFreeQueue *image_methods, *class_methods;
+ MonoLockFreeQueueNode *node;
+
+ if (!coverage_initialized)
+ return FALSE;
+
+ COVERAGE_DEBUG(fprintf (stderr, "Coverage filter for %s\n", mono_method_get_name (method));)
+
+ flags = mono_method_get_flags (method, &iflags);
+ if ((iflags & 0x1000 /*METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL*/) ||
+ (flags & 0x2000 /*METHOD_ATTRIBUTE_PINVOKE_IMPL*/)) {
+ COVERAGE_DEBUG(fprintf (stderr, " Internal call or pinvoke - ignoring\n");)
+ return FALSE;
+ }
+
+ // Don't need to do anything else if we're already tracking this method
+ if (mono_conc_hashtable_lookup (coverage_methods, method)) {
+ COVERAGE_DEBUG(fprintf (stderr, " Already tracking\n");)
+ return TRUE;
+ }
+
+ klass = mono_method_get_class (method);
+ image = mono_class_get_image (klass);
+
+ // Don't handle coverage for the core assemblies
+ if (mono_conc_hashtable_lookup (suppressed_assemblies, (gpointer) mono_image_get_name (image)) != NULL)
+ return FALSE;
+
+ if (prof->coverage_filters) {
+ /* Check already filtered classes first */
+ if (mono_conc_hashtable_lookup (filtered_classes, klass)) {
+ COVERAGE_DEBUG(fprintf (stderr, " Already filtered\n");)
+ return FALSE;
+ }
+
+ classname = mono_type_get_name (mono_class_get_type (klass));
+
+ fqn = g_strdup_printf ("[%s]%s", mono_image_get_name (image), classname);
+
+ COVERAGE_DEBUG(fprintf (stderr, " Looking for %s in filter\n", fqn);)
+ // Check positive filters first
+ has_positive = FALSE;
+ found = FALSE;
+ for (guint i = 0; i < prof->coverage_filters->len; ++i) {
+ char *filter = (char *)g_ptr_array_index (prof->coverage_filters, i);
+
+ if (filter [0] == '+') {
+ filter = &filter [1];
+
+ COVERAGE_DEBUG(fprintf (stderr, " Checking against +%s ...", filter);)
+
+ if (strstr (fqn, filter) != NULL) {
+ COVERAGE_DEBUG(fprintf (stderr, "matched\n");)
+ found = TRUE;
+ } else
+ COVERAGE_DEBUG(fprintf (stderr, "no match\n");)
+
+ has_positive = TRUE;
+ }
+ }
+
+ if (has_positive && !found) {
+ COVERAGE_DEBUG(fprintf (stderr, " Positive match was not found\n");)
+
+ mono_os_mutex_lock (&coverage_mutex);
+ mono_conc_hashtable_insert (filtered_classes, klass, klass);
+ mono_os_mutex_unlock (&coverage_mutex);
+ g_free (fqn);
+ g_free (classname);
+
+ return FALSE;
+ }
+
+ for (guint i = 0; i < prof->coverage_filters->len; ++i) {
+ // FIXME: Is substring search sufficient?
+ char *filter = (char *)g_ptr_array_index (prof->coverage_filters, i);
+ if (filter [0] == '+')
+ continue;
+
+ // Skip '-'
+ filter = &filter [1];
+ COVERAGE_DEBUG(fprintf (stderr, " Checking against -%s ...", filter);)
+
+ if (strstr (fqn, filter) != NULL) {
+ COVERAGE_DEBUG(fprintf (stderr, "matched\n");)
+
+ mono_os_mutex_lock (&coverage_mutex);
+ mono_conc_hashtable_insert (filtered_classes, klass, klass);
+ mono_os_mutex_unlock (&coverage_mutex);
+ g_free (fqn);
+ g_free (classname);
+
+ return FALSE;
+ } else
+ COVERAGE_DEBUG(fprintf (stderr, "no match\n");)
+
+ }
+
+ g_free (fqn);
+ g_free (classname);
+ }
+
+ COVERAGE_DEBUG(fprintf (stderr, " Handling coverage for %s\n", mono_method_get_name (method));)
+ header = mono_method_get_header_checked (method, &error);
+ mono_error_cleanup (&error);
+
+ mono_method_header_get_code (header, &code_size, NULL);
+
+ assembly = mono_image_get_assembly (image);
+
+ mono_os_mutex_lock (&coverage_mutex);
+ mono_conc_hashtable_insert (coverage_methods, method, method);
+ mono_conc_hashtable_insert (coverage_assemblies, assembly, assembly);
+ mono_os_mutex_unlock (&coverage_mutex);
+
+ image_methods = (MonoLockFreeQueue *)mono_conc_hashtable_lookup (image_to_methods, image);
+
+ if (image_methods == NULL) {
+ image_methods = (MonoLockFreeQueue *)g_malloc (sizeof (MonoLockFreeQueue));
+ mono_lock_free_queue_init (image_methods);
+ mono_os_mutex_lock (&coverage_mutex);
+ mono_conc_hashtable_insert (image_to_methods, image, image_methods);
+ mono_os_mutex_unlock (&coverage_mutex);
+ }
+
+ node = create_method_node (method);
+ mono_lock_free_queue_enqueue (image_methods, node);
+
+ class_methods = (MonoLockFreeQueue *)mono_conc_hashtable_lookup (coverage_classes, klass);
+
+ if (class_methods == NULL) {
+ class_methods = (MonoLockFreeQueue *)g_malloc (sizeof (MonoLockFreeQueue));
+ mono_lock_free_queue_init (class_methods);
+ mono_os_mutex_lock (&coverage_mutex);
+ mono_conc_hashtable_insert (coverage_classes, klass, class_methods);
+ mono_os_mutex_unlock (&coverage_mutex);
+ }
+
+ node = create_method_node (method);
+ mono_lock_free_queue_enqueue (class_methods, node);
+
+ return TRUE;
+}
+
+#define LINE_BUFFER_SIZE 4096
+/* Max file limit of 128KB */
+#define MAX_FILE_SIZE 128 * 1024
+static char *
+get_file_content (FILE *stream)
+{
+ char *buffer;
+ ssize_t bytes_read;
+ long filesize;
+ int res, offset = 0;
+
+ res = fseek (stream, 0, SEEK_END);
+ if (res < 0)
+ return NULL;
+
+ filesize = ftell (stream);
+ if (filesize < 0)
+ return NULL;
+
+ res = fseek (stream, 0, SEEK_SET);
+ if (res < 0)
+ return NULL;
+
+ if (filesize > MAX_FILE_SIZE)
+ return NULL;
+
+ buffer = (char *)g_malloc ((filesize + 1) * sizeof (char));
+ while ((bytes_read = fread (buffer + offset, 1, LINE_BUFFER_SIZE, stream)) > 0)
+ offset += bytes_read;
+
+ /* NULL terminate our buffer */
+ buffer[filesize] = '\0';
+ return buffer;
+}
+
+static char *
+get_next_line (char *contents, char **next_start)
+{
+ char *p = contents;
+
+ if (p == NULL || *p == '\0') {
+ *next_start = NULL;
+ return NULL;
+ }
+
+ while (*p != '\n' && *p != '\0')
+ p++;
+
+ if (*p == '\n') {
+ *p = '\0';
+ *next_start = p + 1;
+ } else
+ *next_start = NULL;
+
+ return contents;
+}
+
+static void
+init_suppressed_assemblies (void)
+{
+ char *content;
+ char *line;
+ FILE *sa_file;
+
+ suppressed_assemblies = mono_conc_hashtable_new (g_str_hash, g_str_equal);
+ sa_file = fopen (SUPPRESSION_DIR "/mono-profiler-log.suppression", "r");
+ if (sa_file == NULL)
+ return;
+
+ /* Don't need to free @content as it is referred to by the lines stored in @suppressed_assemblies */
+ content = get_file_content (sa_file);
+ if (content == NULL) {
+ g_error ("mono-profiler-log.suppression is greater than 128kb - aborting\n");
+ }
+
+ while ((line = get_next_line (content, &content))) {
+ line = g_strchomp (g_strchug (line));
+ /* No locking needed as we're doing initialization */
+ mono_conc_hashtable_insert (suppressed_assemblies, line, line);
+ }
+
+ fclose (sa_file);
+}
+
+#endif /* DISABLE_HELPER_THREAD */
+
+static void
+coverage_init (MonoProfiler *prof)
+{
+#ifndef DISABLE_HELPER_THREAD
+ assert (!coverage_initialized);
+
+ COVERAGE_DEBUG(fprintf (stderr, "Coverage initialized\n");)
+
+ mono_os_mutex_init (&coverage_mutex);
+ coverage_methods = mono_conc_hashtable_new (NULL, NULL);
+ coverage_assemblies = mono_conc_hashtable_new (NULL, NULL);
+ coverage_classes = mono_conc_hashtable_new (NULL, NULL);
+ filtered_classes = mono_conc_hashtable_new (NULL, NULL);
+ entered_methods = mono_conc_hashtable_new (NULL, NULL);
+ image_to_methods = mono_conc_hashtable_new (NULL, NULL);
+ init_suppressed_assemblies ();
+
+ coverage_initialized = TRUE;
#endif /* DISABLE_HELPER_THREAD */
+}
static void
log_shutdown (MonoProfiler *prof)
#ifndef DISABLE_HELPER_THREAD
counters_and_perfcounters_sample (prof);
+ dump_coverage (prof);
+
if (prof->command_port) {
char c = 1;
ign_res (write (prof->pipes [1], &c, 1));
read_perf_mmap (prof, i);
}
#endif
- dump_sample_hits (prof, prof->stat_buffers);
+
+ g_ptr_array_free (prof->sorted_sample_events, TRUE);
if (TLS_GET (LogBuffer, tlsbuffer))
send_buffer (prof, TLS_GET (GPtrArray, tlsmethodlist), TLS_GET (LogBuffer, tlsbuffer));
fclose (prof->file);
mono_conc_hashtable_destroy (prof->method_table);
- mono_mutex_destroy (&prof->method_table_mutex);
+ mono_os_mutex_destroy (&prof->method_table_mutex);
+
+ if (coverage_initialized) {
+ mono_conc_hashtable_destroy (coverage_methods);
+ mono_conc_hashtable_destroy (coverage_assemblies);
+ mono_conc_hashtable_destroy (coverage_classes);
+ mono_conc_hashtable_destroy (filtered_classes);
+
+ mono_conc_hashtable_destroy (entered_methods);
+ mono_conc_hashtable_destroy (image_to_methods);
+ mono_conc_hashtable_destroy (suppressed_assemblies);
+ mono_os_mutex_destroy (&coverage_mutex);
+ }
free (prof);
}
1900 + ts->tm_year, 1 + ts->tm_mon, ts->tm_mday, ts->tm_hour, ts->tm_min, ts->tm_sec);
s_date = strlen (time_buf);
s_pid = strlen (pid_buf);
- d = res = malloc (strlen (filename) + s_date * count_dates + s_pid * count_pids);
+ d = res = (char *)malloc (strlen (filename) + s_date * count_dates + s_pid * count_pids);
for (p = filename; *p; p++) {
if (*p != '%') {
*d++ = *p;
static void*
helper_thread (void* arg)
{
- MonoProfiler* prof = arg;
+ MonoProfiler* prof = (MonoProfiler *)arg;
int command_socket;
int len;
char buf [64];
if (len < 0) {
if (errno == EINTR)
continue;
-
+
g_warning ("Error in proflog server: %s", strerror (errno));
return NULL;
}
-
+
if (FD_ISSET (prof->pipes [0], &rfds)) {
char c;
int r = read (prof->pipes [0], &c, 1);
continue;
}
/* time to shut down */
+ dump_sample_hits (prof, prof->stat_buffers);
if (thread)
mono_thread_detach (thread);
if (do_debug)
if (strcmp (buf, "heapshot\n") == 0) {
heapshot_requested = 1;
//fprintf (stderr, "perform heapshot\n");
- if (runtime_inited && !thread) {
+ if (InterlockedRead (&runtime_inited) && !thread) {
thread = mono_thread_attach (mono_get_root_domain ());
/*fprintf (stderr, "attached\n");*/
}
static void *
writer_thread (void *arg)
{
- MonoProfiler *prof = arg;
+ MonoProfiler *prof = (MonoProfiler *)arg;
mono_threads_attach_tools_thread ();
* methods have metadata emitted before they're referenced.
*/
for (guint i = 0; i < entry->methods->len; i++) {
- MethodInfo *info = g_ptr_array_index (entry->methods, i);
+ MethodInfo *info = (MethodInfo *)g_ptr_array_index (entry->methods, i);
if (mono_conc_hashtable_lookup (prof->method_table, info->method))
continue;
* method lists will just be empty for the rest of the
* app's lifetime.
*/
+ mono_os_mutex_lock (&prof->method_table_mutex);
mono_conc_hashtable_insert (prof->method_table, info->method, info->method);
+ mono_os_mutex_unlock (&prof->method_table_mutex);
char *name = mono_method_full_name (info->method, 1);
int nlen = strlen (name) + 1;
- uint64_t now = current_time ();
-
- method_buffer = ensure_logbuf_inner (method_buffer, 32 + nlen);
+ void *cstart = info->ji ? mono_jit_info_get_code_start (info->ji) : NULL;
+ int csize = info->ji ? mono_jit_info_get_code_size (info->ji) : 0;
+
+ method_buffer = ensure_logbuf_inner (method_buffer,
+ EVENT_SIZE /* event */ +
+ LEB128_SIZE /* time */ +
+ LEB128_SIZE /* method */ +
+ LEB128_SIZE /* start */ +
+ LEB128_SIZE /* size */ +
+ nlen /* name */
+ );
emit_byte (method_buffer, TYPE_JIT | TYPE_METHOD);
- emit_time (method_buffer, now);
+ emit_time (method_buffer, info->time);
emit_method_inner (method_buffer, info->method);
- emit_ptr (method_buffer, mono_jit_info_get_code_start (info->ji));
- emit_value (method_buffer, mono_jit_info_get_code_size (info->ji));
+ emit_ptr (method_buffer, cstart);
+ emit_value (method_buffer, csize);
- memcpy (method_buffer->data, name, nlen);
- method_buffer->data += nlen;
+ memcpy (method_buffer->cursor, name, nlen);
+ method_buffer->cursor += nlen;
mono_free (name);
free (info);
return !pthread_create (&prof->writer_thread, NULL, writer_thread, prof);
}
+static void
+runtime_initialized (MonoProfiler *profiler)
+{
+#ifndef DISABLE_HELPER_THREAD
+ if (hs_mode_ondemand || need_helper_thread) {
+ if (!start_helper_thread (profiler))
+ profiler->command_port = 0;
+ }
+#endif
+
+ start_writer_thread (profiler);
+
+ InterlockedWrite (&runtime_inited, 1);
+#ifndef DISABLE_HELPER_THREAD
+ counters_init (profiler);
+ counters_sample (profiler, 0);
+#endif
+ /* ensure the main thread data and startup are available soon */
+ safe_send (profiler, ensure_logbuf (0));
+}
+
static MonoProfiler*
-create_profiler (const char *filename)
+create_profiler (const char *filename, GPtrArray *filters)
{
MonoProfiler *prof;
char *nf;
int force_delete = 0;
- int need_helper_thread = 0;
- prof = calloc (1, sizeof (MonoProfiler));
+ prof = (MonoProfiler *)calloc (1, sizeof (MonoProfiler));
prof->command_port = command_port;
if (filename && *filename == '-') {
nf = new_filename (filename);
if (do_report) {
int s = strlen (nf) + 32;
- char *p = malloc (s);
+ char *p = (char *)malloc (s);
snprintf (p, s, "|mprof-report '--out=%s' -", nf);
free (nf);
nf = p;
if (do_counters && !need_helper_thread) {
need_helper_thread = 1;
}
-#ifndef DISABLE_HELPER_THREAD
- if (hs_mode_ondemand || need_helper_thread) {
- if (!start_helper_thread (prof))
- prof->command_port = 0;
- }
-#else
+
+ prof->sorted_sample_events = g_ptr_array_sized_new (BUFFER_SIZE / SAMPLE_EVENT_SIZE_IN_SLOTS (0));
+
+#ifdef DISABLE_HELPER_THREAD
if (hs_mode_ondemand)
fprintf (stderr, "Ondemand heapshot unavailable on this arch.\n");
+
+ if (do_coverage)
+ fprintf (stderr, "Coverage unavailable on this arch.\n");
+
#endif
mono_lock_free_queue_init (&prof->writer_queue);
- mono_mutex_init (&prof->method_table_mutex);
- prof->method_table = mono_conc_hashtable_new (&prof->method_table_mutex, NULL, NULL);
+ mono_os_mutex_init (&prof->method_table_mutex);
+ prof->method_table = mono_conc_hashtable_new (NULL, NULL);
- start_writer_thread (prof);
+ if (do_coverage)
+ coverage_init (prof);
+ prof->coverage_filters = filters;
prof->startup_time = current_time ();
return prof;
printf ("Log profiler version %d.%d (format: %d)\n", LOG_VERSION_MAJOR, LOG_VERSION_MINOR, LOG_DATA_VERSION);
printf ("Usage: mono --profile=log[:OPTION1[,OPTION2...]] program.exe\n");
printf ("Options:\n");
- printf ("\thelp show this usage info\n");
- printf ("\t[no]alloc enable/disable recording allocation info\n");
- printf ("\t[no]calls enable/disable recording enter/leave method events\n");
- printf ("\theapshot[=MODE] record heap shot info (by default at each major collection)\n");
- printf ("\t MODE: every XXms milliseconds, every YYgc collections, ondemand\n");
- printf ("\tcounters sample counters every 1s\n");
- printf ("\tsample[=TYPE] use statistical sampling mode (by default cycles/1000)\n");
- printf ("\t TYPE: cycles,instr,cacherefs,cachemiss,branches,branchmiss\n");
- printf ("\t TYPE can be followed by /FREQUENCY\n");
- printf ("\ttime=fast use a faster (but more inaccurate) timer\n");
- printf ("\tmaxframes=NUM collect up to NUM stack frames\n");
- printf ("\tcalldepth=NUM ignore method events for call chain depth bigger than NUM\n");
- printf ("\toutput=FILENAME write the data to file FILENAME (-FILENAME to overwrite)\n");
- printf ("\toutput=|PROGRAM write the data to the stdin of PROGRAM\n");
- printf ("\t %%t is subtituted with date and time, %%p with the pid\n");
- printf ("\treport create a report instead of writing the raw data to a file\n");
- printf ("\tzip compress the output data\n");
- printf ("\tport=PORTNUM use PORTNUM for the listening command server\n");
+ printf ("\thelp show this usage info\n");
+ printf ("\t[no]alloc enable/disable recording allocation info\n");
+ printf ("\t[no]calls enable/disable recording enter/leave method events\n");
+ printf ("\theapshot[=MODE] record heap shot info (by default at each major collection)\n");
+ printf ("\t MODE: every XXms milliseconds, every YYgc collections, ondemand\n");
+ printf ("\tcounters sample counters every 1s\n");
+ printf ("\tsample[=TYPE] use statistical sampling mode (by default cycles/1000)\n");
+ printf ("\t TYPE: cycles,instr,cacherefs,cachemiss,branches,branchmiss\n");
+ printf ("\t TYPE can be followed by /FREQUENCY\n");
+ printf ("\ttime=fast use a faster (but more inaccurate) timer\n");
+ printf ("\tmaxframes=NUM collect up to NUM stack frames\n");
+ printf ("\tcalldepth=NUM ignore method events for call chain depth bigger than NUM\n");
+ printf ("\toutput=FILENAME write the data to file FILENAME (-FILENAME to overwrite)\n");
+ printf ("\toutput=|PROGRAM write the data to the stdin of PROGRAM\n");
+ printf ("\t %%t is subtituted with date and time, %%p with the pid\n");
+ printf ("\treport create a report instead of writing the raw data to a file\n");
+ printf ("\tzip compress the output data\n");
+ printf ("\tport=PORTNUM use PORTNUM for the listening command server\n");
+ printf ("\tcoverage enable collection of code coverage data\n");
+ printf ("\tcovfilter=ASSEMBLY add an assembly to the code coverage filters\n");
+ printf ("\t add a + to include the assembly or a - to exclude it\n");
+ printf ("\t filter=-mscorlib\n");
+ printf ("\tcovfilter-file=FILE use FILE to generate the list of assemblies to be filtered\n");
if (do_exit)
exit (1);
}
} else {
l = end - opt;
}
- val = malloc (l + 1);
+ val = (char *)malloc (l + 1);
memcpy (val, opt, l);
val [l] = 0;
*rval = val;
free (val);
}
-/*
+/*
* declaration to silence the compiler: this is the entry point that
* mono will load from the shared library and call.
*/
mono_profiler_startup (const char *desc)
{
MonoProfiler *prof;
+ GPtrArray *filters = NULL;
char *filename = NULL;
const char *p;
const char *opt;
int calls_enabled = 0;
int allocs_enabled = 0;
int only_counters = 0;
+ int only_coverage = 0;
int events = MONO_PROFILE_GC|MONO_PROFILE_ALLOCATIONS|
MONO_PROFILE_GC_MOVES|MONO_PROFILE_CLASS_EVENTS|MONO_PROFILE_THREADS|
MONO_PROFILE_ENTER_LEAVE|MONO_PROFILE_JIT_COMPILATION|MONO_PROFILE_EXCEPTIONS|
- MONO_PROFILE_MONITOR_EVENTS|MONO_PROFILE_MODULE_EVENTS|MONO_PROFILE_GC_ROOTS;
+ MONO_PROFILE_MONITOR_EVENTS|MONO_PROFILE_MODULE_EVENTS|MONO_PROFILE_GC_ROOTS|
+ MONO_PROFILE_INS_COVERAGE|MONO_PROFILE_APPDOMAIN_EVENTS|MONO_PROFILE_CONTEXT_EVENTS|
+ MONO_PROFILE_ASSEMBLY_EVENTS;
p = desc;
if (strncmp (p, "log", 3))
only_counters = 1;
continue;
}
+ if ((opt = match_option (p, "coverage", NULL)) != p) {
+ do_coverage = 1;
+ events |= MONO_PROFILE_ENTER_LEAVE;
+ debug_coverage = (g_getenv ("MONO_PROFILER_DEBUG_COVERAGE") != NULL);
+ continue;
+ }
+ if ((opt = match_option (p, "onlycoverage", NULL)) != p) {
+ only_coverage = 1;
+ continue;
+ }
+ if ((opt = match_option (p, "covfilter-file", &val)) != p) {
+ FILE *filter_file;
+ char *line, *content;
+
+ if (filters == NULL)
+ filters = g_ptr_array_new ();
+
+ filter_file = fopen (val, "r");
+ if (filter_file == NULL) {
+ fprintf (stderr, "Unable to open %s\n", val);
+ exit (0);
+ }
+
+ /* Don't need to free content as it is referred to by the lines stored in @filters */
+ content = get_file_content (filter_file);
+ if (content == NULL)
+ fprintf (stderr, "WARNING: %s is greater than 128kb - ignoring\n", val);
+
+ while ((line = get_next_line (content, &content)))
+ g_ptr_array_add (filters, g_strchug (g_strchomp (line)));
+
+ fclose (filter_file);
+ continue;
+ }
+ if ((opt = match_option (p, "covfilter", &val)) != p) {
+ if (filters == NULL)
+ filters = g_ptr_array_new ();
+
+ g_ptr_array_add (filters, val);
+ continue;
+ }
if (opt == p) {
usage (0);
exit (0);
events |= MONO_PROFILE_ALLOCATIONS;
if (only_counters)
events = 0;
+ if (only_coverage)
+ events = MONO_PROFILE_ENTER_LEAVE | MONO_PROFILE_INS_COVERAGE;
+
utils_init (fast_time);
- prof = create_profiler (filename);
+ prof = create_profiler (filename, filters);
if (!prof)
return;
init_thread ();
mono_profiler_install_allocation (gc_alloc);
mono_profiler_install_gc_moves (gc_moves);
mono_profiler_install_gc_roots (gc_handle, gc_roots);
- mono_profiler_install_class (NULL, class_loaded, NULL, NULL);
- mono_profiler_install_module (NULL, image_loaded, NULL, NULL);
+ mono_profiler_install_appdomain (NULL, domain_loaded, NULL, domain_unloaded);
+ mono_profiler_install_appdomain_name (domain_name);
+ mono_profiler_install_context (context_loaded, context_unloaded);
+ mono_profiler_install_class (NULL, class_loaded, NULL, class_unloaded);
+ mono_profiler_install_module (NULL, image_loaded, NULL, image_unloaded);
+ mono_profiler_install_assembly (NULL, assembly_loaded, assembly_unloaded, NULL);
mono_profiler_install_thread (thread_start, thread_end);
mono_profiler_install_thread_name (thread_name);
mono_profiler_install_enter_leave (method_enter, method_leave);
mono_profiler_install_exception (throw_exc, method_exc_leave, clause_exc);
mono_profiler_install_monitor (monitor_event);
mono_profiler_install_runtime_initialized (runtime_initialized);
+ if (do_coverage)
+ mono_profiler_install_coverage_filter (coverage_filter);
-
if (do_mono_sample && sample_type == SAMPLE_CYCLES && !only_counters) {
events |= MONO_PROFILE_STATISTICAL;
mono_profiler_set_statistical_mode (sampling_mode, 1000000 / sample_freq);
mono_profiler_install_statistical (mono_sample_hit);
}
- mono_profiler_set_events (events);
+ mono_profiler_set_events ((MonoProfileFlags)events);
TLS_INIT (tlsbuffer);
TLS_INIT (tlsmethodlist);
}
-