Skip to content

Commit 24eb2f2

Browse files
committed
Fix up the Code Coverage support
Manual patch of #909 Change coverage data to be stored in a separate hash table per MonoDomain, so that we can throw away coverage data when a domain is unloaded. Support for resetting (zeroing) coverage stats for one/all methods. Support for getting coverage stats for all methods. Also fixed PPDB support to provide column information to mono_ppdb_lookup_location.
1 parent 21aa337 commit 24eb2f2

File tree

8 files changed

+240
-32
lines changed

8 files changed

+240
-32
lines changed

mono/metadata/debug-internals.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,6 @@ mono_debug_free_method_async_debug_info (MonoDebugMethodAsyncInfo *info);
106106
gboolean
107107
mono_debug_image_has_debug_info (MonoImage *image);
108108

109-
MonoDebugSourceLocation *
110-
mono_debug_lookup_source_location_by_il (MonoMethod *method, guint32 il_offset, MonoDomain *domain);
111-
112109
char*
113110
mono_debug_image_get_sourcelink (MonoImage *image);
114111

mono/metadata/domain.c

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

488488
mono_debug_domain_create (domain);
489+
mono_profiler_coverage_domain_init (domain);
489490

490491
#ifdef ENABLE_NETCORE
491492
mono_alc_create_default (domain);
@@ -1326,6 +1327,8 @@ mono_domain_free (MonoDomain *domain, gboolean force)
13261327

13271328
if (domain == mono_root_domain)
13281329
mono_root_domain = NULL;
1330+
1331+
mono_profiler_coverage_domain_free (domain);
13291332
#else
13301333
g_assert_not_reached ();
13311334
#endif

mono/metadata/mono-debug.h

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

208+
MONO_API MonoDebugSourceLocation*
209+
mono_debug_lookup_source_location_by_il (MonoMethod* method, uint32_t il_offset, MonoDomain* domain);
210+
208211
MONO_API int32_t
209212
mono_debug_il_offset_from_address (MonoMethod *method, MonoDomain *domain, uint32_t native_offset);
210213

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;
@@ -116,7 +116,18 @@ mono_profiler_installed (void)
116116
}
117117

118118
gboolean mono_profiler_coverage_instrumentation_enabled (MonoMethod *method);
119-
MonoProfilerCoverageInfo *mono_profiler_coverage_alloc (MonoMethod *method, guint32 entries);
119+
MonoProfilerCoverageInfo *mono_profiler_coverage_alloc (MonoDomain* domain, MonoMethod *method, guint32 entries);
120+
121+
struct _MonoDomainCoverage
122+
{
123+
MonoDomain* domain;
124+
GHashTable* coverage_hash;
125+
mono_mutex_t mutex;
126+
MonoDomainCoverage* next;
127+
};
128+
129+
void mono_profiler_coverage_domain_init (MonoDomain* domain);
130+
void mono_profiler_coverage_domain_free (MonoDomain* domain);
120131

