libsanitizer: add filtering of sleep intervals for BackgroundThread.
[platform/upstream/linaro-gcc.git] / libsanitizer / sanitizer_common / sanitizer_common_libcdep.cc
index 3c42911..656552f 100644 (file)
@@ -23,6 +23,9 @@
 
 namespace __sanitizer {
 
+// Used externally
+SleepInterval last_sleep_int = {common_flags()->bgthread_min_sleep_ms, true};
+
 bool ReportFile::SupportsColors() {
   SpinMutexLock l(mu);
   ReopenIfNecessary();
@@ -68,6 +71,123 @@ void SetSoftRssLimitExceededCallback(void (*Callback)(bool exceeded)) {
   SoftRssLimitExceededCallback = Callback;
 }
 
+static int MinNonZeroTimeout(void)
+{
+  int T = Max(common_flags()->heap_profile_full_out_time,
+                common_flags()->heap_profile_short_out_time);
+  int t = Min(common_flags()->heap_profile_full_out_time,
+                common_flags()->heap_profile_short_out_time);
+  return t > 0 ? t : T;
+}
+
+static int Mid(uptr a, uptr b)
+{
+  int m = Min(a, b);
+  int M = Max(a, b);
+  return m + (M - m + 1) / 2;
+}
+
+// Calculate new BackgroundThread sleep duration based on previous
+// sleep intervals.
+//
+// This algorithm remembers the last several sleeps (namely 2), their
+// durations and whether or not they were followed by some interesting
+// events (i.e. delivery of memory usage statistics). Based on that criteria,
+// it considers every interval to be either interesting or uninteresting.
+// If the last sleeping interval was interesting, then we probably need to
+// sleep less, and vice versa. How much less (or more) is determined by
+// "reference points":
+//
+//   * if last and last but one intervals have different
+//     interesting/uninteresting attributes, then change is considered to be
+//     smooth, and duration of last but one interval will serve as a reference
+//     point:
+//  0                 low_ref       new                            duration, ms
+// -|------|-------------|-----------|------------|-------------|-------->
+//    low_thrhold    last_but_one               last       high_thrhold
+//                 (uninteresting)          (interesting)
+//
+//   * if 2 last intervals are both interesting (both uninteresting), then
+//     change is considered to be rapid, and low/high threshold will serve
+//     as a reference point:
+//  0                                             new    high_ref  duration, ms
+// -|------|-------------|---------------|---------|---------|----------->
+//    low_thrhold   last_but_one       last             high_thrhold
+//                (uninteresting) (uninteresting)
+//
+// High/low thresholds, in turn, are determined by both
+// bgthread_min/max_sleep_ms values and profiler timeouts (if the user wants
+// us to gather statistics every Tms, there's no sense in sleeping longer
+// than Tms).
+//
+// The algorithm also tries not to change interval duration too rapidly,
+// thus multiplying/dividing it by factor of 2 together with calculating
+// Mid() value with high/low reference points and choosing more "smooth"
+// option.
+static int CalcNewSleepInterval(void)
+{
+  static SleepInterval last_but_one_sleep_int =
+                        {common_flags()->bgthread_min_sleep_ms, true};
+
+  static const int wanted_timeout = MinNonZeroTimeout();
+  static const int high_threshold = wanted_timeout > 0 ?
+                               Min(common_flags()->bgthread_max_sleep_ms,
+                                   wanted_timeout) :
+                               common_flags()->bgthread_max_sleep_ms;
+  static const int low_threshold  = wanted_timeout > 0 ?
+                               Min(common_flags()->bgthread_min_sleep_ms,
+                                   wanted_timeout) :
+                               common_flags()->bgthread_min_sleep_ms;
+
+  static int last_eventful_dur  = high_threshold;
+  static int last_eventless_dur = low_threshold;
+
+  int new_sleep_dur;
+
+  if (!last_sleep_int.ends_with_event) {
+    // Increase sleep interval length
+    last_eventless_dur = last_sleep_int.duration_ms;
+    if (last_eventful_dur <= last_sleep_int.duration_ms) {
+      last_eventful_dur = high_threshold;
+    }
+
+    int high_ref;
+    if (!last_but_one_sleep_int.ends_with_event ||
+        last_but_one_sleep_int.duration_ms <= last_sleep_int.duration_ms) {
+      high_ref = high_threshold;
+    } else {
+      high_ref = last_but_one_sleep_int.duration_ms;
+    }
+
+    new_sleep_dur = Min(Min(last_sleep_int.duration_ms * 2,
+                            Mid(last_sleep_int.duration_ms, high_ref)),
+                        last_eventful_dur);
+  } else {
+    // Decrease sleep interval length
+    last_eventful_dur = last_sleep_int.duration_ms;
+    if (last_eventless_dur >= last_sleep_int.duration_ms) {
+      last_eventless_dur = low_threshold;
+    }
+
+    int low_ref;
+    if (last_but_one_sleep_int.ends_with_event ||
+        last_but_one_sleep_int.duration_ms >= last_sleep_int.duration_ms) {
+      low_ref = low_threshold;
+    } else  {
+      low_ref = last_but_one_sleep_int.duration_ms;
+    }
+
+    new_sleep_dur = Max(Max(last_sleep_int.duration_ms / 2,
+                            Mid(last_sleep_int.duration_ms, low_ref)),
+                        last_eventless_dur);
+  }
+
+  last_but_one_sleep_int = last_sleep_int;
+  last_sleep_int = {new_sleep_dur, false};
+
+  return new_sleep_dur;
+}
+
 #if SANITIZER_LINUX && !SANITIZER_GO
 void BackgroundThread(void *arg) {
   uptr hard_rss_limit_mb = common_flags()->hard_rss_limit_mb;
@@ -78,7 +198,7 @@ void BackgroundThread(void *arg) {
   bool reached_soft_rss_limit = false;
 
   while (true) {
-    SleepForMillis(100);
+    SleepForMillis(CalcNewSleepInterval());
     uptr current_rss_mb = GetRSS() >> 20;
     if (Verbosity()) {
       // If RSS has grown 10% since last time, print some information.