Update from https://crrev.com/312600

TBR=jamesr@chromium.org

Review URL: https://codereview.chromium.org/863253002
diff --git a/BUILD.gn b/BUILD.gn
index 065f871..e20b744 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -40,10 +40,8 @@
     "android/fifo_utils.h",
     "android/important_file_writer_android.cc",
     "android/important_file_writer_android.h",
-    "android/locale_utils.cc",
-    "android/locale_utils.h",
-    "android/scoped_java_ref.cc",
-    "android/scoped_java_ref.h",
+    "android/java_handler_thread.cc",
+    "android/java_handler_thread.h",
     "android/jni_android.cc",
     "android/jni_android.h",
     "android/jni_array.cc",
@@ -56,17 +54,21 @@
     "android/jni_utils.h",
     "android/jni_weak_ref.cc",
     "android/jni_weak_ref.h",
+    "android/library_loader/library_load_from_apk_status_codes.h",
     "android/library_loader/library_loader_hooks.cc",
     "android/library_loader/library_loader_hooks.h",
-    "android/library_loader/library_load_from_apk_status_codes.h",
+    "android/locale_utils.cc",
+    "android/locale_utils.h",
     "android/memory_pressure_listener_android.cc",
     "android/memory_pressure_listener_android.h",
-    "android/java_handler_thread.cc",
-    "android/java_handler_thread.h",
     "android/path_service_android.cc",
     "android/path_service_android.h",
     "android/path_utils.cc",
     "android/path_utils.h",
+    "android/record_histogram.cc",
+    "android/record_histogram.h",
+    "android/scoped_java_ref.cc",
+    "android/scoped_java_ref.h",
     "android/sys_utils.cc",
     "android/sys_utils.h",
     "android/thread_utils.h",
@@ -184,9 +186,6 @@
     "deferred_sequenced_task_runner.h",
     "environment.cc",
     "environment.h",
-    "event_recorder.h",
-    "event_recorder_stubs.cc",
-    "event_recorder_win.cc",
     "file_descriptor_posix.h",
     "file_version_info.h",
     "file_version_info_mac.h",
@@ -857,7 +856,6 @@
   # Windows.
   if (is_win) {
     sources -= [
-      "event_recorder_stubs.cc",
       "message_loop/message_pump_libevent.cc",
       "strings/string16.cc",
 
@@ -1454,17 +1452,18 @@
       "android/java/src/org/chromium/base/FieldTrialList.java",
       "android/java/src/org/chromium/base/ImportantFileWriterAndroid.java",
       "android/java/src/org/chromium/base/JNIUtils.java",
-      "android/java/src/org/chromium/base/library_loader/LibraryLoader.java",
+      "android/java/src/org/chromium/base/JavaHandlerThread.java",
       "android/java/src/org/chromium/base/LocaleUtils.java",
       "android/java/src/org/chromium/base/MemoryPressureListener.java",
-      "android/java/src/org/chromium/base/JavaHandlerThread.java",
       "android/java/src/org/chromium/base/PathService.java",
       "android/java/src/org/chromium/base/PathUtils.java",
       "android/java/src/org/chromium/base/PowerMonitor.java",
-      "android/java/src/org/chromium/base/SystemMessageHandler.java",
       "android/java/src/org/chromium/base/SysUtils.java",
+      "android/java/src/org/chromium/base/SystemMessageHandler.java",
       "android/java/src/org/chromium/base/ThreadUtils.java",
       "android/java/src/org/chromium/base/TraceEvent.java",
+      "android/java/src/org/chromium/base/library_loader/LibraryLoader.java",
+      "android/java/src/org/chromium/base/metrics/RecordHistogram.java",
     ]
     jni_package = "base"
   }
diff --git a/android/base_jni_registrar.cc b/android/base_jni_registrar.cc
index c0f0af7..9529b71 100644
--- a/android/base_jni_registrar.cc
+++ b/android/base_jni_registrar.cc
@@ -20,6 +20,7 @@
 #include "base/android/memory_pressure_listener_android.h"
 #include "base/android/path_service_android.h"
 #include "base/android/path_utils.h"
+#include "base/android/record_histogram.h"
 #include "base/android/sys_utils.h"
 #include "base/android/thread_utils.h"
 #include "base/android/trace_event_binding.h"
