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;