#include <mono/metadata/mono-config.h>
#include <mono/metadata/mono-gc.h>
#include <mono/metadata/mono-perfcounters.h>
-#include <mono/metadata/profiler.h>
#include <mono/utils/atomic.h>
#include <mono/utils/hazard-pointer.h>
#include <mono/utils/lock-free-alloc.h>
#ifdef HAVE_DLFCN_H
#include <dlfcn.h>
#endif
+#include <fcntl.h>
#ifdef HAVE_LINK_H
#include <link.h>
#endif
/* Worst-case size in bytes of a 64-bit value encoded with LEB128. */
#define LEB128_SIZE 10
+
/* Size of a value encoded as a single byte. */
+#undef BYTE_SIZE // mach/i386/vm_param.h on OS X defines this to 8, but it isn't used for anything.
#define BYTE_SIZE 1
+
/* Size in bytes of the event prefix (ID + time). */
#define EVENT_SIZE (BYTE_SIZE + LEB128_SIZE)
static int use_zip = 0;
static int do_report = 0;
static int do_heap_shot = 0;
-static int max_call_depth = 100;
+static int max_call_depth = 0;
static int command_port = 0;
static int heapshot_requested = 0;
-static int sample_freq = 0;
static int do_mono_sample = 0;
static int do_debug = 0;
-static int do_counters = 0;
static int do_coverage = 0;
+static gboolean no_counters = FALSE;
+static gboolean only_coverage = FALSE;
static gboolean debug_coverage = FALSE;
-static MonoProfileSamplingMode sampling_mode = MONO_PROFILER_STAT_MODE_PROCESS;
static int max_allocated_sample_hits;
// Statistics for internal profiler data structures.
}
/*
- * These macros create a scope to avoid leaking the buffer returned
- * from ensure_logbuf () as it may have been invalidated by a GC
- * thread during STW. If you called init_thread () with add_to_lls =
- * FALSE, then don't use these macros.
+ * These macros should be used when writing an event to a log buffer. They take
+ * care of a bunch of stuff that can be repetitive and error-prone, such as
+ * acquiring/releasing the buffer lock, incrementing the event counter,
+ * expanding the log buffer, processing requests, etc. They also create a scope
+ * so that it's harder to leak the LogBuffer pointer, which can be problematic
+ * as the pointer is unstable when the buffer lock isn't acquired.
*/
#define ENTER_LOG(COUNTER, BUFFER, SIZE) \
g_assert (!thread__->busy && "Why are we trying to write a new event while already writing one?"); \
thread__->busy = TRUE; \
InterlockedIncrement ((COUNTER)); \
- LogBuffer *BUFFER = ensure_logbuf_unsafe ((SIZE))
+ LogBuffer *BUFFER = ensure_logbuf_unsafe (thread__, (SIZE))
#define EXIT_LOG_EXPLICIT(SEND, REQUESTS) \
thread__->busy = FALSE; \
if ((SEND)) \
- send_log_unsafe (FALSE, TRUE); \
+ send_log_unsafe (TRUE); \
if (thread__->attached) \
buffer_unlock (); \
if ((REQUESTS)) \
process_requests (); \
} while (0)
-#define EXIT_LOG EXIT_LOG_EXPLICIT (TRUE, TRUE)
+// Pass these to EXIT_LOG_EXPLICIT () for easier reading.
+#define DO_SEND TRUE
+#define NO_SEND FALSE
+#define DO_REQUESTS TRUE
+#define NO_REQUESTS FALSE
+
+#define EXIT_LOG EXIT_LOG_EXPLICIT (DO_SEND, DO_REQUESTS)
static volatile gint32 buffer_rwlock_count;
static volatile gpointer buffer_rwlock_exclusive;
#define PROF_TLS_SET(VAL) (pthread_setspecific (profiler_tls, (VAL)))
#define PROF_TLS_GET() ((MonoProfilerThread *) pthread_getspecific (profiler_tls))
#define PROF_TLS_INIT() (pthread_key_create (&profiler_tls, NULL))
-#define PROF_TLS_FREE() (pthread_key_delete (&profiler_tls))
+#define PROF_TLS_FREE() (pthread_key_delete (profiler_tls))
static pthread_key_t profiler_tls;
pstrdup (const char *s)
{
int len = strlen (s) + 1;
- char *p = (char *)malloc (len);
+ char *p = (char *) g_malloc (len);
memcpy (p, s, len);
return p;
}
}
static LogBuffer*
-create_buffer (void)
+create_buffer (uintptr_t tid)
{
LogBuffer* buf = (LogBuffer *) alloc_buffer (BUFFER_SIZE);
buf->size = BUFFER_SIZE;
buf->time_base = current_time ();
buf->last_time = buf->time_base;
- buf->buf_end = (unsigned char*)buf + buf->size;
+ buf->buf_end = (unsigned char *) buf + buf->size;
buf->cursor = buf->buf;
+ buf->thread_id = tid;
return buf;
}
static void
init_buffer_state (MonoProfilerThread *thread)
{
- thread->buffer = create_buffer ();
+ thread->buffer = create_buffer (thread->node.key);
thread->methods = NULL;
}
if (thread)
return thread;
- thread = malloc (sizeof (MonoProfilerThread));
+ thread = g_malloc (sizeof (MonoProfilerThread));
thread->node.key = thread_id ();
thread->profiler = prof;
thread->attached = add_to_lls;
{
g_assert (!thread->attached && "Why are we manually freeing an attached thread?");
- free (thread);
+ g_free (thread);
PROF_TLS_SET (NULL);
}
-static LogBuffer *
-ensure_logbuf_inner (LogBuffer *old, int bytes)
-{
- if (old && old->cursor + bytes + 100 < old->buf_end)
- return old;
-
- LogBuffer *new_ = create_buffer ();
- new_->next = old;
-
- return new_;
-}
-
// Only valid if init_thread () was called with add_to_lls = FALSE.
static LogBuffer *
-ensure_logbuf_unsafe (int bytes)
+ensure_logbuf_unsafe (MonoProfilerThread *thread, int bytes)
{
- MonoProfilerThread *thread = PROF_TLS_GET ();
LogBuffer *old = thread->buffer;
- LogBuffer *new_ = ensure_logbuf_inner (old, bytes);
- if (new_ == old)
- return old; // Still enough space.
+ if (old && old->cursor + bytes + 100 < old->buf_end)
+ return old;
+ LogBuffer *new_ = create_buffer (thread->node.key);
+ new_->next = old;
thread->buffer = new_;
return new_;
MonoProfilerThread *thread = PROF_TLS_GET ();
if (!mono_conc_hashtable_lookup (thread->profiler->method_table, method)) {
- MethodInfo *info = (MethodInfo *) malloc (sizeof (MethodInfo));
+ MethodInfo *info = (MethodInfo *) g_malloc (sizeof (MethodInfo));
info->method = method;
info->ji = ji;
const char *arch = mono_config_get_cpu ();
const char *os = mono_config_get_os ();
- char *hbuf = malloc (
+ char *hbuf = g_malloc (
sizeof (gint32) /* header id */ +
sizeof (gint8) /* major version */ +
sizeof (gint8) /* minor version */ +
fflush (profiler->file);
}
- free (hbuf);
+ g_free (hbuf);
}
/*
InterlockedIncrement (&thread_ends_ctr);
- thread->buffer = ensure_logbuf_inner (thread->buffer,
+ LogBuffer *buf = ensure_logbuf_unsafe (thread,
EVENT_SIZE /* event */ +
BYTE_SIZE /* type */ +
LEB128_SIZE /* tid */
);
- emit_event (thread->buffer, TYPE_END_UNLOAD | TYPE_METADATA);
- emit_byte (thread->buffer, TYPE_THREAD);
- emit_ptr (thread->buffer, (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);
- free (thread);
+ g_free (thread);
}
static void
mono_gc_collect (mono_gc_max_generation ());
}
-// Avoid calling this directly if possible. Use the functions below.
+// Only valid if init_thread () was called with add_to_lls = FALSE.
static void
-send_log_unsafe (gboolean lock, gboolean if_needed)
+send_log_unsafe (gboolean if_needed)
{
MonoProfilerThread *thread = PROF_TLS_GET ();
- if (lock)
- buffer_lock ();
-
if (!if_needed || (if_needed && thread->buffer->next)) {
if (!thread->attached)
for (LogBuffer *iter = thread->buffer; iter; iter = iter->next)
send_buffer (thread);
init_buffer_state (thread);
}
-
- if (lock)
- buffer_unlock ();
}
// Assumes that the exclusive lock is held.
g_assert (InterlockedReadPointer (&buffer_rwlock_exclusive) == (gpointer) thread_id () && "Why don't we hold the exclusive lock?");
MONO_LLS_FOREACH_SAFE (&profiler_thread_list, MonoProfilerThread, thread) {
+ g_assert (thread->attached && "Why is a thread in the LLS not attached?");
+
send_buffer (thread);
init_buffer_state (thread);
} MONO_LLS_FOREACH_SAFE_END
emit_event (logbuffer, TYPE_META | TYPE_SYNC_POINT);
emit_byte (logbuffer, type);
- EXIT_LOG_EXPLICIT (FALSE, FALSE);
+ EXIT_LOG_EXPLICIT (NO_SEND, NO_REQUESTS);
- send_log_unsafe (FALSE, FALSE);
+ send_log_unsafe (FALSE);
}
// Assumes that the exclusive lock is held.
emit_obj (logbuffer, refs [i]);
}
- EXIT_LOG_EXPLICIT (TRUE, FALSE);
+ EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
return 0;
}
emit_event (logbuffer, TYPE_HEAP_START | TYPE_HEAP);
- EXIT_LOG_EXPLICIT (TRUE, FALSE);
+ EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
mono_gc_walk_heap (0, gc_reference, NULL);
emit_event (logbuffer, TYPE_HEAP_END | TYPE_HEAP);
- EXIT_LOG_EXPLICIT (TRUE, FALSE);
+ EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
}
static void
emit_value (logbuffer, extra_info [i]);
}
- EXIT_LOG_EXPLICIT (TRUE, FALSE);
+ EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
}
static void
emit_byte (logbuffer, ev);
emit_byte (logbuffer, generation);
- EXIT_LOG_EXPLICIT (FALSE, FALSE);
+ EXIT_LOG_EXPLICIT (NO_SEND, NO_REQUESTS);
switch (ev) {
case MONO_GC_EVENT_START:
emit_event (logbuffer, TYPE_GC_RESIZE | TYPE_GC);
emit_value (logbuffer, new_size);
- EXIT_LOG_EXPLICIT (TRUE, FALSE);
+ EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
}
-// If you alter MAX_FRAMES, you may need to alter SAMPLE_BLOCK_SIZE too.
-#define MAX_FRAMES 32
-
typedef struct {
int count;
MonoMethod* methods [MAX_FRAMES];
for (int i = 0; i < num; ++i)
emit_obj (logbuffer, objects [i]);
- EXIT_LOG_EXPLICIT (TRUE, FALSE);
+ EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
}
static void
char buf [1024];
char *p;
push_nesting (buf, klass);
- p = (char *)malloc (strlen (buf) + 1);
+ p = (char *) g_malloc (strlen (buf) + 1);
strcpy (p, buf);
return p;
}
{
process_method_enter_coverage (prof, method);
- if (PROF_TLS_GET ()->call_depth++ <= max_call_depth) {
+ if (!only_coverage && PROF_TLS_GET ()->call_depth++ <= max_call_depth) {
ENTER_LOG (&method_entries_ctr, logbuffer,
EVENT_SIZE /* event */ +
LEB128_SIZE /* method */
static void
method_leave (MonoProfiler *prof, MonoMethod *method)
{
- if (--PROF_TLS_GET ()->call_depth <= max_call_depth) {
+ if (!only_coverage && --PROF_TLS_GET ()->call_depth <= max_call_depth) {
ENTER_LOG (&method_exits_ctr, logbuffer,
EVENT_SIZE /* event */ +
LEB128_SIZE /* method */
static void
method_exc_leave (MonoProfiler *prof, MonoMethod *method)
{
- if (!nocalls && --PROF_TLS_GET ()->call_depth <= max_call_depth) {
+ if (!only_coverage && !nocalls && --PROF_TLS_GET ()->call_depth <= max_call_depth) {
ENTER_LOG (&method_exception_exits_ctr, logbuffer,
EVENT_SIZE /* event */ +
LEB128_SIZE /* method */
emit_byte (logbuffer, TYPE_THREAD);
emit_ptr (logbuffer, (void*) tid);
- EXIT_LOG_EXPLICIT (FALSE, FALSE);
+ EXIT_LOG_EXPLICIT (NO_SEND, NO_REQUESTS);
MonoProfilerThread *thread = PROF_TLS_GET ();
size_code_pages *= 2;
if (size_code_pages == 0)
size_code_pages = 16;
- n = (uintptr_t *)calloc (sizeof (uintptr_t) * size_code_pages, 1);
+ n = (uintptr_t *) g_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]);
memcpy (logbuffer->cursor, filename, len);
logbuffer->cursor += len;
- EXIT_LOG_EXPLICIT (TRUE, FALSE);
+ EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
}
#endif
memcpy (logbuffer->cursor, name, len);
logbuffer->cursor += len;
- EXIT_LOG_EXPLICIT (TRUE, FALSE);
+ EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
}
/* ELF code crashes on some systems. */
}
}
-static int
-mono_cpu_count (void)
-{
-#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
- * of present is always of the form "0-(n-1)" when there is more than
- * 1 core, n being the number of CPU cores in the system. Otherwise
- * the value is simply 0
- */
- if (present != -1 && read (present, (char*)buffer, sizeof (buffer)) > 3)
- count = strtol (((char*)buffer) + 2, NULL, 10);
- if (present != -1)
- close (present);
- 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
- {
- 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;
- mib [1] = HW_NCPU;
- if (sysctl (mib, 2, &count, &len, NULL, 0) == 0)
- return count;
- }
-#endif
-#ifdef HOST_WIN32
- {
- SYSTEM_INFO info;
- GetSystemInfo (&info);
- return info.dwNumberOfProcessors;
- }
-#endif
-
- static gboolean warned;
-
- if (!warned) {
- g_warning ("Don't know how to determine CPU count on this platform; assuming 1");
- warned = TRUE;
- }
-
- return 1;
-}
-
typedef struct MonoCounterAgent {
MonoCounter *counter;
// MonoCounterAgent specific data :
}
}
- agent = (MonoCounterAgent *)malloc (sizeof (MonoCounterAgent));
+ agent = (MonoCounterAgent *) g_malloc (sizeof (MonoCounterAgent));
agent->counter = counter;
agent->value = NULL;
agent->value_size = 0;
agent->emitted = 1;
}
- EXIT_LOG_EXPLICIT (TRUE, FALSE);
+ EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
done:
mono_os_mutex_unlock (&counters_mutex);
counter = agent->counter;
size = mono_counter_get_size (counter);
- if (size < 0) {
- continue; // FIXME error
- } else if (size > buffer_size) {
+
+ if (size > buffer_size) {
buffer_size = size;
buffer = g_realloc (buffer, buffer_size);
}
memset (buffer, 0, buffer_size);
- if (mono_counters_sample (counter, buffer, size) < 0)
- continue; // FIXME error
+ g_assert (mono_counters_sample (counter, buffer, size));
type = mono_counter_get_type (counter);
emit_value (logbuffer, 0);
- EXIT_LOG_EXPLICIT (TRUE, FALSE);
+ EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
mono_os_mutex_unlock (&counters_mutex);
}
pcagent->emitted = 1;
}
- EXIT_LOG_EXPLICIT (TRUE, FALSE);
+ EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
}
static gboolean
emit_value (logbuffer, 0);
- EXIT_LOG_EXPLICIT (TRUE, FALSE);
+ EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
done:
mono_os_mutex_unlock (&counters_mutex);
if (name == NULL || *name == '\0')
return g_strdup ("");
- if (!(ret = new_name = (char *)calloc (strlen (name) * 4 + 1, sizeof (char))))
+ if (!(ret = new_name = (char *) g_calloc (strlen (name) * 4 + 1, sizeof (char))))
return NULL;
do {
emit_uvalue (logbuffer, method_id);
emit_value (logbuffer, coverage_data->len);
- EXIT_LOG_EXPLICIT (TRUE, FALSE);
+ EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
for (i = 0; i < coverage_data->len; i++) {
CoverageEntry *entry = (CoverageEntry *)coverage_data->pdata[i];
emit_uvalue (logbuffer, entry->line);
emit_uvalue (logbuffer, entry->column);
- EXIT_LOG_EXPLICIT (TRUE, FALSE);
+ EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
}
method_id++;
while ((node = mono_lock_free_queue_dequeue (queue))) {
count++;
- mono_thread_hazardous_try_free (node, free);
+ mono_thread_hazardous_try_free (node, g_free);
}
return count;
emit_uvalue (logbuffer, fully_covered);
emit_uvalue (logbuffer, partially_covered);
- EXIT_LOG_EXPLICIT (TRUE, FALSE);
+ EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
g_free (class_name);
}
emit_uvalue (logbuffer, fully_covered);
emit_uvalue (logbuffer, partially_covered);
- EXIT_LOG_EXPLICIT (TRUE, FALSE);
+ EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
}
static void
static MonoLockFreeQueueNode *
create_method_node (MonoMethod *method)
{
- MethodNode *node = (MethodNode *)g_malloc (sizeof (MethodNode));
+ MethodNode *node = (MethodNode *) g_malloc (sizeof (MethodNode));
mono_lock_free_queue_node_init ((MonoLockFreeQueueNode *) node, FALSE);
node->method = method;
image_methods = (MonoLockFreeQueue *)mono_conc_hashtable_lookup (image_to_methods, image);
if (image_methods == NULL) {
- image_methods = (MonoLockFreeQueue *)g_malloc (sizeof (MonoLockFreeQueue));
+ 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);
class_methods = (MonoLockFreeQueue *)mono_conc_hashtable_lookup (coverage_classes, klass);
if (class_methods == NULL) {
- class_methods = (MonoLockFreeQueue *)g_malloc (sizeof (MonoLockFreeQueue));
+ 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);
if (filesize > MAX_FILE_SIZE)
return NULL;
- buffer = (char *)g_malloc ((filesize + 1) * sizeof (char));
+ buffer = (char *) g_malloc ((filesize + 1) * sizeof (char));
while ((bytes_read = fread (buffer + offset, 1, LINE_BUFFER_SIZE, stream)) > 0)
offset += bytes_read;
fclose (sa_file);
}
+static void
+parse_cov_filter_file (GPtrArray *filters, const char *file)
+{
+ FILE *filter_file;
+ char *line, *content;
+
+ if (filters == NULL)
+ filters = g_ptr_array_new ();
+
+ filter_file = fopen (file, "r");
+ if (filter_file == NULL) {
+ fprintf (stderr, "Unable to open %s\n", file);
+ return;
+ }
+
+ /* 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", file);
+
+ while ((line = get_next_line (content, &content)))
+ g_ptr_array_add (filters, g_strchug (g_strchomp (line)));
+
+ fclose (filter_file);
+}
+
static void
coverage_init (MonoProfiler *prof)
{
{
InterlockedWrite (&in_shutdown, 1);
- counters_and_perfcounters_sample (prof);
+ if (!no_counters)
+ counters_and_perfcounters_sample (prof);
dump_coverage (prof);
*/
while (profiler_thread_list.head) {
MONO_LLS_FOREACH_SAFE (&profiler_thread_list, MonoProfilerThread, thread) {
+ g_assert (thread->attached && "Why is a thread in the LLS not attached?");
+
remove_thread (thread);
} MONO_LLS_FOREACH_SAFE_END
}
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 = (char *)malloc (strlen (filename) + s_date * count_dates + s_pid * count_pids);
+ d = res = (char *) g_malloc (strlen (filename) + s_date * count_dates + s_pid * count_pids);
for (p = filename; *p; p++) {
if (*p != '%') {
*d++ = *p;
exit (1);
}
- counters_and_perfcounters_sample (prof);
+ if (!no_counters)
+ counters_and_perfcounters_sample (prof);
buffer_lock_excl ();
g_array_free (command_sockets, TRUE);
- send_log_unsafe (FALSE, FALSE);
+ send_log_unsafe (FALSE);
deinit_thread (thread);
mono_thread_info_detach ();
memcpy (logbuffer->cursor, name, nlen);
logbuffer->cursor += nlen;
- EXIT_LOG_EXPLICIT (FALSE, FALSE);
+ EXIT_LOG_EXPLICIT (NO_SEND, NO_REQUESTS);
mono_free (name);
for (int i = 0; i < sample->count; ++i)
emit_method (logbuffer, sample->frames [i].method);
- EXIT_LOG_EXPLICIT (TRUE, FALSE);
+ EXIT_LOG_EXPLICIT (DO_SEND, NO_REQUESTS);
mono_thread_hazardous_try_free (sample, reuse_sample_hit);
/* Drain any remaining entries on shutdown. */
while (handle_dumper_queue_entry (prof));
- send_log_unsafe (FALSE, FALSE);
+ send_log_unsafe (FALSE);
deinit_thread (thread);
mono_thread_info_detach ();
{
InterlockedWrite (&runtime_inited, 1);
- start_writer_thread (profiler);
- start_dumper_thread (profiler);
-
register_counter ("Sample events allocated", &sample_allocations_ctr);
register_counter ("Log buffers allocated", &buffer_allocations_ctr);
counters_init (profiler);
+ /*
+ * We must start the helper thread before the writer thread. This is
+ * because the helper thread sets up the command port which is written to
+ * the log header by the writer thread.
+ */
start_helper_thread (profiler);
+ start_writer_thread (profiler);
+ start_dumper_thread (profiler);
}
static MonoProfiler*
MonoProfiler *prof;
char *nf;
int force_delete = 0;
- prof = (MonoProfiler *)calloc (1, sizeof (MonoProfiler));
+ prof = (MonoProfiler *) g_calloc (1, sizeof (MonoProfiler));
prof->args = pstrdup (args);
prof->command_port = command_port;
if (filename && *filename == '-') {
force_delete = 1;
filename++;
+ printf ("WARNING: the output:-FILENAME option is deprecated, the profiler now always overrides the output file\n");
}
if (!filename) {
if (do_report)
nf = new_filename (filename);
if (do_report) {
int s = strlen (nf) + 32;
- char *p = (char *)malloc (s);
+ char *p = (char *) g_malloc (s);
snprintf (p, s, "|mprof-report '--out=%s' -", nf);
g_free (nf);
nf = p;
return prof;
}
-static void
-usage (int do_exit)
-{
- 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/100)\n");
- printf ("\t TYPE: cycles,instr,cacherefs,cachemiss,branches,branchmiss\n");
- printf ("\t TYPE can be followed by /FREQUENCY\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);
-}
-
-static const char*
-match_option (const char* p, const char *opt, char **rval)
-{
- int len = strlen (opt);
- if (strncmp (p, opt, len) == 0) {
- if (rval) {
- if (p [len] == '=' && p [len + 1]) {
- const char *opt = p + len + 1;
- const char *end = strchr (opt, ',');
- char *val;
- int l;
- if (end == NULL) {
- l = strlen (opt);
- } else {
- l = end - opt;
- }
- val = (char *)malloc (l + 1);
- memcpy (val, opt, l);
- val [l] = 0;
- *rval = val;
- return opt + l;
- }
- if (p [len] == 0 || p [len] == ',') {
- *rval = NULL;
- return p + len + (p [len] == ',');
- }
- usage (1);
- } else {
- if (p [len] == 0)
- return p + len;
- if (p [len] == ',')
- return p + len + 1;
- }
- }
- return p;
-}
-
-static void
-set_sample_freq (char *val)
-{
- do_mono_sample = 1;
- sample_freq = 100;
-
- if (!val)
- return;
-
- char *p = val;
-
- // Is it only the frequency (new option style)?
- if (isdigit (*p))
- goto parse;
-
- // Skip the sample type for backwards compatibility.
- while (isalpha (*p))
- p++;
-
- // Skip the forward slash only if we got a sample type.
- if (p != val && *p == '/') {
- p++;
-
- char *end;
-
- parse:
- sample_freq = strtoul (p, &end, 10);
-
- if (p == end)
- usage (1);
-
- p = end;
- }
-
- if (*p)
- usage (1);
-
- g_free (val);
-}
-
-static void
-set_hsmode (char* val, int allow_empty)
-{
- char *end;
- unsigned int count;
- if (allow_empty && !val)
- return;
- if (strcmp (val, "ondemand") == 0) {
- hs_mode_ondemand = 1;
- g_free (val);
- return;
- }
- count = strtoul (val, &end, 10);
- if (val == end)
- usage (1);
- if (strcmp (end, "ms") == 0)
- hs_mode_ms = count;
- else if (strcmp (end, "gc") == 0)
- hs_mode_gc = count;
- else
- usage (1);
- g_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 (desc);
}
+static ProfilerConfig config;
+
void
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_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_INS_COVERAGE|MONO_PROFILE_APPDOMAIN_EVENTS|MONO_PROFILE_CONTEXT_EVENTS|
- MONO_PROFILE_ASSEMBLY_EVENTS|MONO_PROFILE_GC_FINALIZATION;
-
- max_allocated_sample_hits = mono_cpu_count () * 1000;
-
- p = desc;
- if (strncmp (p, "log", 3))
- usage (1);
- p += 3;
- if (*p == ':')
- p++;
- for (; *p; p = opt) {
- char *val;
- if (*p == ',') {
- opt = p + 1;
- continue;
- }
- if ((opt = match_option (p, "help", NULL)) != p) {
- usage (0);
- continue;
- }
- if ((opt = match_option (p, "calls", NULL)) != p) {
- calls_enabled = 1;
- continue;
- }
- if ((opt = match_option (p, "nocalls", NULL)) != p) {
- events &= ~MONO_PROFILE_ENTER_LEAVE;
- nocalls = 1;
- continue;
- }
- if ((opt = match_option (p, "alloc", NULL)) != p) {
- allocs_enabled = 1;
- continue;
- }
- if ((opt = match_option (p, "noalloc", NULL)) != p) {
- events &= ~MONO_PROFILE_ALLOCATIONS;
- events &= ~MONO_PROFILE_GC_MOVES;
- continue;
- }
- if ((opt = match_option (p, "time", &val)) != p) {
- if (strcmp (val, "fast") && strcmp (val, "null"))
- usage (1);
- g_free (val);
- continue;
- }
- if ((opt = match_option (p, "report", NULL)) != p) {
- do_report = 1;
- continue;
- }
- if ((opt = match_option (p, "debug", NULL)) != p) {
- do_debug = 1;
- continue;
- }
- if ((opt = match_option (p, "sampling-real", NULL)) != p) {
- sampling_mode = MONO_PROFILER_STAT_MODE_REAL;
- continue;
- }
- if ((opt = match_option (p, "sampling-process", NULL)) != p) {
- sampling_mode = MONO_PROFILER_STAT_MODE_PROCESS;
- continue;
- }
- if ((opt = match_option (p, "heapshot", &val)) != p) {
- events &= ~MONO_PROFILE_ALLOCATIONS;
- events &= ~MONO_PROFILE_GC_MOVES;
- events &= ~MONO_PROFILE_ENTER_LEAVE;
- nocalls = 1;
- do_heap_shot = 1;
- set_hsmode (val, 1);
- continue;
- }
- if ((opt = match_option (p, "sample", &val)) != p) {
- events &= ~MONO_PROFILE_ALLOCATIONS;
- events &= ~MONO_PROFILE_GC_MOVES;
- events &= ~MONO_PROFILE_ENTER_LEAVE;
- nocalls = 1;
- set_sample_freq (val);
- continue;
- }
- if ((opt = match_option (p, "zip", NULL)) != p) {
- use_zip = 1;
- continue;
- }
- if ((opt = match_option (p, "output", &val)) != p) {
- filename = val;
- continue;
- }
- if ((opt = match_option (p, "port", &val)) != p) {
- char *end;
- command_port = strtoul (val, &end, 10);
- g_free (val);
- continue;
- }
- if ((opt = match_option (p, "maxframes", &val)) != p) {
- char *end;
- num_frames = strtoul (val, &end, 10);
- if (num_frames > MAX_FRAMES)
- num_frames = MAX_FRAMES;
- g_free (val);
- notraces = num_frames == 0;
- continue;
- }
- if ((opt = match_option (p, "maxsamples", &val)) != p) {
- char *end;
- max_allocated_sample_hits = strtoul (val, &end, 10);
- if (!max_allocated_sample_hits)
- max_allocated_sample_hits = G_MAXINT32;
- g_free (val);
- continue;
- }
- if ((opt = match_option (p, "calldepth", &val)) != p) {
- char *end;
- max_call_depth = strtoul (val, &end, 10);
- g_free (val);
- continue;
- }
- if ((opt = match_option (p, "counters", NULL)) != p) {
- do_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);
- }
- }
-
- if (calls_enabled) {
- events |= MONO_PROFILE_ENTER_LEAVE;
- nocalls = 0;
- }
-
- if (allocs_enabled) {
- events |= MONO_PROFILE_ALLOCATIONS;
- events |= MONO_PROFILE_GC_MOVES;
- }
+ MonoProfiler *prof;
- // Only activate the bare minimum events the profiler needs to function.
- if (only_coverage) {
- if (!do_coverage) {
- fprintf (stderr, "The onlycoverage option is only valid when paired with the coverage option\n");
- exit (1);
+ if (desc [3] == ':')
+ proflog_parse_args (&config, desc + 4);
+ else
+ proflog_parse_args (&config, "");
+ //XXX maybe later cleanup to use config directly
+ nocalls = !(config.effective_mask & PROFLOG_CALL_EVENTS);
+ no_counters = !(config.effective_mask & PROFLOG_COUNTER_EVENTS);
+ do_report = config.do_report;
+ do_debug = config.do_debug;
+ do_heap_shot = (config.effective_mask & PROFLOG_HEAPSHOT_FEATURE);
+ hs_mode_ondemand = config.hs_mode_ondemand;
+ hs_mode_ms = config.hs_mode_ms;
+ hs_mode_gc = config.hs_mode_gc;
+ do_mono_sample = (config.effective_mask & PROFLOG_SAMPLING_FEATURE);
+ use_zip = config.use_zip;
+ command_port = config.command_port;
+ num_frames = config.num_frames;
+ notraces = config.notraces;
+ max_allocated_sample_hits = config.max_allocated_sample_hits;
+ max_call_depth = config.max_call_depth;
+ do_coverage = (config.effective_mask & PROFLOG_CODE_COV_FEATURE);
+ debug_coverage = config.debug_coverage;
+ only_coverage = config.only_coverage;
+
+ if (config.cov_filter_files) {
+ filters = g_ptr_array_new ();
+ int i;
+ for (i = 0; i < config.cov_filter_files->len; ++i) {
+ const char *name = config.cov_filter_files->pdata [i];
+ parse_cov_filter_file (filters, name);
}
-
- events = MONO_PROFILE_GC | MONO_PROFILE_THREADS | MONO_PROFILE_ENTER_LEAVE | MONO_PROFILE_INS_COVERAGE;
}
init_time ();
PROF_TLS_INIT ();
- prof = create_profiler (desc, filename, filters);
+ prof = create_profiler (desc, config.output_filename, filters);
if (!prof) {
PROF_TLS_FREE ();
return;
init_thread (prof, TRUE);
+ //This two events are required for the profiler to work
+ int events = MONO_PROFILE_THREADS | MONO_PROFILE_GC;
+
+ //Required callbacks
mono_profiler_install (prof, log_shutdown);
+ mono_profiler_install_runtime_initialized (runtime_initialized);
+
mono_profiler_install_gc (gc_event, gc_resize);
- 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_gc_finalize (finalize_begin, finalize_object_begin, finalize_object_end, finalize_end);
- mono_profiler_install_appdomain (NULL, domain_loaded, domain_unloaded, NULL);
- mono_profiler_install_appdomain_name (domain_name);
- mono_profiler_install_context (context_loaded, context_unloaded);
- mono_profiler_install_class (NULL, class_loaded, class_unloaded, NULL);
- mono_profiler_install_module (NULL, image_loaded, image_unloaded, NULL);
- mono_profiler_install_assembly (NULL, assembly_loaded, assembly_unloaded, NULL);
mono_profiler_install_thread (thread_start, thread_end);
+
+ //It's questionable whether we actually want this to be mandatory, maybe put it behind the actual event?
mono_profiler_install_thread_name (thread_name);
- mono_profiler_install_enter_leave (method_enter, method_leave);
- mono_profiler_install_jit_end (method_jitted);
- mono_profiler_install_code_buffer_new (code_buffer_new);
- 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)
+
+
+ if (config.effective_mask & PROFLOG_DOMAIN_EVENTS) {
+ events |= MONO_PROFILE_APPDOMAIN_EVENTS;
+ mono_profiler_install_appdomain (NULL, domain_loaded, domain_unloaded, NULL);
+ mono_profiler_install_appdomain_name (domain_name);
+ }
+
+ if (config.effective_mask & PROFLOG_ASSEMBLY_EVENTS) {
+ events |= MONO_PROFILE_ASSEMBLY_EVENTS;
+ mono_profiler_install_assembly (NULL, assembly_loaded, assembly_unloaded, NULL);
+ }
+
+ if (config.effective_mask & PROFLOG_MODULE_EVENTS) {
+ events |= MONO_PROFILE_MODULE_EVENTS;
+ mono_profiler_install_module (NULL, image_loaded, image_unloaded, NULL);
+ }
+
+ if (config.effective_mask & PROFLOG_CLASS_EVENTS) {
+ events |= MONO_PROFILE_CLASS_EVENTS;
+ mono_profiler_install_class (NULL, class_loaded, class_unloaded, NULL);
+ }
+
+ if (config.effective_mask & PROFLOG_JIT_COMPILATION_EVENTS) {
+ events |= MONO_PROFILE_JIT_COMPILATION;
+ mono_profiler_install_jit_end (method_jitted);
+ mono_profiler_install_code_buffer_new (code_buffer_new);
+ }
+
+ if (config.effective_mask & PROFLOG_EXCEPTION_EVENTS) {
+ events |= MONO_PROFILE_EXCEPTIONS;
+ mono_profiler_install_exception (throw_exc, method_exc_leave, clause_exc);
+ }
+
+ if (config.effective_mask & PROFLOG_ALLOCATION_EVENTS) {
+ events |= MONO_PROFILE_ALLOCATIONS;
+ mono_profiler_install_allocation (gc_alloc);
+ }
+
+ //PROFLOG_GC_EVENTS is mandatory
+ //PROFLOG_THREAD_EVENTS is mandatory
+
+ if (config.effective_mask & PROFLOG_CALL_EVENTS) {
+ events |= MONO_PROFILE_ENTER_LEAVE;
+ mono_profiler_install_enter_leave (method_enter, method_leave);
+ }
+
+ if (config.effective_mask & PROFLOG_INS_COVERAGE_EVENTS) {
+ events |= MONO_PROFILE_INS_COVERAGE;
mono_profiler_install_coverage_filter (coverage_filter);
+ }
- if (do_mono_sample && sample_freq) {
+ //XXX should we check for PROFLOG_SAMPLING_FEATURE instead??
+ if (config.effective_mask & PROFLOG_SAMPLING_EVENTS) {
events |= MONO_PROFILE_STATISTICAL;
- mono_profiler_set_statistical_mode (sampling_mode, sample_freq);
+ mono_profiler_set_statistical_mode (config.sampling_mode, config.sample_freq);
mono_profiler_install_statistical (mono_sample_hit);
}
+ if (config.effective_mask & PROFLOG_MONITOR_EVENTS) {
+ events |= MONO_PROFILE_MONITOR_EVENTS;
+ mono_profiler_install_monitor (monitor_event);
+ }
+
+ if (config.effective_mask & PROFLOG_GC_MOVES_EVENTS) {
+ events |= MONO_PROFILE_GC_MOVES;
+ mono_profiler_install_gc_moves (gc_moves);
+ }
+
+ // TODO split those in two profiler events
+ if (config.effective_mask & (PROFLOG_GC_ROOT_EVENTS | PROFLOG_GC_HANDLE_EVENTS)) {
+ events |= MONO_PROFILE_GC_ROOTS;
+ mono_profiler_install_gc_roots (
+ config.effective_mask & (PROFLOG_GC_HANDLE_EVENTS) ? gc_handle : NULL,
+ (config.effective_mask & PROFLOG_GC_ROOT_EVENTS) ? gc_roots : NULL);
+ }
+
+ if (config.effective_mask & PROFLOG_CONTEXT_EVENTS) {
+ events |= MONO_PROFILE_CONTEXT_EVENTS;
+ mono_profiler_install_context (context_loaded, context_unloaded);
+ }
+
+ if (config.effective_mask & PROFLOG_FINALIZATION_EVENTS) {
+ events |= MONO_PROFILE_GC_FINALIZATION;
+ mono_profiler_install_gc_finalize (finalize_begin, finalize_object_begin, finalize_object_end, finalize_end);
+ }
+
+ //PROFLOG_COUNTER_EVENTS is a pseudo event controled by the no_counters global var
+ //PROFLOG_GC_HANDLE_EVENTS is handled together with PROFLOG_GC_ROOT_EVENTS
+
mono_profiler_set_events ((MonoProfileFlags)events);
}