@@ -32,28 +33,29 @@
 namespace android {
 
 static RegistrationMethod kBaseRegisteredMethods[] = {
-  { "ApplicationStatusListener",
-      base::android::ApplicationStatusListener::RegisterBindings },
-  { "BuildInfo", base::android::BuildInfo::RegisterBindings },
-  { "CommandLine", base::android::RegisterCommandLine },
-  { "ContentUriUtils", base::RegisterContentUriUtils },
-  { "CpuFeatures", base::android::RegisterCpuFeatures },
-  { "EventLog", base::android::RegisterEventLog },
-  { "FieldTrialList", base::android::RegisterFieldTrialList },
-  { "ImportantFileWriterAndroid",
-    base::android::RegisterImportantFileWriterAndroid },
-  { "JNIUtils", base::android::RegisterJNIUtils },
-  { "LocaleUtils", base::android::RegisterLocaleUtils },
-  { "MemoryPressureListenerAndroid",
-      base::android::MemoryPressureListenerAndroid::Register },
-  { "JavaHandlerThread", base::android::JavaHandlerThread::RegisterBindings },
-  { "PathService", base::android::RegisterPathService },
-  { "PathUtils", base::android::RegisterPathUtils },
-  { "SystemMessageHandler", base::MessagePumpForUI::RegisterBindings },
-  { "SysUtils", base::android::SysUtils::Register },
-  { "PowerMonitor", base::RegisterPowerMonitor },
-  { "ThreadUtils", base::RegisterThreadUtils },
-  { "TraceEvent", base::android::RegisterTraceEvent },
+    {"ApplicationStatusListener",
+     base::android::ApplicationStatusListener::RegisterBindings},
+    {"BuildInfo", base::android::BuildInfo::RegisterBindings},
+    {"CommandLine", base::android::RegisterCommandLine},
+    {"ContentUriUtils", base::RegisterContentUriUtils},
+    {"CpuFeatures", base::android::RegisterCpuFeatures},
+    {"EventLog", base::android::RegisterEventLog},
+    {"FieldTrialList", base::android::RegisterFieldTrialList},
+    {"ImportantFileWriterAndroid",
+     base::android::RegisterImportantFileWriterAndroid},
+    {"JNIUtils", base::android::RegisterJNIUtils},
+    {"LocaleUtils", base::android::RegisterLocaleUtils},
+    {"MemoryPressureListenerAndroid",
+     base::android::MemoryPressureListenerAndroid::Register},
+    {"JavaHandlerThread", base::android::JavaHandlerThread::RegisterBindings},
+    {"PathService", base::android::RegisterPathService},
+    {"PathUtils", base::android::RegisterPathUtils},
+    {"PowerMonitor", base::RegisterPowerMonitor},
+    {"RecordHistogram", base::android::RegisterRecordHistogram},
+    {"SystemMessageHandler", base::MessagePumpForUI::RegisterBindings},
+    {"SysUtils", base::android::SysUtils::Register},
+    {"ThreadUtils", base::RegisterThreadUtils},
+    {"TraceEvent", base::android::RegisterTraceEvent},
 };
 
 bool RegisterJni(JNIEnv* env) {
diff --git a/android/java/src/org/chromium/base/metrics/RecordHistogram.java b/android/java/src/org/chromium/base/metrics/RecordHistogram.java
new file mode 100644
index 0000000..8295a85
--- /dev/null
+++ b/android/java/src/org/chromium/base/metrics/RecordHistogram.java
@@ -0,0 +1,62 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.metrics;
+
+import org.chromium.base.JNINamespace;
+import org.chromium.base.VisibleForTesting;
+
+/**
+ * Java API for recording UMA histograms. As opposed to macros used in native code, these calls are
+ * not caching the histogram pointer; also, the JNI calls are relatively costly - avoid calling
+ * these methods in performance-critical code.
+ */
+@JNINamespace("base::android")
+public class RecordHistogram {
+    /**
+     * Records a sample in a boolean UMA histogram of the given name. Boolean histogram has two
+     * buckets, corresponding to success (true) and failure (false).
+     * @param name name of the histogram
+     * @param sample sample to be recorded, either true or false
+     */
+    public static void recordBooleanHistogram(String name, boolean sample) {
+        nativeRecordBooleanHistogram(name, sample);
+    }
+
+    /**
+     * Records a sample in an enumerated histogram of the given name and boundary. Note that
+     * |boundary| identifies the histogram - it should be the same at every invocation.
+     * @param name name of the histogram
+     * @param sample sample to be recorded, at least 0 and at most |boundary| - 1
+     * @param boundary upper bound for legal sample values - all sample values has to be strictly
+     *        lower than |boundary|
+     */
+    public static void recordEnumeratedHistogram(String name, int sample, int boundary) {
+        nativeRecordEnumeratedHistogram(name, sample, boundary);
+    }
+
+    /**
+     * Returns the number of samples recorded in the given bucket of the given histogram.
+     * @param name name of the histogram to look up
+     * @param sample the bucket containing this sample value will be looked up
+     */
+    @VisibleForTesting
+    public static int getHistogramValueCountForTesting(String name, int sample) {
+        return nativeGetHistogramValueCountForTesting(name, sample);
+    }
+
+    /**
+     * Initializes the metrics system.
+     */
+    public static void initialize() {
+        nativeInitialize();
+    }
+
+    private static native void nativeRecordBooleanHistogram(String name, boolean sample);
+    private static native void nativeRecordEnumeratedHistogram(
+            String name, int sample, int boundary);
+
+    private static native int nativeGetHistogramValueCountForTesting(String name, int sample);
+    private static native void nativeInitialize();
+}
diff --git a/android/javatests/src/org/chromium/base/metrics/RecordHistogramTest.java b/android/javatests/src/org/chromium/base/metrics/RecordHistogramTest.java
new file mode 100644
index 0000000..82c1ca9
--- /dev/null
+++ b/android/javatests/src/org/chromium/base/metrics/RecordHistogramTest.java
@@ -0,0 +1,78 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.metrics;
+
+import android.test.InstrumentationTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.chromium.base.library_loader.LibraryLoader;
+import org.chromium.base.test.util.MetricsUtils.HistogramDelta;
+
+/**
+ * Tests for the Java API for recording UMA histograms.
+ */
+public class RecordHistogramTest extends InstrumentationTestCase {
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        LibraryLoader.ensureInitialized();
+        RecordHistogram.initialize();
+    }
+
+    /**
+     * Tests recording of boolean histograms.
+     */
+    @SmallTest
+    public void testRecordBooleanHistogram() {
+        String histogram = "HelloWorld.BooleanMetric";
+        HistogramDelta falseCount = new HistogramDelta(histogram, 0);
+        HistogramDelta trueCount = new HistogramDelta(histogram, 1);
+        assertEquals(0, trueCount.getDelta());
+        assertEquals(0, falseCount.getDelta());
+
+        RecordHistogram.recordBooleanHistogram(histogram, true);
+        assertEquals(1, trueCount.getDelta());
+        assertEquals(0, falseCount.getDelta());
+
+        RecordHistogram.recordBooleanHistogram(histogram, true);
+        assertEquals(2, trueCount.getDelta());
+        assertEquals(0, falseCount.getDelta());
+
+        RecordHistogram.recordBooleanHistogram(histogram, false);
+        assertEquals(2, trueCount.getDelta());
+        assertEquals(1, falseCount.getDelta());
+    }
+
+    /**
+     * Tests recording of enumerated histograms.
+     */
+    @SmallTest
+    public void testRecordEnumeratedHistogram() {
+        String histogram = "HelloWorld.EnumeratedMetric";
+        HistogramDelta zeroCount = new HistogramDelta(histogram, 0);
+        HistogramDelta oneCount = new HistogramDelta(histogram, 1);
+        HistogramDelta twoCount = new HistogramDelta(histogram, 2);
+        final int boundary = 3;
+
+        assertEquals(0, zeroCount.getDelta());
+        assertEquals(0, oneCount.getDelta());
+        assertEquals(0, twoCount.getDelta());
+
+        RecordHistogram.recordEnumeratedHistogram(histogram, 0, boundary);
+        assertEquals(1, zeroCount.getDelta());
+        assertEquals(0, oneCount.getDelta());
+        assertEquals(0, twoCount.getDelta());
+
+        RecordHistogram.recordEnumeratedHistogram(histogram, 0, boundary);
+        assertEquals(2, zeroCount.getDelta());
+        assertEquals(0, oneCount.getDelta());
+        assertEquals(0, twoCount.getDelta());
+
+        RecordHistogram.recordEnumeratedHistogram(histogram, 2, boundary);
+        assertEquals(2, zeroCount.getDelta());
+        assertEquals(0, oneCount.getDelta());
+        assertEquals(1, twoCount.getDelta());
+    }
+}
diff --git a/android/record_histogram.cc b/android/record_histogram.cc
new file mode 100644
index 0000000..9a72460
--- /dev/null
+++ b/android/record_histogram.cc
@@ -0,0 +1,69 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/android/record_histogram.h"
+#include "base/metrics/histogram.h"
+#include "base/metrics/statistics_recorder.h"
+#include "jni/RecordHistogram_jni.h"
+
+namespace base {
+namespace android {
+
+void RecordBooleanHistogram(JNIEnv* env,
+                            jclass clazz,
+                            jstring j_histogram_name,
+                            jboolean j_sample) {
+  std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
+  bool sample = static_cast<bool>(j_sample);
+
+  BooleanHistogram::FactoryGet(histogram_name,
+                               HistogramBase::kUmaTargetedHistogramFlag)
+      ->AddBoolean(sample);
+}
+
+void RecordEnumeratedHistogram(JNIEnv* env,
+                               jclass clazz,
+                               jstring j_histogram_name,
+                               jint j_sample,
+                               jint j_boundary) {
+  std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
+  int sample = static_cast<int>(j_sample);
+  int boundary = static_cast<int>(j_boundary);
+
+  LinearHistogram::FactoryGet(histogram_name, 1, boundary, boundary + 1,
+                              HistogramBase::kUmaTargetedHistogramFlag)
+      ->Add(sample);
+}
+
+void Initialize(JNIEnv* env, jclass) {
+  StatisticsRecorder::Initialize();
+}
+
+// This backs a Java test util for testing histograms -
+// MetricsUtils.HistogramDelta. It should live in a test-specific file, but we
+// currently can't have test-specific native code packaged in test-specific Java
+// targets - see http://crbug.com/415945.
+jint GetHistogramValueCountForTesting(JNIEnv* env,
+                                      jclass clazz,
+                                      jstring histogram_name,
+                                      jint sample) {
+  HistogramBase* histogram = StatisticsRecorder::FindHistogram(
+      android::ConvertJavaStringToUTF8(env, histogram_name));
+  if (histogram == nullptr) {
+    // No samples have been recorded for this histogram (yet?).
+    return 0;
+  }
+
+  scoped_ptr<HistogramSamples> samples = histogram->SnapshotSamples();
+  return samples->GetCount(static_cast<int>(sample));
+}
+
+bool RegisterRecordHistogram(JNIEnv* env) {
+  return RegisterNativesImpl(env);
+}
+
+}  // namespace android
+}  // namespace base
diff --git a/android/record_histogram.h b/android/record_histogram.h
new file mode 100644
index 0000000..caa10f0
--- /dev/null
+++ b/android/record_histogram.h
@@ -0,0 +1,18 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_ANDROID_RECORD_HISTOGRAM_H_
+#define BASE_ANDROID_RECORD_HISTOGRAM_H_
+
+#include <jni.h>
+
+namespace base {
+namespace android {
+
+bool RegisterRecordHistogram(JNIEnv* env);
+
+}  // namespace android
+}  // namespace base
+
+#endif  // BASE_ANDROID_RECORD_HISTOGRAM_H_
diff --git a/base.gyp b/base.gyp
index 5d26fc4..114cad6 100644
--- a/base.gyp
+++ b/base.gyp
@@ -230,15 +230,10 @@
         }],
       ],
       'sources': [
-        'third_party/xdg_user_dirs/xdg_user_dir_lookup.cc',
-        'third_party/xdg_user_dirs/xdg_user_dir_lookup.h',
         'async_socket_io_handler.h',
         'async_socket_io_handler_posix.cc',
         'async_socket_io_handler_win.cc',
         'auto_reset.h',
-        'event_recorder.h',
-        'event_recorder_stubs.cc',
-        'event_recorder_win.cc',
         'linux_util.cc',
         'linux_util.h',
         'message_loop/message_pump_android.cc',
@@ -256,8 +251,10 @@
         'posix/file_descriptor_shuffle.cc',
         'posix/file_descriptor_shuffle.h',
         'sync_socket.h',
-        'sync_socket_win.cc',
         'sync_socket_posix.cc',
+        'sync_socket_win.cc',
+        'third_party/xdg_user_dirs/xdg_user_dir_lookup.cc',
+        'third_party/xdg_user_dirs/xdg_user_dir_lookup.h',
       ],
       'includes': [
         '../build/android/increase_size_for_speed.gypi',
@@ -1166,15 +1163,10 @@
             4267,
           ],
           'sources': [
-            'third_party/xdg_user_dirs/xdg_user_dir_lookup.cc',
-            'third_party/xdg_user_dirs/xdg_user_dir_lookup.h',
             'async_socket_io_handler.h',
             'async_socket_io_handler_posix.cc',
             'async_socket_io_handler_win.cc',
             'auto_reset.h',
-            'event_recorder.h',
-            'event_recorder_stubs.cc',
-            'event_recorder_win.cc',
             'linux_util.cc',
             'linux_util.h',
             'md5.cc',
@@ -1186,8 +1178,10 @@
             'posix/file_descriptor_shuffle.cc',
             'posix/file_descriptor_shuffle.h',
             'sync_socket.h',
-            'sync_socket_win.cc',
             'sync_socket_posix.cc',
+            'sync_socket_win.cc',
+            'third_party/xdg_user_dirs/xdg_user_dir_lookup.cc',
+            'third_party/xdg_user_dirs/xdg_user_dir_lookup.h',
           ],
         },
         {
@@ -1342,17 +1336,18 @@
             'android/java/src/org/chromium/base/FieldTrialList.java',
             'android/java/src/org/chromium/base/ImportantFileWriterAndroid.java',
             'android/java/src/org/chromium/base/JNIUtils.java',
-            'android/java/src/org/chromium/base/library_loader/LibraryLoader.java',
+            'android/java/src/org/chromium/base/JavaHandlerThread.java',
             'android/java/src/org/chromium/base/LocaleUtils.java',
             'android/java/src/org/chromium/base/MemoryPressureListener.java',
-            'android/java/src/org/chromium/base/JavaHandlerThread.java',
             'android/java/src/org/chromium/base/PathService.java',
             'android/java/src/org/chromium/base/PathUtils.java',
             'android/java/src/org/chromium/base/PowerMonitor.java',
-            'android/java/src/org/chromium/base/SystemMessageHandler.java',
             'android/java/src/org/chromium/base/SysUtils.java',
+            'android/java/src/org/chromium/base/SystemMessageHandler.java',
             'android/java/src/org/chromium/base/ThreadUtils.java',
             'android/java/src/org/chromium/base/TraceEvent.java',
+            'android/java/src/org/chromium/base/library_loader/LibraryLoader.java',
+            'android/java/src/org/chromium/base/metrics/RecordHistogram.java',
           ],
           'variables': {
             'jni_gen_package': 'base',
diff --git a/base.gypi b/base.gypi
index 349308c..0c6ee7e 100644
--- a/base.gypi
+++ b/base.gypi
@@ -72,6 +72,8 @@
           'android/path_service_android.h',
           'android/path_utils.cc',
           'android/path_utils.h',
+          'android/record_histogram.cc',
+          'android/record_histogram.h',
           'android/sys_utils.cc',
           'android/sys_utils.h',
           'android/thread_utils.h',
@@ -927,7 +929,6 @@
               '<(DEPTH)/third_party/wtl/include',
             ],
             'sources!': [
-              'event_recorder_stubs.cc',
               'files/file_path_watcher_fsevents.cc',
               'files/file_path_watcher_fsevents.h',
               'files/file_path_watcher_kqueue.cc',
diff --git a/chromeos/memory_pressure_observer_chromeos.cc b/chromeos/memory_pressure_observer_chromeos.cc
index 4da7d24..8112c76 100644
--- a/chromeos/memory_pressure_observer_chromeos.cc
+++ b/chromeos/memory_pressure_observer_chromeos.cc
@@ -24,25 +24,56 @@
     kModerateMemoryPressureCooldownMs / kMemoryPressureIntervalMs;
 
 // Threshold constants to emit pressure events.
-const int kMemoryPressureModerateThresholdPercent = 70;
-const int kMemoryPressureCriticalThresholdPercent = 90;
+const int kNormalMemoryPressureModerateThresholdPercent = 60;
+const int kNormalMemoryPressureCriticalThresholdPercent = 90;
+const int kAggressiveMemoryPressureModerateThresholdPercent = 35;
+const int kAggressiveMemoryPressureCriticalThresholdPercent = 70;
+
+// Converts a |MemoryPressureThreshold| value into a used memory percentage for
+// the moderate pressure event.
+int GetModerateMemoryThresholdInPercent(
+    MemoryPressureObserverChromeOS::MemoryPressureThresholds thresholds) {
+  return thresholds == MemoryPressureObserverChromeOS::
+                           THRESHOLD_AGGRESSIVE_CACHE_DISCARD ||
+         thresholds == MemoryPressureObserverChromeOS::THRESHOLD_AGGRESSIVE
+             ? kAggressiveMemoryPressureModerateThresholdPercent
+             : kNormalMemoryPressureModerateThresholdPercent;
+}
+
+// Converts a |MemoryPressureThreshold| value into a used memory percentage for
+// the critical pressure event.
+int GetCriticalMemoryThresholdInPercent(
+    MemoryPressureObserverChromeOS::MemoryPressureThresholds thresholds) {
+  return thresholds == MemoryPressureObserverChromeOS::
+                           THRESHOLD_AGGRESSIVE_TAB_DISCARD ||
+         thresholds == MemoryPressureObserverChromeOS::THRESHOLD_AGGRESSIVE
+             ? kAggressiveMemoryPressureCriticalThresholdPercent
+             : kNormalMemoryPressureCriticalThresholdPercent;
+}
 
 // Converts free percent of memory into a memory pressure value.
 MemoryPressureListener::MemoryPressureLevel GetMemoryPressureLevelFromFillLevel(
-    int memory_fill_level) {
-  if (memory_fill_level < kMemoryPressureModerateThresholdPercent)
+    int actual_fill_level,
+    int moderate_threshold,
+    int critical_threshold) {
+  if (actual_fill_level < moderate_threshold)
     return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
-  return memory_fill_level < kMemoryPressureCriticalThresholdPercent ?
-      MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE :
-      MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL;
+  return actual_fill_level < critical_threshold
+             ? MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE
+             : MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL;
 }
 
 }  // namespace
 
-MemoryPressureObserverChromeOS::MemoryPressureObserverChromeOS()
+MemoryPressureObserverChromeOS::MemoryPressureObserverChromeOS(
+    MemoryPressureThresholds thresholds)
     : current_memory_pressure_level_(
-        MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE),
+          MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE),
       moderate_pressure_repeat_count_(0),
+      moderate_pressure_threshold_percent_(
+          GetModerateMemoryThresholdInPercent(thresholds)),
+      critical_pressure_threshold_percent_(
+          GetCriticalMemoryThresholdInPercent(thresholds)),
       weak_ptr_factory_(this) {
   StartObserving();
 }
@@ -74,7 +105,9 @@
   MemoryPressureListener::MemoryPressureLevel old_pressure =
       current_memory_pressure_level_;
   current_memory_pressure_level_ =
