Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix up Code Coverage support #909

Merged
merged 9 commits into from
Jan 9, 2019
3 changes: 0 additions & 3 deletions mono/metadata/debug-internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,4 @@ mono_debug_free_method_async_debug_info (MonoDebugMethodAsyncInfo *info);
gboolean
mono_debug_image_has_debug_info (MonoImage *image);

MonoDebugSourceLocation *
mono_debug_lookup_source_location_by_il (MonoMethod *method, guint32 il_offset, MonoDomain *domain);

#endif /* __DEBUG_INTERNALS_H__ */
1 change: 1 addition & 0 deletions mono/metadata/debug-mono-ppdb.c
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@ mono_ppdb_lookup_location (MonoDebugMethodInfo *minfo, uint32_t offset)
location = g_new0 (MonoDebugSourceLocation, 1);
location->source_file = docname;
location->row = start_line;
location->column = start_col;
location->il_offset = iloffset;

return location;
Expand Down
3 changes: 3 additions & 0 deletions mono/metadata/domain.c
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,7 @@ mono_domain_create (void)
#endif

mono_debug_domain_create (domain);
mono_profiler_coverage_domain_init (domain);

if (create_domain_hook)
create_domain_hook (domain);
Expand Down Expand Up @@ -1270,6 +1271,8 @@ mono_domain_free (MonoDomain *domain, gboolean force)

if (domain == mono_root_domain)
mono_root_domain = NULL;

mono_profiler_coverage_domain_free(domain);
}

