/*
* decode.c: mprof-report program source: decode and analyze the log profiler data
*
- * Author:
+ * Authors:
* Paolo Molaro (lupus@ximian.com)
+ * Alex Rønne Petersen (alexrp@xamarin.com)
*
* Copyright 2010 Novell, Inc (http://www.novell.com)
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
+ */
+
+/*
+ * The Coverage XML output schema
+ * <coverage>
+ * <assembly/>
+ * <class/>
+ * <method>
+ * <statement/>
+ * </method>
+ * </coverage>
+ *
+ * Elements:
+ * <coverage> - The root element of the documentation. It can contain any number of
+ * <assembly>, <class> or <method> elements.
+ * Attributes:
+ * - version: The version number for the file format - (eg: "0.3")
+ * <assembly> - Contains data about assemblies. Has no child elements
+ * Attributes:
+ * - name: The name of the assembly - (eg: "System.Xml")
+ * - guid: The GUID of the assembly
+ * - filename: The filename of the assembly
+ * - method-count: The number of methods in the assembly
+ * - full: The number of fully covered methods
+ * - partial: The number of partially covered methods
+ * <class> - Contains data about classes. Has no child elements
+ * Attributes:
+ * - name: The name of the class
+ * - method-count: The number of methods in the class
+ * - full: The number of fully covered methods
+ * - partial: The number of partially covered methods
+ * <method> - Contains data about methods. Can contain any number of <statement> elements
+ * Attributes:
+ * - assembly: The name of the parent assembly
+ * - class: The name of the parent class
+ * - name: The name of the method, with all it's parameters
+ * - filename: The name of the source file containing this method
+ * - token
+ * <statement> - Contains data about IL statements. Has no child elements
+ * Attributes:
+ * - offset: The offset of the statement in the IL code after the previous
+ * statement's offset
+ * - counter: 1 if the line was covered, 0 if it was not
+ * - line: The line number in the parent method's file
+ * - column: The column on the line
*/
#include <config.h>
#include "utils.c"
static int use_time_filter = 0;
static uint64_t startup_time = 0;
static FILE* outfile = NULL;
+static FILE* coverage_outfile = NULL;
static int32_t
read_int16 (unsigned char *p)
pstrdup (const char *s)
{
int len = strlen (s) + 1;
- char *p = malloc (len);
+ char *p = (char *)malloc (len);
memcpy (p, s, len);
return p;
}
CounterSection *csection, *s;
CounterList *clist;
- clist = calloc (1, sizeof (CounterList));
+ clist = (CounterList *)calloc (1, sizeof (CounterList));
clist->counter = counter;
for (csection = counters_sections; csection; csection = csection->next) {
}
/* If section does not exist */
- csection = calloc (1, sizeof (CounterSection));
+ csection = (CounterSection *)calloc (1, sizeof (CounterSection));
csection->value = counter->section;
csection->counters = clist;
csection->counters_last = clist;
if (list->counter->index == index)
return;
- counter = calloc (1, sizeof (Counter));
+ counter = (Counter *)calloc (1, sizeof (Counter));
counter->section = section;
counter->name = name;
counter->type = type;
counter->variance = variance;
counter->index = index;
- list = calloc (1, sizeof (CounterList));
+ list = (CounterList *)calloc (1, sizeof (CounterList));
list->counter = counter;
if (!counters) {
CounterSection *csection;
CounterList *clist;
- clist = calloc (1, sizeof (CounterList));
+ clist = (CounterList *)calloc (1, sizeof (CounterList));
clist->counter = counter;
for (ctimestamp = counters_timestamps; ctimestamp; ctimestamp = ctimestamp->next) {
}
/* if timestamp exist and section does not exist */
- csection = calloc (1, sizeof (CounterSection));
+ csection = (CounterSection *)calloc (1, sizeof (CounterSection));
csection->value = counter->section;
csection->counters = clist;
csection->counters_last = clist;
}
/* If timestamp do not exist and section does not exist */
- csection = calloc (1, sizeof (CounterSection));
+ csection = (CounterSection *)calloc (1, sizeof (CounterSection));
csection->value = counter->section;
csection->counters = clist;
csection->counters_last = clist;
- ctimestamp = calloc (1, sizeof (CounterTimestamp));
+ ctimestamp = (CounterTimestamp *)calloc (1, sizeof (CounterTimestamp));
ctimestamp->value = timestamp;
ctimestamp->sections = csection;
ctimestamp->sections_last = csection;
add_image (intptr_t image, char *name)
{
int slot = ((image >> 2) & 0xffff) % SMALL_HASH_SIZE;
- ImageDesc *cd = malloc (sizeof (ImageDesc));
+ ImageDesc *cd = (ImageDesc *)malloc (sizeof (ImageDesc));
cd->image = image;
cd->filename = pstrdup (name);
cd->next = image_hash [slot];
num_images++;
}
+static int num_assemblies;
+
+typedef struct _AssemblyDesc AssemblyDesc;
+struct _AssemblyDesc {
+ AssemblyDesc *next;
+ intptr_t assembly;
+ char *asmname;
+};
+
+static AssemblyDesc* assembly_hash [SMALL_HASH_SIZE] = {0};
+
+static void
+add_assembly (intptr_t assembly, char *name)
+{
+ int slot = ((assembly >> 2) & 0xffff) % SMALL_HASH_SIZE;
+ AssemblyDesc *cd = (AssemblyDesc *)malloc (sizeof (AssemblyDesc));
+ cd->assembly = assembly;
+ cd->asmname = pstrdup (name);
+ cd->next = assembly_hash [slot];
+ assembly_hash [slot] = cd;
+ num_assemblies++;
+}
+
typedef struct _BackTrace BackTrace;
typedef struct {
uint64_t count;
cd->name = pstrdup (name);
return cd;
}
- cd = calloc (sizeof (ClassDesc), 1);
+ cd = (ClassDesc *)calloc (sizeof (ClassDesc), 1);
cd->klass = klass;
cd->name = pstrdup (name);
cd->next = class_hash [slot];
cd->name = pstrdup (name);
return cd;
}
- cd = calloc (sizeof (MethodDesc), 1);
+ cd = (MethodDesc *)calloc (sizeof (MethodDesc), 1);
cd->method = method;
cd->name = pstrdup (name);
cd->code = code;
size_stat_samples *= 2;
if (!size_stat_samples)
size_stat_samples = 32;
- stat_samples = realloc (stat_samples, size_stat_samples * sizeof (uintptr_t));
- stat_sample_desc = realloc (stat_sample_desc, size_stat_samples * sizeof (int));
+ stat_samples = (uintptr_t *)realloc (stat_samples, size_stat_samples * sizeof (uintptr_t));
+ stat_sample_desc = (int *)realloc (stat_sample_desc, size_stat_samples * sizeof (int));
}
stat_samples [num_stat_samples] = ip;
stat_sample_desc [num_stat_samples++] = type;
static int
compare_method_samples (const void *a, const void *b)
{
- MethodDesc *const*A = a;
- MethodDesc *const*B = b;
+ MethodDesc *const *A = (MethodDesc *const *)a;
+ MethodDesc *const *B = (MethodDesc *const *)b;
if ((*A)->sample_hits == (*B)->sample_hits)
return 0;
if ((*B)->sample_hits < (*A)->sample_hits)
static int
compare_usymbol_addr (const void *a, const void *b)
{
- UnmanagedSymbol *const*A = a;
- UnmanagedSymbol *const*B = b;
+ UnmanagedSymbol *const *A = (UnmanagedSymbol *const *)a;
+ UnmanagedSymbol *const *B = (UnmanagedSymbol *const *)b;
if ((*B)->addr == (*A)->addr)
return 0;
if ((*B)->addr > (*A)->addr)
static int
compare_usymbol_samples (const void *a, const void *b)
{
- UnmanagedSymbol *const*A = a;
- UnmanagedSymbol *const*B = b;
+ UnmanagedSymbol *const *A = (UnmanagedSymbol *const *)a;
+ UnmanagedSymbol *const *B = (UnmanagedSymbol *const *)b;
if ((*B)->sample_hits == (*A)->sample_hits)
return 0;
if ((*B)->sample_hits < (*A)->sample_hits)
int new_size = usymbols_size * 2;
if (!new_size)
new_size = 16;
- usymbols = realloc (usymbols, sizeof (void*) * new_size);
+ usymbols = (UnmanagedSymbol **)realloc (usymbols, sizeof (void*) * new_size);
usymbols_size = new_size;
}
- sym = calloc (sizeof (UnmanagedSymbol), 1);
+ sym = (UnmanagedSymbol *)calloc (sizeof (UnmanagedSymbol), 1);
sym->addr = addr;
sym->name = name;
sym->size = size;
int new_size = ubinaries_size * 2;
if (!new_size)
new_size = 16;
- ubinaries = realloc (ubinaries, sizeof (void*) * new_size);
+ ubinaries = (UnmanagedSymbol **)realloc (ubinaries, sizeof (void*) * new_size);
ubinaries_size = new_size;
}
- sym = calloc (sizeof (UnmanagedSymbol), 1);
+ sym = (UnmanagedSymbol *)calloc (sizeof (UnmanagedSymbol), 1);
sym->addr = addr;
sym->name = name;
sym->size = size;
msize *= 2;
if (!msize)
msize = 4;
- cachedm = realloc (cachedm, sizeof (void*) * msize);
+ cachedm = (MethodDesc **)realloc (cachedm, sizeof (void*) * msize);
}
cachedm [count++] = m;
}
usize *= 2;
if (!usize)
usize = 4;
- cachedus = realloc (cachedus, sizeof (void*) * usize);
+ cachedus = (UnmanagedSymbol **)realloc (cachedus, sizeof (void*) * usize);
}
cachedus [ucount++] = usym;
}
to->rev_hash_size *= 2;
if (to->rev_hash_size == 0)
to->rev_hash_size = 4;
- n = calloc (sizeof (HeapClassRevRef) * to->rev_hash_size, 1);
+ n = (HeapClassRevRef *)calloc (sizeof (HeapClassRevRef) * to->rev_hash_size, 1);
for (i = 0; i < old_size; ++i) {
if (to->rev_hash [i].klass)
add_rev_class_hashed (n, to->rev_hash_size, to->rev_hash [i].klass, to->rev_hash [i].count);
static HeapShot*
new_heap_shot (uint64_t timestamp)
{
- HeapShot *hs = calloc (sizeof (HeapShot), 1);
+ HeapShot *hs = (HeapShot *)calloc (sizeof (HeapShot), 1);
hs->hash_size = 4;
- hs->class_hash = calloc (sizeof (void*), hs->hash_size);
+ hs->class_hash = (HeapClassDesc **)calloc (sizeof (void*), hs->hash_size);
hs->timestamp = timestamp;
num_heap_shots++;
hs->next = heap_shots;
hash [i] = *retv;
return 1;
}
- hash [i] = calloc (sizeof (HeapClassDesc), 1);
+ hash [i] = (HeapClassDesc *)calloc (sizeof (HeapClassDesc), 1);
hash [i]->klass = klass;
hash [i]->total_size += size;
hash [i]->count += count;
hs->hash_size *= 2;
if (hs->hash_size == 0)
hs->hash_size = 4;
- n = calloc (sizeof (void*) * hs->hash_size, 1);
+ n = (HeapClassDesc **)calloc (sizeof (void*) * hs->hash_size, 1);
for (i = 0; i < old_size; ++i) {
res = hs->class_hash [i];
if (hs->class_hash [i])
static HeapObjectDesc*
alloc_heap_obj (uintptr_t objaddr, HeapClassDesc *hklass, uintptr_t num_refs)
{
- HeapObjectDesc* ho = calloc (sizeof (HeapObjectDesc) + num_refs * sizeof (uintptr_t), 1);
+ HeapObjectDesc* ho = (HeapObjectDesc *)calloc (sizeof (HeapObjectDesc) + num_refs * sizeof (uintptr_t), 1);
ho->objaddr = objaddr;
ho->hklass = hklass;
ho->num_refs = num_refs;
/* should not happen */
printf ("failed heap obj update\n");
return NULL;
-
+
}
static uintptr_t
hs->objects_hash_size *= 2;
if (hs->objects_hash_size == 0)
hs->objects_hash_size = 4;
- n = calloc (sizeof (void*) * hs->objects_hash_size, 1);
+ n = (HeapObjectDesc **)calloc (sizeof (void*) * hs->objects_hash_size, 1);
for (i = 0; i < old_size; ++i) {
if (hs->objects_hash [i])
add_heap_hashed_obj (n, hs->objects_hash_size, hs->objects_hash [i]);
if (!debug)
return;
/* consistency checks: it seems not all the objects are walked in the heap in some cases */
- marks = calloc (hs->objects_hash_size, 1);
+ marks = (unsigned char *)calloc (hs->objects_hash_size, 1);
if (!marks)
return;
for (i = 0; i < hs->num_roots; ++i) {
return bt;
bt = bt->next;
}
- bt = malloc (sizeof (BackTrace) + ((count - 1) * sizeof (void*)));
+ bt = (BackTrace *)malloc (sizeof (BackTrace) + ((count - 1) * sizeof (void*)));
bt->next = backtrace_hash [slot];
backtrace_hash [slot] = bt;
if (next_backtrace == num_backtraces) {
num_backtraces *= 2;
if (!num_backtraces)
num_backtraces = 16;
- backtraces = realloc (backtraces, sizeof (void*) * num_backtraces);
+ backtraces = (BackTrace **)realloc (backtraces, sizeof (void*) * num_backtraces);
}
bt->id = next_backtrace++;
backtraces [bt->id] = bt;
typedef struct _MonitorDesc MonitorDesc;
typedef struct _ThreadContext ThreadContext;
+typedef struct _DomainContext DomainContext;
+typedef struct _RemCtxContext RemCtxContext;
typedef struct {
FILE *file;
int port;
uint64_t startup_time;
ThreadContext *threads;
- ThreadContext *current;
+ ThreadContext *current_thread;
+ DomainContext *domains;
+ DomainContext *current_domain;
+ RemCtxContext *remctxs;
+ RemCtxContext *current_remctx;
} ProfContext;
struct _ThreadContext {
uint64_t gc_start_times [3];
};
+struct _DomainContext {
+ DomainContext *next;
+ intptr_t domain_id;
+ const char *friendly_name;
+};
+
+struct _RemCtxContext {
+ RemCtxContext *next;
+ intptr_t remctx_id;
+ intptr_t domain_id;
+};
+
static void
ensure_buffer (ProfContext *ctx, int size)
{
if (ctx->size < size) {
- ctx->buf = realloc (ctx->buf, size);
+ ctx->buf = (unsigned char *)realloc (ctx->buf, size);
ctx->size = size;
}
}
if (r == 0)
return size == 0? 1: 0;
return r == size;
- } else
+ } else
#endif
{
int r = fread (ctx->buf, size, 1, ctx->file);
get_thread (ProfContext *ctx, intptr_t thread_id)
{
ThreadContext *thread;
- if (ctx->current && ctx->current->thread_id == thread_id)
- return ctx->current;
+ if (ctx->current_thread && ctx->current_thread->thread_id == thread_id)
+ return ctx->current_thread;
thread = ctx->threads;
while (thread) {
if (thread->thread_id == thread_id) {
}
thread = thread->next;
}
- thread = calloc (sizeof (ThreadContext), 1);
+ thread = (ThreadContext *)calloc (sizeof (ThreadContext), 1);
thread->next = ctx->threads;
ctx->threads = thread;
thread->thread_id = thread_id;
thread->last_time = 0;
thread->stack_id = 0;
thread->stack_size = 32;
- thread->stack = malloc (thread->stack_size * sizeof (void*));
- thread->time_stack = malloc (thread->stack_size * sizeof (uint64_t));
- thread->callee_time_stack = malloc (thread->stack_size * sizeof (uint64_t));
+ thread->stack = (MethodDesc **)malloc (thread->stack_size * sizeof (void*));
+ thread->time_stack = (uint64_t *)malloc (thread->stack_size * sizeof (uint64_t));
+ thread->callee_time_stack = (uint64_t *)malloc (thread->stack_size * sizeof (uint64_t));
return thread;
}
+static DomainContext *
+get_domain (ProfContext *ctx, intptr_t domain_id)
+{
+ if (ctx->current_domain && ctx->current_domain->domain_id == domain_id)
+ return ctx->current_domain;
+
+ DomainContext *domain = ctx->domains;
+
+ while (domain) {
+ if (domain->domain_id == domain_id)
+ return domain;
+
+ domain = domain->next;
+ }
+
+ domain = (DomainContext *)calloc (sizeof (DomainContext), 1);
+ domain->next = ctx->domains;
+ ctx->domains = domain;
+ domain->domain_id = domain_id;
+
+ return domain;
+}
+
+static RemCtxContext *
+get_remctx (ProfContext *ctx, intptr_t remctx_id)
+{
+ if (ctx->current_remctx && ctx->current_remctx->remctx_id == remctx_id)
+ return ctx->current_remctx;
+
+ RemCtxContext *remctx = ctx->remctxs;
+
+ while (remctx) {
+ if (remctx->remctx_id == remctx_id)
+ return remctx;
+
+ remctx = remctx->next;
+ }
+
+ remctx = (RemCtxContext *)calloc (sizeof (RemCtxContext), 1);
+ remctx->next = ctx->remctxs;
+ ctx->remctxs = remctx;
+ remctx->remctx_id = remctx_id;
+
+ return remctx;
+}
+
static ThreadContext*
load_thread (ProfContext *ctx, intptr_t thread_id)
{
ThreadContext *thread = get_thread (ctx, thread_id);
- ctx->current = thread;
+ ctx->current_thread = thread;
return thread;
}
{
if (thread->stack_id == thread->stack_size) {
thread->stack_size *= 2;
- thread->stack = realloc (thread->stack, thread->stack_size * sizeof (void*));
- thread->time_stack = realloc (thread->time_stack, thread->stack_size * sizeof (uint64_t));
- thread->callee_time_stack = realloc (thread->callee_time_stack, thread->stack_size * sizeof (uint64_t));
+ thread->stack = (MethodDesc **)realloc (thread->stack, thread->stack_size * sizeof (void*));
+ thread->time_stack = (uint64_t *)realloc (thread->time_stack, thread->stack_size * sizeof (uint64_t));
+ thread->callee_time_stack = (uint64_t *)realloc (thread->callee_time_stack, thread->stack_size * sizeof (uint64_t));
}
}
trace->size *= 2;
if (trace->size == 0)
trace->size = 4;
- n = calloc (sizeof (CallContext) * trace->size, 1);
+ n = (CallContext *)calloc (sizeof (CallContext) * trace->size, 1);
for (i = 0; i < old_size; ++i) {
if (trace->traces [i].bt)
add_trace_hashed (n, trace->size, trace->traces [i].bt, trace->traces [i].count);
int new_size = ctx->size_roots * 2;
if (!new_size)
new_size = 4;
- ctx->roots = realloc (ctx->roots, new_size * sizeof (uintptr_t));
- ctx->roots_extra = realloc (ctx->roots_extra, new_size * sizeof (uintptr_t));
- ctx->roots_types = realloc (ctx->roots_types, new_size * sizeof (int));
+ ctx->roots = (uintptr_t *)realloc (ctx->roots, new_size * sizeof (uintptr_t));
+ ctx->roots_extra = (uintptr_t *)realloc (ctx->roots_extra, new_size * sizeof (uintptr_t));
+ ctx->roots_types = (int *)realloc (ctx->roots_types, new_size * sizeof (int));
ctx->size_roots = new_size;
}
ctx->roots_types [ctx->num_roots] = root_type;
static int
compare_callc (const void *a, const void *b)
{
- const CallContext *A = a;
- const CallContext *B = b;
+ const CallContext *A = (const CallContext *)a;
+ const CallContext *B = (const CallContext *)b;
if (B->count == A->count)
return 0;
if (B->count < A->count)
uint64_t live;
uint64_t max_live;
TraceDesc traces;
+ TraceDesc destroy_traces;
} HandleInfo;
static HandleInfo handle_info [4];
while (cd && cd->objid != objid)
cd = cd->next;
if (!cd) {
- cd = calloc (sizeof (MonitorDesc), 1);
+ cd = (MonitorDesc *)calloc (sizeof (MonitorDesc), 1);
cd->objid = objid;
cd->next = monitor_hash [slot];
monitor_hash [slot] = cd;
if (flags != 0)
return NULL;
if (count > *size)
- frames = malloc (count * sizeof (void*));
+ frames = (MethodDesc **)malloc (count * sizeof (void*));
else
frames = sframes;
for (i = 0; i < count; ++i) {
}
static void
-track_handle (uintptr_t obj, int htype, uint32_t handle)
+track_handle (uintptr_t obj, int htype, uint32_t handle, BackTrace *bt, uint64_t timestamp)
{
int i;
for (i = 0; i < num_tracked_objects; ++i) {
- if (tracked_objects [i] == obj)
- fprintf (outfile, "Object %p referenced from handle %u\n", (void*)obj, handle);
+ if (tracked_objects [i] != obj)
+ continue;
+ fprintf (outfile, "Object %p referenced from handle %u at %.3f secs.\n", (void*)obj, handle, (timestamp - startup_time) / 1000000000.0);
+ if (bt && bt->count) {
+ int k;
+ for (k = 0; k < bt->count; ++k)
+ fprintf (outfile, "\t%s\n", bt->methods [k]->name);
+ }
}
}
{
int i;
for (i = 0; i < num_tracked_objects; ++i) {
- if (tracked_objects [i] == obj)
+ if (tracked_objects [i] == obj)
fprintf (outfile, "Object %p referenced from %p (%s).\n", (void*)obj, (void*)parent, cd->name);
}
}
found_object (uintptr_t obj)
{
num_tracked_objects ++;
- tracked_objects = realloc (tracked_objects, num_tracked_objects * sizeof (tracked_objects [0]));
+ tracked_objects = (uintptr_t *)realloc (tracked_objects, num_tracked_objects * sizeof (tracked_objects [0]));
tracked_objects [num_tracked_objects - 1] = obj;
}
return "monitor/lock";
case MONO_PROFILER_CODE_BUFFER_DELEGATE_INVOKE:
return "delegate invoke";
+ case MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING:
+ return "exception handling";
default:
return "unspecified";
}
}
+typedef struct _CoverageAssembly CoverageAssembly;
+struct _CoverageAssembly {
+ char *name;
+ char *guid;
+ char *filename;
+ int number_of_methods;
+ int fully_covered;
+ int partially_covered;
+};
+
+typedef struct _CoverageClass CoverageClass;
+struct _CoverageClass {
+ char *assembly_name;
+ char *class_name;
+ int number_of_methods;
+ int fully_covered;
+ int partially_covered;
+};
+
+typedef struct _CoverageCoverage CoverageCoverage;
+struct _CoverageCoverage {
+ int method_id;
+ int offset;
+ int count;
+ int line;
+ int column;
+};
+
+typedef struct _CoverageMethod CoverageMethod;
+struct _CoverageMethod {
+ char *assembly_name;
+ char *class_name;
+ char *method_name;
+ char *method_signature;
+ char *filename;
+ int token;
+ int n_statements;
+ int method_id;
+ GPtrArray *coverage;
+};
+static GPtrArray *coverage_assemblies = NULL;
+static GPtrArray *coverage_methods = NULL;
+static GPtrArray *coverage_statements = NULL;
+static GHashTable *coverage_methods_hash = NULL;
+static GPtrArray *coverage_classes = NULL;
+static GHashTable *coverage_assembly_classes = NULL;
+
+static void
+gather_coverage_statements (void)
+{
+ for (guint i = 0; i < coverage_statements->len; i++) {
+ CoverageCoverage *coverage = (CoverageCoverage *)coverage_statements->pdata[i];
+ CoverageMethod *method = (CoverageMethod *)g_hash_table_lookup (coverage_methods_hash, GINT_TO_POINTER (coverage->method_id));
+ if (method == NULL) {
+ fprintf (outfile, "Cannot find method with ID: %d\n", coverage->method_id);
+ continue;
+ }
+
+ g_ptr_array_add (method->coverage, coverage);
+ }
+}
+
+static void
+coverage_add_assembly (CoverageAssembly *assembly)
+{
+ if (coverage_assemblies == NULL)
+ coverage_assemblies = g_ptr_array_new ();
+
+ g_ptr_array_add (coverage_assemblies, assembly);
+}
+
+static void
+coverage_add_method (CoverageMethod *method)
+{
+ if (coverage_methods == NULL) {
+ coverage_methods = g_ptr_array_new ();
+ coverage_methods_hash = g_hash_table_new (NULL, NULL);
+ }
+
+ g_ptr_array_add (coverage_methods, method);
+ g_hash_table_insert (coverage_methods_hash, GINT_TO_POINTER (method->method_id), method);
+}
+
+static void
+coverage_add_class (CoverageClass *klass)
+{
+ GPtrArray *classes = NULL;
+
+ if (coverage_classes == NULL) {
+ coverage_classes = g_ptr_array_new ();
+ coverage_assembly_classes = g_hash_table_new (g_str_hash, g_str_equal);
+ }
+
+ g_ptr_array_add (coverage_classes, klass);
+ classes = (GPtrArray *)g_hash_table_lookup (coverage_assembly_classes, klass->assembly_name);
+ if (classes == NULL) {
+ classes = g_ptr_array_new ();
+ g_hash_table_insert (coverage_assembly_classes, klass->assembly_name, classes);
+ }
+ g_ptr_array_add (classes, klass);
+}
+
+static void
+coverage_add_coverage (CoverageCoverage *coverage)
+{
+ if (coverage_statements == NULL)
+ coverage_statements = g_ptr_array_new ();
+
+ g_ptr_array_add (coverage_statements, coverage);
+}
+
#define OBJ_ADDR(diff) ((obj_base + diff) << 3)
#define LOG_TIME(base,diff) /*fprintf("outfile, time %llu + %llu near offset %d\n", base, diff, p - ctx->buf)*/
+
+/* Stats */
+#define BUFFER_HEADER_SIZE 48
+
+typedef struct {
+ int count, min_size, max_size, bytes;
+} EventStat;
+
+static int buffer_count;
+static EventStat stats [256];
+
+static void
+record_event_stats (int type, int size)
+{
+ ++stats [type].count;
+ if (!stats [type].min_size)
+ stats [type].min_size = size;
+ stats [type].min_size = MIN (stats [type].min_size, size);
+ stats [type].max_size = MAX (stats [type].max_size, size);
+ stats [type].bytes += size;
+}
+
static int
decode_buffer (ProfContext *ctx)
{
thread = load_thread (ctx, thread_id);
if (!load_data (ctx, len))
return 0;
+
+ ++buffer_count;
+
if (!startup_time) {
startup_time = time_base;
if (use_time_filter) {
p = ctx->buf;
end = p + len;
while (p < end) {
+ unsigned char *start = p;
+ unsigned char event = *p;
switch (*p & 0xf) {
case TYPE_GC: {
int subtype = *p & 0xf0;
fprintf (outfile, "moved obj %p to %p\n", (void*)OBJ_ADDR (obj1diff), (void*)OBJ_ADDR (obj2diff));
}
}
- } else if (subtype == TYPE_GC_HANDLE_CREATED) {
+ } else if (subtype == TYPE_GC_HANDLE_CREATED || subtype == TYPE_GC_HANDLE_CREATED_BT) {
+ int has_bt = subtype == TYPE_GC_HANDLE_CREATED_BT;
+ int num_bt = 0;
+ MethodDesc *sframes [8];
+ MethodDesc **frames = sframes;
int htype = decode_uleb128 (p, &p);
uint32_t handle = decode_uleb128 (p, &p);
intptr_t objdiff = decode_sleb128 (p, &p);
+ if (has_bt) {
+ num_bt = 8;
+ frames = decode_bt (sframes, &num_bt, p, &p, ptr_base);
+ if (!frames) {
+ fprintf (outfile, "Cannot load backtrace\n");
+ return 0;
+ }
+ }
if (htype > 3)
return 0;
- handle_info [htype].created++;
- handle_info [htype].live++;
- add_trace_thread (thread, &handle_info [htype].traces, 1);
- /* FIXME: we don't take into account timing here */
- if (handle_info [htype].live > handle_info [htype].max_live)
- handle_info [htype].max_live = handle_info [htype].live;
- if (num_tracked_objects)
- track_handle (OBJ_ADDR (objdiff), htype, handle);
+ if ((thread_filter && thread_filter == thread->thread_id) || (time_base >= time_from && time_base < time_to)) {
+ handle_info [htype].created++;
+ handle_info [htype].live++;
+ if (handle_info [htype].live > handle_info [htype].max_live)
+ handle_info [htype].max_live = handle_info [htype].live;
+ BackTrace *bt;
+ if (has_bt)
+ bt = add_trace_methods (frames, num_bt, &handle_info [htype].traces, 1);
+ else
+ bt = add_trace_thread (thread, &handle_info [htype].traces, 1);
+ if (num_tracked_objects)
+ track_handle (OBJ_ADDR (objdiff), htype, handle, bt, time_base);
+ }
if (debug)
fprintf (outfile, "handle (%s) %u created for object %p\n", get_handle_name (htype), handle, (void*)OBJ_ADDR (objdiff));
- } else if (subtype == TYPE_GC_HANDLE_DESTROYED) {
+ if (frames != sframes)
+ free (frames);
+ } else if (subtype == TYPE_GC_HANDLE_DESTROYED || subtype == TYPE_GC_HANDLE_DESTROYED_BT) {
+ int has_bt = subtype == TYPE_GC_HANDLE_DESTROYED_BT;
+ int num_bt = 0;
+ MethodDesc *sframes [8];
+ MethodDesc **frames = sframes;
int htype = decode_uleb128 (p, &p);
uint32_t handle = decode_uleb128 (p, &p);
+ if (has_bt) {
+ num_bt = 8;
+ frames = decode_bt (sframes, &num_bt, p, &p, ptr_base);
+ if (!frames) {
+ fprintf (outfile, "Cannot load backtrace\n");
+ return 0;
+ }
+ }
if (htype > 3)
return 0;
- handle_info [htype].destroyed ++;
- handle_info [htype].live--;
+ if ((thread_filter && thread_filter == thread->thread_id) || (time_base >= time_from && time_base < time_to)) {
+ handle_info [htype].destroyed ++;
+ handle_info [htype].live--;
+ BackTrace *bt;
+ if (has_bt)
+ bt = add_trace_methods (frames, num_bt, &handle_info [htype].destroy_traces, 1);
+ else
+ bt = add_trace_thread (thread, &handle_info [htype].destroy_traces, 1);
+ /* TODO: track_handle_free () - would need to record and keep track of the associated object address... */
+ }
if (debug)
fprintf (outfile, "handle (%s) %u destroyed\n", get_handle_name (htype), handle);
+ if (frames != sframes)
+ free (frames);
}
break;
}
case TYPE_METADATA: {
- int error = *p & TYPE_LOAD_ERR;
+ int subtype = *p & 0xf0;
+ const char *load_str = subtype == TYPE_END_LOAD ? "loaded" : "unloaded";
uint64_t tdiff = decode_uleb128 (p + 1, &p);
int mtype = *p++;
intptr_t ptrdiff = decode_sleb128 (p, &p);
return 0;
}
if (debug)
- fprintf (outfile, "loaded class %p (%s in %p) at %llu\n", (void*)(ptr_base + ptrdiff), p, (void*)(ptr_base + imptrdiff), (unsigned long long) time_base);
- if (!error)
+ fprintf (outfile, "%s class %p (%s in %p) at %llu\n", load_str, (void*)(ptr_base + ptrdiff), p, (void*)(ptr_base + imptrdiff), (unsigned long long) time_base);
+ if (subtype == TYPE_END_LOAD)
add_class (ptr_base + ptrdiff, (char*)p);
while (*p) p++;
p++;
return 0;
}
if (debug)
- fprintf (outfile, "loaded image %p (%s) at %llu\n", (void*)(ptr_base + ptrdiff), p, (unsigned long long) time_base);
- if (!error)
+ fprintf (outfile, "%s image %p (%s) at %llu\n", load_str, (void*)(ptr_base + ptrdiff), p, (unsigned long long) time_base);
+ if (subtype == TYPE_END_LOAD)
add_image (ptr_base + ptrdiff, (char*)p);
while (*p) p++;
p++;
- } else if (mtype == TYPE_THREAD) {
- ThreadContext *nt;
+ } else if (mtype == TYPE_ASSEMBLY) {
uint64_t flags = decode_uleb128 (p, &p);
if (flags) {
- fprintf (outfile, "non-zero flags in thread\n");
+ fprintf (outfile, "non-zero flags in assembly\n");
return 0;
}
- nt = get_thread (ctx, ptr_base + ptrdiff);
- nt->name = pstrdup ((char*)p);
if (debug)
- fprintf (outfile, "thread %p named: %s\n", (void*)(ptr_base + ptrdiff), p);
+ fprintf (outfile, "%s assembly %p (%s) at %llu\n", load_str, (void*)(ptr_base + ptrdiff), p, (unsigned long long) time_base);
+ if (subtype == TYPE_END_LOAD)
+ add_assembly (ptr_base + ptrdiff, (char*)p);
while (*p) p++;
p++;
+ } else if (mtype == TYPE_DOMAIN) {
+ uint64_t flags = decode_uleb128 (p, &p);
+ if (flags) {
+ fprintf (outfile, "non-zero flags in domain\n");
+ return 0;
+ }
+ DomainContext *nd = get_domain (ctx, ptr_base + ptrdiff);
+ /* no subtype means it's a name event, rather than start/stop */
+ if (subtype == 0)
+ nd->friendly_name = pstrdup ((char *) p);
+ if (debug) {
+ if (subtype == 0)
+ fprintf (outfile, "domain %p named at %llu: %s\n", (void *) (ptr_base + ptrdiff), (unsigned long long) time_base, p);
+ else
+ fprintf (outfile, "%s thread %p at %llu\n", load_str, (void *) (ptr_base + ptrdiff), (unsigned long long) time_base);
+ }
+ if (subtype == 0) {
+ while (*p) p++;
+ p++;
+ }
+ } else if (mtype == TYPE_CONTEXT) {
+ uint64_t flags = decode_uleb128 (p, &p);
+ if (flags) {
+ fprintf (outfile, "non-zero flags in context\n");
+ return 0;
+ }
+ intptr_t domaindiff = decode_sleb128 (p, &p);
+ if (debug)
+ fprintf (outfile, "%s context %p (%p) at %llu\n", load_str, (void*)(ptr_base + ptrdiff), (void *) (ptr_base + domaindiff), (unsigned long long) time_base);
+ if (subtype == TYPE_END_LOAD)
+ get_remctx (ctx, ptr_base + ptrdiff)->domain_id = ptr_base + domaindiff;
+ } else if (mtype == TYPE_THREAD) {
+ uint64_t flags = decode_uleb128 (p, &p);
+ if (flags) {
+ fprintf (outfile, "non-zero flags in thread\n");
+ return 0;
+ }
+ ThreadContext *nt = get_thread (ctx, ptr_base + ptrdiff);
+ /* no subtype means it's a name event, rather than start/stop */
+ if (subtype == 0)
+ nt->name = pstrdup ((char*)p);
+ if (debug) {
+ if (subtype == 0)
+ fprintf (outfile, "thread %p named at %llu: %s\n", (void*)(ptr_base + ptrdiff), (unsigned long long) time_base, p);
+ else
+ fprintf (outfile, "%s thread %p at %llu\n", load_str, (void *) (ptr_base + ptrdiff), (unsigned long long) time_base);
+ }
+ if (subtype == 0) {
+ while (*p) p++;
+ p++;
+ }
}
break;
}
int codelen = decode_uleb128 (p, &p);
const char *name;
if (type == MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE) {
- name = (void*)p;
+ name = (const char *)p;
while (*p) p++;
p++;
} else {
int i;
int sample_type = decode_uleb128 (p + 1, &p);
uint64_t tstamp = decode_uleb128 (p, &p);
+ void *tid = (void *) thread_id;
+ if (ctx->data_version > 10)
+ tid = (void *) (ptr_base + decode_sleb128 (p, &p));
int count = decode_uleb128 (p, &p);
for (i = 0; i < count; ++i) {
uintptr_t ip = ptr_base + decode_sleb128 (p, &p);
if ((tstamp >= time_from && tstamp < time_to))
add_stat_sample (sample_type, ip);
if (debug)
- fprintf (outfile, "sample hit, type: %d at %p\n", sample_type, (void*)ip);
+ fprintf (outfile, "sample hit, type: %d at %p for thread %p\n", sample_type, (void*)ip, tid);
}
if (ctx->data_version > 5) {
count = decode_uleb128 (p, &p);
type = decode_uleb128 (p, &p);
- value = calloc (1, sizeof (CounterValue));
+ value = (CounterValue *)calloc (1, sizeof (CounterValue));
value->timestamp = timestamp;
switch (type) {
#if SIZEOF_VOID_P == 4
case MONO_COUNTER_WORD:
#endif
- value->buffer = malloc (sizeof (int32_t));
+ value->buffer = (unsigned char *)malloc (sizeof (int32_t));
*(int32_t*)value->buffer = (int32_t)decode_sleb128 (p, &p) + (previous ? (*(int32_t*)previous->buffer) : 0);
break;
case MONO_COUNTER_UINT:
- value->buffer = malloc (sizeof (uint32_t));
+ value->buffer = (unsigned char *)malloc (sizeof (uint32_t));
*(uint32_t*)value->buffer = (uint32_t)decode_uleb128 (p, &p) + (previous ? (*(uint32_t*)previous->buffer) : 0);
break;
case MONO_COUNTER_LONG:
case MONO_COUNTER_WORD:
#endif
case MONO_COUNTER_TIME_INTERVAL:
- value->buffer = malloc (sizeof (int64_t));
+ value->buffer = (unsigned char *)malloc (sizeof (int64_t));
*(int64_t*)value->buffer = (int64_t)decode_sleb128 (p, &p) + (previous ? (*(int64_t*)previous->buffer) : 0);
break;
case MONO_COUNTER_ULONG:
- value->buffer = malloc (sizeof (uint64_t));
+ value->buffer = (unsigned char *)malloc (sizeof (uint64_t));
*(uint64_t*)value->buffer = (uint64_t)decode_uleb128 (p, &p) + (previous ? (*(uint64_t*)previous->buffer) : 0);
break;
case MONO_COUNTER_DOUBLE:
- value->buffer = malloc (sizeof (double));
+ value->buffer = (unsigned char *)malloc (sizeof (double));
#if TARGET_BYTE_ORDER == G_LITTLE_ENDIAN
for (i = 0; i < sizeof (double); i++)
#else
}
break;
}
+ case TYPE_COVERAGE:{
+ int subtype = *p & 0xf0;
+ switch (subtype) {
+ case TYPE_COVERAGE_METHOD: {
+ CoverageMethod *method = g_new0 (CoverageMethod, 1);
+ const char *assembly, *klass, *name, *sig, *filename;
+ int token, n_offsets, method_id;
+
+ p++;
+ assembly = (const char *)p; while (*p) p++; p++;
+ klass = (const char *)p; while (*p) p++; p++;
+ name = (const char *)p; while (*p) p++; p++;
+ sig = (const char *)p; while (*p) p++; p++;
+ filename = (const char *)p; while (*p) p++; p++;
+
+ token = decode_uleb128 (p, &p);
+ method_id = decode_uleb128 (p, &p);
+ n_offsets = decode_uleb128 (p, &p);
+
+ method->assembly_name = g_strdup (assembly);
+ method->class_name = g_strdup (klass);
+ method->method_name = g_strdup (name);
+ method->method_signature = g_strdup (sig);
+ method->filename = g_strdup (filename);
+ method->token = token;
+ method->n_statements = n_offsets;
+ method->coverage = g_ptr_array_new ();
+ method->method_id = method_id;
+
+ coverage_add_method (method);
+
+ break;
+ }
+ case TYPE_COVERAGE_STATEMENT: {
+ CoverageCoverage *coverage = g_new0 (CoverageCoverage, 1);
+ int offset, count, line, column, method_id;
+
+ p++;
+ method_id = decode_uleb128 (p, &p);
+ offset = decode_uleb128 (p, &p);
+ count = decode_uleb128 (p, &p);
+ line = decode_uleb128 (p, &p);
+ column = decode_uleb128 (p, &p);
+
+ coverage->method_id = method_id;
+ coverage->offset = offset;
+ coverage->count = count;
+ coverage->line = line;
+ coverage->column = column;
+
+ coverage_add_coverage (coverage);
+ break;
+ }
+ case TYPE_COVERAGE_ASSEMBLY: {
+ CoverageAssembly *assembly = g_new0 (CoverageAssembly, 1);
+ char *name, *guid, *filename;
+ int number_of_methods, fully_covered, partially_covered;
+ p++;
+
+ name = (char *)p; while (*p) p++; p++;
+ guid = (char *)p; while (*p) p++; p++;
+ filename = (char *)p; while (*p) p++; p++;
+ number_of_methods = decode_uleb128 (p, &p);
+ fully_covered = decode_uleb128 (p, &p);
+ partially_covered = decode_uleb128 (p, &p);
+
+ assembly->name = g_strdup (name);
+ assembly->guid = g_strdup (guid);
+ assembly->filename = g_strdup (filename);
+ assembly->number_of_methods = number_of_methods;
+ assembly->fully_covered = fully_covered;
+ assembly->partially_covered = partially_covered;
+
+ coverage_add_assembly (assembly);
+ break;
+ }
+ case TYPE_COVERAGE_CLASS: {
+ CoverageClass *klass = g_new0 (CoverageClass, 1);
+ char *assembly_name, *class_name;
+ int number_of_methods, fully_covered, partially_covered;
+ p++;
+
+ assembly_name = (char *)p; while (*p) p++; p++;
+ class_name = (char *)p; while (*p) p++; p++;
+ number_of_methods = decode_uleb128 (p, &p);
+ fully_covered = decode_uleb128 (p, &p);
+ partially_covered = decode_uleb128 (p, &p);
+
+ klass->assembly_name = g_strdup (assembly_name);
+ klass->class_name = g_strdup (class_name);
+ klass->number_of_methods = number_of_methods;
+ klass->fully_covered = fully_covered;
+ klass->partially_covered = partially_covered;
+
+ coverage_add_class (klass);
+ break;
+ }
+
+ default:
+ break;
+ }
+ break;
+ }
default:
fprintf (outfile, "unhandled profiler event: 0x%x at file offset: %llu + %lld (len: %d\n)\n", *p, (unsigned long long) file_offset, (long long) (p - ctx->buf), len);
exit (1);
}
+ record_event_stats (event, p - start);
}
thread->last_time = time_base;
for (i = 0; i < thread->stack_id; ++i)
load_file (char *name)
{
unsigned char *p;
- ProfContext *ctx = calloc (sizeof (ProfContext), 1);
+ ProfContext *ctx = (ProfContext *)calloc (sizeof (ProfContext), 1);
if (strcmp (name, "-") == 0)
ctx->file = stdin;
else
static int
compare_class (const void *a, const void *b)
{
- ClassDesc *const*A = a;
- ClassDesc *const*B = b;
+ ClassDesc *const *A = (ClassDesc *const *)a;
+ ClassDesc *const *B = (ClassDesc *const *)b;
uint64_t vala, valb;
if (alloc_sort_mode == ALLOC_SORT_BYTES) {
vala = (*A)->alloc_size;
}
}
+static void
+dump_domains (ProfContext *ctx)
+{
+ fprintf (outfile, "\nDomain summary\n");
+
+ for (DomainContext *domain = ctx->domains; domain; domain = domain->next)
+ fprintf (outfile, "\tDomain: %p, friendly name: \"%s\"\n", (void *) domain->domain_id, domain->friendly_name);
+}
+
+static void
+dump_remctxs (ProfContext *ctx)
+{
+ fprintf (outfile, "\nContext summary\n");
+
+ for (RemCtxContext *remctx = ctx->remctxs; remctx; remctx = remctx->next)
+ fprintf (outfile, "\tContext: %p, domain: %p\n", (void *) remctx->remctx_id, (void *) remctx->domain_id);
+}
+
static void
dump_exceptions (void)
{
static int
compare_monitor (const void *a, const void *b)
{
- MonitorDesc *const*A = a;
- MonitorDesc *const*B = b;
+ MonitorDesc *const *A = (MonitorDesc *const *)a;
+ MonitorDesc *const *B = (MonitorDesc *const *)b;
if ((*B)->wait_time == (*A)->wait_time)
return 0;
if ((*B)->wait_time < (*A)->wait_time)
int i, j;
if (!num_monitors)
return;
- monitors = malloc (sizeof (void*) * num_monitors);
+ monitors = (MonitorDesc **)malloc (sizeof (void*) * num_monitors);
for (i = 0, j = 0; i < SMALL_HASH_SIZE; ++i) {
MonitorDesc *mdesc = monitor_hash [i];
while (mdesc) {
(unsigned long long) (handle_info [i].destroyed),
(unsigned long long) (handle_info [i].max_live));
dump_traces (&handle_info [i].traces, "created");
+ dump_traces (&handle_info [i].destroy_traces, "destroyed");
}
}
intptr_t allocs = 0;
uint64_t size = 0;
int header_done = 0;
- ClassDesc **classes = malloc (num_classes * sizeof (void*));
+ ClassDesc **classes = (ClassDesc **)malloc (num_classes * sizeof (void*));
ClassDesc *cd;
c = 0;
for (i = 0; i < HASH_SIZE; ++i) {
static int
compare_method (const void *a, const void *b)
{
- MethodDesc *const*A = a;
- MethodDesc *const*B = b;
+ MethodDesc *const *A = (MethodDesc *const *)a;
+ MethodDesc *const *B = (MethodDesc *const *)b;
uint64_t vala, valb;
if (method_sort_mode == METHOD_SORT_SELF) {
vala = (*A)->self_time;
}
}
}
-
+ fprintf (outfile, "\tLoaded assemblies: %d\n", num_assemblies);
+ if (verbose) {
+ AssemblyDesc *assembly;
+ int i;
+ for (i = 0; i < SMALL_HASH_SIZE; ++i) {
+ assembly = assembly_hash [i];
+ while (assembly) {
+ fprintf (outfile, "\t\t%s\n", assembly->asmname);
+ assembly = assembly->next;
+ }
+ }
+ }
}
static void
int i, c;
uint64_t calls = 0;
int header_done = 0;
- MethodDesc **methods = malloc (num_methods * sizeof (void*));
+ MethodDesc **methods = (MethodDesc **)malloc (num_methods * sizeof (void*));
MethodDesc *cd;
c = 0;
for (i = 0; i < HASH_SIZE; ++i) {
static int
compare_heap_class (const void *a, const void *b)
{
- HeapClassDesc *const*A = a;
- HeapClassDesc *const*B = b;
+ HeapClassDesc *const *A = (HeapClassDesc *const *)a;
+ HeapClassDesc *const *B = (HeapClassDesc *const *)b;
uint64_t vala, valb;
if (alloc_sort_mode == ALLOC_SORT_BYTES) {
vala = (*A)->total_size;
static int
compare_rev_class (const void *a, const void *b)
{
- const HeapClassRevRef *A = a;
- const HeapClassRevRef *B = b;
+ const HeapClassRevRef *A = (const HeapClassRevRef *)a;
+ const HeapClassRevRef *B = (const HeapClassRevRef *)b;
if (B->count == A->count)
return 0;
if (B->count < A->count)
int i;
HeapClassDesc *cd;
HeapClassDesc **sorted;
- sorted = malloc (sizeof (void*) * hs->class_count);
+ sorted = (HeapClassDesc **)malloc (sizeof (void*) * hs->class_count);
for (i = 0; i < hs->hash_size; ++i) {
cd = hs->class_hash [i];
if (!cd)
}
if (!collect_traces)
continue;
- rev_sorted = malloc (cd->rev_count * sizeof (HeapClassRevRef));
+ rev_sorted = (HeapClassRevRef *)malloc (cd->rev_count * sizeof (HeapClassRevRef));
k = 0;
for (j = 0; j < cd->rev_hash_size; ++j) {
if (cd->rev_hash [j].klass)
static int
compare_heap_shots (const void *a, const void *b)
{
- HeapShot *const*A = a;
- HeapShot *const*B = b;
+ HeapShot *const *A = (HeapShot *const *)a;
+ HeapShot *const *B = (HeapShot *const *)b;
if ((*B)->timestamp == (*A)->timestamp)
return 0;
if ((*B)->timestamp > (*A)->timestamp)
int i;
if (!heap_shots)
return;
- hs_sorted = malloc (num_heap_shots * sizeof (void*));
+ hs_sorted = (HeapShot **)malloc (num_heap_shots * sizeof (void*));
fprintf (outfile, "\nHeap shot summary\n");
i = 0;
for (hs = heap_shots; hs; hs = hs->next)
}
}
+/* This is a very basic escape function that escapes < > and &
+ Ideally we'd use g_markup_escape_string but that function isn't
+ available in Mono's eglib. This was written without looking at the
+ source of that function in glib. */
+static char *
+escape_string_for_xml (const char *string)
+{
+ GString *string_builder = g_string_new (NULL);
+ const char *start, *p;
+
+ start = p = string;
+ while (*p) {
+ while (*p && *p != '&' && *p != '<' && *p != '>')
+ p++;
+
+ g_string_append_len (string_builder, start, p - start);
+
+ if (*p == '\0')
+ break;
+
+ switch (*p) {
+ case '<':
+ g_string_append (string_builder, "<");
+ break;
+
+ case '>':
+ g_string_append (string_builder, ">");
+ break;
+
+ case '&':
+ g_string_append (string_builder, "&");
+ break;
+
+ default:
+ break;
+ }
+
+ p++;
+ start = p;
+ }
+
+ return g_string_free (string_builder, FALSE);
+}
+
+static int
+sort_assemblies (gconstpointer a, gconstpointer b)
+{
+ CoverageAssembly *assembly_a = *(CoverageAssembly **)a;
+ CoverageAssembly *assembly_b = *(CoverageAssembly **)b;
+
+ if (assembly_a->name == NULL && assembly_b->name == NULL)
+ return 0;
+ else if (assembly_a->name == NULL)
+ return -1;
+ else if (assembly_b->name == NULL)
+ return 1;
+
+ return strcmp (assembly_a->name, assembly_b->name);
+}
+
+static void
+dump_coverage (void)
+{
+ if (!coverage_methods && !coverage_assemblies)
+ return;
+
+ gather_coverage_statements ();
+ fprintf (outfile, "\nCoverage Summary:\n");
+
+ if (coverage_outfile) {
+ fprintf (coverage_outfile, "<?xml version=\"1.0\"?>\n");
+ fprintf (coverage_outfile, "<coverage version=\"0.3\">\n");
+ }
+
+ g_ptr_array_sort (coverage_assemblies, sort_assemblies);
+
+ for (guint i = 0; i < coverage_assemblies->len; i++) {
+ CoverageAssembly *assembly = (CoverageAssembly *)coverage_assemblies->pdata[i];
+ GPtrArray *classes;
+
+ if (assembly->number_of_methods != 0) {
+ int percentage = ((assembly->fully_covered + assembly->partially_covered) * 100) / assembly->number_of_methods;
+ fprintf (outfile, "\t%s (%s) %d%% covered (%d methods - %d covered)\n", assembly->name, assembly->filename, percentage, assembly->number_of_methods, assembly->fully_covered);
+ } else
+ fprintf (outfile, "\t%s (%s) ?%% covered (%d methods - %d covered)\n", assembly->name, assembly->filename, assembly->number_of_methods, assembly->fully_covered);
+
+ if (coverage_outfile) {
+ char *escaped_name, *escaped_filename;
+ escaped_name = escape_string_for_xml (assembly->name);
+ escaped_filename = escape_string_for_xml (assembly->filename);
+
+ fprintf (coverage_outfile, "\t<assembly name=\"%s\" guid=\"%s\" filename=\"%s\" method-count=\"%d\" full=\"%d\" partial=\"%d\"/>\n", escaped_name, assembly->guid, escaped_filename, assembly->number_of_methods, assembly->fully_covered, assembly->partially_covered);
+
+ g_free (escaped_name);
+ g_free (escaped_filename);
+ }
+
+ classes = (GPtrArray *)g_hash_table_lookup (coverage_assembly_classes, assembly->name);
+ if (classes) {
+ for (guint j = 0; j < classes->len; j++) {
+ CoverageClass *klass = (CoverageClass *)classes->pdata [j];
+
+ if (klass->number_of_methods > 0) {
+ int percentage = ((klass->fully_covered + klass->partially_covered) * 100) / klass->number_of_methods;
+ fprintf (outfile, "\t\t%s %d%% covered (%d methods - %d covered)\n", klass->class_name, percentage, klass->number_of_methods, klass->fully_covered);
+ } else
+ fprintf (outfile, "\t\t%s ?%% covered (%d methods - %d covered)\n", klass->class_name, klass->number_of_methods, klass->fully_covered);
+
+ if (coverage_outfile) {
+ char *escaped_name;
+ escaped_name = escape_string_for_xml (klass->class_name);
+
+ fprintf (coverage_outfile, "\t\t<class name=\"%s\" method-count=\"%d\" full=\"%d\" partial=\"%d\"/>\n", escaped_name, klass->number_of_methods, klass->fully_covered, klass->partially_covered);
+ g_free (escaped_name);
+ }
+ }
+ }
+ }
+
+ for (guint i = 0; i < coverage_methods->len; i++) {
+ CoverageMethod *method = (CoverageMethod *)coverage_methods->pdata [i];
+
+ if (coverage_outfile) {
+ char *escaped_assembly, *escaped_class, *escaped_method, *escaped_sig, *escaped_filename;
+
+ escaped_assembly = escape_string_for_xml (method->assembly_name);
+ escaped_class = escape_string_for_xml (method->class_name);
+ escaped_method = escape_string_for_xml (method->method_name);
+ escaped_sig = escape_string_for_xml (method->method_signature);
+ escaped_filename = escape_string_for_xml (method->filename);
+
+ fprintf (coverage_outfile, "\t<method assembly=\"%s\" class=\"%s\" name=\"%s (%s)\" filename=\"%s\" token=\"%d\">\n", escaped_assembly, escaped_class, escaped_method, escaped_sig, escaped_filename, method->token);
+
+ g_free (escaped_assembly);
+ g_free (escaped_class);
+ g_free (escaped_method);
+ g_free (escaped_sig);
+ g_free (escaped_filename);
+
+ for (guint j = 0; j < method->coverage->len; j++) {
+ CoverageCoverage *coverage = (CoverageCoverage *)method->coverage->pdata [j];
+ fprintf (coverage_outfile, "\t\t<statement offset=\"%d\" counter=\"%d\" line=\"%d\" column=\"%d\"/>\n", coverage->offset, coverage->count, coverage->line, coverage->column);
+ }
+ fprintf (coverage_outfile, "\t</method>\n");
+ }
+ }
+
+ if (coverage_outfile) {
+ fprintf (coverage_outfile, "</coverage>\n");
+ fclose (coverage_outfile);
+ coverage_outfile = NULL;
+ }
+}
+
+#define DUMP_EVENT_STAT(EVENT,SUBTYPE) dump_event (#EVENT, #SUBTYPE, EVENT, SUBTYPE);
+
+static void
+dump_event (const char *event_name, const char *subtype_name, int event, int subtype)
+{
+ int idx = event | subtype;
+ EventStat evt = stats [idx];
+ if (!evt.count)
+ return;
+
+ fprintf (outfile, "\t%16s\t%26s\tcount %6d\tmin %3d\tmax %6d\tbytes %d\n", event_name, subtype_name, evt.count, evt.min_size, evt.max_size, evt.bytes);
+}
+
+static void
+dump_stats (void)
+{
+ fprintf (outfile, "\nMlpd statistics\n");
+ fprintf (outfile, "\tBuffer count %d\toverhead %d (%d bytes per header)\n", buffer_count, buffer_count * BUFFER_HEADER_SIZE, BUFFER_HEADER_SIZE);
+ fprintf (outfile, "\nEvent details:\n");
+
+ DUMP_EVENT_STAT (TYPE_ALLOC, TYPE_ALLOC_NO_BT);
+ DUMP_EVENT_STAT (TYPE_ALLOC, TYPE_ALLOC_BT);
+
+ DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_EVENT);
+ DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_RESIZE);
+ DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_MOVE);
+ DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_CREATED);
+ DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_DESTROYED);
+ DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_CREATED_BT);
+ DUMP_EVENT_STAT (TYPE_GC, TYPE_GC_HANDLE_DESTROYED_BT);
+
+ DUMP_EVENT_STAT (TYPE_METADATA, TYPE_END_LOAD);
+ DUMP_EVENT_STAT (TYPE_METADATA, TYPE_END_UNLOAD);
+
+ DUMP_EVENT_STAT (TYPE_METHOD, TYPE_LEAVE);
+ DUMP_EVENT_STAT (TYPE_METHOD, TYPE_ENTER);
+ DUMP_EVENT_STAT (TYPE_METHOD, TYPE_EXC_LEAVE);
+ DUMP_EVENT_STAT (TYPE_METHOD, TYPE_JIT);
+
+ DUMP_EVENT_STAT (TYPE_EXCEPTION, TYPE_THROW);
+ DUMP_EVENT_STAT (TYPE_EXCEPTION, TYPE_CLAUSE);
+ DUMP_EVENT_STAT (TYPE_EXCEPTION, TYPE_EXCEPTION_BT);
+
+ DUMP_EVENT_STAT (TYPE_MONITOR, TYPE_MONITOR_NO_BT);
+ DUMP_EVENT_STAT (TYPE_MONITOR, TYPE_MONITOR_BT);
+
+ DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_START);
+ DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_END);
+ DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_OBJECT);
+ DUMP_EVENT_STAT (TYPE_HEAP, TYPE_HEAP_ROOT);
+
+ DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_HIT);
+ DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_USYM);
+ DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_UBIN);
+ DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_COUNTERS_DESC);
+ DUMP_EVENT_STAT (TYPE_SAMPLE, TYPE_SAMPLE_COUNTERS);
+
+ DUMP_EVENT_STAT (TYPE_RUNTIME, TYPE_JITHELPER);
+
+ DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_ASSEMBLY);
+ DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_METHOD);
+ DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_STATEMENT);
+ DUMP_EVENT_STAT (TYPE_COVERAGE, TYPE_COVERAGE_CLASS);
+}
+
+
+
static void
flush_context (ProfContext *ctx)
{
}
}
-static const char *reports = "header,jit,gc,sample,alloc,call,metadata,exception,monitor,thread,heapshot,counters";
+static const char *reports = "header,jit,gc,sample,alloc,call,metadata,exception,monitor,thread,heapshot,counters,coverage";
static const char*
match_option (const char *p, const char *opt)
dump_threads (ctx);
continue;
}
+ if ((opt = match_option (p, "domain")) != p) {
+ if (!parse_only)
+ dump_domains (ctx);
+ continue;
+ }
+ if ((opt = match_option (p, "context")) != p) {
+ if (!parse_only)
+ dump_remctxs (ctx);
+ continue;
+ }
if ((opt = match_option (p, "gc")) != p) {
if (!parse_only)
dump_gcs ();
dump_counters ();
continue;
}
+ if ((opt = match_option (p, "coverage")) != p) {
+ if (!parse_only)
+ dump_coverage ();
+ continue;
+ }
+ if ((opt = match_option (p, "stats")) != p) {
+ if (!parse_only)
+ dump_stats ();
+ continue;
+ }
return 0;
}
return 1;
printf ("Options:\n");
printf ("\t--help display this help\n");
printf ("\t--out=FILE write to FILE instead of stdout\n");
- printf ("\t--traces collect and show backtraces\n");
+ printf ("\t--traces collect and show backtraces\n");
printf ("\t--maxframes=NUM limit backtraces to NUM entries\n");
printf ("\t--reports=R1[,R2...] print the specified reports. Defaults are:\n");
printf ("\t %s\n", reports);
printf ("\t--time=FROM-TO consider data FROM seconds from startup up to TO seconds\n");
printf ("\t--verbose increase verbosity level\n");
printf ("\t--debug display decoding debug info for mprof-report devs\n");
+ printf ("\t--coverage-out=FILE write the coverage info to FILE as XML\n");
}
int
} else if (strcmp ("--traces", argv [i]) == 0) {
show_traces = 1;
collect_traces = 1;
+ } else if (strncmp ("--coverage-out=", argv [i], 15) == 0) {
+ const char *val = argv [i] + 15;
+ coverage_outfile = fopen (val, "w");
+ if (!coverage_outfile) {
+ printf ("Cannot open output file: %s\n", val);
+ return 1;
+ }
} else {
break;
}
print_reports (ctx, reports, 0);
return 0;
}
-