-      GetMemoryPressureLevelFromFillLevel(GetUsedMemoryInPercent());
+      GetMemoryPressureLevelFromFillLevel(GetUsedMemoryInPercent(),
+                                          moderate_pressure_threshold_percent_,
+                                          critical_pressure_threshold_percent_);
   // In case there is no memory pressure we do not notify.
   if (current_memory_pressure_level_ ==
       MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE) {
diff --git a/chromeos/memory_pressure_observer_chromeos.h b/chromeos/memory_pressure_observer_chromeos.h
index 23b6b34..739b795 100644
--- a/chromeos/memory_pressure_observer_chromeos.h
+++ b/chromeos/memory_pressure_observer_chromeos.h
@@ -27,7 +27,27 @@
  public:
   using GetUsedMemoryInPercentCallback = int (*)();
 
-  MemoryPressureObserverChromeOS();
+  // There are two memory pressure events:
+  // MODERATE - which will mainly release caches.
+  // CRITICAL - which will discard tabs.
+  // The |MemoryPressureThresholds| enum selects the strategy of firing these
+  // events: A conservative strategy will keep as much content in memory as
+  // possible (causing the system to swap to zram) and an aggressive strategy
+  // will release memory earlier to avoid swapping.
+  enum MemoryPressureThresholds {
+    // Use the system default.
+    THRESHOLD_DEFAULT = 0,
+    // Try to keep as much content in memory as possible.
+    THRESHOLD_CONSERVATIVE = 1,
+    // Discard caches earlier, allowing to keep more tabs in memory.
+    THRESHOLD_AGGRESSIVE_CACHE_DISCARD = 2,
+    // Discard tabs earlier, allowing the system to get faster.
+    THRESHOLD_AGGRESSIVE_TAB_DISCARD = 3,
+    // Discard caches and tabs earlier to allow the system to be faster.
+    THRESHOLD_AGGRESSIVE = 4
+  };
+
+  explicit MemoryPressureObserverChromeOS(MemoryPressureThresholds thresholds);
   virtual ~MemoryPressureObserverChromeOS();
 
   // Redo the memory pressure calculation soon and call again if a critical
@@ -71,6 +91,10 @@
   // gets used to count the number of events since the last event occured.
   int moderate_pressure_repeat_count_;
 
+  // The thresholds for moderate and critical pressure.
+  const int moderate_pressure_threshold_percent_;
+  const int critical_pressure_threshold_percent_;
+
   base::WeakPtrFactory<MemoryPressureObserverChromeOS> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(MemoryPressureObserverChromeOS);
diff --git a/chromeos/memory_pressure_observer_chromeos_unittest.cc b/chromeos/memory_pressure_observer_chromeos_unittest.cc
index 8b28957..a227f93 100644
--- a/chromeos/memory_pressure_observer_chromeos_unittest.cc
+++ b/chromeos/memory_pressure_observer_chromeos_unittest.cc
@@ -42,7 +42,10 @@
 
 class TestMemoryPressureObserver : public MemoryPressureObserverChromeOS {
  public:
-  TestMemoryPressureObserver() : memory_in_percent_override_(0) {
+  TestMemoryPressureObserver() :
+    MemoryPressureObserverChromeOS(
+        MemoryPressureObserverChromeOS::THRESHOLD_DEFAULT),
+    memory_in_percent_override_(0) {
     // Disable any timers which are going on and set a special memory reporting
     // function.
     StopObserving();
diff --git a/debug/trace_event_impl.h b/debug/trace_event_impl.h
index c80826c..1b1fce3 100644
--- a/debug/trace_event_impl.h
+++ b/debug/trace_event_impl.h
@@ -745,7 +745,6 @@
   // This lock protects accesses to thread_names_, thread_event_start_times_
   // and thread_colors_.
   Lock thread_info_lock_;
-  int locked_line_;
   Mode mode_;
   int num_traces_recorded_;
   scoped_ptr<TraceBuffer> logged_events_;
diff --git a/event_recorder.h b/event_recorder.h
deleted file mode 100644
index bff87ed..0000000
--- a/event_recorder.h
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_EVENT_RECORDER_H_
-#define BASE_EVENT_RECORDER_H_
-
-#include "base/base_export.h"
-#include "base/basictypes.h"
-#include "build/build_config.h"
-
-#if defined(OS_WIN)
-#include <stdio.h>
-#include <string.h>
-#include <windows.h>
-#endif
-
-namespace base {
-
-class FilePath;
-
-// A class for recording and playing back keyboard and mouse input events.
-//
-// Note - if you record events, and the playback with the windows in
-//        different sizes or positions, the playback will fail.  When
-//        recording and playing, you should move the relevant windows
-//        to constant sizes and locations.
-// TODO(mbelshe) For now this is a singleton.  I believe that this class
-//        could be easily modified to:
-//             support two simultaneous recorders
-//             be playing back events while already recording events.
-//        Why?  Imagine if the product had a "record a macro" feature.
-//        You might be recording globally, while recording or playing back
-//        a macro.  I don't think two playbacks make sense.
-class BASE_EXPORT EventRecorder {
- public:
-  // Get the singleton EventRecorder.
-  // We can only handle one recorder/player at a time.
-  static EventRecorder* current() {
-    if (!current_)
-      current_ = new EventRecorder();
-    return current_;
-  }
-
-  // Starts recording events.
-  // Will clobber the file if it already exists.
-  // Returns true on success, or false if an error occurred.
-  bool StartRecording(const FilePath& filename);
-
-  // Stops recording.
-  void StopRecording();
-
-  // Is the EventRecorder currently recording.
-  bool is_recording() const { return is_recording_; }
-
-  // Plays events previously recorded.
-  // Returns true on success, or false if an error occurred.
-  bool StartPlayback(const FilePath& filename);
-
-  // Stops playback.
-  void StopPlayback();
-
-  // Is the EventRecorder currently playing.
-  bool is_playing() const { return is_playing_; }
-
-#if defined(OS_WIN)
-  // C-style callbacks for the EventRecorder.
-  // Used for internal purposes only.
-  LRESULT RecordWndProc(int nCode, WPARAM wParam, LPARAM lParam);
-  LRESULT PlaybackWndProc(int nCode, WPARAM wParam, LPARAM lParam);
-#endif
-
- private:
-  // Create a new EventRecorder.  Events are saved to the file filename.
-  // If the file already exists, it will be deleted before recording
-  // starts.
-  EventRecorder()
-      : is_recording_(false),
-        is_playing_(false),
-#if defined(OS_WIN)
-        journal_hook_(NULL),
-        file_(NULL),
-#endif
-        playback_first_msg_time_(0),
-        playback_start_time_(0) {
-#if defined(OS_WIN)
-    memset(&playback_msg_, 0, sizeof(playback_msg_));
-#endif
-  }
-  ~EventRecorder();
-
-  static EventRecorder* current_;  // Our singleton.
-
-  bool is_recording_;
-  bool is_playing_;
-#if defined(OS_WIN)
-  HHOOK journal_hook_;
-  FILE* file_;
-  EVENTMSG playback_msg_;
-#endif
-  int playback_first_msg_time_;
-  int playback_start_time_;
-
-  DISALLOW_COPY_AND_ASSIGN(EventRecorder);
-};
-
-}  // namespace base
-
-#endif // BASE_EVENT_RECORDER_H_
diff --git a/event_recorder_stubs.cc b/event_recorder_stubs.cc
deleted file mode 100644
index 91f2e07..0000000
--- a/event_recorder_stubs.cc
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/event_recorder.h"
-
-// This file implements a link stub for EventRecorder that can be used on
-// platforms that don't have a working EventRecorder implementation.
-
-namespace base {
-
-EventRecorder* EventRecorder::current_;  // Our singleton.
-
-bool EventRecorder::StartRecording(const FilePath& filename) {
-  return true;
-}
-
-void EventRecorder::StopRecording() {
-}
-
-bool EventRecorder::StartPlayback(const FilePath& filename) {
-  return false;
-}
-
-void EventRecorder::StopPlayback() {
-}
-
-}  // namespace
diff --git a/event_recorder_win.cc b/event_recorder_win.cc
deleted file mode 100644
index b3076a1..0000000
--- a/event_recorder_win.cc
+++ /dev/null
@@ -1,258 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <stddef.h>
-#include <windows.h>
-#include <mmsystem.h>
-
-#include "base/event_recorder.h"
-#include "base/files/file_util.h"
-#include "base/logging.h"
-
-// A note about time.
-// For perfect playback of events, you'd like a very accurate timer
-// so that events are played back at exactly the same time that
-// they were recorded.  However, windows has a clock which is only
-// granular to ~15ms.  We see more consistent event playback when
-// using a higher resolution timer.  To do this, we use the
-// timeGetTime API instead of the default GetTickCount() API.
-
-namespace base {
-
-EventRecorder* EventRecorder::current_ = NULL;
-
-LRESULT CALLBACK StaticRecordWndProc(int nCode, WPARAM wParam,
-                                     LPARAM lParam) {
-  DCHECK(EventRecorder::current());
-  return EventRecorder::current()->RecordWndProc(nCode, wParam, lParam);
-}
-
-LRESULT CALLBACK StaticPlaybackWndProc(int nCode, WPARAM wParam,
-                                       LPARAM lParam) {
-  DCHECK(EventRecorder::current());
-  return EventRecorder::current()->PlaybackWndProc(nCode, wParam, lParam);
-}
-
-EventRecorder::~EventRecorder() {
-  // Try to assert early if the caller deletes the recorder
-  // while it is still in use.
-  DCHECK(!journal_hook_);
-  DCHECK(!is_recording_ && !is_playing_);
-}
-
-bool EventRecorder::StartRecording(const FilePath& filename) {
-  if (journal_hook_ != NULL)
-    return false;
-  if (is_recording_ || is_playing_)
-    return false;
-
-  // Open the recording file.
-  DCHECK(!file_);
-  file_ = OpenFile(filename, "wb+");
-  if (!file_) {
-    DLOG(ERROR) << "EventRecorder could not open log file";
-    return false;
-  }
-
-  // Set the faster clock, if possible.
-  ::timeBeginPeriod(1);
-
-  // Set the recording hook.  JOURNALRECORD can only be used as a global hook.
-  journal_hook_ = ::SetWindowsHookEx(WH_JOURNALRECORD, StaticRecordWndProc,
-                                     GetModuleHandle(NULL), 0);
-  if (!journal_hook_) {
-    DLOG(ERROR) << "EventRecorder Record Hook failed";
-    CloseFile(file_);
-    return false;
-  }
-
-  is_recording_ = true;
-  return true;
-}
-
-void EventRecorder::StopRecording() {
-  if (is_recording_) {
-    DCHECK(journal_hook_ != NULL);
-
-    if (!::UnhookWindowsHookEx(journal_hook_)) {
-      DLOG(ERROR) << "EventRecorder Unhook failed";
-      // Nothing else we can really do here.
-      return;
-    }
-
-    ::timeEndPeriod(1);
-
-    DCHECK(file_ != NULL);
-    CloseFile(file_);
-    file_ = NULL;
-
-    journal_hook_ = NULL;
-    is_recording_ = false;
-  }
-}
-
-bool EventRecorder::StartPlayback(const FilePath& filename) {
-  if (journal_hook_ != NULL)
-    return false;
-  if (is_recording_ || is_playing_)
-    return false;
-
-  // Open the recording file.
-  DCHECK(!file_);
-  file_ = OpenFile(filename, "rb");
-  if (!file_) {
-    DLOG(ERROR) << "EventRecorder Playback could not open log file";
-    return false;
-  }
-  // Read the first event from the record.
-  if (fread(&playback_msg_, sizeof(EVENTMSG), 1, file_) != 1) {
-    DLOG(ERROR) << "EventRecorder Playback has no records!";
-    CloseFile(file_);
-    return false;
-  }
-
-  // Set the faster clock, if possible.
-  ::timeBeginPeriod(1);
-
-  // Playback time is tricky.  When playing back, we read a series of events,
-  // each with timeouts.  Simply subtracting the delta between two timers will
-  // lead to fast playback (about 2x speed).  The API has two events, one
-  // which advances to the next event (HC_SKIP), and another that requests the
-  // event (HC_GETNEXT).  The same event will be requested multiple times.
-  // Each time the event is requested, we must calculate the new delay.
-  // To do this, we track the start time of the playback, and constantly
-  // re-compute the delay.   I mention this only because I saw two examples
-  // of how to use this code on the net, and both were broken :-)
-  playback_start_time_ = timeGetTime();
-  playback_first_msg_time_ = playback_msg_.time;
-
-  // Set the hook.  JOURNALPLAYBACK can only be used as a global hook.
-  journal_hook_ = ::SetWindowsHookEx(WH_JOURNALPLAYBACK, StaticPlaybackWndProc,
-                                     GetModuleHandle(NULL), 0);
-  if (!journal_hook_) {
-    DLOG(ERROR) << "EventRecorder Playback Hook failed";
-    return false;
-  }
-
-  is_playing_ = true;
-
-  return true;
-}
-
-void EventRecorder::StopPlayback() {
-  if (is_playing_) {
-    DCHECK(journal_hook_ != NULL);
-
-    if (!::UnhookWindowsHookEx(journal_hook_)) {
-      DLOG(ERROR) << "EventRecorder Unhook failed";
-      // Nothing else we can really do here.
-    }
-
-    DCHECK(file_ != NULL);
-    CloseFile(file_);
-    file_ = NULL;
-
-    ::timeEndPeriod(1);
-
-    journal_hook_ = NULL;
-    is_playing_ = false;
-  }
-}
-
-// Windows callback hook for the recorder.
-LRESULT EventRecorder::RecordWndProc(int nCode, WPARAM wParam, LPARAM lParam) {
-  static bool recording_enabled = true;
-  EVENTMSG* msg_ptr = NULL;
-
-  // The API says we have to do this.
-  // See http://msdn2.microsoft.com/en-us/library/ms644983(VS.85).aspx
-  if (nCode < 0)
-    return ::CallNextHookEx(journal_hook_, nCode, wParam, lParam);
-
-  // Check for the break key being pressed and stop recording.
-  if (::GetKeyState(VK_CANCEL) & 0x8000) {
-    StopRecording();
-    return ::CallNextHookEx(journal_hook_, nCode, wParam, lParam);
-  }
-
-  // The Journal Recorder must stop recording events when system modal
-  // dialogs are present. (see msdn link above)
-  switch (nCode) {
-    case HC_SYSMODALON:
-      recording_enabled = false;
-      break;
-    case HC_SYSMODALOFF:
-      recording_enabled = true;
-      break;
-  }
-
-  if (nCode == HC_ACTION && recording_enabled) {
-    // Aha - we have an event to record.
-    msg_ptr = reinterpret_cast<EVENTMSG*>(lParam);
-    msg_ptr->time = timeGetTime();
-    fwrite(msg_ptr, sizeof(EVENTMSG), 1, file_);
-    fflush(file_);
-  }
-
-  return CallNextHookEx(journal_hook_, nCode, wParam, lParam);
-}
-
-// Windows callback for the playback mode.
-LRESULT EventRecorder::PlaybackWndProc(int nCode, WPARAM wParam,
-                                       LPARAM lParam) {
-  static bool playback_enabled = true;
-  int delay = 0;
-
-  switch (nCode) {
-    // A system modal dialog box is being displayed.  Stop playing back
-    // messages.
-    case HC_SYSMODALON:
-      playback_enabled = false;
-      break;
-
-    // A system modal dialog box is destroyed.  We can start playing back
-    // messages again.
-    case HC_SYSMODALOFF:
-      playback_enabled = true;
-      break;
-
-    // Prepare to copy the next mouse or keyboard event to playback.
-    case HC_SKIP:
-      if (!playback_enabled)
-        break;
-
-      // Read the next event from the record.
-      if (fread(&playback_msg_, sizeof(EVENTMSG), 1, file_) != 1)
-        this->StopPlayback();
-      break;
-
-    // Copy the mouse or keyboard event to the EVENTMSG structure in lParam.
-    case HC_GETNEXT:
-      if (!playback_enabled)
-        break;
-
-      memcpy(reinterpret_cast<void*>(lParam), &playback_msg_,
-             sizeof(playback_msg_));
-
-      // The return value is the amount of time (in milliseconds) to wait
-      // before playing back the next message in the playback queue.  Each
-      // time this is called, we recalculate the delay relative to our current
-      // wall clock.
-      delay = (playback_msg_.time - playback_first_msg_time_) -
-              (timeGetTime() - playback_start_time_);
-      if (delay < 0)
-        delay = 0;
-      return delay;
-
-    // An application has called PeekMessage with wRemoveMsg set to PM_NOREMOVE
-    // indicating that the message is not removed from the message queue after
-    // PeekMessage processing.
-    case HC_NOREMOVE:
-      break;
-  }
-
-  return CallNextHookEx(journal_hook_, nCode, wParam, lParam);
-}
-
-}  // namespace base
diff --git a/files/file_util_proxy_unittest.cc b/files/file_util_proxy_unittest.cc
index 17bbba8..5eb6819 100644
--- a/files/file_util_proxy_unittest.cc
+++ b/files/file_util_proxy_unittest.cc
@@ -19,8 +19,6 @@
   FileUtilProxyTest()
       : file_thread_("FileUtilProxyTestFileThread"),
         error_(File::FILE_OK),
-        created_(false),
-        bytes_written_(-1),
         weak_factory_(this) {}
 
   void SetUp() override {
@@ -52,11 +50,9 @@
 
   ScopedTempDir dir_;
   File::Error error_;
-  bool created_;
   FilePath path_;
   File::Info file_info_;
   std::vector<char> buffer_;
-  int bytes_written_;
   WeakPtrFactory<FileUtilProxyTest> weak_factory_;
 };
 
diff --git a/i18n/i18n_constants.cc b/i18n/i18n_constants.cc
index 9b8c571..7d2f5fc 100644
--- a/i18n/i18n_constants.cc
+++ b/i18n/i18n_constants.cc
@@ -8,8 +8,6 @@
 
 const char kCodepageLatin1[] = "ISO-8859-1";
 const char kCodepageUTF8[] = "UTF-8";
-const char kCodepageUTF16BE[] = "UTF-16BE";
-const char kCodepageUTF16LE[] = "UTF-16LE";
 
 }  // namespace base
 
diff --git a/i18n/i18n_constants.h b/i18n/i18n_constants.h
index c2de842..c1bd87d 100644
--- a/i18n/i18n_constants.h
+++ b/i18n/i18n_constants.h
@@ -12,8 +12,9 @@
 // Names of codepages (charsets) understood by icu.
 BASE_I18N_EXPORT extern const char kCodepageLatin1[];  // a.k.a. ISO 8859-1
 BASE_I18N_EXPORT extern const char kCodepageUTF8[];
-BASE_I18N_EXPORT extern const char kCodepageUTF16BE[];
-BASE_I18N_EXPORT extern const char kCodepageUTF16LE[];
+
+// The other possible options are UTF-16BE and UTF-16LE, but they are unused in
+// Chromium as of this writing.
 
 }  // namespace base
 
diff --git a/message_loop/message_loop.cc b/message_loop/message_loop.cc
index 8180733..86771e4 100644
--- a/message_loop/message_loop.cc
+++ b/message_loop/message_loop.cc
@@ -117,8 +117,10 @@
 
 MessageLoop::MessageLoop(Type type)
     : type_(type),
+#if defined(OS_WIN)
       pending_high_res_tasks_(0),
       in_high_res_mode_(false),
+#endif
       nestable_tasks_allowed_(true),
 #if defined(OS_WIN)
       os_modal_loop_(false),
@@ -133,8 +135,10 @@
 MessageLoop::MessageLoop(scoped_ptr<MessagePump> pump)
     : pump_(pump.Pass()),
       type_(TYPE_CUSTOM),
+#if defined(OS_WIN)
       pending_high_res_tasks_(0),
       in_high_res_mode_(false),
+#endif
       nestable_tasks_allowed_(true),
 #if defined(OS_WIN)
       os_modal_loop_(false),
@@ -422,10 +426,13 @@
 void MessageLoop::RunTask(const PendingTask& pending_task) {
   DCHECK(nestable_tasks_allowed_);
 
+#if defined(OS_WIN)
   if (pending_task.is_high_res) {
     pending_high_res_tasks_--;
-    CHECK(pending_high_res_tasks_ >= 0);
+    CHECK_GE(pending_high_res_tasks_, 0);
   }
+#endif
+
   // Execute the task and assume the worst: It is probably not reentrant.
   nestable_tasks_allowed_ = false;
 
@@ -495,8 +502,12 @@
   // load. That reduces the number of locks-per-task significantly when our
   // queues get large.
   if (work_queue_.empty()) {
+#if defined(OS_WIN)
     pending_high_res_tasks_ +=
         incoming_task_queue_->ReloadWorkQueue(&work_queue_);
+#else
+    incoming_task_queue_->ReloadWorkQueue(&work_queue_);
+#endif
   }
 }
 
@@ -692,8 +703,8 @@
 bool MessageLoopForIO::WatchFileDescriptor(int fd,
                                            bool persistent,
                                            Mode mode,
-                                           FileDescriptorWatcher *controller,
-                                           Watcher *delegate) {
+                                           FileDescriptorWatcher* controller,
+                                           Watcher* delegate) {
   return ToPumpIO(pump_.get())->WatchFileDescriptor(
       fd,
       persistent,
diff --git a/message_loop/message_loop.h b/message_loop/message_loop.h
index 32e826d..7c76616 100644
--- a/message_loop/message_loop.h
+++ b/message_loop/message_loop.h
@@ -106,7 +106,7 @@
     TYPE_IO,
 #if defined(OS_ANDROID)
     TYPE_JAVA,
-#endif // defined(OS_ANDROID)
+#endif  // defined(OS_ANDROID)
   };
 
   // Normally, it is not necessary to instantiate a MessageLoop.  Instead, it
@@ -452,6 +452,7 @@
   // this queue is only accessed (push/pop) by our current thread.
   TaskQueue work_queue_;
 
+#if defined(OS_WIN)
   // How many high resolution tasks are in the pending task queue. This value
   // increases by N every time we call ReloadWorkQueue() and decreases by 1
   // every time we call RunTask() if the task needs a high resolution timer.
@@ -459,6 +460,7 @@
   // Tracks if we have requested high resolution timers. Its only use is to
   // turn off the high resolution timer upon loop destruction.
   bool in_high_res_mode_;
+#endif
 
   // Contains delayed tasks, sorted by their 'delayed_run_time' property.
   DelayedTaskQueue delayed_work_queue_;
@@ -639,8 +641,8 @@
   bool WatchFileDescriptor(int fd,
                            bool persistent,
                            Mode mode,
-                           FileDescriptorWatcher *controller,
-                           Watcher *delegate);
+                           FileDescriptorWatcher* controller,
+                           Watcher* delegate);
 #endif  // defined(OS_IOS) || defined(OS_POSIX)
 #endif  // !defined(OS_NACL_SFI)
 };
diff --git a/message_loop/message_pump_win.cc b/message_loop/message_pump_win.cc
index a7a1485..a219aa6 100644
--- a/message_loop/message_pump_win.cc
+++ b/message_loop/message_pump_win.cc
@@ -352,6 +352,11 @@
 }
 
 bool MessagePumpForUI::ProcessMessageHelper(const MSG& msg) {
+  // TODO(vadimt): Remove ScopedTracker below once crbug.com/440919 is fixed.
+  tracked_objects::ScopedTracker tracking_profile1(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "440919 MessagePumpForUI::ProcessMessageHelper1"));
+
   TRACE_EVENT1("base", "MessagePumpForUI::ProcessMessageHelper",
                "message", msg.message);
   if (WM_QUIT == msg.message) {
@@ -380,12 +385,29 @@
           "440919 MessagePumpForUI::ProcessMessageHelper3"));
 
   uint32_t action = MessagePumpDispatcher::POST_DISPATCH_PERFORM_DEFAULT;
