[profiler] Limit the number of allocated sample hit structures.
authorAndi McClure <andi.mcclure@xamarin.com>
Sat, 9 Apr 2016 01:16:10 +0000 (03:16 +0200)
committerRodrigo Kumpera <kumpera@gmail.com>
Tue, 19 Apr 2016 21:34:46 +0000 (14:34 -0700)
man/mprof-report.1
mono/profiler/proflog.c

index 71b3e0dfd7565d046c7c086ba69e4be4bc6b388d..af61efa37cb84a91b735581972a4f8dec8565285 100644 (file)
@@ -171,6 +171,24 @@ TIMER can have the following values:
 collect \f[I]NUM\f[] frames at the most.
 The default is 8.
 .IP \[bu] 2
+\f[I]maxsamples=NUM\f[]: stop allocating reusable sample events
+once \f[I]NUM\f[] events have been allocated (a value of zero for
+all intents and purposes means unlimited). By default, the value
+of this setting is the number of CPU cores multiplied by 1000. This
+is usually a good enough value for typical desktop and mobile apps.
+If you're losing too many samples due to this default (which is
+possible in apps with an unusually high amount of threads), you
+may want to tinker with this value to find a good balance between
+sample hit rate and performance impact on the app. The way it works
+is that sample events are enqueued for reuse after they're flushed
+to the output file; if a thread gets a sampling signal but there are
+no sample events in the reuse queue and the profiler has reached the
+maximum number of sample allocations, the sample gets dropped. So a
+higher number for this setting will increase the chance that a
+thread is able to collect a sample, but also necessarily means that
+there will be more work done by the profiler. You can run Mono with
+the \f[I]--stats\f[] option to see statistics about sample events.
+.IP \[bu] 2
 \f[I]calldepth=NUM\f[]: ignore method enter/leave events when the
 call chain depth is bigger than NUM.
 .IP \[bu] 2
index d33ab9f2795d0426cfdc6b6d8332a0320a2b1998..9e3964f8ae55140f6412b033cc93e79028c6b434 100644 (file)
@@ -124,6 +124,7 @@ static int do_counters = 0;
 static int do_coverage = 0;
 static gboolean debug_coverage = FALSE;
 static MonoProfileSamplingMode sampling_mode = MONO_PROFILER_STAT_MODE_PROCESS;
+static int max_allocated_sample_hits;
 
 static gint32 sample_hits;
 static gint32 sample_flushes;
@@ -2036,6 +2037,13 @@ mono_sample_hit (MonoProfiler *profiler, unsigned char *ip, void *context)
        SampleHit *sample = (SampleHit *) mono_lock_free_queue_dequeue (&profiler->sample_reuse_queue);
 
        if (!sample) {
+               /*
+                * If we're out of reusable sample events and we're not allowed to
+                * allocate more, we have no choice but to drop the event.
+                */
+               if (InterlockedRead (&sample_allocations) >= max_allocated_sample_hits)
+                       return;
+
                sample = mono_lock_free_alloc (&profiler->sample_allocator);
                sample->prof = profiler;
 
@@ -2401,8 +2409,6 @@ dump_unmanaged_coderefs (MonoProfiler *prof)
        }
 }
 
-#if USE_PERF_EVENTS
-
 static int
 mono_cpu_count (void)
 {
@@ -2481,6 +2487,8 @@ mono_cpu_count (void)
        return 1;
 }
 
+#if USE_PERF_EVENTS
+
 typedef struct {
        int perf_fd;
        unsigned int prev_pos;
@@ -4678,6 +4686,8 @@ mono_profiler_startup (const char *desc)
                MONO_PROFILE_INS_COVERAGE|MONO_PROFILE_APPDOMAIN_EVENTS|MONO_PROFILE_CONTEXT_EVENTS|
                MONO_PROFILE_ASSEMBLY_EVENTS;
 
+       max_allocated_sample_hits = mono_cpu_count () * 1000;
+
        mono_counters_register ("Sample hits", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &sample_hits);
        mono_counters_register ("Sample flushes", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &sample_flushes);
        mono_counters_register ("Sample events allocated", MONO_COUNTER_UINT | MONO_COUNTER_PROFILER | MONO_COUNTER_MONOTONIC, &sample_allocations);
@@ -4785,6 +4795,14 @@ mono_profiler_startup (const char *desc)
                        notraces = num_frames == 0;
                        continue;
                }
+               if ((opt = match_option (p, "maxsamples", &val)) != p) {
+                       char *end;
+                       max_allocated_sample_hits = strtoul (val, &end, 10);
+                       if (!max_allocated_sample_hits)
+                               max_allocated_sample_hits = G_MAXINT32;
+                       free (val);
+                       continue;
+               }
                if ((opt = match_option (p, "calldepth", &val)) != p) {
                        char *end;
                        max_call_depth = strtoul (val, &end, 10);