/**
Expand Down
3 changes: 3 additions & 0 deletions mono/metadata/mono-debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,9 @@ mono_debug_method_lookup_location (MonoDebugMethodInfo *minfo, int il_offset);
MONO_API MonoDebugSourceLocation *
mono_debug_lookup_source_location (MonoMethod *method, uint32_t address, MonoDomain *domain);

MONO_API MonoDebugSourceLocation *
mono_debug_lookup_source_location_by_il (MonoMethod *method, uint32_t il_offset, MonoDomain *domain);

MONO_API int32_t
mono_debug_il_offset_from_address (MonoMethod *method, MonoDomain *domain, uint32_t native_offset);

Expand Down
15 changes: 13 additions & 2 deletions mono/metadata/profiler-private.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ typedef struct {

gboolean code_coverage;
mono_mutex_t coverage_mutex;
GHashTable *coverage_hash;
MonoDomainCoverage *coverage_domains;

MonoProfilerHandle sampling_owner;
MonoSemType sampling_semaphore;
Expand Down Expand Up @@ -112,7 +112,18 @@ mono_profiler_installed (void)
return !!mono_profiler_state.profilers;
}

MonoProfilerCoverageInfo *mono_profiler_coverage_alloc (MonoMethod *method, guint32 entries);
MonoProfilerCoverageInfo *mono_profiler_coverage_alloc (MonoDomain* domain, MonoMethod *method, guint32 entries);

struct _MonoDomainCoverage
{
MonoDomain* domain;
GHashTable *coverage_hash;
mono_mutex_t mutex;
MonoDomainCoverage *next;
};

void mono_profiler_coverage_domain_init (MonoDomain* domain);
void mono_profiler_coverage_domain_free (MonoDomain* domain);

struct _MonoProfilerCallContext {
/*
Expand Down
237 changes: 212 additions & 25 deletions mono/metadata/profiler.c
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,6 @@ mono_profiler_enable_coverage (void)
return FALSE;

mono_os_mutex_init (&mono_profiler_state.coverage_mutex);
mono_profiler_state.coverage_hash = g_hash_table_new (NULL, NULL);

if (!mono_debug_enabled ())
mono_debug_init (MONO_DEBUG_FORMAT_MONO);
Expand Down Expand Up @@ -279,17 +278,89 @@ mono_profiler_set_coverage_filter_callback (MonoProfilerHandle handle, MonoProfi
}

static void
coverage_lock (void)
coverage_domains_lock (void)
{
mono_os_mutex_lock (&mono_profiler_state.coverage_mutex);
}

static void
coverage_unlock (void)
coverage_domains_unlock (void)
{
mono_os_mutex_unlock (&mono_profiler_state.coverage_mutex);
}

static MonoDomainCoverage *
get_coverage_for_domain(MonoDomain* domain)
{
coverage_domains_lock();
MonoDomainCoverage* cov = mono_profiler_state.coverage_domains;
while (cov)
{
if (cov->domain == domain)
break;
cov = cov->next;
}
coverage_domains_unlock();
return cov;
}

void
mono_profiler_coverage_domain_init(MonoDomain* domain)
{
if (!mono_profiler_state.code_coverage)
return;

MonoDomainCoverage* cov = g_new0(MonoDomainCoverage, 1);
cov->domain = domain;
cov->coverage_hash = g_hash_table_new(NULL, NULL);
mono_os_mutex_init(&cov->mutex);

coverage_domains_lock();
cov->next = mono_profiler_state.coverage_domains;
mono_profiler_state.coverage_domains = cov;
coverage_domains_unlock();
}

void
mono_profiler_coverage_domain_free(MonoDomain* domain)
{
if (!mono_profiler_state.code_coverage)
return;

coverage_domains_lock();

MonoDomainCoverage* cov = mono_profiler_state.coverage_domains;
MonoDomainCoverage** prev = &mono_profiler_state.coverage_domains;
while (cov)
{
if (cov->domain == domain)
break;

prev = &cov->next;
cov = cov->next;
}

if (cov != NULL)
{
*prev = cov->next;

GHashTableIter iter;
g_hash_table_iter_init (&iter, cov->coverage_hash);

MonoProfilerCoverageInfo *info;
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info))
g_free (info);

g_hash_table_destroy(cov->coverage_hash);

mono_os_mutex_destroy(&cov->mutex);

g_free(cov);
}

coverage_domains_unlock();
}

/**
* mono_profiler_get_coverage_data:
*
Expand All @@ -313,11 +384,13 @@ mono_profiler_get_coverage_data (MonoProfilerHandle handle, MonoMethod *method,
if ((method->flags & METHOD_ATTRIBUTE_ABSTRACT) || (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) || (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) || (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL))
return FALSE;

coverage_lock ();
MonoDomainCoverage* domain = get_coverage_for_domain(mono_domain_get());

mono_os_mutex_lock(&domain->mutex);

MonoProfilerCoverageInfo *info = g_hash_table_lookup (mono_profiler_state.coverage_hash, method);
MonoProfilerCoverageInfo *info = g_hash_table_lookup (domain->coverage_hash, method);

coverage_unlock ();
mono_os_mutex_unlock(&domain->mutex);

MonoError error;
MonoMethodHeader *header = mono_method_get_header_checked (method, &error);
Expand Down Expand Up @@ -407,10 +480,137 @@ mono_profiler_get_coverage_data (MonoProfilerHandle handle, MonoMethod *method,

return TRUE;
}

typedef struct
{
MonoProfilerCoverageCallback cb;
MonoProfilerHandle handle;
} InvokeCallbackInfo;

static void invoke_coverage_callback_for_hashtable_entry (gpointer key, gpointer value, gpointer user_data)
{
InvokeCallbackInfo* invokeInfo = (InvokeCallbackInfo*) user_data;
MonoMethod* method = (MonoMethod*)key;
MonoProfilerCoverageInfo *info = (MonoProfilerCoverageInfo*)value;

MonoError error;
MonoMethodHeader *header = mono_method_get_header_checked (method, &error);
mono_error_assert_ok (&error);

guint32 size;

const unsigned char *start = mono_method_header_get_code (header, &size, NULL);
const unsigned char *end = start + size;
MonoDebugMethodInfo *minfo = mono_debug_lookup_method (method);

for (guint32 i = 0; i < info->entries; i++) {
guchar *cil_code = info->data [i].cil_code;

if (cil_code && cil_code >= start && cil_code < end) {
guint32 offset = cil_code - start;

MonoProfilerCoverageData data = {
.method = method,
.il_offset = offset,
.counter = info->data [i].count,
.line = 1,
.column = 1,
};

if (minfo) {
MonoDebugSourceLocation *loc = mono_debug_method_lookup_location (minfo, offset);

if (loc) {
data.file_name = g_strdup (loc->source_file);
data.line = loc->row;
data.column = loc->column;

mono_debug_free_source_location (loc);
}
}

invokeInfo->cb (invokeInfo->handle->prof, &data);

g_free ((char *) data.file_name);
}
}

mono_metadata_free_mh (header);
}

mono_bool
mono_profiler_get_all_coverage_data(MonoProfilerHandle handle, MonoProfilerCoverageCallback cb)
{
if (!mono_profiler_state.code_coverage)
return FALSE;

InvokeCallbackInfo info;
info.cb = cb;
info.handle = handle;

MonoDomainCoverage* domain = get_coverage_for_domain(mono_domain_get());

mono_os_mutex_lock(&domain->mutex);

g_hash_table_foreach (domain->coverage_hash, invoke_coverage_callback_for_hashtable_entry, &info);

mono_os_mutex_unlock(&domain->mutex);

return TRUE;
}

mono_bool
mono_profiler_reset_coverage(MonoMethod* method)
{
if (!mono_profiler_state.code_coverage)
return FALSE;

if ((method->flags & METHOD_ATTRIBUTE_ABSTRACT) || (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) || (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) || (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL))
return FALSE;

MonoDomainCoverage* domain = get_coverage_for_domain(mono_domain_get());

mono_os_mutex_lock(&domain->mutex);

MonoProfilerCoverageInfo *info = g_hash_table_lookup (domain->coverage_hash, method);

mono_os_mutex_unlock(&domain->mutex);

if (!info)
return TRUE;

for (guint32 i = 0; i < info->entries; i++)
info->data[i].count = 0;

return TRUE;
}

static void reset_coverage_for_hashtable_entry (gpointer key, gpointer value, gpointer user_data)
{
MonoProfilerCoverageInfo *info = (MonoProfilerCoverageInfo*)value;

for (guint32 i = 0; i < info->entries; i++)
info->data[i].count = 0;
}

void mono_profiler_reset_all_coverage()
{
if (!mono_profiler_state.code_coverage)
return;

MonoDomainCoverage* domain = get_coverage_for_domain(mono_domain_get());

mono_os_mutex_lock(&domain->mutex);

g_hash_table_foreach (domain->coverage_hash, reset_coverage_for_hashtable_entry, NULL);

mono_os_mutex_unlock(&domain->mutex);
}

#endif

MonoProfilerCoverageInfo *
mono_profiler_coverage_alloc (MonoMethod *method, guint32 entries)
mono_profiler_coverage_alloc (MonoDomain *domain, MonoMethod *method, guint32 entries)
{
if (!mono_profiler_state.code_coverage)
return FALSE;
Expand All @@ -430,15 +630,17 @@ mono_profiler_coverage_alloc (MonoMethod *method, guint32 entries)
if (!cover)
return NULL;

coverage_lock ();
MonoDomainCoverage* covdomain = get_coverage_for_domain(domain);

mono_os_mutex_lock(&covdomain->mutex);

MonoProfilerCoverageInfo *info = g_malloc0 (sizeof (MonoProfilerCoverageInfo) + SIZEOF_VOID_P * 2 * entries);

info->entries = entries;

g_hash_table_insert (mono_profiler_state.coverage_hash, method, info);
g_hash_table_insert (covdomain->coverage_hash, method, info);

coverage_unlock ();
mono_os_mutex_unlock(&covdomain->mutex);

return info;
}
Expand Down Expand Up @@ -837,21 +1039,6 @@ mono_profiler_cleanup (void)
g_free (cur);
}

if (mono_profiler_state.code_coverage) {
mono_os_mutex_destroy (&mono_profiler_state.coverage_mutex);

GHashTableIter iter;

g_hash_table_iter_init (&iter, mono_profiler_state.coverage_hash);

MonoProfilerCoverageInfo *info;

while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info))
g_free (info);

g_hash_table_destroy (mono_profiler_state.coverage_hash);
}

if (mono_profiler_state.sampling_owner)
mono_os_sem_destroy (&mono_profiler_state.sampling_semaphore);
}
Expand Down
6 changes: 6 additions & 0 deletions mono/metadata/profiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,19 @@ typedef struct {
uint32_t column;
} MonoProfilerCoverageData;

typedef struct _MonoDomainCoverage MonoDomainCoverage;

typedef mono_bool (*MonoProfilerCoverageFilterCallback) (MonoProfiler *prof, MonoMethod *method);
typedef void (*MonoProfilerCoverageCallback) (MonoProfiler *prof, const MonoProfilerCoverageData *data);

MONO_API mono_bool mono_profiler_enable_coverage (void);
MONO_API void mono_profiler_set_coverage_filter_callback (MonoProfilerHandle handle, MonoProfilerCoverageFilterCallback cb);
#ifndef RUNTIME_IL2CPP
MONO_API mono_bool mono_profiler_get_coverage_data (MonoProfilerHandle handle, MonoMethod *method, MonoProfilerCoverageCallback cb);
MONO_API mono_bool mono_profiler_get_all_coverage_data (MonoProfilerHandle handle, MonoProfilerCoverageCallback cb);

MONO_API mono_bool mono_profiler_reset_coverage (MonoMethod* method);
MONO_API void mono_profiler_reset_all_coverage (void);
#endif

typedef enum {
Expand Down
2 changes: 1 addition & 1 deletion mono/mini/method-to-ir.c
Original file line number Diff line number Diff line change
Expand Up @@ -7255,7 +7255,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
}

if (cfg->method == method)
cfg->coverage_info = mono_profiler_coverage_alloc (cfg->method, header->code_size);
cfg->coverage_info = mono_profiler_coverage_alloc (cfg->domain, cfg->method, header->code_size);
if (cfg->compile_aot && cfg->coverage_info)
g_error ("Coverage profiling is not supported with AOT.");

Expand Down