-  if (state_->dispatcher)
+  if (state_->dispatcher) {
+    // TODO(vadimt): Remove ScopedTracker below once crbug.com/440919 is fixed.
+    tracked_objects::ScopedTracker tracking_profile4(
+        FROM_HERE_WITH_EXPLICIT_FUNCTION(
+            "440919 MessagePumpForUI::ProcessMessageHelper4"));
+
     action = state_->dispatcher->Dispatch(msg);
+  }
   if (action & MessagePumpDispatcher::POST_DISPATCH_QUIT_LOOP)
     state_->should_quit = true;
   if (action & MessagePumpDispatcher::POST_DISPATCH_PERFORM_DEFAULT) {
+    // TODO(vadimt): Remove ScopedTracker below once crbug.com/440919 is fixed.
+    tracked_objects::ScopedTracker tracking_profile5(
+        FROM_HERE_WITH_EXPLICIT_FUNCTION(
+            "440919 MessagePumpForUI::ProcessMessageHelper5"));
+
     TranslateMessage(&msg);
+
+    // TODO(vadimt): Remove ScopedTracker below once crbug.com/440919 is fixed.
+    tracked_objects::ScopedTracker tracking_profile6(
+        FROM_HERE_WITH_EXPLICIT_FUNCTION(
+            "440919 MessagePumpForUI::ProcessMessageHelper6"));
+
     DispatchMessage(&msg);
   }
 
diff --git a/process/launch.h b/process/launch.h
index eca4f8f..de1bc0a 100644
--- a/process/launch.h
+++ b/process/launch.h
@@ -291,6 +291,25 @@
 // binary. This should not be called in production/released code.
 BASE_EXPORT LaunchOptions LaunchOptionsForTest();
 
+#if defined(OS_LINUX)
+// A wrapper for clone with fork-like behavior, meaning that it returns the
+// child's pid in the parent and 0 in the child. |flags|, |ptid|, and |ctid| are
+// as in the clone system call (the CLONE_VM flag is not supported).
+//
+// This function uses the libc clone wrapper (which updates libc's pid cache)
+// internally, so callers may expect things like getpid() to work correctly
+// after in both the child and parent. An exception is when this code is run
+// under Valgrind. Valgrind does not support the libc clone wrapper, so the libc
+// pid cache may be incorrect after this function is called under Valgrind.
+//
+// As with fork(), callers should be extremely careful when calling this while
+// multiple threads are running, since at the time the fork happened, the
+// threads could have been in any state (potentially holding locks, etc.).
+// Callers should most likely call execve() in the child soon after calling
+// this.
+BASE_EXPORT pid_t ForkWithFlags(unsigned long flags, pid_t* ptid, pid_t* ctid);
+#endif
+
 }  // namespace base
 
 #endif  // BASE_PROCESS_LAUNCH_H_
