Skip to content

Commit a1597e0

Browse files
authored
Merge pull request #909 from Unity-Technologies/feature/coverage
Fix up Code Coverage support to support domains
2 parents 14c1736 + 8d5e782 commit a1597e0

File tree

8 files changed

+239
-31
lines changed

8 files changed

+239
-31
lines changed

mono/metadata/debug-internals.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,4 @@ mono_debug_free_method_async_debug_info (MonoDebugMethodAsyncInfo *info);
8484
gboolean
8585
mono_debug_image_has_debug_info (MonoImage *image);
8686

87-
MonoDebugSourceLocation *
88-
mono_debug_lookup_source_location_by_il (MonoMethod *method, guint32 il_offset, MonoDomain *domain);
89-
9087
#endif /* __DEBUG_INTERNALS_H__ */

mono/metadata/debug-mono-ppdb.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,7 @@ mono_ppdb_lookup_location (MonoDebugMethodInfo *minfo, uint32_t offset)
370370
location = g_new0 (MonoDebugSourceLocation, 1);
371371
location->source_file = docname;
372372
location->row = start_line;
373+
location->column = start_col;
373374
location->il_offset = iloffset;
374375

375376
return location;

mono/metadata/domain.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,7 @@ mono_domain_create (void)
453453
#endif
454454

455455
mono_debug_domain_create (domain);
456+
mono_profiler_coverage_domain_init (domain);
456457

457458
if (create_domain_hook)
458459
create_domain_hook (domain);
@@ -1270,6 +1271,8 @@ mono_domain_free (MonoDomain *domain, gboolean force)
12701271

12711272
if (domain == mono_root_domain)
12721273
mono_root_domain = NULL;
1274+
1275+
mono_profiler_coverage_domain_free(domain);
12731276
}
12741277