121132
struct _MonoProfilerCallContext {
122133
/*

mono/metadata/profiler.c

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

265265
mono_os_mutex_init (&mono_profiler_state.coverage_mutex);
266-
mono_profiler_state.coverage_hash = g_hash_table_new (NULL, NULL);
267266

268267
if (!mono_debug_enabled ())
269268
mono_debug_init (MONO_DEBUG_FORMAT_MONO);
@@ -294,17 +293,89 @@ mono_profiler_set_coverage_filter_callback (MonoProfilerHandle handle, MonoProfi
294293
}
295294

296295
static void
297-
coverage_lock (void)
296+
coverage_domains_lock (void)
298297
{
299298
mono_os_mutex_lock (&mono_profiler_state.coverage_mutex);
300299
}
301300

302301
static void
303-
coverage_unlock (void)
302+
coverage_domains_unlock (void)
304303
{
305304
mono_os_mutex_unlock (&mono_profiler_state.coverage_mutex);
306305
}
307306

307+
static MonoDomainCoverage*
308+
get_coverage_for_domain (MonoDomain* domain)
309+
{
310+
coverage_domains_lock ();
311+
MonoDomainCoverage* cov = mono_profiler_state.coverage_domains;
312+
while (cov)
313+
{
314+
if (cov->domain == domain)
315+
break;
316+
cov = cov->next;
317+
}
318+
coverage_domains_unlock ();
319+
return cov;
320+
}
321+
322+
void
323+
mono_profiler_coverage_domain_init (MonoDomain* domain)
324+
{
325+
if (!mono_profiler_state.code_coverage)
326+
return;
327+
328+
MonoDomainCoverage* cov = g_new0 (MonoDomainCoverage, 1);
329+
cov->domain = domain;
330+
cov->coverage_hash = g_hash_table_new (NULL, NULL);
331+
mono_os_mutex_init (&cov->mutex);
332+
333+
coverage_domains_lock ();
334+
cov->next = mono_profiler_state.coverage_domains;
335+
mono_profiler_state.coverage_domains = cov;
336+
coverage_domains_unlock ();
337+
}
338+
339+
void
340+
mono_profiler_coverage_domain_free (MonoDomain* domain)
341+
{
342+
if (!mono_profiler_state.code_coverage)
343+
return;
344+
345+
coverage_domains_lock ();
346+
347+
MonoDomainCoverage* cov = mono_profiler_state.coverage_domains;
348+
MonoDomainCoverage** prev = &mono_profiler_state.coverage_domains;
349+
while (cov)
350+
{
351+
if (cov->domain == domain)
352+
break;
353+
354+
prev = &cov->next;
355+
cov = cov->next;
356+
}
357+
358+
if (cov != NULL)
359+
{
360+
*prev = cov->next;
361+
362+
GHashTableIter iter;
363+
g_hash_table_iter_init (&iter, cov->coverage_hash);
364+
365+
MonoProfilerCoverageInfo* info;
366+
while (g_hash_table_iter_next (&iter, NULL, (gpointer*)&info))
367+
g_free (info);
368+
369+
g_hash_table_destroy (cov->coverage_hash);
370+
371+
mono_os_mutex_destroy (&cov->mutex);
372+
373+
g_free (cov);
374+
}
375+
376+
coverage_domains_unlock ();
377+
}
378+
308379
/**
309380
* mono_profiler_get_coverage_data:
310381
*
@@ -328,11 +399,13 @@ mono_profiler_get_coverage_data (MonoProfilerHandle handle, MonoMethod *method,
328399
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))
329400
return FALSE;
330401

331-
coverage_lock ();
402+
MonoDomainCoverage* domain = get_coverage_for_domain (mono_domain_get ());
403+
404+
mono_os_mutex_lock (&domain->mutex);
332405

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

335-
coverage_unlock ();
408+
mono_os_mutex_unlock (&domain->mutex);
336409

337410
MonoMethodHeaderSummary header;
338411

@@ -417,6 +490,133 @@ mono_profiler_get_coverage_data (MonoProfilerHandle handle, MonoMethod *method,
417490

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

422622
gboolean
@@ -435,23 +635,25 @@ mono_profiler_coverage_instrumentation_enabled (MonoMethod *method)
435635
}
436636

437637
MonoProfilerCoverageInfo *
438-
mono_profiler_coverage_alloc (MonoMethod *method, guint32 entries)
638+
mono_profiler_coverage_alloc (MonoDomain* domain, MonoMethod *method, guint32 entries)
439639
{
440640
if (!mono_profiler_state.code_coverage)
441641
return NULL;
442642

443643
if (!mono_profiler_coverage_instrumentation_enabled (method))
444644
return NULL;
445645

446-
coverage_lock ();
646+
MonoDomainCoverage* covdomain = get_coverage_for_domain (domain);
647+
648+
mono_os_mutex_lock (&covdomain->mutex);
447649

448650
MonoProfilerCoverageInfo *info = g_malloc0 (sizeof (MonoProfilerCoverageInfo) + sizeof (MonoProfilerCoverageInfoEntry) * entries);
449651

450652
info->entries = entries;
451653

452-
g_hash_table_insert (mono_profiler_state.coverage_hash, method, info);
654+
g_hash_table_insert (covdomain->coverage_hash, method, info);
453655

454-
coverage_unlock ();
656+
mono_os_mutex_unlock (&covdomain->mutex);
455657

456658
return info;
457659
}
@@ -860,21 +1062,6 @@ mono_profiler_cleanup (void)
8601062
g_free (cur);
8611063
}
8621064

863-
if (mono_profiler_state.code_coverage) {
864-
mono_os_mutex_destroy (&mono_profiler_state.coverage_mutex);
865-
866-
GHashTableIter iter;
867-
868-
g_hash_table_iter_init (&iter, mono_profiler_state.coverage_hash);
869-
870-
MonoProfilerCoverageInfo *info;
871-
872-
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info))
873-
g_free (info);
874-
875-
g_hash_table_destroy (mono_profiler_state.coverage_hash);
876-
}
877-
8781065
if (mono_profiler_state.sampling_owner)
8791066
mono_os_sem_destroy (&mono_profiler_state.sampling_semaphore);
8801067
}

mono/metadata/profiler.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,20 @@ typedef struct {
5050
uint32_t column;
5151
} MonoProfilerCoverageData;
5252

53+
typedef struct _MonoDomainCoverage MonoDomainCoverage;
54+
5355
typedef mono_bool (*MonoProfilerCoverageFilterCallback) (MonoProfiler *prof, MonoMethod *method);
5456
typedef void (*MonoProfilerCoverageCallback) (MonoProfiler *prof, const MonoProfilerCoverageData *data);
5557

5658
MONO_API mono_bool mono_profiler_enable_coverage (void);
5759
MONO_API void mono_profiler_set_coverage_filter_callback (MonoProfilerHandle handle, MonoProfilerCoverageFilterCallback cb);
5860
#ifndef RUNTIME_IL2CPP
5961
MONO_API mono_bool mono_profiler_get_coverage_data (MonoProfilerHandle handle, MonoMethod *method, MonoProfilerCoverageCallback cb);
62+
63+
MONO_API mono_bool mono_profiler_get_all_coverage_data (MonoProfilerHandle handle, MonoProfilerCoverageCallback cb);
64+
65+
MONO_API mono_bool mono_profiler_reset_coverage (MonoMethod* method);
66+
MONO_API void mono_profiler_reset_all_coverage (void);
6067
#endif
6168

6269
typedef enum {

mono/mini/interp/transform.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8307,7 +8307,7 @@ generate (MonoMethod *method, MonoMethodHeader *header, InterpMethod *rtm, MonoG
83078307
rtm->data_items = td->data_items;
83088308

83098309
if (td->prof_coverage)
8310-
td->coverage_info = mono_profiler_coverage_alloc (method, header->code_size);
8310+
td->coverage_info = mono_profiler_coverage_alloc (rtm->domain, method, header->code_size);
83118311

83128312
interp_method_compute_offsets (td, rtm, mono_method_signature_internal (method), header, error);
83138313
goto_if_nok (error, exit);

0 commit comments

Comments
 (0)