diff --git a/process/launch_posix.cc b/process/launch_posix.cc
index 6c9ed3f..ce93d50 100644
--- a/process/launch_posix.cc
+++ b/process/launch_posix.cc
@@ -7,9 +7,12 @@
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <sched.h>
+#include <setjmp.h>
 #include <signal.h>
 #include <stdlib.h>
 #include <sys/resource.h>
+#include <sys/syscall.h>
 #include <sys/time.h>
 #include <sys/types.h>
 #include <sys/wait.h>
@@ -35,8 +38,10 @@
 #include "base/strings/stringprintf.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/third_party/dynamic_annotations/dynamic_annotations.h"
+#include "base/third_party/valgrind/valgrind.h"
 #include "base/threading/platform_thread.h"
 #include "base/threading/thread_restrictions.h"
+#include "build/build_config.h"
 
 #if defined(OS_LINUX)
 #include <sys/prctl.h>
@@ -184,6 +189,54 @@
 #endif  // !defined(OS_LINUX) ||
         // (!defined(__i386__) && !defined(__x86_64__) && !defined(__arm__))
 
+#if defined(OS_LINUX)
+bool IsRunningOnValgrind() {
+  return RUNNING_ON_VALGRIND;
+}
+
+// This function runs on the stack specified on the clone call. It uses longjmp
+// to switch back to the original stack so the child can return from sys_clone.
+int CloneHelper(void* arg) {
+  jmp_buf* env_ptr = reinterpret_cast<jmp_buf*>(arg);
+  longjmp(*env_ptr, 1);
+
+  // Should not be reached.
+  RAW_CHECK(false);
+  return 1;
+}
+
+// This function is noinline to ensure that stack_buf is below the stack pointer
+// that is saved when setjmp is called below. This is needed because when
+// compiled with FORTIFY_SOURCE, glibc's longjmp checks that the stack is moved
+// upwards. See crbug.com/442912 for more details.
+#if defined(ADDRESS_SANITIZER)
+// Disable AddressSanitizer instrumentation for this function to make sure
+// |stack_buf| is allocated on thread stack instead of ASan's fake stack.
+// Under ASan longjmp() will attempt to clean up the area between the old and
+// new stack pointers and print a warning that may confuse the user.
+__attribute__((no_sanitize_address))
+#endif
+NOINLINE pid_t CloneAndLongjmpInChild(unsigned long flags,
+                                      pid_t* ptid,
+                                      pid_t* ctid,
+                                      jmp_buf* env) {
+  // We use the libc clone wrapper instead of making the syscall
+  // directly because making the syscall may fail to update the libc's
+  // internal pid cache. The libc interface unfortunately requires
+  // specifying a new stack, so we use setjmp/longjmp to emulate
+  // fork-like behavior.
+  char stack_buf[PTHREAD_STACK_MIN];
+#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM_FAMILY) || \
+    defined(ARCH_CPU_MIPS64_FAMILY) || defined(ARCH_CPU_MIPS_FAMILY)
+  // The stack grows downward.
+  void* stack = stack_buf + sizeof(stack_buf);
+#else
+#error "Unsupported architecture"
+#endif
+  return clone(&CloneHelper, stack, flags, env, ptid, nullptr, ctid);
+}
+#endif  // defined(OS_LINUX)
+
 }  // anonymous namespace
 
 // Functor for |ScopedDIR| (below).
@@ -671,4 +724,45 @@
   return result == EXECUTE_SUCCESS;
 }
 
+#if defined(OS_LINUX)
+pid_t ForkWithFlags(unsigned long flags, pid_t* ptid, pid_t* ctid) {
+  const bool clone_tls_used = flags & CLONE_SETTLS;
+  const bool invalid_ctid =
+      (flags & (CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID)) && !ctid;
+  const bool invalid_ptid = (flags & CLONE_PARENT_SETTID) && !ptid;
+
+  // We do not support CLONE_VM.
+  const bool clone_vm_used = flags & CLONE_VM;
+
+  if (clone_tls_used || invalid_ctid || invalid_ptid || clone_vm_used) {
+    RAW_LOG(FATAL, "Invalid usage of ForkWithFlags");
+  }
+
+  // Valgrind's clone implementation does not support specifiying a child_stack
+  // without CLONE_VM, so we cannot use libc's clone wrapper when running under
+  // Valgrind. As a result, the libc pid cache may be incorrect under Valgrind.
+  // See crbug.com/442817 for more details.
+  if (IsRunningOnValgrind()) {
+    // See kernel/fork.c in Linux. There is different ordering of sys_clone
+    // parameters depending on CONFIG_CLONE_BACKWARDS* configuration options.
+#if defined(ARCH_CPU_X86_64)
+    return syscall(__NR_clone, flags, nullptr, ptid, ctid, nullptr);
+#elif defined(ARCH_CPU_X86) || defined(ARCH_CPU_ARM_FAMILY) || \
+    defined(ARCH_CPU_MIPS_FAMILY) || defined(ARCH_CPU_MIPS64_FAMILY)
+    // CONFIG_CLONE_BACKWARDS defined.
+    return syscall(__NR_clone, flags, nullptr, ptid, nullptr, ctid);
+#else
+#error "Unsupported architecture"
+#endif
+  }
+
+  jmp_buf env;
+  if (setjmp(env) == 0) {
+    return CloneAndLongjmpInChild(flags, ptid, ctid, &env);
+  }
+
+  return 0;
+}
+#endif  // defined(OS_LINUX)
+
 }  // namespace base
diff --git a/process/process.h b/process/process.h
index b0fd8d9..d556d8e 100644
--- a/process/process.h
+++ b/process/process.h
@@ -117,25 +117,6 @@
 #endif
 };
 
-#if defined(OS_LINUX)
-// A wrapper for clone with fork-like behavior, meaning that it returns the
-// child's pid in the parent and 0 in the child. |flags|, |ptid|, and |ctid| are
-// as in the clone system call (the CLONE_VM flag is not supported).
-//
-// This function uses the libc clone wrapper (which updates libc's pid cache)
-// internally, so callers may expect things like getpid() to work correctly
-// after in both the child and parent. An exception is when this code is run
-// under Valgrind. Valgrind does not support the libc clone wrapper, so the libc
-// pid cache may be incorrect after this function is called under Valgrind.
-//
-// As with fork(), callers should be extremely careful when calling this while
-// multiple threads are running, since at the time the fork happened, the
-// threads could have been in any state (potentially holding locks, etc.).
-// Callers should most likely call execve() in the child soon after calling
-// this.
-BASE_EXPORT pid_t ForkWithFlags(unsigned long flags, pid_t* ptid, pid_t* ctid);
-#endif
-
 }  // namespace base
 
 #endif  // BASE_PROCESS_PROCESS_PROCESS_H_
diff --git a/process/process_linux.cc b/process/process_linux.cc
index cfa3ed5..59ee288 100644
--- a/process/process_linux.cc
+++ b/process/process_linux.cc
@@ -5,21 +5,14 @@
 #include "base/process/process.h"
 
 #include <errno.h>
-#include <pthread.h>
-#include <sched.h>
-#include <setjmp.h>
 #include <sys/resource.h>
-#include <sys/syscall.h>
 
-#include "base/compiler_specific.h"
 #include "base/files/file_util.h"
 #include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/strings/string_split.h"
 #include "base/strings/stringprintf.h"
 #include "base/synchronization/lock.h"
-#include "base/third_party/valgrind/valgrind.h"
-#include "build/build_config.h"
 
 namespace base {
 
@@ -85,52 +78,6 @@
   bool can_reraise_priority;
 };
 
-bool IsRunningOnValgrind() {
-  return RUNNING_ON_VALGRIND;
-}
-
-// This function runs on the stack specified on the clone call. It uses longjmp
-// to switch back to the original stack so the child can return from sys_clone.
-int CloneHelper(void* arg) {
-  jmp_buf* env_ptr = reinterpret_cast<jmp_buf*>(arg);
-  longjmp(*env_ptr, 1);
-
-  // Should not be reached.
-  RAW_CHECK(false);
-  return 1;
-}
-
-// This function is noinline to ensure that stack_buf is below the stack pointer
-// that is saved when setjmp is called below. This is needed because when
-// compiled with FORTIFY_SOURCE, glibc's longjmp checks that the stack is moved
-// upwards. See crbug.com/442912 for more details.
-#if defined(ADDRESS_SANITIZER)
-// Disable AddressSanitizer instrumentation for this function to make sure
-// |stack_buf| is allocated on thread stack instead of ASan's fake stack.
-// Under ASan longjmp() will attempt to clean up the area between the old and
-// new stack pointers and print a warning that may confuse the user.
-__attribute__((no_sanitize_address))
-#endif
-NOINLINE pid_t CloneAndLongjmpInChild(unsigned long flags,
-                                      pid_t* ptid,
-                                      pid_t* ctid,
-                                      jmp_buf* env) {
-  // We use the libc clone wrapper instead of making the syscall
-  // directly because making the syscall may fail to update the libc's
-  // internal pid cache. The libc interface unfortunately requires
-  // specifying a new stack, so we use setjmp/longjmp to emulate
-  // fork-like behavior.
-  char stack_buf[PTHREAD_STACK_MIN];
-#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM_FAMILY) || \
-    defined(ARCH_CPU_MIPS64_FAMILY) || defined(ARCH_CPU_MIPS_FAMILY)
-  // The stack grows downward.
-  void* stack = stack_buf + sizeof(stack_buf);
-#else
-#error "Unsupported architecture"
-#endif
-  return clone(&CloneHelper, stack, flags, env, ptid, nullptr, ctid);
-}
-
 }  // namespace
 
 // static
@@ -189,43 +136,4 @@
   return result == 0;
 }
 
-pid_t ForkWithFlags(unsigned long flags, pid_t* ptid, pid_t* ctid) {
-  const bool clone_tls_used = flags & CLONE_SETTLS;
-  const bool invalid_ctid =
-      (flags & (CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID)) && !ctid;
-  const bool invalid_ptid = (flags & CLONE_PARENT_SETTID) && !ptid;
-
-  // We do not support CLONE_VM.
-  const bool clone_vm_used = flags & CLONE_VM;
-
-  if (clone_tls_used || invalid_ctid || invalid_ptid || clone_vm_used) {
-    RAW_LOG(FATAL, "Invalid usage of ForkWithFlags");
-  }
-
-  // Valgrind's clone implementation does not support specifiying a child_stack
-  // without CLONE_VM, so we cannot use libc's clone wrapper when running under
-  // Valgrind. As a result, the libc pid cache may be incorrect under Valgrind.
-  // See crbug.com/442817 for more details.
-  if (IsRunningOnValgrind()) {
-    // See kernel/fork.c in Linux. There is different ordering of sys_clone
-    // parameters depending on CONFIG_CLONE_BACKWARDS* configuration options.
-#if defined(ARCH_CPU_X86_64)
-    return syscall(__NR_clone, flags, nullptr, ptid, ctid, nullptr);
-#elif defined(ARCH_CPU_X86) || defined(ARCH_CPU_ARM_FAMILY) || \
-    defined(ARCH_CPU_MIPS_FAMILY) || defined(ARCH_CPU_MIPS64_FAMILY)
-    // CONFIG_CLONE_BACKWARDS defined.
-    return syscall(__NR_clone, flags, nullptr, ptid, nullptr, ctid);
-#else
-#error "Unsupported architecture"
-#endif
-  }
-
-  jmp_buf env;
-  if (setjmp(env) == 0) {
-    return CloneAndLongjmpInChild(flags, ptid, ctid, &env);
-  }
-
-  return 0;
-}
-
 }  // namespace base
diff --git a/process/process_metrics.h b/process/process_metrics.h
index d06e018..d011958 100644
--- a/process/process_metrics.h
+++ b/process/process_metrics.h
@@ -85,17 +85,6 @@
   size_t image;
 };
 
-// Free memory (Megabytes marked as free) in the 2G process address space.
-// total : total amount in megabytes marked as free. Maximum value is 2048.
-// largest : size of the largest contiguous amount of memory found. It is
-//   always smaller or equal to FreeMBytes::total.
-// largest_ptr: starting address of the largest memory block.
-struct FreeMBytes {
-  size_t total;
-  size_t largest;
-  void* largest_ptr;
-};
-
 // Convert a POSIX timeval to microseconds.
 BASE_EXPORT int64 TimeValToMicroseconds(const struct timeval& tv);
 