12751278
/**

mono/metadata/mono-debug.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,9 @@ mono_debug_method_lookup_location (MonoDebugMethodInfo *minfo, int il_offset);
201201
MONO_API MonoDebugSourceLocation *
202202
mono_debug_lookup_source_location (MonoMethod *method, uint32_t address, MonoDomain *domain);
203203

204+
MONO_API MonoDebugSourceLocation *
205+
mono_debug_lookup_source_location_by_il (MonoMethod *method, uint32_t il_offset, MonoDomain *domain);
206+
204207
MONO_API int32_t
205208
mono_debug_il_offset_from_address (MonoMethod *method, MonoDomain *domain, uint32_t native_offset);
206209

mono/metadata/profiler-private.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ typedef struct {
5151

5252
gboolean code_coverage;
5353
mono_mutex_t coverage_mutex;
54-
GHashTable *coverage_hash;
54+
MonoDomainCoverage *coverage_domains;
5555

5656
MonoProfilerHandle sampling_owner;
5757
MonoSemType sampling_semaphore;
@@ -112,7 +112,18 @@ mono_profiler_installed (void)
112112
return !!mono_profiler_state.profilers;
113113
}
114114

115-
MonoProfilerCoverageInfo *mono_profiler_coverage_alloc (MonoMethod *method, guint32 entries);
115+
MonoProfilerCoverageInfo *mono_profiler_coverage_alloc (MonoDomain* domain, MonoMethod *method, guint32 entries);
116+
117+
struct _MonoDomainCoverage
118+
{
119+
MonoDomain* domain;
120+
GHashTable *coverage_hash;
121+
mono_mutex_t mutex;
122+
MonoDomainCoverage *next;
123+
};
124+
125+
void mono_profiler_coverage_domain_init (MonoDomain* domain);
126+
void mono_profiler_coverage_domain_free (MonoDomain* domain);
116127

117128
struct _MonoProfilerCallContext {
118129
/*

mono/metadata/profiler.c

Lines changed: 212 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,6 @@ mono_profiler_enable_coverage (void)
248248
return FALSE;
249249

250250
mono_os_mutex_init (&mono_profiler_state.coverage_mutex);
251-
mono_profiler_state.coverage_hash = g_hash_table_new (NULL, NULL);
252251

253252
if (!mono_debug_enabled ())
254253
mono_debug_init (MONO_DEBUG_FORMAT_MONO);
@@ -279,17 +278,89 @@ mono_profiler_set_coverage_filter_callback (MonoProfilerHandle handle, MonoProfi
279278
}
280279

281280
static void
282-
coverage_lock (void)
281+
coverage_domains_lock (void)
283282
{
284283
mono_os_mutex_lock (&mono_profiler_state.coverage_mutex);
285284
}
286285

287286
static void
288-
coverage_unlock (void)
287+
coverage_domains_unlock (void)
289288
{
290289
mono_os_mutex_unlock (&mono_profiler_state.coverage_mutex);
291290
}
292291

292+
static MonoDomainCoverage *
293+
get_coverage_for_domain(MonoDomain* domain)
294+
{
295+
coverage_domains_lock();
296+
MonoDomainCoverage* cov = mono_profiler_state.coverage_domains;
297+
while (cov)
298+
{
299+
if (cov->domain == domain)
300+
break;
301+
cov = cov->next;
302+
}
303+
coverage_domains_unlock();
304+
return cov;
305+
}
306+
307+
void
308+
mono_profiler_coverage_domain_init(MonoDomain* domain)
309+
{
310+
if (!mono_profiler_state.code_coverage)
311+
return;
312+
313+
MonoDomainCoverage* cov = g_new0(MonoDomainCoverage, 1);
314+
cov->domain = domain;
315+
cov->coverage_hash = g_hash_table_new(NULL, NULL);
316+
mono_os_mutex_init(&cov->mutex);
317+
318+
coverage_domains_lock();
319+
cov->next = mono_profiler_state.coverage_domains;
320+
mono_profiler_state.coverage_domains = cov;
321+
coverage_domains_unlock();
322+
}
323+
324+
void
325+
mono_profiler_coverage_domain_free(MonoDomain* domain)
326+
{
327+
if (!mono_profiler_state.code_coverage)
328+
return;
329+
330+
coverage_domains_lock();
331+
332+
MonoDomainCoverage* cov = mono_profiler_state.coverage_domains;
333+
MonoDomainCoverage** prev = &mono_profiler_state.coverage_domains;
334+
while (cov)
335+
{
336+
if (cov->domain == domain)
337+
break;
338+
339+
prev = &cov->next;
340+
cov = cov->next;
341+
}
342+
343+
if (cov != NULL)
344+
{
345+
*prev = cov->next;
346+
347+
GHashTableIter iter;
348+
g_hash_table_iter_init (&iter, cov->coverage_hash);
349+
350+
MonoProfilerCoverageInfo *info;
351+
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info))
352+
g_free (info);
353+
354+
g_hash_table_destroy(cov->coverage_hash);
355+
356+
mono_os_mutex_destroy(&cov->mutex);
357+
358+
g_free(cov);
359+
}
360+
361+
coverage_domains_unlock();
362+
}
363+
293364
/**
294365
* mono_profiler_get_coverage_data:
295366
*
@@ -313,11 +384,13 @@ mono_profiler_get_coverage_data (MonoProfilerHandle handle, MonoMethod *method,
313384
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))
314385
return FALSE;
315386

316-
coverage_lock ();
387+
MonoDomainCoverage* domain = get_coverage_for_domain(mono_domain_get());
388+
389+
mono_os_mutex_lock(&domain->mutex);
317390

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

320-
coverage_unlock ();
393+
mono_os_mutex_unlock(&domain->mutex);
321394

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

408481
return TRUE;
409482
}
483+
484+
typedef struct
485+
{
486+
MonoProfilerCoverageCallback cb;
487+
MonoProfilerHandle handle;
488+
} InvokeCallbackInfo;
489+
490+
static void invoke_coverage_callback_for_hashtable_entry (gpointer key, gpointer value, gpointer user_data)
491+
{
492+
InvokeCallbackInfo* invokeInfo = (InvokeCallbackInfo*) user_data;
493+
MonoMethod* method = (MonoMethod*)key;
494+
MonoProfilerCoverageInfo *info = (MonoProfilerCoverageInfo*)value;
495+
496+
MonoError error;
497+
MonoMethodHeader *header = mono_method_get_header_checked (method, &error);
498+
mono_error_assert_ok (&error);
499+
500+
guint32 size;
501+
502+
const unsigned char *start = mono_method_header_get_code (header, &size, NULL);
503+
const unsigned char *end = start + size;
504+
MonoDebugMethodInfo *minfo = mono_debug_lookup_method (method);
505+
506+
for (guint32 i = 0; i < info->entries; i++) {
507+
guchar *cil_code = info->data [i].cil_code;
508+
509+
if (cil_code && cil_code >= start && cil_code < end) {
510+
guint32 offset = cil_code - start;
511+
512+
MonoProfilerCoverageData data = {
513+
.method = method,
514+
.il_offset = offset,
515+
.counter = info->data [i].count,
516+
.line = 1,
517+
.column = 1,
518+
};
519+
520+
if (minfo) {
521+
MonoDebugSourceLocation *loc = mono_debug_method_lookup_location (minfo, offset);
522+
523+
if (loc) {
524+
data.file_name = g_strdup (loc->source_file);
525+
data.line = loc->row;
526+
data.column = loc->column;
527+
528+
mono_debug_free_source_location (loc);
529+
}
530+
}
531+
532+
invokeInfo->cb (invokeInfo->handle->prof, &data);
533+
534+
g_free ((char *) data.file_name);
535+
}
536+
}
537+
538+
mono_metadata_free_mh (header);
539+
}
540+
541+
mono_bool
542+
mono_profiler_get_all_coverage_data(MonoProfilerHandle handle, MonoProfilerCoverageCallback cb)
543+
{
544+
if (!mono_profiler_state.code_coverage)
545+
return FALSE;
546+
547+
InvokeCallbackInfo info;
548+
info.cb = cb;
549+
info.handle = handle;
550+
551+
MonoDomainCoverage* domain = get_coverage_for_domain(mono_domain_get());
552+
553+
mono_os_mutex_lock(&domain->mutex);
554+
555+
g_hash_table_foreach (domain->coverage_hash, invoke_coverage_callback_for_hashtable_entry, &info);
556+
557+
mono_os_mutex_unlock(&domain->mutex);
558+
559+
return TRUE;
560+
}
561+
562+
mono_bool
563+
mono_profiler_reset_coverage(MonoMethod* method)
564+
{
565+
if (!mono_profiler_state.code_coverage)
566+
return FALSE;
567+
568+
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))
569+
return FALSE;
570+
571+
MonoDomainCoverage* domain = get_coverage_for_domain(mono_domain_get());
572+
573+
mono_os_mutex_lock(&domain->mutex);
574+
575+
MonoProfilerCoverageInfo *info = g_hash_table_lookup (domain->coverage_hash, method);
576+
577+
mono_os_mutex_unlock(&domain->mutex);
578+
579+
if (!info)
580+
return TRUE;
581+
582+
for (guint32 i = 0; i < info->entries; i++)
583+
info->data[i].count = 0;
584+
585+
return TRUE;
586+
}
587+
588+
static void reset_coverage_for_hashtable_entry (gpointer key, gpointer value, gpointer user_data)
589+
{
590+
MonoProfilerCoverageInfo *info = (MonoProfilerCoverageInfo*)value;
591+
592+
for (guint32 i = 0; i < info->entries; i++)
593+
info->data[i].count = 0;
594+
}
595+
596+
void mono_profiler_reset_all_coverage()
597+
{
598+
if (!mono_profiler_state.code_coverage)
599+
return;
600+
601+
MonoDomainCoverage* domain = get_coverage_for_domain(mono_domain_get());
602+
603+
mono_os_mutex_lock(&domain->mutex);
604+
605+
g_hash_table_foreach (domain->coverage_hash, reset_coverage_for_hashtable_entry, NULL);
606+
607+
mono_os_mutex_unlock(&domain->mutex);
608+
}
609+
410610
#endif
411611

412612
MonoProfilerCoverageInfo *
413-
mono_profiler_coverage_alloc (MonoMethod *method, guint32 entries)
613+
mono_profiler_coverage_alloc (MonoDomain *domain, MonoMethod *method, guint32 entries)
414614
{
415615
if (!mono_profiler_state.code_coverage)
416616
return FALSE;
@@ -430,15 +630,17 @@ mono_profiler_coverage_alloc (MonoMethod *method, guint32 entries)
430630
if (!cover)
431631
return NULL;
432632

433-
coverage_lock ();
633+
MonoDomainCoverage* covdomain = get_coverage_for_domain(domain);
634+
635+
mono_os_mutex_lock(&covdomain->mutex);
434636

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

437639
info->entries = entries;
438640

439-
g_hash_table_insert (mono_profiler_state.coverage_hash, method, info);
641+
g_hash_table_insert (covdomain->coverage_hash, method, info);
440642

441-
coverage_unlock ();
643+
mono_os_mutex_unlock(&covdomain->mutex);
442644

443645
return info;
444646
}
@@ -837,21 +1039,6 @@ mono_profiler_cleanup (void)
8371039
g_free (cur);
8381040
}
8391041

840-
if (mono_profiler_state.code_coverage) {
841-
mono_os_mutex_destroy (&mono_profiler_state.coverage_mutex);
842-
843-
GHashTableIter iter;
844-
845-
g_hash_table_iter_init (&iter, mono_profiler_state.coverage_hash);
846-
847-
MonoProfilerCoverageInfo *info;
848-
849-
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info))
850-
g_free (info);
851-
852-
g_hash_table_destroy (mono_profiler_state.coverage_hash);
853-
}
854-
8551042
if (mono_profiler_state.sampling_owner)
8561043
mono_os_sem_destroy (&mono_profiler_state.sampling_semaphore);
8571044
}

mono/metadata/profiler.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,19 @@ typedef struct {
3838
uint32_t column;
3939
} MonoProfilerCoverageData;
4040

41+
typedef struct _MonoDomainCoverage MonoDomainCoverage;
42+
4143
typedef mono_bool (*MonoProfilerCoverageFilterCallback) (MonoProfiler *prof, MonoMethod *method);
4244
typedef void (*MonoProfilerCoverageCallback) (MonoProfiler *prof, const MonoProfilerCoverageData *data);
4345

4446
MONO_API mono_bool mono_profiler_enable_coverage (void);
4547
MONO_API void mono_profiler_set_coverage_filter_callback (MonoProfilerHandle handle, MonoProfilerCoverageFilterCallback cb);
4648
#ifndef RUNTIME_IL2CPP
4749
MONO_API mono_bool mono_profiler_get_coverage_data (MonoProfilerHandle handle, MonoMethod *method, MonoProfilerCoverageCallback cb);
50+
MONO_API mono_bool mono_profiler_get_all_coverage_data (MonoProfilerHandle handle, MonoProfilerCoverageCallback cb);
51+
52+
MONO_API mono_bool mono_profiler_reset_coverage (MonoMethod* method);
53+
MONO_API void mono_profiler_reset_all_coverage (void);
4854
#endif
4955

5056
typedef enum {

mono/mini/method-to-ir.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7255,7 +7255,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
72557255
}
72567256

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

0 commit comments

Comments
 (0)