Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

392 lines (314 sloc) 9.802 kb
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <glib.h>
#include <mono/metadata/class.h>
#include <mono/metadata/assembly.h>
#include <mono/metadata/debug-helpers.h>
#include <mono/metadata/profiler.h>
struct _MonoProfiler {
/* Contains the methods for which we have coverage data */
GHashTable *methods;
/* A list of classes for which we are collecting coverage data */
GHashTable *classes;
/* A list of assemblies for which we are collecting coverage data */
GHashTable *assemblies;
char *outfile_name;
GPtrArray *filters;
GPtrArray *filters_as_str;
GHashTable *filtered_classes;
FILE *outfile;
};
static char
*parse_generic_type_names(char *string);
static void
add_filter (MonoProfiler *prof, const char *filter);
static void
assembly_load (MonoProfiler *prof, MonoAssembly *assembly, int result);
static void
coverage_shutdown (MonoProfiler *prof);
static gboolean
collect_coverage_for (MonoProfiler *prof, MonoMethod *method);
void
mono_profiler_startup (char *arg)
{
gchar **ptr;
char *filterfile_name = NULL;
gchar **args;
/* Why does the runtime passes the module name to us ? */
if (strstr (arg, ":"))
arg = strstr (arg, ":") + 1;
else
arg = NULL;
args = g_strsplit (arg ? arg : "", ",", -1);
MonoProfiler *prof = g_new0 (MonoProfiler, 1);
prof->methods = g_hash_table_new (NULL, NULL);
prof->classes = g_hash_table_new (NULL, NULL);
prof->assemblies = g_hash_table_new (NULL, NULL);
for (ptr = args; ptr && *ptr; ptr++) {
const char *arg = *ptr;
gchar *message;
if (strncmp (arg, "filterfile=", 11) == 0)
filterfile_name = g_strdup (arg + 11);
else
if (strncmp (arg, "outfile=", 8) == 0)
prof->outfile_name = g_strdup (arg + 8);
else
if (strncmp (arg, "-", 1) == 0) {
add_filter (prof, arg);
}
else if (strncmp (arg, "+", 1) == 0) {
add_filter (prof, arg);
}
else {
message = g_strdup_printf ("Unknown argument '%s'.", arg);
fprintf (stderr, "monocov | Error while processing arguments: %s\n", message);
g_free (message);
}
}
g_strfreev (args);
if (filterfile_name) {
FILE *filterfile;
filterfile = fopen (filterfile_name, "r");
if (!filterfile) {
fprintf (stderr, "coverage.c: Unable to open filter file '%s'.\n", filterfile_name);
exit (1);
}
char buf [2048];
while (fgets (buf, 2048, filterfile) != NULL) {
buf [sizeof (buf) - 1] = '\0';
if ((buf [0] == '#') || (buf [0] == '\0'))
continue;
if (buf [strlen (buf) - 1] == '\n')
buf [strlen (buf) - 1] = '\0';
add_filter (prof, buf);
}
fclose (filterfile);
}
mono_profiler_install (prof, coverage_shutdown);
mono_profiler_set_events (MONO_PROFILE_INS_COVERAGE | MONO_PROFILE_ASSEMBLY_EVENTS);
mono_profiler_install_coverage_filter (collect_coverage_for);
mono_profiler_install_assembly (NULL, assembly_load, NULL, NULL);
/* we don't deal with unloading, so disable it for now */
setenv ("MONO_NO_UNLOAD", "1", 1);
}
static void
assembly_load (MonoProfiler *prof, MonoAssembly *assembly, int result)
{
/* Unfortunately, this doesn't get called... */
}
static gboolean
collect_coverage_for (MonoProfiler *prof, MonoMethod *method)
{
int i;
char *classname;
char *fqn;
MonoMethodHeader *header;
gboolean has_positive, found;
guint32 iflags, flags, code_size;
MonoClass *klass;
MonoImage *image;
flags = mono_method_get_flags (method, &iflags);
if ((iflags & 0x1000 /*METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL*/) ||
(flags & 0x2000 /*METHOD_ATTRIBUTE_PINVOKE_IMPL*/))
return FALSE;
//if (method->wrapper_type != MONO_WRAPPER_NONE)
// return FALSE;
klass = mono_method_get_class (method);
image = mono_class_get_image (klass);
/* Hacky way of determining the executing assembly */
if (! prof->outfile_name && (strcmp (mono_method_get_name (method), "Main") == 0)) {
prof->outfile_name = g_strdup_printf ("%s.cov", mono_image_get_filename (image));
}
/* Check filters */
if (prof->filters) {
/* Check already filtered classes first */
if (g_hash_table_lookup (prof->filtered_classes, klass))
return FALSE;
classname = mono_type_get_name (mono_class_get_type (klass));
fqn = g_strdup_printf ("[%s]%s", mono_image_get_name (image), classname);
// Check positive filters first
has_positive = FALSE;
found = FALSE;
for (i = 0; i < prof->filters->len; ++i) {
char *filter = g_ptr_array_index (prof->filters_as_str, i);
if (filter [0] == '+') {
filter = &filter [1];
if (strstr (fqn, filter) != NULL)
found = TRUE;
has_positive = TRUE;
}
}
if (has_positive && !found)
return FALSE;
for (i = 0; i < prof->filters->len; ++i) {
// Is substring search suffices ???
// GPatternSpec *spec = g_ptr_array_index (filters, i);
// if (g_pattern_match_string (spec, classname)) {
char *filter = g_ptr_array_index (prof->filters_as_str, i);
if (filter [0] == '+')
continue;
// Skip '-'
filter = &filter [1];
if (strstr (fqn, filter) != NULL) {
g_hash_table_insert (prof->filtered_classes, klass, klass);
return FALSE;
}
}
g_free (fqn);
g_free (classname);
}
header = mono_method_get_header (method);
mono_method_header_get_code (header, &code_size, NULL);
if (code_size > 20000) {
exit (1);
g_warning ("Unable to instrument method %s:%s since it is too complex.", mono_class_get_name (klass), mono_method_get_name (method));
return FALSE;
}
g_hash_table_insert (prof->methods, method, method);
g_hash_table_insert (prof->classes, klass, klass);
g_hash_table_insert (prof->assemblies, mono_image_get_assembly (image), mono_image_get_assembly (image));
return TRUE;
}
static void
add_filter (MonoProfiler *prof, const char *filter)
{
GPatternSpec *spec;
if (prof->filters == NULL) {
prof->filters = g_ptr_array_new ();
prof->filters_as_str = g_ptr_array_new ();
prof->filtered_classes = g_hash_table_new (NULL, NULL);
}
spec = NULL; /* compile a pattern later */
g_ptr_array_add (prof->filters, spec);
g_ptr_array_add (prof->filters_as_str, g_strdup (filter));
}
static void
output_filters (MonoProfiler *prof, FILE *outfile)
{
int i;
if (prof->filters) {
for (i = 0; i < prof->filters_as_str->len; ++i) {
char *str = g_ptr_array_index (prof->filters_as_str, i);
fprintf (outfile, "\t<filter pattern=\"%s\"/>\n",
g_markup_escape_text (str, strlen (str)));
}
}
}
static void
output_assembly (MonoAssembly *assembly, MonoAssembly *assembly2, FILE *outfile)
{
MonoImage *image = mono_assembly_get_image (assembly);
fprintf (outfile, "\t<assembly name=\"%s\" guid=\"%s\" filename=\"%s\"/>\n",
mono_image_get_name (image), mono_image_get_guid (image), mono_image_get_filename (image));
}
static int count;
static int prev_offset;
static void
output_entry (MonoProfiler *prof, const MonoProfileCoverageEntry *entry)
{
count ++;
if ((count % 8) == 0)
fprintf (prof->outfile, "\n\t\t");
fprintf (prof->outfile, "%d %d\t", entry->iloffset - prev_offset, entry->counter);
prev_offset = entry->iloffset;
}
static char
*parse_generic_type_names(char *name)
{
char *new_name,*ret;
int within_generic_declaration=0, generic_members=1;
if( !(ret = new_name = calloc(strlen(name) * 4 + 1, sizeof(char))) )
return NULL;
do
{
switch(*name)
{
case '<':
within_generic_declaration = 1;
break;
case '>':
within_generic_declaration = 0;
if( *(name-1) != '<')
{
*new_name++ = '`';
*new_name++ = '0' + generic_members;
}
else
{
memcpy(new_name,"&lt;&gt;",8);
new_name+=8;
}
generic_members = 0;
break;
case ',':
generic_members++;
break;
default:
if(!within_generic_declaration)
*new_name++ = *name;
break;
}
}while(*name++);
return ret;
}
static void
output_method (MonoMethod *method, gpointer dummy, MonoProfiler *prof)
{
MonoMethodHeader *header;
char *classname;
char *tmpsig;
char *tmpname;
FILE *outfile;
MonoClass *klass;
MonoImage *image;
outfile = prof->outfile;
header = mono_method_get_header (method);
tmpsig = mono_signature_get_desc (mono_method_signature (method), TRUE);
tmpsig = g_markup_escape_text (tmpsig, strlen (tmpsig));
klass = mono_method_get_class (method);
classname = parse_generic_type_names (mono_type_get_name (mono_class_get_type (klass)));
image = mono_class_get_image (klass);
tmpname = (char*)mono_method_get_name (method);
tmpname = g_markup_escape_text (tmpname, strlen (tmpname));
fprintf (outfile, "\t<method assembly=\"%s\" class=\"%s\" name=\"%s (%s)\" token=\"%d\">\n",
mono_image_get_name (image),
classname, tmpname,
tmpsig, mono_method_get_token (method));
g_free (tmpsig);
g_free (tmpname);
fprintf (outfile, "\t\t");
count = 0;
prev_offset = 0;
mono_profiler_coverage_get (prof, method, output_entry);
fprintf (outfile, "\n");
fprintf (outfile, "\t</method>\n");
}
static void
coverage_shutdown (MonoProfiler *prof)
{
FILE *outfile;
if (!prof->outfile_name)
prof->outfile_name = g_strdup ("/dev/stdout");
printf ("Dumping coverage data to %s ...\n", prof->outfile_name);
outfile = fopen (prof->outfile_name, "w");
if (!outfile) {
fprintf (stderr, "coverage: unable to create result file %s: %s.\n",
prof->outfile_name, strerror (errno));
return;
}
prof->outfile = outfile;
fprintf (outfile, "<?xml version=\"1.0\"?>\n");
fprintf (outfile, "<coverage version=\"%s\">\n", VERSION);
/*
* The UI doesn't deal well with this enabled.
* output_filters (prof, outfile);
*/
g_hash_table_foreach (prof->assemblies, (GHFunc)output_assembly, outfile);
g_hash_table_foreach (prof->methods, (GHFunc)output_method, prof);
fprintf (outfile, "</coverage>\n");
fclose (outfile);
printf ("Done.\n");
}
Jump to Line
Something went wrong with that request. Please try again.