diff --git a/process/process_unittest.cc b/process/process_unittest.cc
index 8130726..1a2af50 100644
--- a/process/process_unittest.cc
+++ b/process/process_unittest.cc
@@ -4,28 +4,13 @@
 
 #include "base/process/process.h"
 
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/files/scoped_file.h"
-#include "base/posix/eintr_wrapper.h"
 #include "base/process/kill.h"
 #include "base/test/multiprocess_test.h"
 #include "base/test/test_timeouts.h"
-#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
 #include "base/threading/platform_thread.h"
-#include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/multiprocess_func_list.h"
 
-#if defined(OS_LINUX)
-#include <errno.h>
-#include <sched.h>
-#include <sys/syscall.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-#endif
-
 namespace {
 
 #if defined(OS_WIN)
@@ -215,105 +200,4 @@
   EXPECT_EQ(old_priority, new_priority);
 }
 
-#if defined(OS_LINUX)
-const int kSuccess = 0;
-
-MULTIPROCESS_TEST_MAIN(CheckPidProcess) {
-  const pid_t kInitPid = 1;
-  const pid_t pid = syscall(__NR_getpid);
-  CHECK(pid == kInitPid);
-  CHECK(getpid() == pid);
-  return kSuccess;
-}
-
-TEST_F(ProcessTest, CloneFlags) {
-  if (RunningOnValgrind() || !PathExists(FilePath("/proc/self/ns/user")) ||
-      !PathExists(FilePath("/proc/self/ns/pid"))) {
-    // User or PID namespaces are not supported.
-    return;
-  }
-
-  LaunchOptions options;
-  options.clone_flags = CLONE_NEWUSER | CLONE_NEWPID;
-
-  Process process(SpawnChildWithOptions("CheckPidProcess", options));
-  ASSERT_TRUE(process.IsValid());
-
-  int exit_code = 42;
-  EXPECT_TRUE(process.WaitForExit(&exit_code));
-  EXPECT_EQ(kSuccess, exit_code);
-}
-
-TEST(ForkWithFlagsTest, UpdatesPidCache) {
-  // The libc clone function, which allows ForkWithFlags to keep the pid cache
-  // up to date, does not work on Valgrind.
-  if (RunningOnValgrind()) {
-    return;
-  }
-
-  // Warm up the libc pid cache, if there is one.
-  ASSERT_EQ(syscall(__NR_getpid), getpid());
-
-  pid_t ctid = 0;
-  const pid_t pid = ForkWithFlags(SIGCHLD | CLONE_CHILD_SETTID, nullptr, &ctid);
-  if (pid == 0) {
-    // In child.  Check both the raw getpid syscall and the libc getpid wrapper
-    // (which may rely on a pid cache).
-    RAW_CHECK(syscall(__NR_getpid) == ctid);
-    RAW_CHECK(getpid() == ctid);
-    _exit(kSuccess);
-  }
-
-  ASSERT_NE(-1, pid);
-  int status = 42;
-  ASSERT_EQ(pid, HANDLE_EINTR(waitpid(pid, &status, 0)));
-  ASSERT_TRUE(WIFEXITED(status));
-  EXPECT_EQ(kSuccess, WEXITSTATUS(status));
-}
-#endif
-
-#if defined(OS_POSIX) && !defined(OS_ANDROID)
-const char kPipeValue = '\xcc';
-
-class ReadFromPipeDelegate : public LaunchOptions::PreExecDelegate {
- public:
-  explicit ReadFromPipeDelegate(int fd) : fd_(fd) {}
-  ~ReadFromPipeDelegate() override {}
-  void RunAsyncSafe() override {
-    char c;
-    RAW_CHECK(HANDLE_EINTR(read(fd_, &c, 1)) == 1);
-    RAW_CHECK(IGNORE_EINTR(close(fd_)) == 0);
-    RAW_CHECK(c == kPipeValue);
-  }
-
- private:
-  int fd_;
-  DISALLOW_COPY_AND_ASSIGN(ReadFromPipeDelegate);
-};
-
-TEST_F(ProcessTest, PreExecHook) {
-  int pipe_fds[2];
-  ASSERT_EQ(0, pipe(pipe_fds));
-
-  ScopedFD read_fd(pipe_fds[0]);
-  ScopedFD write_fd(pipe_fds[1]);
-  base::FileHandleMappingVector fds_to_remap;
-  fds_to_remap.push_back(std::make_pair(read_fd.get(), read_fd.get()));
-
-  ReadFromPipeDelegate read_from_pipe_delegate(read_fd.get());
-  LaunchOptions options;
-  options.fds_to_remap = &fds_to_remap;
-  options.pre_exec_delegate = &read_from_pipe_delegate;
-  Process process(SpawnChildWithOptions("SimpleChildProcess", options));
-  ASSERT_TRUE(process.IsValid());
-
-  read_fd.reset();
-  ASSERT_EQ(1, HANDLE_EINTR(write(write_fd.get(), &kPipeValue, 1)));
-
-  int exit_code = 42;
-  EXPECT_TRUE(process.WaitForExit(&exit_code));
-  EXPECT_EQ(0, exit_code);
-}
-#endif  // defined(OS_POSIX) && !defined(OS_ANDROID)
-
 }  // namespace base
diff --git a/process/process_util_unittest.cc b/process/process_util_unittest.cc
index 4b2a575..88b4af3 100644
--- a/process/process_util_unittest.cc
+++ b/process/process_util_unittest.cc
@@ -10,6 +10,8 @@
 #include "base/debug/alias.h"
 #include "base/debug/stack_trace.h"
 #include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/path_service.h"
@@ -27,21 +29,26 @@
 #include "base/third_party/dynamic_annotations/dynamic_annotations.h"
 #include "base/threading/platform_thread.h"
 #include "base/threading/thread.h"
+#include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/multiprocess_func_list.h"
 
 #if defined(OS_LINUX)
 #include <malloc.h>
 #include <sched.h>
+#include <sys/syscall.h>
 #endif
 #if defined(OS_POSIX)
 #include <dlfcn.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <sched.h>
 #include <signal.h>
 #include <sys/resource.h>
 #include <sys/socket.h>
+#include <sys/types.h>
 #include <sys/wait.h>
+#include <unistd.h>
 #endif
 #if defined(OS_WIN)
 #include <windows.h>
@@ -913,4 +920,107 @@
   return 0;
 }
 
+#if !defined(OS_ANDROID)
+const char kPipeValue = '\xcc';
+
+class ReadFromPipeDelegate : public base::LaunchOptions::PreExecDelegate {
+ public:
+  explicit ReadFromPipeDelegate(int fd) : fd_(fd) {}
+  ~ReadFromPipeDelegate() override {}
+  void RunAsyncSafe() override {
+    char c;
+    RAW_CHECK(HANDLE_EINTR(read(fd_, &c, 1)) == 1);
+    RAW_CHECK(IGNORE_EINTR(close(fd_)) == 0);
+    RAW_CHECK(c == kPipeValue);
+  }
+
+ private:
+  int fd_;
+  DISALLOW_COPY_AND_ASSIGN(ReadFromPipeDelegate);
+};
+
+TEST_F(ProcessUtilTest, PreExecHook) {
+  int pipe_fds[2];
+  ASSERT_EQ(0, pipe(pipe_fds));
+
+  base::ScopedFD read_fd(pipe_fds[0]);
+  base::ScopedFD write_fd(pipe_fds[1]);
+  base::FileHandleMappingVector fds_to_remap;
+  fds_to_remap.push_back(std::make_pair(read_fd.get(), read_fd.get()));
+
+  ReadFromPipeDelegate read_from_pipe_delegate(read_fd.get());
+  base::LaunchOptions options;
+  options.fds_to_remap = &fds_to_remap;
+  options.pre_exec_delegate = &read_from_pipe_delegate;
+  base::Process process(SpawnChildWithOptions("SimpleChildProcess", options));
+  ASSERT_TRUE(process.IsValid());
+
+  read_fd.reset();
+  ASSERT_EQ(1, HANDLE_EINTR(write(write_fd.get(), &kPipeValue, 1)));
+
+  int exit_code = 42;
+  EXPECT_TRUE(process.WaitForExit(&exit_code));
+  EXPECT_EQ(0, exit_code);
+}
+#endif  // !defined(OS_ANDROID)
+
 #endif  // defined(OS_POSIX)
+
+#if defined(OS_LINUX)
+const int kSuccess = 0;
+
+MULTIPROCESS_TEST_MAIN(CheckPidProcess) {
+  const pid_t kInitPid = 1;
+  const pid_t pid = syscall(__NR_getpid);
+  CHECK(pid == kInitPid);
+  CHECK(getpid() == pid);
+  return kSuccess;
+}
+
+TEST_F(ProcessUtilTest, CloneFlags) {
+  if (RunningOnValgrind() ||
+      !base::PathExists(FilePath("/proc/self/ns/user")) ||
+      !base::PathExists(FilePath("/proc/self/ns/pid"))) {
+    // User or PID namespaces are not supported.
+    return;
+  }
+
+  base::LaunchOptions options;
+  options.clone_flags = CLONE_NEWUSER | CLONE_NEWPID;
+
+  base::Process process(SpawnChildWithOptions("CheckPidProcess", options));
+  ASSERT_TRUE(process.IsValid());
+
+  int exit_code = 42;
+  EXPECT_TRUE(process.WaitForExit(&exit_code));
+  EXPECT_EQ(kSuccess, exit_code);
+}
+
+TEST(ForkWithFlagsTest, UpdatesPidCache) {
+  // The libc clone function, which allows ForkWithFlags to keep the pid cache
+  // up to date, does not work on Valgrind.
+  if (RunningOnValgrind()) {
+    return;
+  }
+
+  // Warm up the libc pid cache, if there is one.
+  ASSERT_EQ(syscall(__NR_getpid), getpid());
+
+  pid_t ctid = 0;
+  const pid_t pid =
+      base::ForkWithFlags(SIGCHLD | CLONE_CHILD_SETTID, nullptr, &ctid);
+  if (pid == 0) {
+    // In child.  Check both the raw getpid syscall and the libc getpid wrapper
+    // (which may rely on a pid cache).
+    RAW_CHECK(syscall(__NR_getpid) == ctid);
+    RAW_CHECK(getpid() == ctid);
+    _exit(kSuccess);
+  }
+
+  ASSERT_NE(-1, pid);
+  int status = 42;
+  ASSERT_EQ(pid, HANDLE_EINTR(waitpid(pid, &status, 0)));
+  ASSERT_TRUE(WIFEXITED(status));
+  EXPECT_EQ(kSuccess, WEXITSTATUS(status));
+}
+#endif
diff --git a/test/android/javatests/src/org/chromium/base/test/util/MetricsUtils.java b/test/android/javatests/src/org/chromium/base/test/util/MetricsUtils.java
new file mode 100644
index 0000000..c4664d6
--- /dev/null
+++ b/test/android/javatests/src/org/chromium/base/test/util/MetricsUtils.java
@@ -0,0 +1,43 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.test.util;
+
+import org.chromium.base.metrics.RecordHistogram;
+
+/**
+ * Helpers for testing UMA metrics.
+ */
+public class MetricsUtils {
+    /**
+     * Helper class that snapshots the given bucket of the given UMA histogram on its creation,
+     * allowing to inspect the number of samples recorded during its lifetime.
+     */
+    public static class HistogramDelta {
+        private final String mHistogram;
+        private final int mSampleValue;
+
+        private final int mInitialCount;
+
+        private int get() {
+            return RecordHistogram.getHistogramValueCountForTesting(mHistogram, mSampleValue);
+        }
+
+        /**
+         * Snapshots the given bucket of the given histogram.
+         * @param histogram name of the histogram to snapshot
+         * @param sampleValue the bucket that contains this value will be snapshot
+         */
+        public HistogramDelta(String histogram, int sampleValue) {
+            mHistogram = histogram;
+            mSampleValue = sampleValue;
+            mInitialCount = get();
+        }
+
+        /** Returns the number of samples of the snapshot bucket recorded since creation */
+        public int getDelta() {
+            return get() - mInitialCount;
+        }
+    }
+}
diff --git a/test/launcher/unit_test_launcher.cc b/test/launcher/unit_test_launcher.cc
index 9a8b41f..a31b6db 100644
--- a/test/launcher/unit_test_launcher.cc
+++ b/test/launcher/unit_test_launcher.cc
@@ -244,325 +244,302 @@
 
 bool UnitTestLauncherDelegate::ShouldRunTest(const std::string& test_case_name,
                                              const std::string& test_name) {
-    DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(thread_checker_.CalledOnValidThread());
 
-    // There is no additional logic to disable specific tests.
-    return true;
+  // There is no additional logic to disable specific tests.
+  return true;
+}
+
+size_t UnitTestLauncherDelegate::RunTests(
+    TestLauncher* test_launcher,
+    const std::vector<std::string>& test_names) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  std::vector<std::string> batch;
+  for (size_t i = 0; i < test_names.size(); i++) {
+    batch.push_back(test_names[i]);
+
+    if (batch.size() >= batch_limit_) {
+      RunBatch(test_launcher, batch);
+      batch.clear();
+    }
   }
 
