*
* Copyright 2010 Novell, Inc (http://www.novell.com)
*/
+
+/*
+ * 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"
#include "proflog.h"
#if defined (HAVE_SYS_ZLIB)
#include <zlib.h>
#endif
+#include <glib.h>
#include <mono/metadata/profiler.h>
#include <mono/metadata/object.h>
#include <mono/metadata/debug-helpers.h>
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)
typedef struct _Counter Counter;
struct _Counter {
int index;
- int section;
+ const char *section;
const char *name;
int type;
int unit;
typedef struct _CounterSection CounterSection;
struct _CounterSection {
- int value;
+ const char *value;
CounterList *counters;
CounterList *counters_last;
CounterSection *next;
clist->counter = counter;
for (csection = counters_sections; csection; csection = csection->next) {
- if (csection->value == counter->section) {
+ if (strcmp (csection->value, counter->section) == 0) {
/* If section exist */
if (!csection->counters)
csection->counters = clist;
}
static void
-add_counter (int section, const char *name, int type, int unit, int variance, int index)
+add_counter (const char *section, const char *name, int type, int unit, int variance, int index)
{
CounterList *list, *l;
Counter *counter;
for (ctimestamp = counters_timestamps; ctimestamp; ctimestamp = ctimestamp->next) {
if (ctimestamp->value == timestamp) {
for (csection = ctimestamp->sections; csection; csection = csection->next) {
- if (csection->value == counter->section) {
+ if (strcmp (csection->value, counter->section) == 0) {
/* if timestamp exist and section exist */
if (!csection->counters)
csection->counters = clist;
for (i = 0; counters_to_print [i][0] != 0; i++) {
if (strcmp (counters_to_print [i], counter->name) == 0) {
if (!section_printed) {
- fprintf (outfile, "\t%s:\n", section_name (csection->value));
+ fprintf (outfile, "\t%s:\n", csection->value);
section_printed = 1;
}
}
} else if (counters_sort_mode == COUNTERS_SORT_TIME) {
for (ctimestamp = counters_timestamps; ctimestamp; ctimestamp = ctimestamp->next) {
- fprintf (outfile, "\t%lld:%02lld:%02lld:%02lld.%03lld:\n", ctimestamp->value / 1000 / 60 / 60 / 24 % 1000,
- ctimestamp->value / 1000 / 60 / 60 % 24, ctimestamp->value / 1000 / 60 % 60,
- ctimestamp->value / 1000 % 60, ctimestamp->value % 1000);
+ fprintf (outfile, "\t%llu:%02llu:%02llu:%02llu.%03llu:\n",
+ (unsigned long long) (ctimestamp->value / 1000 / 60 / 60 / 24 % 1000),
+ (unsigned long long) (ctimestamp->value / 1000 / 60 / 60 % 24),
+ (unsigned long long) (ctimestamp->value / 1000 / 60 % 60),
+ (unsigned long long) (ctimestamp->value / 1000 % 60),
+ (unsigned long long) (ctimestamp->value % 1000));
for (csection = ctimestamp->sections; csection; csection = csection->next) {
- fprintf (outfile, "\t\t%s:\n", section_name (csection->value));
+ fprintf (outfile, "\t\t%s:\n", csection->value);
for (clist = csection->counters; clist; clist = clist->next) {
counter = clist->counter;
}
} else if (counters_sort_mode == COUNTERS_SORT_CATEGORY) {
for (csection = counters_sections; csection; csection = csection->next) {
- fprintf (outfile, "\t%s:\n", section_name (csection->value));
+ fprintf (outfile, "\t%s:\n", csection->value);
for (clist = csection->counters; clist; clist = clist->next) {
counter = clist->counter;
counter->name, type_name (counter->type), unit_name (counter->unit), variance_name (counter->variance));
for (cvalue = counter->values; cvalue; cvalue = cvalue->next) {
- snprintf (strtimestamp, sizeof (strtimestamp), "%lld:%02lld:%02lld:%02lld.%03lld", cvalue->timestamp / 1000 / 60 / 60 / 24 % 1000,
- cvalue->timestamp / 1000 / 60 / 60 % 24, cvalue->timestamp / 1000 / 60 % 60,
- cvalue->timestamp / 1000 % 60, cvalue->timestamp % 1000);
+ snprintf (strtimestamp, sizeof (strtimestamp), "%llu:%02llu:%02llu:%02llu.%03llu",
+ (unsigned long long) (cvalue->timestamp / 1000 / 60 / 60 / 24 % 1000),
+ (unsigned long long) (cvalue->timestamp / 1000 / 60 / 60 % 24),
+ (unsigned long long) (cvalue->timestamp / 1000 / 60 % 60),
+ (unsigned long long) (cvalue->timestamp / 1000 % 60),
+ (unsigned long long) (cvalue->timestamp % 1000));
dump_counters_value (counter, "\t\t\t%s", strtimestamp, cvalue->buffer);
}
print_usym (UnmanagedSymbol* um)
{
if (um->parent)
- fprintf (outfile, "\t%6d %6.2f %-36s in %s\n", um->sample_hits, um->sample_hits*100.0/num_stat_samples, um->name, um->parent->name);
+ fprintf (outfile, "\t%6zd %6.2f %-36s in %s\n", um->sample_hits, um->sample_hits*100.0/num_stat_samples, um->name, um->parent->name);
else
- fprintf (outfile, "\t%6d %6.2f %s\n", um->sample_hits, um->sample_hits*100.0/num_stat_samples, um->name);
+ fprintf (outfile, "\t%6zd %6.2f %s\n", um->sample_hits, um->sample_hits*100.0/num_stat_samples, um->name);
}
static int
/* should not happen */
printf ("failed heap obj update\n");
return NULL;
-
+
}
static uintptr_t
fprintf (outfile, "object %p (%s) unmarked\n", (void*)hs->objects_hash [i], hs->objects_hash [i]->hklass->klass->name);
}
}
- fprintf (outfile, "Total unmarked: %d/%d\n", num_unmarked, hs->objects_count);
+ fprintf (outfile, "Total unmarked: %zd/%zd\n", num_unmarked, hs->objects_count);
free (marks);
}
typedef struct {
FILE *file;
#if defined (HAVE_SYS_ZLIB)
- gzFile *gzfile;
+ gzFile gzfile;
#endif
unsigned char *buf;
int size;
if (r == 0)
return size == 0? 1: 0;
return r == size;
- } else
+ } else
#endif
{
int r = fread (ctx->buf, size, 1, ctx->file);
for (i = 0; i < num_tracked_objects; ++i) {
if (tracked_objects [i] != obj)
continue;
- fprintf (outfile, "Object %p created (%s, %llu bytes) at %.3f secs.\n", (void*)obj, cd->name, size, (timestamp - startup_time)/1000000000.0);
+ fprintf (outfile, "Object %p created (%s, %llu bytes) at %.3f secs.\n", (void*)obj, cd->name, (unsigned long long) size, (timestamp - startup_time)/1000000000.0);
if (bt && bt->count) {
int k;
for (k = 0; k < bt->count; ++k)
{
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);
}
}
tracked_objects [num_tracked_objects - 1] = obj;
}
+static int num_jit_helpers = 0;
+static int jit_helpers_code_size = 0;
+
+static const char*
+code_buffer_desc (int type)
+{
+ switch (type) {
+ case MONO_PROFILER_CODE_BUFFER_METHOD:
+ return "method";
+ case MONO_PROFILER_CODE_BUFFER_METHOD_TRAMPOLINE:
+ return "method trampoline";
+ case MONO_PROFILER_CODE_BUFFER_UNBOX_TRAMPOLINE:
+ return "unbox trampoline";
+ case MONO_PROFILER_CODE_BUFFER_IMT_TRAMPOLINE:
+ return "imt trampoline";
+ case MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE:
+ return "generics trampoline";
+ case MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE:
+ return "specific trampoline";
+ case MONO_PROFILER_CODE_BUFFER_HELPER:
+ return "misc helper";
+ case MONO_PROFILER_CODE_BUFFER_MONITOR:
+ 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 = coverage_statements->pdata[i];
+ CoverageMethod *method = 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 = 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)*/
thread_id = read_int64 (p + 32);
method_base = read_int64 (p + 40);
if (debug)
- fprintf (outfile, "buf: thread:%x, len: %d, time: %llu, file offset: %llu\n", thread_id, len, time_base, file_offset);
+ fprintf (outfile, "buf: thread:%zx, len: %d, time: %llu, file offset: %llu\n", thread_id, len, (unsigned long long) time_base, (unsigned long long) file_offset);
thread = load_thread (ctx, thread_id);
if (!load_data (ctx, len))
return 0;
if (subtype == TYPE_GC_RESIZE) {
uint64_t new_size = decode_uleb128 (p, &p);
if (debug)
- fprintf (outfile, "gc heap resized to %llu\n", new_size);
+ fprintf (outfile, "gc heap resized to %llu\n", (unsigned long long) new_size);
gc_resizes++;
if (new_size > max_heap_size)
max_heap_size = new_size;
uint64_t ev = decode_uleb128 (p, &p);
int gen = decode_uleb128 (p, &p);
if (debug)
- fprintf (outfile, "gc event for gen%d: %s at %llu (thread: 0x%x)\n", gen, gc_event_name (ev), time_base, thread->thread_id);
+ fprintf (outfile, "gc event for gen%d: %s at %llu (thread: 0x%zx)\n", gen, gc_event_name (ev), (unsigned long long) time_base, thread->thread_id);
if (gen > 2) {
fprintf (outfile, "incorrect gc gen: %d\n", gen);
break;
return 0;
}
if (debug)
- fprintf (outfile, "loaded class %p (%s in %p) at %llu\n", (void*)(ptr_base + ptrdiff), p, (void*)(ptr_base + imptrdiff), time_base);
+ 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)
add_class (ptr_base + ptrdiff, (char*)p);
while (*p) p++;
return 0;
}
if (debug)
- fprintf (outfile, "loaded image %p (%s) at %llu\n", (void*)(ptr_base + ptrdiff), p, time_base);
+ fprintf (outfile, "loaded image %p (%s) at %llu\n", (void*)(ptr_base + ptrdiff), p, (unsigned long long) time_base);
if (!error)
add_image (ptr_base + ptrdiff, (char*)p);
while (*p) p++;
LOG_TIME (time_base, tdiff);
time_base += tdiff;
if (debug)
- fprintf (outfile, "alloced object %p, size %llu (%s) at %llu\n", (void*)OBJ_ADDR (objdiff), len, lookup_class (ptr_base + ptrdiff)->name, time_base);
+ fprintf (outfile, "alloced object %p, size %llu (%s) at %llu\n", (void*)OBJ_ADDR (objdiff), (unsigned long long) len, lookup_class (ptr_base + ptrdiff)->name, (unsigned long long) time_base);
if (has_bt) {
num_bt = 8;
frames = decode_bt (sframes, &num_bt, p, &p, ptr_base);
case TYPE_HEAP: {
int subtype = *p & 0xf0;
if (subtype == TYPE_HEAP_OBJECT) {
- HeapObjectDesc *ho;
+ HeapObjectDesc *ho = NULL;
int i;
intptr_t objdiff = decode_sleb128 (p + 1, &p);
intptr_t ptrdiff = decode_sleb128 (p, &p);
uint64_t size = decode_uleb128 (p, &p);
uintptr_t num = decode_uleb128 (p, &p);
- uintptr_t ref_offset;
+ uintptr_t ref_offset = 0;
uintptr_t last_obj_offset = 0;
ClassDesc *cd = lookup_class (ptr_base + ptrdiff);
if (size) {
track_obj_reference (OBJ_ADDR (obj1diff), OBJ_ADDR (objdiff), cd);
}
if (debug && size)
- fprintf (outfile, "traced object %p, size %llu (%s), refs: %d\n", (void*)OBJ_ADDR (objdiff), size, cd->name, num);
+ fprintf (outfile, "traced object %p, size %llu (%s), refs: %zd\n", (void*)OBJ_ADDR (objdiff), (unsigned long long) size, cd->name, num);
} else if (subtype == TYPE_HEAP_ROOT) {
uintptr_t num = decode_uleb128 (p + 1, &p);
- uintptr_t gc_num = decode_uleb128 (p, &p);
+ uintptr_t gc_num G_GNUC_UNUSED = decode_uleb128 (p, &p);
int i;
for (i = 0; i < num; ++i) {
intptr_t objdiff = decode_sleb128 (p, &p);
}
break;
}
+ case TYPE_RUNTIME: {
+ int subtype = *p & 0xf0;
+ uint64_t tdiff = decode_uleb128 (p + 1, &p);
+ LOG_TIME (time_base, tdiff);
+ time_base += tdiff;
+ if (subtype == TYPE_JITHELPER) {
+ int type = decode_uleb128 (p, &p);
+ intptr_t codediff = decode_sleb128 (p, &p);
+ int codelen = decode_uleb128 (p, &p);
+ const char *name;
+ if (type == MONO_PROFILER_CODE_BUFFER_SPECIFIC_TRAMPOLINE) {
+ name = (void*)p;
+ while (*p) p++;
+ p++;
+ } else {
+ name = code_buffer_desc (type);
+ }
+ num_jit_helpers++;
+ jit_helpers_code_size += codelen;
+ if (debug)
+ fprintf (outfile, "jit helper %s, size: %d, code: %p\n", name, codelen, (void*)(ptr_base + codediff));
+ }
+ break;
+ }
case TYPE_SAMPLE: {
int subtype = *p & 0xf0;
if (subtype == TYPE_SAMPLE_HIT) {
/* un unmanaged binary loaded in memory */
uint64_t tdiff = decode_uleb128 (p + 1, &p);
uintptr_t addr = decode_sleb128 (p, &p);
- uint64_t offset = decode_uleb128 (p, &p);
+ uint64_t offset G_GNUC_UNUSED = decode_uleb128 (p, &p);
uintptr_t size = decode_uleb128 (p, &p);
char *name;
LOG_TIME (time_base, tdiff);
} else if (subtype == TYPE_SAMPLE_COUNTERS_DESC) {
uint64_t i, len = decode_uleb128 (p + 1, &p);
for (i = 0; i < len; i++) {
+ uint64_t type, unit, variance, index;
uint64_t section = decode_uleb128 (p, &p);
- char *name = pstrdup ((char*)p);
+ char *section_str, *name;
+ if (section != MONO_COUNTER_PERFCOUNTERS) {
+ section_str = (char*) section_name (section);
+ } else {
+ section_str = pstrdup ((char*)p);
+ while (*p++);
+ }
+ name = pstrdup ((char*)p);
while (*p++);
- uint64_t type = decode_uleb128 (p, &p);
- uint64_t unit = decode_uleb128 (p, &p);
- uint64_t variance = decode_uleb128 (p, &p);
- uint64_t index = decode_uleb128 (p, &p);
- add_counter ((int)section, name, (int)type, (int)unit, (int)variance, (int)index);
+ type = decode_uleb128 (p, &p);
+ unit = decode_uleb128 (p, &p);
+ variance = decode_uleb128 (p, &p);
+ index = decode_uleb128 (p, &p);
+ add_counter (section_str, name, (int)type, (int)unit, (int)variance, (int)index);
}
} else if (subtype == TYPE_SAMPLE_COUNTERS) {
int i;
uint64_t timestamp = decode_uleb128 (p + 1, &p);
uint64_t time_between = timestamp / 1000 * 1000 * 1000 * 1000 + startup_time;
while (1) {
- uint64_t index = decode_uleb128 (p, &p);
+ uint64_t type, index = decode_uleb128 (p, &p);
if (index == 0)
break;
}
}
- uint64_t type = decode_uleb128 (p, &p);
+ type = decode_uleb128 (p, &p);
value = calloc (1, sizeof (CounterValue));
value->timestamp = timestamp;
}
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 = (void *)p; while (*p) p++; p++;
+ klass = (void *)p; while (*p) p++; p++;
+ name = (void *)p; while (*p) p++; p++;
+ sig = (void *)p; while (*p) p++; p++;
+ filename = (void *)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 = (void *)p; while (*p) p++; p++;
+ guid = (void *)p; while (*p) p++; p++;
+ filename = (void *)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 = (void *)p; while (*p) p++; p++;
+ class_name = (void *)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 + %d (len: %d\n)\n", *p, file_offset, p - ctx->buf, len);
+ 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);
}
}
bt = traces->traces [j].bt;
if (!bt->count)
continue;
- fprintf (outfile, "\t%llu %s from:\n", traces->traces [j].count, desc);
+ fprintf (outfile, "\t%llu %s from:\n", (unsigned long long) traces->traces [j].count, desc);
for (k = 0; k < bt->count; ++k)
fprintf (outfile, "\t\t%s\n", bt->methods [k]->name);
}
{
int i;
fprintf (outfile, "\nException summary\n");
- fprintf (outfile, "\tThrows: %llu\n", throw_count);
+ fprintf (outfile, "\tThrows: %llu\n", (unsigned long long) throw_count);
dump_traces (&exc_traces, "throws");
for (i = 0; i <= MONO_EXCEPTION_CLAUSE_FAULT; ++i) {
if (!clause_summary [i])
continue;
- fprintf (outfile, "\tExecuted %s clauses: %llu\n", clause_name (i), clause_summary [i]);
+ fprintf (outfile, "\tExecuted %s clauses: %llu\n", clause_name (i), (unsigned long long) clause_summary [i]);
}
}
mdesc->wait_time/1000000000.0, mdesc->max_wait_time/1000000000.0, mdesc->wait_time/1000000000.0/mdesc->contentions);
dump_traces (&mdesc->traces, "contentions");
}
- fprintf (outfile, "\tLock contentions: %llu\n", monitor_contention);
- fprintf (outfile, "\tLock acquired: %llu\n", monitor_acquired);
- fprintf (outfile, "\tLock failures: %llu\n", monitor_failed);
+ fprintf (outfile, "\tLock contentions: %llu\n", (unsigned long long) monitor_contention);
+ fprintf (outfile, "\tLock acquired: %llu\n", (unsigned long long) monitor_acquired);
+ fprintf (outfile, "\tLock failures: %llu\n", (unsigned long long) monitor_failed);
}
static void
int i;
fprintf (outfile, "\nGC summary\n");
fprintf (outfile, "\tGC resizes: %d\n", gc_resizes);
- fprintf (outfile, "\tMax heap size: %llu\n", max_heap_size);
- fprintf (outfile, "\tObject moves: %llu\n", gc_object_moves);
+ fprintf (outfile, "\tMax heap size: %llu\n", (unsigned long long) max_heap_size);
+ fprintf (outfile, "\tObject moves: %llu\n", (unsigned long long) gc_object_moves);
for (i = 0; i < 3; ++i) {
if (!gc_info [i].count)
continue;
fprintf (outfile, "\tGen%d collections: %d, max time: %lluus, total time: %lluus, average: %lluus\n",
- i, gc_info [i].count, gc_info [i].max_time / 1000, gc_info [i].total_time / 1000,
- gc_info [i].total_time / gc_info [i].count / 1000);
+ i, gc_info [i].count,
+ (unsigned long long) (gc_info [i].max_time / 1000),
+ (unsigned long long) (gc_info [i].total_time / 1000),
+ (unsigned long long) (gc_info [i].total_time / gc_info [i].count / 1000));
}
for (i = 0; i < 3; ++i) {
if (!handle_info [i].max_live)
continue;
fprintf (outfile, "\tGC handles %s: created: %llu, destroyed: %llu, max: %llu\n",
- get_handle_name (i), handle_info [i].created, handle_info [i].destroyed, handle_info [i].max_live);
+ get_handle_name (i),
+ (unsigned long long) (handle_info [i].created),
+ (unsigned long long) (handle_info [i].destroyed),
+ (unsigned long long) (handle_info [i].max_live));
dump_traces (&handle_info [i].traces, "created");
}
}
}
fprintf (outfile, "\tCompiled methods: %d\n", compiled_methods);
fprintf (outfile, "\tGenerated code size: %d\n", code_size);
+ fprintf (outfile, "\tJIT helpers: %d\n", num_jit_helpers);
+ fprintf (outfile, "\tJIT helpers code size: %d\n", jit_helpers_code_size);
}
static void
fprintf (outfile, "\nAllocation summary\n");
fprintf (outfile, "%10s %10s %8s Type name\n", "Bytes", "Count", "Average");
}
- fprintf (outfile, "%10llu %10d %8llu %s\n", cd->alloc_size, cd->allocs, cd->alloc_size / cd->allocs, cd->name);
+ fprintf (outfile, "%10llu %10zd %8llu %s\n",
+ (unsigned long long) (cd->alloc_size),
+ cd->allocs,
+ (unsigned long long) (cd->alloc_size / cd->allocs),
+ cd->name);
dump_traces (&cd->traces, "bytes");
}
if (allocs)
- fprintf (outfile, "Total memory allocated: %llu bytes in %d objects\n", size, allocs);
+ fprintf (outfile, "Total memory allocated: %llu bytes in %zd objects\n", (unsigned long long) size, allocs);
}
enum {
fprintf (outfile, "\nMethod call summary\n");
fprintf (outfile, "%8s %8s %10s Method name\n", "Total(ms)", "Self(ms)", "Calls");
}
- fprintf (outfile, "%8llu %8llu %10llu %s\n", msecs, smsecs, cd->calls, cd->name);
+ fprintf (outfile, "%8llu %8llu %10llu %s\n",
+ (unsigned long long) (msecs),
+ (unsigned long long) (smsecs),
+ (unsigned long long) (cd->calls),
+ cd->name);
dump_traces (&cd->traces, "calls");
}
if (calls)
- fprintf (outfile, "Total calls: %llu\n", calls);
+ fprintf (outfile, "Total calls: %llu\n", (unsigned long long) calls);
}
static int
return;
for (j = 0; j < count; ++j) {
HeapClassDesc *cd = revs [j].klass;
- fprintf (outfile, "\t\t%llu references from: %s\n", revs [j].count, cd->klass->name);
+ fprintf (outfile, "\t\t%llu references from: %s\n",
+ (unsigned long long) (revs [j].count),
+ cd->klass->name);
}
}
}
hs->sorted = sorted;
qsort (sorted, ccount, sizeof (void*), compare_heap_class);
- fprintf (outfile, "\n\tHeap shot %d at %.3f secs: size: %llu, object count: %llu, class count: %d, roots: %d\n",
- hs_num, (hs->timestamp - startup_time)/1000000000.0, size, count, ccount, hs->num_roots);
+ fprintf (outfile, "\n\tHeap shot %d at %.3f secs: size: %llu, object count: %llu, class count: %d, roots: %zd\n",
+ hs_num,
+ (hs->timestamp - startup_time)/1000000000.0,
+ (unsigned long long) (size),
+ (unsigned long long) (count),
+ ccount, hs->num_roots);
if (!verbose && ccount > 30)
ccount = 30;
fprintf (outfile, "\t%10s %10s %8s Class name\n", "Bytes", "Count", "Average");
cd = sorted [i];
if (last_hs)
ocd = heap_class_lookup (last_hs, cd->klass);
- fprintf (outfile, "\t%10llu %10llu %8llu %s", cd->total_size, cd->count, cd->total_size / cd->count, cd->klass->name);
+ fprintf (outfile, "\t%10llu %10llu %8llu %s",
+ (unsigned long long) (cd->total_size),
+ (unsigned long long) (cd->count),
+ (unsigned long long) (cd->total_size / cd->count),
+ cd->klass->name);
if (ocd) {
int64_t bdiff = cd->total_size - ocd->total_size;
int64_t cdiff = cd->count - ocd->count;
- fprintf (outfile, " (bytes: %+lld, count: %+lld)\n", bdiff, cdiff);
+ fprintf (outfile, " (bytes: %+lld, count: %+lld)\n", (long long) bdiff, (long long) cdiff);
} else {
fprintf (outfile, "\n");
}
assert (cd->rev_count == k);
qsort (rev_sorted, cd->rev_count, sizeof (HeapClassRevRef), compare_rev_class);
if (cd->root_references)
- fprintf (outfile, "\t\t%d root references (%d pinning)\n", cd->root_references, cd->pinned_references);
+ fprintf (outfile, "\t\t%zd root references (%zd pinning)\n", cd->root_references, cd->pinned_references);
dump_rev_claases (rev_sorted, cd->rev_count);
free (rev_sorted);
}
}
}
+/* 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 = 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 = g_hash_table_lookup (coverage_assembly_classes, assembly->name);
+ if (classes) {
+ for (guint j = 0; j < classes->len; j++) {
+ CoverageClass *klass = 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 = 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 = 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;
+ }
+}
+
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_counters ();
continue;
}
+ if ((opt = match_option (p, "coverage")) != p) {
+ if (!parse_only)
+ dump_coverage ();
+ 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;
}
-