-  size_t UnitTestLauncherDelegate::RunTests(
-      TestLauncher* test_launcher,
-      const std::vector<std::string>& test_names) {
-    DCHECK(thread_checker_.CalledOnValidThread());
+  RunBatch(test_launcher, batch);
 
+  return test_names.size();
+}
+
+size_t UnitTestLauncherDelegate::RetryTests(
+    TestLauncher* test_launcher,
+    const std::vector<std::string>& test_names) {
+  MessageLoop::current()->PostTask(
+      FROM_HERE, Bind(&UnitTestLauncherDelegate::RunSerially, Unretained(this),
+                      test_launcher, test_names));
+  return test_names.size();
+}
+
+void UnitTestLauncherDelegate::RunSerially(
+    TestLauncher* test_launcher,
+    const std::vector<std::string>& test_names) {
+  if (test_names.empty())
+    return;
+
+  std::vector<std::string> new_test_names(test_names);
+  std::string test_name(new_test_names.back());
+  new_test_names.pop_back();
+
+  // Create a dedicated temporary directory to store the xml result data
+  // per run to ensure clean state and make it possible to launch multiple
+  // processes in parallel.
+  base::FilePath output_file;
+  CHECK(CreateNewTempDirectory(FilePath::StringType(), &output_file));
+  output_file = output_file.AppendASCII("test_results.xml");
+
+  std::vector<std::string> current_test_names;
+  current_test_names.push_back(test_name);
+  CommandLine cmd_line(
+      GetCommandLineForChildGTestProcess(current_test_names, output_file));
+
+  GTestCallbackState callback_state;
+  callback_state.test_launcher = test_launcher;
+  callback_state.test_names = current_test_names;
+  callback_state.output_file = output_file;
+
+  test_launcher->LaunchChildGTestProcess(
+      cmd_line, std::string(), TestTimeouts::test_launcher_timeout(),
+      use_job_objects_ ? TestLauncher::USE_JOB_OBJECTS : 0,
+      Bind(&UnitTestLauncherDelegate::SerialGTestCallback, Unretained(this),
+           callback_state, new_test_names));
+}
+
+void UnitTestLauncherDelegate::RunBatch(
+    TestLauncher* test_launcher,
+    const std::vector<std::string>& test_names) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  if (test_names.empty())
+    return;
+
+  // Create a dedicated temporary directory to store the xml result data
+  // per run to ensure clean state and make it possible to launch multiple
+  // processes in parallel.
+  base::FilePath output_file;
+  CHECK(CreateNewTempDirectory(FilePath::StringType(), &output_file));
+  output_file = output_file.AppendASCII("test_results.xml");
+
+  CommandLine cmd_line(
+      GetCommandLineForChildGTestProcess(test_names, output_file));
+
+  // Adjust the timeout depending on how many tests we're running
+  // (note that e.g. the last batch of tests will be smaller).
+  // TODO(phajdan.jr): Consider an adaptive timeout, which can change
+  // depending on how many tests ran and how many remain.
+  // Note: do NOT parse child's stdout to do that, it's known to be
+  // unreliable (e.g. buffering issues can mix up the output).
+  base::TimeDelta timeout =
+      test_names.size() * TestTimeouts::test_launcher_timeout();
+
+  GTestCallbackState callback_state;
+  callback_state.test_launcher = test_launcher;
+  callback_state.test_names = test_names;
+  callback_state.output_file = output_file;
+
+  test_launcher->LaunchChildGTestProcess(
+      cmd_line, std::string(), timeout,
+      use_job_objects_ ? TestLauncher::USE_JOB_OBJECTS : 0,
+      Bind(&UnitTestLauncherDelegate::GTestCallback, Unretained(this),
+           callback_state));
+}
+
+void UnitTestLauncherDelegate::GTestCallback(
+    const GTestCallbackState& callback_state,
+    int exit_code,
+    const TimeDelta& elapsed_time,
+    bool was_timeout,
+    const std::string& output) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  std::vector<std::string> tests_to_relaunch;
+  ProcessTestResults(callback_state.test_launcher, callback_state.test_names,
+                     callback_state.output_file, output, exit_code, was_timeout,
+                     &tests_to_relaunch);
+
+  // Relaunch requested tests in parallel, but only use single
+  // test per batch for more precise results (crashes, test passes
+  // but non-zero exit codes etc).
+  for (size_t i = 0; i < tests_to_relaunch.size(); i++) {
     std::vector<std::string> batch;
+    batch.push_back(tests_to_relaunch[i]);
+    RunBatch(callback_state.test_launcher, batch);
+  }
+
+  // The temporary file's directory is also temporary.
+  DeleteFile(callback_state.output_file.DirName(), true);
+}
+
+void UnitTestLauncherDelegate::SerialGTestCallback(
+    const GTestCallbackState& callback_state,
+    const std::vector<std::string>& test_names,
+    int exit_code,
+    const TimeDelta& elapsed_time,
+    bool was_timeout,
+    const std::string& output) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  std::vector<std::string> tests_to_relaunch;
+  bool called_any_callbacks =
+      ProcessTestResults(callback_state.test_launcher,
+                         callback_state.test_names, callback_state.output_file,
+                         output, exit_code, was_timeout, &tests_to_relaunch);
+
+  // There is only one test, there cannot be other tests to relaunch
+  // due to a crash.
+  DCHECK(tests_to_relaunch.empty());
+
+  // There is only one test, we should have called back with its result.
+  DCHECK(called_any_callbacks);
+
+  // The temporary file's directory is also temporary.
+  DeleteFile(callback_state.output_file.DirName(), true);
+
+  MessageLoop::current()->PostTask(
+      FROM_HERE, Bind(&UnitTestLauncherDelegate::RunSerially, Unretained(this),
+                      callback_state.test_launcher, test_names));
+}
+
+// static
+bool UnitTestLauncherDelegate::ProcessTestResults(
+    TestLauncher* test_launcher,
+    const std::vector<std::string>& test_names,
+    const base::FilePath& output_file,
+    const std::string& output,
+    int exit_code,
+    bool was_timeout,
+    std::vector<std::string>* tests_to_relaunch) {
+  std::vector<TestResult> test_results;
+  bool crashed = false;
+  bool have_test_results =
+      ProcessGTestOutput(output_file, &test_results, &crashed);
+
+  bool called_any_callback = false;
+
+  if (have_test_results) {
+    // TODO(phajdan.jr): Check for duplicates and mismatches between
+    // the results we got from XML file and tests we intended to run.
+    std::map<std::string, TestResult> results_map;
+    for (size_t i = 0; i < test_results.size(); i++)
+      results_map[test_results[i].full_name] = test_results[i];
+
+    bool had_interrupted_test = false;
+
+    // Results to be reported back to the test launcher.
+    std::vector<TestResult> final_results;
+
     for (size_t i = 0; i < test_names.size(); i++) {
-      batch.push_back(test_names[i]);
+      if (ContainsKey(results_map, test_names[i])) {
+        TestResult test_result = results_map[test_names[i]];
+        if (test_result.status == TestResult::TEST_CRASH) {
+          had_interrupted_test = true;
 
-      if (batch.size() >= batch_limit_) {
-        RunBatch(test_launcher, batch);
-        batch.clear();
-      }
-    }
-
-    RunBatch(test_launcher, batch);
-
-    return test_names.size();
-  }
-
-  size_t UnitTestLauncherDelegate::RetryTests(
-      TestLauncher* test_launcher,
-      const std::vector<std::string>& test_names) {
-    MessageLoop::current()->PostTask(
-        FROM_HERE,
-        Bind(&UnitTestLauncherDelegate::RunSerially,
-             Unretained(this),
-             test_launcher,
-             test_names));
-    return test_names.size();
-  }
-
-  void UnitTestLauncherDelegate::RunSerially(
-      TestLauncher* test_launcher,
-      const std::vector<std::string>& test_names) {
-    if (test_names.empty())
-      return;
-
-    std::vector<std::string> new_test_names(test_names);
-    std::string test_name(new_test_names.back());
-    new_test_names.pop_back();
-
-    // Create a dedicated temporary directory to store the xml result data
-    // per run to ensure clean state and make it possible to launch multiple
-    // processes in parallel.
-    base::FilePath output_file;
-    CHECK(CreateNewTempDirectory(FilePath::StringType(), &output_file));
-    output_file = output_file.AppendASCII("test_results.xml");
-
-    std::vector<std::string> current_test_names;
-    current_test_names.push_back(test_name);
-    CommandLine cmd_line(
-        GetCommandLineForChildGTestProcess(current_test_names, output_file));
-
-    GTestCallbackState callback_state;
-    callback_state.test_launcher = test_launcher;
-    callback_state.test_names = current_test_names;
-    callback_state.output_file = output_file;
-
-    test_launcher->LaunchChildGTestProcess(
-        cmd_line,
-        std::string(),
-        TestTimeouts::test_launcher_timeout(),
-        use_job_objects_ ? TestLauncher::USE_JOB_OBJECTS : 0,
-        Bind(&UnitTestLauncherDelegate::SerialGTestCallback,
-             Unretained(this),
-             callback_state,
-             new_test_names));
-  }
-
-  void UnitTestLauncherDelegate::RunBatch(
-      TestLauncher* test_launcher,
-      const std::vector<std::string>& test_names) {
-    DCHECK(thread_checker_.CalledOnValidThread());
-
-    if (test_names.empty())
-      return;
-
-    // Create a dedicated temporary directory to store the xml result data
-    // per run to ensure clean state and make it possible to launch multiple
-    // processes in parallel.
-    base::FilePath output_file;
-    CHECK(CreateNewTempDirectory(FilePath::StringType(), &output_file));
-    output_file = output_file.AppendASCII("test_results.xml");
-
-    CommandLine cmd_line(
-        GetCommandLineForChildGTestProcess(test_names, output_file));
-
-    // Adjust the timeout depending on how many tests we're running
-    // (note that e.g. the last batch of tests will be smaller).
-    // TODO(phajdan.jr): Consider an adaptive timeout, which can change
-    // depending on how many tests ran and how many remain.
-    // Note: do NOT parse child's stdout to do that, it's known to be
-    // unreliable (e.g. buffering issues can mix up the output).
-    base::TimeDelta timeout =
-        test_names.size() * TestTimeouts::test_launcher_timeout();
-
-    GTestCallbackState callback_state;
-    callback_state.test_launcher = test_launcher;
-    callback_state.test_names = test_names;
-    callback_state.output_file = output_file;
-
-    test_launcher->LaunchChildGTestProcess(
-        cmd_line,
-        std::string(),
-        timeout,
-        use_job_objects_ ? TestLauncher::USE_JOB_OBJECTS : 0,
-        Bind(&UnitTestLauncherDelegate::GTestCallback,
-             Unretained(this),
-             callback_state));
-  }
-
-  void UnitTestLauncherDelegate::GTestCallback(
-      const GTestCallbackState& callback_state,
-      int exit_code,
-      const TimeDelta& elapsed_time,
-      bool was_timeout,
-      const std::string& output) {
-    DCHECK(thread_checker_.CalledOnValidThread());
-    std::vector<std::string> tests_to_relaunch;
-    ProcessTestResults(callback_state.test_launcher,
-                       callback_state.test_names,
-                       callback_state.output_file,
-                       output,
-                       exit_code,
-                       was_timeout,
-                       &tests_to_relaunch);
-
-    // Relaunch requested tests in parallel, but only use single
-    // test per batch for more precise results (crashes, test passes
-    // but non-zero exit codes etc).
-    for (size_t i = 0; i < tests_to_relaunch.size(); i++) {
-      std::vector<std::string> batch;
-      batch.push_back(tests_to_relaunch[i]);
-      RunBatch(callback_state.test_launcher, batch);
-    }
-
-    // The temporary file's directory is also temporary.
-    DeleteFile(callback_state.output_file.DirName(), true);
-  }
-
-  void UnitTestLauncherDelegate::SerialGTestCallback(
-      const GTestCallbackState& callback_state,
-      const std::vector<std::string>& test_names,
-      int exit_code,
-      const TimeDelta& elapsed_time,
-      bool was_timeout,
-      const std::string& output) {
-    DCHECK(thread_checker_.CalledOnValidThread());
-    std::vector<std::string> tests_to_relaunch;
-    bool called_any_callbacks =
-        ProcessTestResults(callback_state.test_launcher,
-                           callback_state.test_names,
-                           callback_state.output_file,
-                           output,
-                           exit_code,
-                           was_timeout,
-                           &tests_to_relaunch);
-
-    // There is only one test, there cannot be other tests to relaunch
-    // due to a crash.
-    DCHECK(tests_to_relaunch.empty());
-
-    // There is only one test, we should have called back with its result.
-    DCHECK(called_any_callbacks);
-
-    // The temporary file's directory is also temporary.
-    DeleteFile(callback_state.output_file.DirName(), true);
-
-    MessageLoop::current()->PostTask(
-        FROM_HERE,
-        Bind(&UnitTestLauncherDelegate::RunSerially,
-             Unretained(this),
-             callback_state.test_launcher,
-             test_names));
-  }
-
-  // static
-  bool UnitTestLauncherDelegate::ProcessTestResults(
-      TestLauncher* test_launcher,
-      const std::vector<std::string>& test_names,
-      const base::FilePath& output_file,
-      const std::string& output,
-      int exit_code,
-      bool was_timeout,
-      std::vector<std::string>* tests_to_relaunch) {
-    std::vector<TestResult> test_results;
-    bool crashed = false;
-    bool have_test_results =
-        ProcessGTestOutput(output_file, &test_results, &crashed);
-
-    bool called_any_callback = false;
-
-    if (have_test_results) {
-      // TODO(phajdan.jr): Check for duplicates and mismatches between
-      // the results we got from XML file and tests we intended to run.
-      std::map<std::string, TestResult> results_map;
-      for (size_t i = 0; i < test_results.size(); i++)
-        results_map[test_results[i].full_name] = test_results[i];
-
-      bool had_interrupted_test = false;
-
-      // Results to be reported back to the test launcher.
-      std::vector<TestResult> final_results;
-
-      for (size_t i = 0; i < test_names.size(); i++) {
-        if (ContainsKey(results_map, test_names[i])) {
-          TestResult test_result = results_map[test_names[i]];
-          if (test_result.status == TestResult::TEST_CRASH) {
-            had_interrupted_test = true;
-
-            if (was_timeout) {
-              // Fix up the test status: we forcibly kill the child process
-              // after the timeout, so from XML results it looks just like
-              // a crash.
-              test_result.status = TestResult::TEST_TIMEOUT;
-            }
-          } else if (test_result.status == TestResult::TEST_SUCCESS ||
-                     test_result.status == TestResult::TEST_FAILURE) {
-            // We run multiple tests in a batch with a timeout applied
-            // to the entire batch. It is possible that with other tests
-            // running quickly some tests take longer than the per-test timeout.
-            // For consistent handling of tests independent of order and other
-            // factors, mark them as timing out.
-            if (test_result.elapsed_time >
-                TestTimeouts::test_launcher_timeout()) {
-              test_result.status = TestResult::TEST_TIMEOUT;
-            }
+          if (was_timeout) {
+            // Fix up the test status: we forcibly kill the child process
+            // after the timeout, so from XML results it looks just like
+            // a crash.
+            test_result.status = TestResult::TEST_TIMEOUT;
           }
-          test_result.output_snippet =
-              GetTestOutputSnippet(test_result, output);
-          final_results.push_back(test_result);
-        } else if (had_interrupted_test) {
-          tests_to_relaunch->push_back(test_names[i]);
-        } else {
-          // TODO(phajdan.jr): Explicitly pass the info that the test didn't
-          // run for a mysterious reason.
-          LOG(ERROR) << "no test result for " << test_names[i];
-          TestResult test_result;
-          test_result.full_name = test_names[i];
-          test_result.status = TestResult::TEST_UNKNOWN;
-          test_result.output_snippet =
-              GetTestOutputSnippet(test_result, output);
-          final_results.push_back(test_result);
+        } else if (test_result.status == TestResult::TEST_SUCCESS ||
+                   test_result.status == TestResult::TEST_FAILURE) {
+          // We run multiple tests in a batch with a timeout applied
+          // to the entire batch. It is possible that with other tests
+          // running quickly some tests take longer than the per-test timeout.
+          // For consistent handling of tests independent of order and other
+          // factors, mark them as timing out.
+          if (test_result.elapsed_time >
+              TestTimeouts::test_launcher_timeout()) {
+            test_result.status = TestResult::TEST_TIMEOUT;
+          }
         }
-      }
-
-      // TODO(phajdan.jr): Handle the case where processing XML output
-      // indicates a crash but none of the test results is marked as crashing.
-
-      if (final_results.empty())
-        return false;
-
-      bool has_non_success_test = false;
-      for (size_t i = 0; i < final_results.size(); i++) {
-        if (final_results[i].status != TestResult::TEST_SUCCESS) {
-          has_non_success_test = true;
-          break;
-        }
-      }
-
-      if (!has_non_success_test && exit_code != 0) {
-        // This is a bit surprising case: all tests are marked as successful,
-        // but the exit code was not zero. This can happen e.g. under memory
-        // tools that report leaks this way.
-
-        if (final_results.size() == 1) {
-          // Easy case. One test only so we know the non-zero exit code
-          // was caused by that one test.
-          final_results[0].status = TestResult::TEST_FAILURE_ON_EXIT;
-        } else {
-          // Harder case. Discard the results and request relaunching all
-          // tests without batching. This will trigger above branch on
-          // relaunch leading to more precise results.
-          LOG(WARNING) << "Not sure which test caused non-zero exit code, "
-                       << "relaunching all of them without batching.";
-
-          for (size_t i = 0; i < final_results.size(); i++)
-            tests_to_relaunch->push_back(final_results[i].full_name);
-
-          return false;
-        }
-      }
-
-      for (size_t i = 0; i < final_results.size(); i++) {
-        // Fix the output snippet after possible changes to the test result.
-        final_results[i].output_snippet =
-            GetTestOutputSnippet(final_results[i], output);
-        test_launcher->OnTestFinished(final_results[i]);
-        called_any_callback = true;
-      }
-    } else {
-      fprintf(stdout,
-              "Failed to get out-of-band test success data, "
-              "dumping full stdio below:\n%s\n",
-              output.c_str());
-      fflush(stdout);
-
-      // We do not have reliable details about test results (parsing test
-      // stdout is known to be unreliable), apply the executable exit code
-      // to all tests.
-      // TODO(phajdan.jr): Be smarter about this, e.g. retry each test
-      // individually.
-      for (size_t i = 0; i < test_names.size(); i++) {
+        test_result.output_snippet = GetTestOutputSnippet(test_result, output);
+        final_results.push_back(test_result);
+      } else if (had_interrupted_test) {
+        tests_to_relaunch->push_back(test_names[i]);
+      } else {
+        // TODO(phajdan.jr): Explicitly pass the info that the test didn't
+        // run for a mysterious reason.
+        LOG(ERROR) << "no test result for " << test_names[i];
         TestResult test_result;
         test_result.full_name = test_names[i];
         test_result.status = TestResult::TEST_UNKNOWN;
-        test_launcher->OnTestFinished(test_result);
-        called_any_callback = true;
+        test_result.output_snippet = GetTestOutputSnippet(test_result, output);
+        final_results.push_back(test_result);
       }
     }
 
-    return called_any_callback;
+    // TODO(phajdan.jr): Handle the case where processing XML output
+    // indicates a crash but none of the test results is marked as crashing.
+
+    if (final_results.empty())
+      return false;
+
+    bool has_non_success_test = false;
+    for (size_t i = 0; i < final_results.size(); i++) {
+      if (final_results[i].status != TestResult::TEST_SUCCESS) {
+        has_non_success_test = true;
+        break;
+      }
+    }
+
+    if (!has_non_success_test && exit_code != 0) {
+      // This is a bit surprising case: all tests are marked as successful,
+      // but the exit code was not zero. This can happen e.g. under memory
+      // tools that report leaks this way.
+
+      if (final_results.size() == 1) {
+        // Easy case. One test only so we know the non-zero exit code
+        // was caused by that one test.
+        final_results[0].status = TestResult::TEST_FAILURE_ON_EXIT;
+      } else {
+        // Harder case. Discard the results and request relaunching all
+        // tests without batching. This will trigger above branch on
+        // relaunch leading to more precise results.
+        LOG(WARNING) << "Not sure which test caused non-zero exit code, "
+                     << "relaunching all of them without batching.";
+
+        for (size_t i = 0; i < final_results.size(); i++)
+          tests_to_relaunch->push_back(final_results[i].full_name);
+
+        return false;
+      }
+    }
+
+    for (size_t i = 0; i < final_results.size(); i++) {
+      // Fix the output snippet after possible changes to the test result.
+      final_results[i].output_snippet =
+          GetTestOutputSnippet(final_results[i], output);
+      test_launcher->OnTestFinished(final_results[i]);
+      called_any_callback = true;
+    }
+  } else {
+    fprintf(stdout,
+            "Failed to get out-of-band test success data, "
+            "dumping full stdio below:\n%s\n",
+            output.c_str());
+    fflush(stdout);
+
+    // We do not have reliable details about test results (parsing test
+    // stdout is known to be unreliable), apply the executable exit code
+    // to all tests.
+    // TODO(phajdan.jr): Be smarter about this, e.g. retry each test
+    // individually.
+    for (size_t i = 0; i < test_names.size(); i++) {
+      TestResult test_result;
+      test_result.full_name = test_names[i];
+      test_result.status = TestResult::TEST_UNKNOWN;
+      test_launcher->OnTestFinished(test_result);
+      called_any_callback = true;
+    }
   }
 
+  return called_any_callback;
+}
+
 }  // namespace base
diff --git a/tuple.h b/tuple.h
index 4408074..41c6a00 100644
--- a/tuple.h
+++ b/tuple.h
@@ -40,6 +40,50 @@
 template <size_t... Ns>
 struct MakeIndexSequenceImpl;
 
+#if defined(_PREFAST_) && defined(OS_WIN)
+
+// Work around VC++ 2013 /analyze internal compiler error:
+// https://connect.microsoft.com/VisualStudio/feedback/details/1053626
+
+template <> struct MakeIndexSequenceImpl<0> {
+  using Type = IndexSequence<>;
+};
+template <> struct MakeIndexSequenceImpl<1> {
+  using Type = IndexSequence<0>;
+};
+template <> struct MakeIndexSequenceImpl<2> {
+  using Type = IndexSequence<0,1>;
+};
+template <> struct MakeIndexSequenceImpl<3> {
+  using Type = IndexSequence<0,1,2>;
+};
+template <> struct MakeIndexSequenceImpl<4> {
+  using Type = IndexSequence<0,1,2,3>;
+};
+template <> struct MakeIndexSequenceImpl<5> {
+  using Type = IndexSequence<0,1,2,3,4>;
+};
+template <> struct MakeIndexSequenceImpl<6> {
+  using Type = IndexSequence<0,1,2,3,4,5>;
+};
+template <> struct MakeIndexSequenceImpl<7> {
+  using Type = IndexSequence<0,1,2,3,4,5,6>;
+};
+template <> struct MakeIndexSequenceImpl<8> {
+  using Type = IndexSequence<0,1,2,3,4,5,6,7>;
+};
+template <> struct MakeIndexSequenceImpl<9> {
+  using Type = IndexSequence<0,1,2,3,4,5,6,7,8>;
+};
+template <> struct MakeIndexSequenceImpl<10> {
+  using Type = IndexSequence<0,1,2,3,4,5,6,7,8,9>;
+};
+template <> struct MakeIndexSequenceImpl<11> {
+  using Type = IndexSequence<0,1,2,3,4,5,6,7,8,9,10>;
+};
+
+#else  // defined(WIN) && defined(_PREFAST_)
+
 template <size_t... Ns>
 struct MakeIndexSequenceImpl<0, Ns...> {
   using Type = IndexSequence<Ns...>;
@@ -49,6 +93,8 @@
 struct MakeIndexSequenceImpl<N, Ns...>
     : MakeIndexSequenceImpl<N - 1, N - 1, Ns...> {};
 
+#endif  // defined(WIN) && defined(_PREFAST_)
+
 template <size_t N>
 using MakeIndexSequence = typename MakeIndexSequenceImpl<N>::Type;