Update from https://crrev.com/319330
- New chromium clang rules require explicit external destructors so
system/lib added for MessagePipe, DataPipe and SharedBuffer
- New chromium clang rules require override and no virtual in
declarations, so many files updated.
- cc_strip_video patch updated.
BUG=
R=jamesr@chromium.org
Review URL: https://codereview.chromium.org/988693005
diff --git a/BUILD.gn b/BUILD.gn
index 9dbc41c..402b19a 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -15,6 +15,8 @@
"allocator/allocator_extension.h",
"allocator/type_profiler_control.cc",
"allocator/type_profiler_control.h",
+ "android/animation_frame_time_histogram.cc",
+ "android/animation_frame_time_histogram.h",
"android/application_status_listener.cc",
"android/application_status_listener.h",
"android/base_jni_onload.cc",
@@ -386,18 +388,18 @@
"metrics/histogram.h",
"metrics/histogram_base.cc",
"metrics/histogram_base.h",
- "metrics/histogram_delta_serialization.cc",
- "metrics/sample_map.cc",
- "metrics/sample_map.h",
- "metrics/sample_vector.cc",
- "metrics/sample_vector.h",
"metrics/histogram_delta_serialization.",
+ "metrics/histogram_delta_serialization.cc",
"metrics/histogram_flattener.h",
"metrics/histogram_macros.h",
"metrics/histogram_samples.cc",
"metrics/histogram_samples.h",
"metrics/histogram_snapshot_manager.cc",
"metrics/histogram_snapshot_manager.h",
+ "metrics/sample_map.cc",
+ "metrics/sample_map.h",
+ "metrics/sample_vector.cc",
+ "metrics/sample_vector.h",
"metrics/sparse_histogram.cc",
"metrics/sparse_histogram.h",
"metrics/statistics_recorder.cc",
@@ -783,9 +785,9 @@
"files/file_proxy.cc",
"files/file_util.cc",
"files/file_util_proxy.cc",
+ "files/scoped_temp_dir.cc",
"path_service.cc",
"scoped_native_library.cc",
- "files/scoped_temp_dir.cc",
]
}
@@ -1066,6 +1068,8 @@
source_set("prefs") {
sources = [
"prefs/base_prefs_export.h",
+ "prefs/base_prefs_switches.cc",
+ "prefs/base_prefs_switches.h",
"prefs/default_pref_store.cc",
"prefs/default_pref_store.h",
"prefs/json_pref_store.cc",
@@ -1163,6 +1167,8 @@
}
if (is_win) {
+ # Target to manually rebuild pe_image_test.dll which is checked into
+ # base/test/data/pe_image.
shared_library("pe_image_test") {
sources = [
"win/pe_image_test.cc",
@@ -1487,10 +1493,6 @@
set_sources_assignment_filter(sources_assignment_filter)
}
- if (is_win) {
- deps += [ ":pe_image_test" ]
- }
-
# TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
}
@@ -1500,6 +1502,7 @@
generate_jni("base_jni_headers") {
sources = [
"android/java/src/org/chromium/base/ApplicationStatus.java",
+ "android/java/src/org/chromium/base/AnimationFrameTimeHistogram.java",
"android/java/src/org/chromium/base/BuildInfo.java",
"android/java/src/org/chromium/base/CommandLine.java",
"android/java/src/org/chromium/base/ContentUriUtils.java",
diff --git a/android/animation_frame_time_histogram.cc b/android/animation_frame_time_histogram.cc
new file mode 100644
index 0000000..0d79619
--- /dev/null
+++ b/android/animation_frame_time_histogram.cc
@@ -0,0 +1,36 @@
+// Copyright 2015 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/animation_frame_time_histogram.h"
+
+#include "base/android/jni_string.h"
+#include "base/metrics/histogram_macros.h"
+#include "jni/AnimationFrameTimeHistogram_jni.h"
+
+// static
+void SaveHistogram(JNIEnv* env,
+ jobject jcaller,
+ jstring j_histogram_name,
+ jlongArray j_frame_times_ms,
+ jint j_count) {
+ jlong *frame_times_ms = env->GetLongArrayElements(j_frame_times_ms, NULL);
+ std::string histogram_name = base::android::ConvertJavaStringToUTF8(
+ env, j_histogram_name);
+
+ for (int i = 0; i < j_count; ++i) {
+ UMA_HISTOGRAM_TIMES(histogram_name.c_str(),
+ base::TimeDelta::FromMilliseconds(frame_times_ms[i]));
+ }
+}
+
+namespace base {
+namespace android {
+
+// static
+bool RegisterAnimationFrameTimeHistogram(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+} // namespace android
+} // namespace base
diff --git a/android/animation_frame_time_histogram.h b/android/animation_frame_time_histogram.h
new file mode 100644
index 0000000..63f938b
--- /dev/null
+++ b/android/animation_frame_time_histogram.h
@@ -0,0 +1,18 @@
+// Copyright 2015 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_ANIMATION_FRAME_TIME_HISTOGRAM_H_
+#define BASE_ANDROID_ANIMATION_FRAME_TIME_HISTOGRAM_H_
+
+#include <jni.h>
+
+namespace base {
+namespace android {
+
+bool RegisterAnimationFrameTimeHistogram(JNIEnv* env);
+
+} // namespace android
+} // namespace base
+
+#endif // BASE_ANDROID_ANIMATION_FRAME_TIME_HISTOGRAM_H_
diff --git a/android/base_jni_registrar.cc b/android/base_jni_registrar.cc
index 4dda482..7439d3c 100644
--- a/android/base_jni_registrar.cc
+++ b/android/base_jni_registrar.cc
@@ -4,6 +4,7 @@
#include "base/android/base_jni_registrar.h"
+#include "base/android/animation_frame_time_histogram.h"
#include "base/android/application_status_listener.h"
#include "base/android/build_info.h"
#include "base/android/command_line_android.h"
@@ -33,6 +34,8 @@
namespace android {
static RegistrationMethod kBaseRegisteredMethods[] = {
+ {"AnimationFrameTimeHistogram",
+ base::android::RegisterAnimationFrameTimeHistogram},
{"ApplicationStatusListener",
base::android::ApplicationStatusListener::RegisterBindings},
{"BuildInfo", base::android::BuildInfo::RegisterBindings},
diff --git a/android/java/src/org/chromium/base/AnimationFrameTimeHistogram.java b/android/java/src/org/chromium/base/AnimationFrameTimeHistogram.java
new file mode 100644
index 0000000..ad5cdd8
--- /dev/null
+++ b/android/java/src/org/chromium/base/AnimationFrameTimeHistogram.java
@@ -0,0 +1,145 @@
+// Copyright 2015 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;
+
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.TimeAnimator;
+import android.animation.TimeAnimator.TimeListener;
+import android.util.Log;
+
+/**
+ * Record Android animation frame rate and save it to UMA histogram. This is mainly for monitoring
+ * any jankiness of short Chrome Android animations. It is limited to few seconds of recording.
+ */
+public class AnimationFrameTimeHistogram {
+ private static final String TAG = "AnimationFrameTimeHistogram";
+ private static final int MAX_FRAME_TIME_NUM = 600; // 10 sec on 60 fps.
+
+ private final Recorder mRecorder = new Recorder();
+ private final String mHistogramName;
+
+ /**
+ * @param histogramName The histogram name that the recorded frame times will be saved.
+ * This must be also defined in histograms.xml
+ * @return An AnimatorListener instance that records frame time histogram on start and end
+ * automatically.
+ */
+ public static AnimatorListener getAnimatorRecorder(final String histogramName) {
+ return new AnimatorListenerAdapter() {
+ private final AnimationFrameTimeHistogram mAnimationFrameTimeHistogram =
+ new AnimationFrameTimeHistogram(histogramName);
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mAnimationFrameTimeHistogram.startRecording();
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mAnimationFrameTimeHistogram.endRecording();
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mAnimationFrameTimeHistogram.endRecording();
+ }
+ };
+ }
+
+ /**
+ * @param histogramName The histogram name that the recorded frame times will be saved.
+ * This must be also defined in histograms.xml
+ */
+ public AnimationFrameTimeHistogram(String histogramName) {
+ mHistogramName = histogramName;
+ }
+
+ /**
+ * Start recording frame times. The recording can fail if it exceeds a few seconds.
+ */
+ public void startRecording() {
+ mRecorder.startRecording();
+ }
+
+ /**
+ * End recording and save it to histogram. It won't save histogram if the recording wasn't
+ * successful.
+ */
+ public void endRecording() {
+ if (mRecorder.endRecording()) {
+ nativeSaveHistogram(mHistogramName,
+ mRecorder.getFrameTimesMs(), mRecorder.getFrameTimesCount());
+ }
+ mRecorder.cleanUp();
+ }
+
+ /**
+ * Record Android animation frame rate and return the result.
+ */
+ private static class Recorder implements TimeListener {
+ // TODO(kkimlabs): If we can use in the future, migrate to Choreographer for minimal
+ // workload.
+ private final TimeAnimator mAnimator = new TimeAnimator();
+ private long[] mFrameTimesMs;
+ private int mFrameTimesCount;
+
+ private Recorder() {
+ mAnimator.setTimeListener(this);
+ }
+
+ private void startRecording() {
+ assert !mAnimator.isRunning();
+ mFrameTimesCount = 0;
+ mFrameTimesMs = new long[MAX_FRAME_TIME_NUM];
+ mAnimator.start();
+ }
+
+ /**
+ * @return Whether the recording was successful. If successful, the result is available via
+ * getFrameTimesNs and getFrameTimesCount.
+ */
+ private boolean endRecording() {
+ boolean succeeded = mAnimator.isStarted();
+ mAnimator.end();
+ return succeeded;
+ }
+
+ private long[] getFrameTimesMs() {
+ return mFrameTimesMs;
+ }
+
+ private int getFrameTimesCount() {
+ return mFrameTimesCount;
+ }
+
+ /**
+ * Deallocates the temporary buffer to record frame times. Must be called after ending
+ * the recording and getting the result.
+ */
+ private void cleanUp() {
+ mFrameTimesMs = null;
+ }
+
+ @Override
+ public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
+ if (mFrameTimesCount == mFrameTimesMs.length) {
+ mAnimator.end();
+ cleanUp();
+ Log.w(TAG, "Animation frame time recording reached the maximum number. It's either"
+ + "the animation took too long or recording end is not called.");
+ return;
+ }
+
+ // deltaTime is 0 for the first frame.
+ if (deltaTime > 0) {
+ mFrameTimesMs[mFrameTimesCount++] = deltaTime;
+ }
+ }
+ }
+
+ private native void nativeSaveHistogram(String histogramName, long[] frameTimesMs, int count);
+}
diff --git a/android/java/src/org/chromium/base/ApiCompatibilityUtils.java b/android/java/src/org/chromium/base/ApiCompatibilityUtils.java
index c03cad2..4198853 100644
--- a/android/java/src/org/chromium/base/ApiCompatibilityUtils.java
+++ b/android/java/src/org/chromium/base/ApiCompatibilityUtils.java
@@ -72,13 +72,6 @@
}
/**
- * @return True if the running version of the Android supports HTML clipboard.
- */
- public static boolean isHTMLClipboardSupported() {
- return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
- }
-
- /**
* @see android.view.View#setLayoutDirection(int)
*/
public static void setLayoutDirection(View view, int layoutDirection) {
diff --git a/base.gyp b/base.gyp
index 30de275..3888ad9 100644
--- a/base.gyp
+++ b/base.gyp
@@ -338,6 +338,8 @@
],
'sources': [
'prefs/base_prefs_export.h',
+ 'prefs/base_prefs_switches.cc',
+ 'prefs/base_prefs_switches.h',
'prefs/default_pref_store.cc',
'prefs/default_pref_store.h',
'prefs/json_pref_store.cc',
@@ -382,12 +384,18 @@
# TODO(pasko): Remove this target when crbug.com/424562 is fixed.
# GN: //base:protect_file_posix
'target_name': 'protect_file_posix',
- 'type': 'static_library',
- 'dependencies': [
- 'base',
- ],
- 'sources': [
- 'files/protect_file_posix.cc',
+ 'conditions': [
+ ['os_posix == 1', {
+ 'type': 'static_library',
+ 'dependencies': [
+ 'base',
+ ],
+ 'sources': [
+ 'files/protect_file_posix.cc',
+ ],
+ }, {
+ 'type': 'none',
+ }],
],
},
{
@@ -778,9 +786,6 @@
'message_loop/message_pump_libevent_unittest.cc',
'threading/worker_pool_posix_unittest.cc',
],
- 'dependencies': [
- 'pe_image_test',
- ],
# TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
'msvs_disabled_warnings': [
4267,
@@ -941,6 +946,8 @@
'test/mock_chrome_application_mac.mm',
'test/mock_devices_changed_observer.cc',
'test/mock_devices_changed_observer.h',
+ 'test/mock_log.cc',
+ 'test/mock_log.h',
'test/multiprocess_test.cc',
'test/multiprocess_test.h',
'test/multiprocess_test_android.cc',
@@ -1336,6 +1343,7 @@
'type': 'none',
'sources': [
'android/java/src/org/chromium/base/ApplicationStatus.java',
+ 'android/java/src/org/chromium/base/AnimationFrameTimeHistogram.java',
'android/java/src/org/chromium/base/BuildInfo.java',
'android/java/src/org/chromium/base/CommandLine.java',
'android/java/src/org/chromium/base/ContentUriUtils.java',
@@ -1551,6 +1559,8 @@
},
},
{
+ # Target to manually rebuild pe_image_test.dll which is checked into
+ # base/test/data/pe_image.
'target_name': 'pe_image_test',
'type': 'shared_library',
'sources': [
diff --git a/base.gypi b/base.gypi
index 148246f..0f8fff2 100644
--- a/base.gypi
+++ b/base.gypi
@@ -17,6 +17,8 @@
'allocator/allocator_extension.h',
'allocator/type_profiler_control.cc',
'allocator/type_profiler_control.h',
+ 'android/animation_frame_time_histogram.cc',
+ 'android/animation_frame_time_histogram.h',
'android/application_status_listener.cc',
'android/application_status_listener.h',
'android/base_jni_onload.cc',
diff --git a/base_nacl.gyp b/base_nacl.gyp
index 63e1ed4..90a2893 100644
--- a/base_nacl.gyp
+++ b/base_nacl.gyp
@@ -7,8 +7,12 @@
'chromium_code': 1,
},
'includes': [
- '../build/common_untrusted.gypi',
+ # base.gypi must be included before common_untrusted.gypi.
+ #
+ # TODO(sergeyu): Replace the target_defaults magic in base.gypi with a
+ # sources variables lists. That way order of includes will not matter.
'base.gypi',
+ '../build/common_untrusted.gypi',
],
'conditions': [
['disable_nacl==0 and disable_nacl_untrusted==0', {
diff --git a/base_unittests.isolate b/base_unittests.isolate
index e5495e3..5126fb3 100644
--- a/base_unittests.isolate
+++ b/base_unittests.isolate
@@ -19,7 +19,6 @@
'--brave-new-test-launcher',
'--test-launcher-bot-mode',
'--asan=<(asan)',
- '--lsan=<(lsan)',
'--msan=<(msan)',
'--tsan=<(tsan)',
],
@@ -52,7 +51,6 @@
'--brave-new-test-launcher',
'--test-launcher-bot-mode',
'--asan=<(asan)',
- '--lsan=<(lsan)',
'--msan=<(msan)',
'--tsan=<(tsan)',
],
@@ -72,13 +70,6 @@
],
},
}],
- ['OS=="win"', {
- 'variables': {
- 'files': [
- '<(PRODUCT_DIR)/pe_image_test.dll',
- ],
- },
- }],
],
'includes': [
'base.isolate',
diff --git a/files/file_path_watcher_win.cc b/files/file_path_watcher_win.cc
index 63e5480..f6d0029 100644
--- a/files/file_path_watcher_win.cc
+++ b/files/file_path_watcher_win.cc
@@ -11,7 +11,6 @@
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop_proxy.h"
-#include "base/profiler/scoped_tracker.h"
#include "base/time/time.h"
#include "base/win/object_watcher.h"
@@ -147,11 +146,6 @@
}
void FilePathWatcherImpl::OnObjectSignaled(HANDLE object) {
- // TODO(vadimt): Remove ScopedTracker below once crbug.com/418183 is fixed.
- tracked_objects::ScopedTracker tracking_profile(
- FROM_HERE_WITH_EXPLICIT_FUNCTION(
- "418183 FilePathWatcherImpl::OnObjectSignaled"));
-
DCHECK(object == handle_);
// Make sure we stay alive through the body of this function.
scoped_refptr<FilePathWatcherImpl> keep_alive(this);
diff --git a/files/important_file_writer.cc b/files/important_file_writer.cc
index d256236..47b0b09 100644
--- a/files/important_file_writer.cc
+++ b/files/important_file_writer.cc
@@ -10,12 +10,14 @@
#include "base/bind.h"
#include "base/critical_closure.h"
+#include "base/debug/alias.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
#include "base/task_runner.h"
#include "base/task_runner_util.h"
#include "base/threading/thread.h"
@@ -54,6 +56,20 @@
// static
bool ImportantFileWriter::WriteFileAtomically(const FilePath& path,
const std::string& data) {
+#if defined(OS_CHROMEOS)
+ // On Chrome OS, chrome gets killed when it cannot finish shutdown quickly,
+ // and this function seems to be one of the slowest shutdown steps.
+ // Include some info to the report for investigation. crbug.com/418627
+ // TODO(hashimoto): Remove this.
+ struct {
+ size_t data_size;
+ char path[128];
+ } file_info;
+ file_info.data_size = data.size();
+ base::strlcpy(file_info.path, path.value().c_str(),
+ arraysize(file_info.path));
+ base::debug::Alias(&file_info);
+#endif
// Write the data to a temp file then rename to avoid data loss if we crash
// while writing the file. Ensure that the temp file is on the same volume
// as target file, so it can be moved in one step, and that the temp file
diff --git a/json/json_file_value_serializer.cc b/json/json_file_value_serializer.cc
index 71033f6..72a0970 100644
--- a/json/json_file_value_serializer.cc
+++ b/json/json_file_value_serializer.cc
@@ -10,16 +10,14 @@
using base::FilePath;
-const char JSONFileValueSerializer::kAccessDenied[] = "Access denied.";
-const char JSONFileValueSerializer::kCannotReadFile[] = "Can't read file.";
-const char JSONFileValueSerializer::kFileLocked[] = "File locked.";
-const char JSONFileValueSerializer::kNoSuchFile[] = "File doesn't exist.";
+const char JSONFileValueDeserializer::kAccessDenied[] = "Access denied.";
+const char JSONFileValueDeserializer::kCannotReadFile[] = "Can't read file.";
+const char JSONFileValueDeserializer::kFileLocked[] = "File locked.";
+const char JSONFileValueDeserializer::kNoSuchFile[] = "File doesn't exist.";
JSONFileValueSerializer::JSONFileValueSerializer(
const base::FilePath& json_file_path)
- : json_file_path_(json_file_path),
- allow_trailing_comma_(false),
- last_read_size_(0U) {
+ : json_file_path_(json_file_path) {
}
JSONFileValueSerializer::~JSONFileValueSerializer() {
@@ -53,7 +51,17 @@
return true;
}
-int JSONFileValueSerializer::ReadFileToString(std::string* json_string) {
+JSONFileValueDeserializer::JSONFileValueDeserializer(
+ const base::FilePath& json_file_path)
+ : json_file_path_(json_file_path),
+ allow_trailing_comma_(false),
+ last_read_size_(0U) {
+}
+
+JSONFileValueDeserializer::~JSONFileValueDeserializer() {
+}
+
+int JSONFileValueDeserializer::ReadFileToString(std::string* json_string) {
DCHECK(json_string);
if (!base::ReadFileToString(json_file_path_, json_string)) {
#if defined(OS_WIN)
@@ -74,7 +82,7 @@
return JSON_NO_ERROR;
}
-const char* JSONFileValueSerializer::GetErrorMessageForCode(int error_code) {
+const char* JSONFileValueDeserializer::GetErrorMessageForCode(int error_code) {
switch (error_code) {
case JSON_NO_ERROR:
return "";
@@ -92,8 +100,8 @@
}
}
-base::Value* JSONFileValueSerializer::Deserialize(int* error_code,
- std::string* error_str) {
+base::Value* JSONFileValueDeserializer::Deserialize(int* error_code,
+ std::string* error_str) {
std::string json_string;
int error = ReadFileToString(&json_string);
if (error != JSON_NO_ERROR) {
@@ -104,7 +112,7 @@
return NULL;
}
- JSONStringValueSerializer serializer(json_string);
- serializer.set_allow_trailing_comma(allow_trailing_comma_);
- return serializer.Deserialize(error_code, error_str);
+ JSONStringValueDeserializer deserializer(json_string);
+ deserializer.set_allow_trailing_comma(allow_trailing_comma_);
+ return deserializer.Deserialize(error_code, error_str);
}
diff --git a/json/json_file_value_serializer.h b/json/json_file_value_serializer.h
index 6cfcbe8..aab47ee 100644
--- a/json/json_file_value_serializer.h
+++ b/json/json_file_value_serializer.h
@@ -14,10 +14,9 @@
class BASE_EXPORT JSONFileValueSerializer : public base::ValueSerializer {
public:
- // |json_file_path_| is the path of a file that will be source of the
- // deserialization or the destination of the serialization.
- // When deserializing, the file should exist, but when serializing, the
- // serializer will attempt to create the file at the specified location.
+ // |json_file_path_| is the path of a file that will be destination of the
+ // serialization. The serializer will attempt to create the file at the
+ // specified location.
explicit JSONFileValueSerializer(const base::FilePath& json_file_path);
~JSONFileValueSerializer() override;
@@ -36,6 +35,22 @@
// output.
bool SerializeAndOmitBinaryValues(const base::Value& root);
+ private:
+ bool SerializeInternal(const base::Value& root, bool omit_binary_values);
+
+ const base::FilePath json_file_path_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(JSONFileValueSerializer);
+};
+
+class BASE_EXPORT JSONFileValueDeserializer : public base::ValueDeserializer {
+ public:
+ // |json_file_path_| is the path of a file that will be source of the
+ // deserialization.
+ explicit JSONFileValueDeserializer(const base::FilePath& json_file_path);
+
+ ~JSONFileValueDeserializer() override;
+
// Attempt to deserialize the data structure encoded in the file passed
// in to the constructor into a structure of Value objects. If the return
// value is NULL, and if |error_code| is non-null, |error_code| will
@@ -69,22 +84,20 @@
allow_trailing_comma_ = new_value;
}
- // Returns the syze (in bytes) of JSON string read from disk in the last
+ // Returns the size (in bytes) of JSON string read from disk in the last
// successful |Deserialize()| call.
size_t get_last_read_size() const { return last_read_size_; }
private:
- bool SerializeInternal(const base::Value& root, bool omit_binary_values);
+ // A wrapper for ReadFileToString which returns a non-zero JsonFileError if
+ // there were file errors.
+ int ReadFileToString(std::string* json_string);
const base::FilePath json_file_path_;
bool allow_trailing_comma_;
size_t last_read_size_;
- // A wrapper for ReadFileToString which returns a non-zero JsonFileError if
- // there were file errors.
- int ReadFileToString(std::string* json_string);
-
- DISALLOW_IMPLICIT_CONSTRUCTORS(JSONFileValueSerializer);
+ DISALLOW_IMPLICIT_CONSTRUCTORS(JSONFileValueDeserializer);
};
#endif // BASE_JSON_JSON_FILE_VALUE_SERIALIZER_H_
diff --git a/json/json_string_value_serializer.cc b/json/json_string_value_serializer.cc
index b626640..debf9f0 100644
--- a/json/json_string_value_serializer.cc
+++ b/json/json_string_value_serializer.cc
@@ -12,17 +12,7 @@
JSONStringValueSerializer::JSONStringValueSerializer(std::string* json_string)
: json_string_(json_string),
- json_string_readonly_(*json_string),
- pretty_print_(false),
- allow_trailing_comma_(false) {
-}
-
-JSONStringValueSerializer::JSONStringValueSerializer(
- const base::StringPiece& json_string)
- : json_string_(nullptr),
- json_string_readonly_(json_string),
- pretty_print_(false),
- allow_trailing_comma_(false) {
+ pretty_print_(false) {
}
JSONStringValueSerializer::~JSONStringValueSerializer() {}
@@ -50,9 +40,17 @@
return base::JSONWriter::WriteWithOptions(&root, options, json_string_);
}
-Value* JSONStringValueSerializer::Deserialize(int* error_code,
- std::string* error_str) {
- return base::JSONReader::ReadAndReturnError(json_string_readonly_,
+JSONStringValueDeserializer::JSONStringValueDeserializer(
+ const base::StringPiece& json_string)
+ : json_string_(json_string),
+ allow_trailing_comma_(false) {
+}
+
+JSONStringValueDeserializer::~JSONStringValueDeserializer() {}
+
+Value* JSONStringValueDeserializer::Deserialize(int* error_code,
+ std::string* error_str) {
+ return base::JSONReader::ReadAndReturnError(json_string_,
allow_trailing_comma_ ? base::JSON_ALLOW_TRAILING_COMMAS :
base::JSON_PARSE_RFC,
error_code, error_str);
diff --git a/json/json_string_value_serializer.h b/json/json_string_value_serializer.h
index 7f99bc9..bc0e66d 100644
--- a/json/json_string_value_serializer.h
+++ b/json/json_string_value_serializer.h
@@ -15,16 +15,11 @@
class BASE_EXPORT JSONStringValueSerializer : public base::ValueSerializer {
public:
- // |json_string| is the string that will be source of the deserialization
- // or the destination of the serialization. The caller of the constructor
- // retains ownership of the string. |json_string| must not be null.
+ // |json_string| is the string that will be the destination of the
+ // serialization. The caller of the constructor retains ownership of the
+ // string. |json_string| must not be null.
explicit JSONStringValueSerializer(std::string* json_string);
- // This version allows initialization with a StringPiece for deserialization
- // only. Retains a reference to the contents of |json_string|, so the data
- // must outlive the JSONStringValueSerializer.
- explicit JSONStringValueSerializer(const base::StringPiece& json_string);
-
~JSONStringValueSerializer() override;
// Attempt to serialize the data structure represented by Value into
@@ -36,6 +31,27 @@
// output.
bool SerializeAndOmitBinaryValues(const base::Value& root);
+ void set_pretty_print(bool new_value) { pretty_print_ = new_value; }
+ bool pretty_print() { return pretty_print_; }
+
+ private:
+ bool SerializeInternal(const base::Value& root, bool omit_binary_values);
+
+ // Owned by the caller of the constructor.
+ std::string* json_string_;
+ bool pretty_print_; // If true, serialization will span multiple lines.
+
+ DISALLOW_COPY_AND_ASSIGN(JSONStringValueSerializer);
+};
+
+class BASE_EXPORT JSONStringValueDeserializer : public base::ValueDeserializer {
+ public:
+ // This retains a reference to the contents of |json_string|, so the data
+ // must outlive the JSONStringValueDeserializer.
+ explicit JSONStringValueDeserializer(const base::StringPiece& json_string);
+
+ ~JSONStringValueDeserializer() override;
+
// Attempt to deserialize the data structure encoded in the string passed
// in to the constructor into a structure of Value objects. If the return
// value is null, and if |error_code| is non-null, |error_code| will
@@ -46,28 +62,17 @@
base::Value* Deserialize(int* error_code,
std::string* error_message) override;
- void set_pretty_print(bool new_value) { pretty_print_ = new_value; }
- bool pretty_print() { return pretty_print_; }
-
void set_allow_trailing_comma(bool new_value) {
allow_trailing_comma_ = new_value;
}
private:
- bool SerializeInternal(const base::Value& root, bool omit_binary_values);
-
- // String for writing. Owned by the caller of the constructor. Will be null if
- // the serializer was initialized with a const string.
- std::string* json_string_;
- // String for reading. Data is owned by the caller of the constructor. If
- // |json_string_| is non-null, this is a view onto |json_string_|.
- base::StringPiece json_string_readonly_;
- bool pretty_print_; // If true, serialization will span multiple lines.
+ // Data is owned by the caller of the constructor.
+ base::StringPiece json_string_;
// If true, deserialization will allow trailing commas.
bool allow_trailing_comma_;
- DISALLOW_COPY_AND_ASSIGN(JSONStringValueSerializer);
+ DISALLOW_COPY_AND_ASSIGN(JSONStringValueDeserializer);
};
#endif // BASE_JSON_JSON_STRING_VALUE_SERIALIZER_H_
-
diff --git a/json/json_value_serializer_unittest.cc b/json/json_value_serializer_unittest.cc
index d2a84de..225ee67 100644
--- a/json/json_value_serializer_unittest.cc
+++ b/json/json_value_serializer_unittest.cc
@@ -86,28 +86,10 @@
ASSERT_EQ(1, value);
}
-// Test proper JSON [de]serialization from string is working.
-TEST(JSONValueSerializerTest, ReadProperJSONFromString) {
+// Test proper JSON deserialization from string is working.
+TEST(JSONValueDeserializerTest, ReadProperJSONFromString) {
// Try to deserialize it through the serializer.
- JSONStringValueSerializer str_deserializer(kProperJSON);
-
- int error_code = 0;
- std::string error_message;
- scoped_ptr<Value> value(
- str_deserializer.Deserialize(&error_code, &error_message));
- ASSERT_TRUE(value.get());
- ASSERT_EQ(0, error_code);
- ASSERT_TRUE(error_message.empty());
- // Verify if the same JSON is still there.
- CheckJSONIsStillTheSame(*value);
-}
-
-// Test proper JSON deserialization from a string pointer is working.
-TEST(JSONValueSerializerTest, ReadProperJSONFromStringPointer) {
- // Try to deserialize a string pointer through the serializer. (This exercises
- // a separate code path to passing a StringPiece.)
- std::string proper_json(kProperJSON);
- JSONStringValueSerializer str_deserializer(&proper_json);
+ JSONStringValueDeserializer str_deserializer(kProperJSON);
int error_code = 0;
std::string error_message;
@@ -121,12 +103,12 @@
}
// Test proper JSON deserialization from a StringPiece substring.
-TEST(JSONValueSerializerTest, ReadProperJSONFromStringPiece) {
+TEST(JSONValueDeserializerTest, ReadProperJSONFromStringPiece) {
// Create a StringPiece for the substring of kProperJSONPadded that matches
// kProperJSON.
base::StringPiece proper_json(kProperJSONPadded);
proper_json = proper_json.substr(5, proper_json.length() - 10);
- JSONStringValueSerializer str_deserializer(proper_json);
+ JSONStringValueDeserializer str_deserializer(proper_json);
int error_code = 0;
std::string error_message;
@@ -141,9 +123,9 @@
// Test that trialing commas are only properly deserialized from string when
// the proper flag for that is set.
-TEST(JSONValueSerializerTest, ReadJSONWithTrailingCommasFromString) {
+TEST(JSONValueDeserializerTest, ReadJSONWithTrailingCommasFromString) {
// Try to deserialize it through the serializer.
- JSONStringValueSerializer str_deserializer(kProperJSONWithCommas);
+ JSONStringValueDeserializer str_deserializer(kProperJSONWithCommas);
int error_code = 0;
std::string error_message;
@@ -161,8 +143,8 @@
CheckJSONIsStillTheSame(*value);
}
-// Test proper JSON [de]serialization from file is working.
-TEST(JSONValueSerializerTest, ReadProperJSONFromFile) {
+// Test proper JSON deserialization from file is working.
+TEST(JSONValueDeserializerTest, ReadProperJSONFromFile) {
ScopedTempDir tempdir;
ASSERT_TRUE(tempdir.CreateUniqueTempDir());
// Write it down in the file.
@@ -171,7 +153,7 @@
WriteFile(temp_file, kProperJSON, strlen(kProperJSON)));
// Try to deserialize it through the serializer.
- JSONFileValueSerializer file_deserializer(temp_file);
+ JSONFileValueDeserializer file_deserializer(temp_file);
int error_code = 0;
std::string error_message;
@@ -186,7 +168,7 @@
// Test that trialing commas are only properly deserialized from file when
// the proper flag for that is set.
-TEST(JSONValueSerializerTest, ReadJSONWithCommasFromFile) {
+TEST(JSONValueDeserializerTest, ReadJSONWithCommasFromFile) {
ScopedTempDir tempdir;
ASSERT_TRUE(tempdir.CreateUniqueTempDir());
// Write it down in the file.
@@ -196,7 +178,7 @@
strlen(kProperJSONWithCommas)));
// Try to deserialize it through the serializer.
- JSONFileValueSerializer file_deserializer(temp_file);
+ JSONFileValueDeserializer file_deserializer(temp_file);
// This must fail without the proper flag.
int error_code = 0;
std::string error_message;
@@ -214,11 +196,27 @@
CheckJSONIsStillTheSame(*value);
}
+TEST(JSONValueDeserializerTest, AllowTrailingComma) {
+ scoped_ptr<Value> root;
+ scoped_ptr<Value> root_expected;
+ static const char kTestWithCommas[] = "{\"key\": [true,],}";
+ static const char kTestNoCommas[] = "{\"key\": [true]}";
+
+ JSONStringValueDeserializer deserializer(kTestWithCommas);
+ deserializer.set_allow_trailing_comma(true);
+ JSONStringValueDeserializer deserializer_expected(kTestNoCommas);
+ root.reset(deserializer.Deserialize(NULL, NULL));
+ ASSERT_TRUE(root.get());
+ root_expected.reset(deserializer_expected.Deserialize(NULL, NULL));
+ ASSERT_TRUE(root_expected.get());
+ ASSERT_TRUE(root->Equals(root_expected.get()));
+}
+
TEST(JSONValueSerializerTest, Roundtrip) {
static const char kOriginalSerialization[] =
"{\"bool\":true,\"double\":3.14,\"int\":42,\"list\":[1,2],\"null\":null}";
- JSONStringValueSerializer serializer(kOriginalSerialization);
- scoped_ptr<Value> root(serializer.Deserialize(NULL, NULL));
+ JSONStringValueDeserializer deserializer(kOriginalSerialization);
+ scoped_ptr<Value> root(deserializer.Deserialize(NULL, NULL));
ASSERT_TRUE(root.get());
ASSERT_TRUE(root->IsType(Value::TYPE_DICTIONARY));
@@ -241,10 +239,6 @@
ASSERT_TRUE(root_dict->GetDouble("double", &double_value));
ASSERT_DOUBLE_EQ(3.14, double_value);
- // We shouldn't be able to write using this serializer, since it was
- // initialized with a const string.
- ASSERT_FALSE(serializer.Serialize(*root_dict));
-
std::string test_serialization;
JSONStringValueSerializer mutable_serializer(&test_serialization);
ASSERT_TRUE(mutable_serializer.Serialize(*root_dict));
@@ -331,7 +325,7 @@
ASSERT_EQ(kExpected, actual);
// escaped ascii text -> json
- JSONStringValueSerializer deserializer(kExpected);
+ JSONStringValueDeserializer deserializer(kExpected);
scoped_ptr<Value> deserial_root(deserializer.Deserialize(NULL, NULL));
ASSERT_TRUE(deserial_root.get());
DictionaryValue* dict_root =
@@ -355,7 +349,7 @@
ASSERT_EQ(kExpected, actual);
// escaped ascii text -> json
- JSONStringValueSerializer deserializer(kExpected);
+ JSONStringValueDeserializer deserializer(kExpected);
scoped_ptr<Value> deserial_root(deserializer.Deserialize(NULL, NULL));
ASSERT_TRUE(deserial_root.get());
DictionaryValue* dict_root =
@@ -366,7 +360,7 @@
// Test converting escaped regular chars
static const char kEscapedChars[] = "{\"test\":\"\\u0067\\u006f\"}";
- JSONStringValueSerializer deserializer2(kEscapedChars);
+ JSONStringValueDeserializer deserializer2(kEscapedChars);
deserial_root.reset(deserializer2.Deserialize(NULL, NULL));
ASSERT_TRUE(deserial_root.get());
dict_root = static_cast<DictionaryValue*>(deserial_root.get());
@@ -374,22 +368,6 @@
ASSERT_EQ(ASCIIToUTF16("go"), test_value);
}
-TEST(JSONValueSerializerTest, AllowTrailingComma) {
- scoped_ptr<Value> root;
- scoped_ptr<Value> root_expected;
- static const char kTestWithCommas[] = "{\"key\": [true,],}";
- static const char kTestNoCommas[] = "{\"key\": [true]}";
-
- JSONStringValueSerializer serializer(kTestWithCommas);
- serializer.set_allow_trailing_comma(true);
- JSONStringValueSerializer serializer_expected(kTestNoCommas);
- root.reset(serializer.Deserialize(NULL, NULL));
- ASSERT_TRUE(root.get());
- root_expected.reset(serializer_expected.Deserialize(NULL, NULL));
- ASSERT_TRUE(root_expected.get());
- ASSERT_TRUE(root->Equals(root_expected.get()));
-}
-
TEST(JSONValueSerializerTest, JSONReaderComments) {
ValidateJsonList("[ // 2, 3, ignore me ] \n1 ]");
ValidateJsonList("[ /* 2, \n3, ignore me ]*/ \n1 ]");
@@ -435,7 +413,7 @@
ASSERT_TRUE(PathExists(original_file_path));
- JSONFileValueSerializer deserializer(original_file_path);
+ JSONFileValueDeserializer deserializer(original_file_path);
scoped_ptr<Value> root;
root.reset(deserializer.Deserialize(NULL, NULL));
@@ -483,7 +461,7 @@
ASSERT_TRUE(PathExists(original_file_path));
- JSONFileValueSerializer deserializer(original_file_path);
+ JSONFileValueDeserializer deserializer(original_file_path);
scoped_ptr<Value> root;
root.reset(deserializer.Deserialize(NULL, NULL));
ASSERT_TRUE(root.get());
@@ -508,9 +486,9 @@
source_file_path = source_file_path.Append(
FILE_PATH_LITERAL("serializer_test_nowhitespace.json"));
ASSERT_TRUE(PathExists(source_file_path));
- JSONFileValueSerializer serializer(source_file_path);
+ JSONFileValueDeserializer deserializer(source_file_path);
scoped_ptr<Value> root;
- root.reset(serializer.Deserialize(NULL, NULL));
+ root.reset(deserializer.Deserialize(NULL, NULL));
ASSERT_TRUE(root.get());
}
diff --git a/mac/foundation_util.h b/mac/foundation_util.h
index accc0d9..353ed7c 100644
--- a/mac/foundation_util.h
+++ b/mac/foundation_util.h
@@ -296,6 +296,7 @@
CF_CAST_DECL(CGColor);
CF_CAST_DECL(CTFont);
+CF_CAST_DECL(CTFontDescriptor);
CF_CAST_DECL(CTRun);
CF_CAST_DECL(SecACL);
diff --git a/mac/foundation_util.mm b/mac/foundation_util.mm
index 2895b66..27d6e7c 100644
--- a/mac/foundation_util.mm
+++ b/mac/foundation_util.mm
@@ -362,6 +362,7 @@
CF_CAST_DEFN(CGColor);
+CF_CAST_DEFN(CTFontDescriptor);
CF_CAST_DEFN(CTRun);
#if defined(OS_IOS)
diff --git a/mac/sdk_forward_declarations.h b/mac/sdk_forward_declarations.h
index 2795b19..25d937e 100644
--- a/mac/sdk_forward_declarations.h
+++ b/mac/sdk_forward_declarations.h
@@ -285,6 +285,10 @@
- (NSString*)UUIDString;
@end
+@interface NSControl (MountainLionSDK)
+@property BOOL allowsExpansionToolTips;
+@end
+
#endif // MAC_OS_X_VERSION_10_8
diff --git a/memory/discardable_memory_android.cc b/memory/discardable_memory_android.cc
index 5dcdfdc..2b35587 100644
--- a/memory/discardable_memory_android.cc
+++ b/memory/discardable_memory_android.cc
@@ -52,8 +52,8 @@
void DiscardableMemory::GetSupportedTypes(
std::vector<DiscardableMemoryType>* types) {
const DiscardableMemoryType supported_types[] = {
- DISCARDABLE_MEMORY_TYPE_ASHMEM,
DISCARDABLE_MEMORY_TYPE_SHMEM,
+ DISCARDABLE_MEMORY_TYPE_ASHMEM,
DISCARDABLE_MEMORY_TYPE_EMULATED
};
types->assign(supported_types, supported_types + arraysize(supported_types));
diff --git a/memory/discardable_memory_mac.cc b/memory/discardable_memory_mac.cc
index e0096e5..2881f5e 100644
--- a/memory/discardable_memory_mac.cc
+++ b/memory/discardable_memory_mac.cc
@@ -22,8 +22,8 @@
void DiscardableMemory::GetSupportedTypes(
std::vector<DiscardableMemoryType>* types) {
const DiscardableMemoryType supported_types[] = {
- DISCARDABLE_MEMORY_TYPE_MACH,
DISCARDABLE_MEMORY_TYPE_SHMEM,
+ DISCARDABLE_MEMORY_TYPE_MACH,
DISCARDABLE_MEMORY_TYPE_EMULATED
};
types->assign(supported_types, supported_types + arraysize(supported_types));
diff --git a/memory/discardable_shared_memory.h b/memory/discardable_shared_memory.h
index 59c5d5b..e3b437c 100644
--- a/memory/discardable_shared_memory.h
+++ b/memory/discardable_shared_memory.h
@@ -74,7 +74,7 @@
// must have been mapped via Map().
void* memory() const;
- // Returns the last know usage time for DiscardableSharedMemory object. This
+ // Returns the last known usage time for DiscardableSharedMemory object. This
// may be earlier than the "true" usage time when memory has been used by a
// different process. Returns NULL time if purged.
Time last_known_usage() const { return last_known_usage_; }
@@ -84,7 +84,7 @@
// for two reasons; object might be locked or our last known usage timestamp
// might be out of date. Last known usage time is updated to |current_time|
// if locked or the actual last usage timestamp if unlocked. It is often
- // neccesary to call this function twice for the object to successfully be
+ // necessary to call this function twice for the object to successfully be
// purged. First call, updates |last_known_usage_|. Second call, successfully
// purges the object using the updated |last_known_usage_|.
// Note: there is no guarantee that multiple calls to this function will
diff --git a/prefs/base_prefs_switches.cc b/prefs/base_prefs_switches.cc
new file mode 100644
index 0000000..304248b
--- /dev/null
+++ b/prefs/base_prefs_switches.cc
@@ -0,0 +1,12 @@
+// Copyright 2015 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/prefs/base_prefs_switches.h"
+
+namespace switches {
+
+// Pretty-prints pref JSON files.
+const char kPrettyPrintPrefs[] = "pretty-print-prefs";
+
+} // namespace switches
diff --git a/prefs/base_prefs_switches.h b/prefs/base_prefs_switches.h
new file mode 100644
index 0000000..7a6b665
--- /dev/null
+++ b/prefs/base_prefs_switches.h
@@ -0,0 +1,14 @@
+// Copyright 2015 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_PREFS_BASE_PREFS_SWITCHES_H_
+#define BASE_PREFS_BASE_PREFS_SWITCHES_H_
+
+namespace switches {
+
+extern const char kPrettyPrintPrefs[];
+
+} // namespace switches
+
+#endif // BASE_PREFS_BASE_PREFS_SWITCHES_H_
diff --git a/prefs/json_pref_store.cc b/prefs/json_pref_store.cc
index c52a95c..2e34b50 100644
--- a/prefs/json_pref_store.cc
+++ b/prefs/json_pref_store.cc
@@ -8,12 +8,14 @@
#include "base/bind.h"
#include "base/callback.h"
+#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/json/json_file_value_serializer.h"
#include "base/json/json_string_value_serializer.h"
#include "base/memory/ref_counted.h"
#include "base/metrics/histogram.h"
+#include "base/prefs/base_prefs_switches.h"
#include "base/prefs/pref_filter.h"
#include "base/sequenced_task_runner.h"
#include "base/strings/string_util.h"
@@ -56,16 +58,16 @@
DVLOG(1) << "Error while loading JSON file: " << error_msg
<< ", file: " << path.value();
switch (error_code) {
- case JSONFileValueSerializer::JSON_ACCESS_DENIED:
+ case JSONFileValueDeserializer::JSON_ACCESS_DENIED:
return PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED;
break;
- case JSONFileValueSerializer::JSON_CANNOT_READ_FILE:
+ case JSONFileValueDeserializer::JSON_CANNOT_READ_FILE:
return PersistentPrefStore::PREF_READ_ERROR_FILE_OTHER;
break;
- case JSONFileValueSerializer::JSON_FILE_LOCKED:
+ case JSONFileValueDeserializer::JSON_FILE_LOCKED:
return PersistentPrefStore::PREF_READ_ERROR_FILE_LOCKED;
break;
- case JSONFileValueSerializer::JSON_NO_SUCH_FILE:
+ case JSONFileValueDeserializer::JSON_NO_SUCH_FILE:
return PersistentPrefStore::PREF_READ_ERROR_NO_FILE;
break;
default:
@@ -119,14 +121,14 @@
std::string error_msg;
scoped_ptr<JsonPrefStore::ReadResult> read_result(
new JsonPrefStore::ReadResult);
- JSONFileValueSerializer serializer(path);
- read_result->value.reset(serializer.Deserialize(&error_code, &error_msg));
+ JSONFileValueDeserializer deserializer(path);
+ read_result->value.reset(deserializer.Deserialize(&error_code, &error_msg));
read_result->error =
HandleReadErrors(read_result->value.get(), path, error_code, error_msg);
read_result->no_dir = !base::PathExists(path.DirName());
if (read_result->error == PersistentPrefStore::PREF_READ_ERROR_NONE)
- RecordJsonDataSizeHistogram(path, serializer.get_last_read_size());
+ RecordJsonDataSizeHistogram(path, deserializer.get_last_read_size());
return read_result.Pass();
}
@@ -398,7 +400,10 @@
pref_filter_->FilterSerializeData(prefs_.get());
JSONStringValueSerializer serializer(output);
- serializer.set_pretty_print(true);
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kPrettyPrintPrefs)) {
+ serializer.set_pretty_print(true);
+ }
return serializer.Serialize(*prefs_);
}
diff --git a/process/kill.cc b/process/kill.cc
index caca348..a647d96 100644
--- a/process/kill.cc
+++ b/process/kill.cc
@@ -14,11 +14,8 @@
bool result = true;
NamedProcessIterator iter(executable_name, filter);
while (const ProcessEntry* entry = iter.NextProcessEntry()) {
-#if defined(OS_WIN)
- result &= KillProcessById(entry->pid(), exit_code, true);
-#else
- result &= KillProcess(entry->pid(), exit_code, true);
-#endif
+ Process process = Process::Open(entry->pid());
+ result &= KillProcess(process.Handle(), exit_code, true);
}
return result;
}
diff --git a/process/kill.h b/process/kill.h
index 8c0a213..df0d95e 100644
--- a/process/kill.h
+++ b/process/kill.h
@@ -57,12 +57,6 @@
BASE_EXPORT bool KillProcessGroup(ProcessHandle process_group_id);
#endif // defined(OS_POSIX)
-#if defined(OS_WIN)
-BASE_EXPORT bool KillProcessById(ProcessId process_id,
- int exit_code,
- bool wait);
-#endif // defined(OS_WIN)
-
// Get the termination status of the process by interpreting the
// circumstances of the child process' death. |exit_code| is set to
// the status returned by waitpid() on POSIX, and from
@@ -94,22 +88,6 @@
ProcessHandle handle, int* exit_code);
#endif // defined(OS_POSIX)
-// Waits for process to exit. On POSIX systems, if the process hasn't been
-// signaled then puts the exit code in |exit_code|; otherwise it's considered
-// a failure. On Windows |exit_code| is always filled. Returns true on success,
-// and closes |handle| in any case.
-BASE_EXPORT bool WaitForExitCode(ProcessHandle handle, int* exit_code);
-
-// Waits for process to exit. If it did exit within |timeout_milliseconds|,
-// then puts the exit code in |exit_code|, and returns true.
-// In POSIX systems, if the process has been signaled then |exit_code| is set
-// to -1. Returns false on failure (the caller is then responsible for closing
-// |handle|).
-// The caller is always responsible for closing the |handle|.
-BASE_EXPORT bool WaitForExitCodeWithTimeout(ProcessHandle handle,
- int* exit_code,
- base::TimeDelta timeout);
-
// Wait for all the processes based on the named executable to exit. If filter
// is non-null, then only processes selected by the filter are waited on.
// Returns after all processes have exited or wait_milliseconds have expired.
diff --git a/process/kill_posix.cc b/process/kill_posix.cc
index 77705ee..298486b 100644
--- a/process/kill_posix.cc
+++ b/process/kill_posix.cc
@@ -22,161 +22,6 @@
namespace {
-#if !defined(OS_NACL_NONSFI)
-bool WaitpidWithTimeout(ProcessHandle handle,
- int* status,
- base::TimeDelta wait) {
- // This POSIX version of this function only guarantees that we wait no less
- // than |wait| for the process to exit. The child process may
- // exit sometime before the timeout has ended but we may still block for up
- // to 256 milliseconds after the fact.
- //
- // waitpid() has no direct support on POSIX for specifying a timeout, you can
- // either ask it to block indefinitely or return immediately (WNOHANG).
- // When a child process terminates a SIGCHLD signal is sent to the parent.
- // Catching this signal would involve installing a signal handler which may
- // affect other parts of the application and would be difficult to debug.
- //
- // Our strategy is to call waitpid() once up front to check if the process
- // has already exited, otherwise to loop for |wait|, sleeping for
- // at most 256 milliseconds each time using usleep() and then calling
- // waitpid(). The amount of time we sleep starts out at 1 milliseconds, and
- // we double it every 4 sleep cycles.
- //
- // usleep() is speced to exit if a signal is received for which a handler
- // has been installed. This means that when a SIGCHLD is sent, it will exit
- // depending on behavior external to this function.
- //
- // This function is used primarily for unit tests, if we want to use it in
- // the application itself it would probably be best to examine other routes.
-
- if (wait.InMilliseconds() == base::kNoTimeout) {
- return HANDLE_EINTR(waitpid(handle, status, 0)) > 0;
- }
-
- pid_t ret_pid = HANDLE_EINTR(waitpid(handle, status, WNOHANG));
- static const int64 kMaxSleepInMicroseconds = 1 << 18; // ~256 milliseconds.
- int64 max_sleep_time_usecs = 1 << 10; // ~1 milliseconds.
- int64 double_sleep_time = 0;
-
- // If the process hasn't exited yet, then sleep and try again.
- TimeTicks wakeup_time = TimeTicks::Now() + wait;
- while (ret_pid == 0) {
- TimeTicks now = TimeTicks::Now();
- if (now > wakeup_time)
- break;
- // Guaranteed to be non-negative!
- int64 sleep_time_usecs = (wakeup_time - now).InMicroseconds();
- // Sleep for a bit while we wait for the process to finish.
- if (sleep_time_usecs > max_sleep_time_usecs)
- sleep_time_usecs = max_sleep_time_usecs;
-
- // usleep() will return 0 and set errno to EINTR on receipt of a signal
- // such as SIGCHLD.
- usleep(sleep_time_usecs);
- ret_pid = HANDLE_EINTR(waitpid(handle, status, WNOHANG));
-
- if ((max_sleep_time_usecs < kMaxSleepInMicroseconds) &&
- (double_sleep_time++ % 4 == 0)) {
- max_sleep_time_usecs *= 2;
- }
- }
-
- return ret_pid > 0;
-}
-
-#if defined(OS_MACOSX)
-// Using kqueue on Mac so that we can wait on non-child processes.
-// We can't use kqueues on child processes because we need to reap
-// our own children using wait.
-static bool WaitForSingleNonChildProcess(ProcessHandle handle,
- TimeDelta wait) {
- DCHECK_GT(handle, 0);
- DCHECK(wait.InMilliseconds() == kNoTimeout || wait > TimeDelta());
-
- ScopedFD kq(kqueue());
- if (!kq.is_valid()) {
- DPLOG(ERROR) << "kqueue";
- return false;
- }
-
- struct kevent change = {0};
- EV_SET(&change, handle, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
- int result = HANDLE_EINTR(kevent(kq.get(), &change, 1, NULL, 0, NULL));
- if (result == -1) {
- if (errno == ESRCH) {
- // If the process wasn't found, it must be dead.
- return true;
- }
-
- DPLOG(ERROR) << "kevent (setup " << handle << ")";
- return false;
- }
-
- // Keep track of the elapsed time to be able to restart kevent if it's
- // interrupted.
- bool wait_forever = wait.InMilliseconds() == kNoTimeout;
- TimeDelta remaining_delta;
- TimeTicks deadline;
- if (!wait_forever) {
- remaining_delta = wait;
- deadline = TimeTicks::Now() + remaining_delta;
- }
-
- result = -1;
- struct kevent event = {0};
-
- while (wait_forever || remaining_delta > TimeDelta()) {
- struct timespec remaining_timespec;
- struct timespec* remaining_timespec_ptr;
- if (wait_forever) {
- remaining_timespec_ptr = NULL;
- } else {
- remaining_timespec = remaining_delta.ToTimeSpec();
- remaining_timespec_ptr = &remaining_timespec;
- }
-
- result = kevent(kq.get(), NULL, 0, &event, 1, remaining_timespec_ptr);
-
- if (result == -1 && errno == EINTR) {
- if (!wait_forever) {
- remaining_delta = deadline - TimeTicks::Now();
- }
- result = 0;
- } else {
- break;
- }
- }
-
- if (result < 0) {
- DPLOG(ERROR) << "kevent (wait " << handle << ")";
- return false;
- } else if (result > 1) {
- DLOG(ERROR) << "kevent (wait " << handle << "): unexpected result "
- << result;
- return false;
- } else if (result == 0) {
- // Timed out.
- return false;
- }
-
- DCHECK_EQ(result, 1);
-
- if (event.filter != EVFILT_PROC ||
- (event.fflags & NOTE_EXIT) == 0 ||
- event.ident != static_cast<uintptr_t>(handle)) {
- DLOG(ERROR) << "kevent (wait " << handle
- << "): unexpected event: filter=" << event.filter
- << ", fflags=" << event.fflags
- << ", ident=" << event.ident;
- return false;
- }
-
- return true;
-}
-#endif // OS_MACOSX
-#endif // !defined(OS_NACL_NONSFI)
-
TerminationStatus GetTerminationStatusImpl(ProcessHandle handle,
bool can_block,
int* exit_code) {
@@ -302,52 +147,6 @@
}
#if !defined(OS_NACL_NONSFI)
-bool WaitForExitCode(ProcessHandle handle, int* exit_code) {
- int status;
- if (HANDLE_EINTR(waitpid(handle, &status, 0)) == -1) {
- NOTREACHED();
- return false;
- }
-
- if (WIFEXITED(status)) {
- *exit_code = WEXITSTATUS(status);
- return true;
- }
-
- // If it didn't exit cleanly, it must have been signaled.
- DCHECK(WIFSIGNALED(status));
- return false;
-}
-
-bool WaitForExitCodeWithTimeout(ProcessHandle handle,
- int* exit_code,
- TimeDelta timeout) {
- ProcessHandle parent_pid = GetParentProcessId(handle);
- ProcessHandle our_pid = GetCurrentProcessHandle();
- if (parent_pid != our_pid) {
-#if defined(OS_MACOSX)
- // On Mac we can wait on non child processes.
- return WaitForSingleNonChildProcess(handle, timeout);
-#else
- // Currently on Linux we can't handle non child processes.
- NOTIMPLEMENTED();
-#endif // OS_MACOSX
- }
-
- int status;
- if (!WaitpidWithTimeout(handle, &status, timeout))
- return false;
- if (WIFSIGNALED(status)) {
- *exit_code = -1;
- return true;
- }
- if (WIFEXITED(status)) {
- *exit_code = WEXITSTATUS(status);
- return true;
- }
- return false;
-}
-
bool WaitForProcessesToExit(const FilePath::StringType& executable_name,
TimeDelta wait,
const ProcessFilter* filter) {
diff --git a/process/kill_win.cc b/process/kill_win.cc
index 7daf5f8..3c93047 100644
--- a/process/kill_win.cc
+++ b/process/kill_win.cc
@@ -12,7 +12,6 @@
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/process/process_iterator.h"
-#include "base/profiler/scoped_tracker.h"
#include "base/win/object_watcher.h"
namespace base {
@@ -71,11 +70,6 @@
}
void TimerExpiredTask::OnObjectSignaled(HANDLE object) {
- // TODO(vadimt): Remove ScopedTracker below once crbug.com/418183 is fixed.
- tracked_objects::ScopedTracker tracking_profile(
- FROM_HERE_WITH_EXPLICIT_FUNCTION(
- "418183 TimerExpiredTask::OnObjectSignaled"));
-
process_.Close();
}
@@ -107,22 +101,6 @@
return result;
}
-// Attempts to kill the process identified by the given process
-// entry structure, giving it the specified exit code.
-// Returns true if this is successful, false otherwise.
-bool KillProcessById(ProcessId process_id, int exit_code, bool wait) {
- HANDLE process = OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE,
- FALSE, // Don't inherit handle
- process_id);
- if (!process) {
- DPLOG(ERROR) << "Unable to open process " << process_id;
- return false;
- }
- bool ret = KillProcess(process, exit_code, wait);
- CloseHandle(process);
- return ret;
-}
-
TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) {
DWORD tmp_exit_code = 0;
@@ -181,26 +159,6 @@
}
}
-bool WaitForExitCode(ProcessHandle handle, int* exit_code) {
- // TODO(rvargas) crbug.com/417532: Remove this function.
- Process process(handle);
- return process.WaitForExit(exit_code);
-}
-
-bool WaitForExitCodeWithTimeout(ProcessHandle handle,
- int* exit_code,
- TimeDelta timeout) {
- if (::WaitForSingleObject(
- handle, static_cast<DWORD>(timeout.InMilliseconds())) != WAIT_OBJECT_0)
- return false;
- DWORD temp_code; // Don't clobber out-parameters in case of failure.
- if (!::GetExitCodeProcess(handle, &temp_code))
- return false;
-
- *exit_code = temp_code;
- return true;
-}
-
bool WaitForProcessesToExit(const FilePath::StringType& executable_name,
TimeDelta wait,
const ProcessFilter* filter) {
diff --git a/process/launch_posix.cc b/process/launch_posix.cc
index 203b7c8..f9963fa 100644
--- a/process/launch_posix.cc
+++ b/process/launch_posix.cc
@@ -33,7 +33,7 @@
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/posix/eintr_wrapper.h"
-#include "base/process/kill.h"
+#include "base/process/process.h"
#include "base/process/process_metrics.h"
#include "base/strings/stringprintf.h"
#include "base/synchronization/waitable_event.h"
@@ -684,7 +684,8 @@
// Always wait for exit code (even if we know we'll declare
// GOT_MAX_OUTPUT).
- bool success = WaitForExitCode(pid, exit_code);
+ Process process(pid);
+ bool success = process.WaitForExit(exit_code);
// If we stopped because we read as much as we wanted, we return
// GOT_MAX_OUTPUT (because the child may exit due to |SIGPIPE|).
diff --git a/process/process_posix.cc b/process/process_posix.cc
index bc2f3f8..a36bf77 100644
--- a/process/process_posix.cc
+++ b/process/process_posix.cc
@@ -5,12 +5,206 @@
#include "base/process/process.h"
#include <sys/resource.h>
-#include <sys/time.h>
-#include <sys/types.h>
+#include <sys/wait.h>
+#include "base/files/scoped_file.h"
#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
#include "base/process/kill.h"
+#if defined(OS_MACOSX)
+#include <sys/event.h>
+#endif
+
+namespace {
+
+#if !defined(OS_NACL_NONSFI)
+
+bool WaitpidWithTimeout(base::ProcessHandle handle,
+ int* status,
+ base::TimeDelta wait) {
+ // This POSIX version of this function only guarantees that we wait no less
+ // than |wait| for the process to exit. The child process may
+ // exit sometime before the timeout has ended but we may still block for up
+ // to 256 milliseconds after the fact.
+ //
+ // waitpid() has no direct support on POSIX for specifying a timeout, you can
+ // either ask it to block indefinitely or return immediately (WNOHANG).
+ // When a child process terminates a SIGCHLD signal is sent to the parent.
+ // Catching this signal would involve installing a signal handler which may
+ // affect other parts of the application and would be difficult to debug.
+ //
+ // Our strategy is to call waitpid() once up front to check if the process
+ // has already exited, otherwise to loop for |wait|, sleeping for
+ // at most 256 milliseconds each time using usleep() and then calling
+ // waitpid(). The amount of time we sleep starts out at 1 milliseconds, and
+ // we double it every 4 sleep cycles.
+ //
+ // usleep() is speced to exit if a signal is received for which a handler
+ // has been installed. This means that when a SIGCHLD is sent, it will exit
+ // depending on behavior external to this function.
+ //
+ // This function is used primarily for unit tests, if we want to use it in
+ // the application itself it would probably be best to examine other routes.
+
+ if (wait == base::TimeDelta::Max()) {
+ return HANDLE_EINTR(waitpid(handle, status, 0)) > 0;
+ }
+
+ pid_t ret_pid = HANDLE_EINTR(waitpid(handle, status, WNOHANG));
+ static const int64 kMaxSleepInMicroseconds = 1 << 18; // ~256 milliseconds.
+ int64 max_sleep_time_usecs = 1 << 10; // ~1 milliseconds.
+ int64 double_sleep_time = 0;
+
+ // If the process hasn't exited yet, then sleep and try again.
+ base::TimeTicks wakeup_time = base::TimeTicks::Now() + wait;
+ while (ret_pid == 0) {
+ base::TimeTicks now = base::TimeTicks::Now();
+ if (now > wakeup_time)
+ break;
+ // Guaranteed to be non-negative!
+ int64 sleep_time_usecs = (wakeup_time - now).InMicroseconds();
+ // Sleep for a bit while we wait for the process to finish.
+ if (sleep_time_usecs > max_sleep_time_usecs)
+ sleep_time_usecs = max_sleep_time_usecs;
+
+ // usleep() will return 0 and set errno to EINTR on receipt of a signal
+ // such as SIGCHLD.
+ usleep(sleep_time_usecs);
+ ret_pid = HANDLE_EINTR(waitpid(handle, status, WNOHANG));
+
+ if ((max_sleep_time_usecs < kMaxSleepInMicroseconds) &&
+ (double_sleep_time++ % 4 == 0)) {
+ max_sleep_time_usecs *= 2;
+ }
+ }
+
+ return ret_pid > 0;
+}
+
+#if defined(OS_MACOSX)
+// Using kqueue on Mac so that we can wait on non-child processes.
+// We can't use kqueues on child processes because we need to reap
+// our own children using wait.
+static bool WaitForSingleNonChildProcess(base::ProcessHandle handle,
+ base::TimeDelta wait) {
+ DCHECK_GT(handle, 0);
+ DCHECK_GT(wait, base::TimeDelta());
+
+ base::ScopedFD kq(kqueue());
+ if (!kq.is_valid()) {
+ DPLOG(ERROR) << "kqueue";
+ return false;
+ }
+
+ struct kevent change = {0};
+ EV_SET(&change, handle, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
+ int result = HANDLE_EINTR(kevent(kq.get(), &change, 1, NULL, 0, NULL));
+ if (result == -1) {
+ if (errno == ESRCH) {
+ // If the process wasn't found, it must be dead.
+ return true;
+ }
+
+ DPLOG(ERROR) << "kevent (setup " << handle << ")";
+ return false;
+ }
+
+ // Keep track of the elapsed time to be able to restart kevent if it's
+ // interrupted.
+ bool wait_forever = (wait == base::TimeDelta::Max());
+ base::TimeDelta remaining_delta;
+ base::TimeTicks deadline;
+ if (!wait_forever) {
+ remaining_delta = wait;
+ deadline = base::TimeTicks::Now() + remaining_delta;
+ }
+
+ result = -1;
+ struct kevent event = {0};
+
+ while (wait_forever || remaining_delta > base::TimeDelta()) {
+ struct timespec remaining_timespec;
+ struct timespec* remaining_timespec_ptr;
+ if (wait_forever) {
+ remaining_timespec_ptr = NULL;
+ } else {
+ remaining_timespec = remaining_delta.ToTimeSpec();
+ remaining_timespec_ptr = &remaining_timespec;
+ }
+
+ result = kevent(kq.get(), NULL, 0, &event, 1, remaining_timespec_ptr);
+
+ if (result == -1 && errno == EINTR) {
+ if (!wait_forever) {
+ remaining_delta = deadline - base::TimeTicks::Now();
+ }
+ result = 0;
+ } else {
+ break;
+ }
+ }
+
+ if (result < 0) {
+ DPLOG(ERROR) << "kevent (wait " << handle << ")";
+ return false;
+ } else if (result > 1) {
+ DLOG(ERROR) << "kevent (wait " << handle << "): unexpected result "
+ << result;
+ return false;
+ } else if (result == 0) {
+ // Timed out.
+ return false;
+ }
+
+ DCHECK_EQ(result, 1);
+
+ if (event.filter != EVFILT_PROC ||
+ (event.fflags & NOTE_EXIT) == 0 ||
+ event.ident != static_cast<uintptr_t>(handle)) {
+ DLOG(ERROR) << "kevent (wait " << handle
+ << "): unexpected event: filter=" << event.filter
+ << ", fflags=" << event.fflags
+ << ", ident=" << event.ident;
+ return false;
+ }
+
+ return true;
+}
+#endif // OS_MACOSX
+
+bool WaitForExitWithTimeoutImpl(base::ProcessHandle handle,
+ int* exit_code,
+ base::TimeDelta timeout) {
+ base::ProcessHandle parent_pid = base::GetParentProcessId(handle);
+ base::ProcessHandle our_pid = base::GetCurrentProcessHandle();
+ if (parent_pid != our_pid) {
+#if defined(OS_MACOSX)
+ // On Mac we can wait on non child processes.
+ return WaitForSingleNonChildProcess(handle, timeout);
+#else
+ // Currently on Linux we can't handle non child processes.
+ NOTIMPLEMENTED();
+#endif // OS_MACOSX
+ }
+
+ int status;
+ if (!WaitpidWithTimeout(handle, &status, timeout))
+ return false;
+ if (WIFSIGNALED(status)) {
+ *exit_code = -1;
+ return true;
+ }
+ if (WIFEXITED(status)) {
+ *exit_code = WEXITSTATUS(status);
+ return true;
+ }
+ return false;
+}
+#endif // !defined(OS_NACL_NONSFI)
+
+} // namespace
+
namespace base {
Process::Process(ProcessHandle handle) : process_(handle) {
@@ -102,15 +296,11 @@
}
bool Process::WaitForExit(int* exit_code) {
- // TODO(rvargas) crbug.com/417532: Remove this constant.
- const int kNoTimeout = -1;
- return WaitForExitWithTimeout(TimeDelta::FromMilliseconds(kNoTimeout),
- exit_code);
+ return WaitForExitWithTimeout(TimeDelta::Max(), exit_code);
}
bool Process::WaitForExitWithTimeout(TimeDelta timeout, int* exit_code) {
- // TODO(rvargas) crbug.com/417532: Move the implementation here.
- return base::WaitForExitCodeWithTimeout(Handle(), exit_code, timeout);
+ return WaitForExitWithTimeoutImpl(Handle(), exit_code, timeout);
}
#if !defined(OS_LINUX)
diff --git a/process/process_win.cc b/process/process_win.cc
index 8e5360b..b62fdb4 100644
--- a/process/process_win.cc
+++ b/process/process_win.cc
@@ -7,6 +7,7 @@
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/metrics/field_trial.h"
+#include "base/numerics/safe_conversions.h"
#include "base/process/kill.h"
#include "base/win/windows_version.h"
@@ -141,10 +142,17 @@
}
bool Process::WaitForExitWithTimeout(TimeDelta timeout, int* exit_code) {
- // TODO(rvargas) crbug.com/417532: Move the implementation here.
- if (timeout > TimeDelta::FromMilliseconds(INFINITE))
- timeout = TimeDelta::FromMilliseconds(INFINITE);
- return base::WaitForExitCodeWithTimeout(Handle(), exit_code, timeout);
+ // Limit timeout to INFINITE.
+ DWORD timeout_ms = saturated_cast<DWORD>(timeout.InMilliseconds());
+ if (::WaitForSingleObject(Handle(), timeout_ms) != WAIT_OBJECT_0)
+ return false;
+
+ DWORD temp_code; // Don't clobber out-parameters in case of failure.
+ if (!::GetExitCodeProcess(Handle(), &temp_code))
+ return false;
+
+ *exit_code = temp_code;
+ return true;
}
bool Process::IsProcessBackgrounded() const {
diff --git a/synchronization/waitable_event.h b/synchronization/waitable_event.h
index 5adc1ec..c35af54 100644
--- a/synchronization/waitable_event.h
+++ b/synchronization/waitable_event.h
@@ -21,9 +21,6 @@
namespace base {
-// This replaces INFINITE from Win32
-static const int kNoTimeout = -1;
-
class TimeDelta;
// A WaitableEvent can be a useful thread synchronization tool when you want to
diff --git a/synchronization/waitable_event_watcher_win.cc b/synchronization/waitable_event_watcher_win.cc
index a04a435..46d47ac 100644
--- a/synchronization/waitable_event_watcher_win.cc
+++ b/synchronization/waitable_event_watcher_win.cc
@@ -5,7 +5,6 @@
#include "base/synchronization/waitable_event_watcher.h"
#include "base/compiler_specific.h"
-#include "base/profiler/scoped_tracker.h"
#include "base/synchronization/waitable_event.h"
#include "base/win/object_watcher.h"
@@ -37,11 +36,6 @@
}
void WaitableEventWatcher::OnObjectSignaled(HANDLE h) {
- // TODO(vadimt): Remove ScopedTracker below once crbug.com/418183 is fixed.
- tracked_objects::ScopedTracker tracking_profile(
- FROM_HERE_WITH_EXPLICIT_FUNCTION(
- "418183 WaitableEventWatcher::OnObjectSignaled"));
-
WaitableEvent* event = event_;
EventCallback callback = callback_;
event_ = NULL;
diff --git a/test/BUILD.gn b/test/BUILD.gn
index 05a3dc3..120159e 100644
--- a/test/BUILD.gn
+++ b/test/BUILD.gn
@@ -36,6 +36,8 @@
"mock_chrome_application_mac.mm",
"mock_devices_changed_observer.cc",
"mock_devices_changed_observer.h",
+ "mock_log.cc",
+ "mock_log.h",
"multiprocess_test.cc",
"multiprocess_test.h",
"multiprocess_test_android.cc",
diff --git a/test/data/pe_image/pe_image_test_32.dll b/test/data/pe_image/pe_image_test_32.dll
new file mode 100755
index 0000000..118ce11
--- /dev/null
+++ b/test/data/pe_image/pe_image_test_32.dll
Binary files differ
diff --git a/test/data/pe_image/pe_image_test_64.dll b/test/data/pe_image/pe_image_test_64.dll
new file mode 100755
index 0000000..70f8ea4
--- /dev/null
+++ b/test/data/pe_image/pe_image_test_64.dll
Binary files differ
diff --git a/test/data/prefs/write.golden.json b/test/data/prefs/write.golden.json
index 9a5523c..fb1fff1 100644
--- a/test/data/prefs/write.golden.json
+++ b/test/data/prefs/write.golden.json
@@ -1,11 +1 @@
-{
- "homepage": "http://www.cnn.com",
- "long_int": {
- "pref": "214748364842"
- },
- "some_directory": "/usr/sbin/",
- "tabs": {
- "max_tabs": 10,
- "new_windows_in_tabs": false
- }
-}
+{"homepage":"http://www.cnn.com","long_int":{"pref":"214748364842"},"some_directory":"/usr/sbin/","tabs":{"max_tabs":10,"new_windows_in_tabs":false}}
\ No newline at end of file
diff --git a/test/gtest_util.cc b/test/gtest_util.cc
index c0bc04a..b811194 100644
--- a/test/gtest_util.cc
+++ b/test/gtest_util.cc
@@ -47,7 +47,7 @@
bool ReadTestNamesFromFile(const FilePath& path,
std::vector<SplitTestName>* output) {
- JSONFileValueSerializer deserializer(path);
+ JSONFileValueDeserializer deserializer(path);
int error_code = 0;
std::string error_message;
scoped_ptr<base::Value> value(
@@ -80,4 +80,4 @@
return true;
}
-} // namespace
+} // namespace base
diff --git a/test/mock_log.cc b/test/mock_log.cc
new file mode 100644
index 0000000..fa511d4
--- /dev/null
+++ b/test/mock_log.cc
@@ -0,0 +1,68 @@
+// Copyright 2015 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/test/mock_log.h"
+
+namespace base {
+namespace test {
+
+// static
+MockLog* MockLog::g_instance_ = nullptr;
+Lock MockLog::g_lock;
+
+MockLog::MockLog() : is_capturing_logs_(false) {
+}
+
+MockLog::~MockLog() {
+ if (is_capturing_logs_) {
+ StopCapturingLogs();
+ }
+}
+
+void MockLog::StartCapturingLogs() {
+ AutoLock scoped_lock(g_lock);
+
+ // We don't use CHECK(), which can generate a new LOG message, and
+ // thus can confuse MockLog objects or other registered
+ // LogSinks.
+ RAW_CHECK(!is_capturing_logs_);
+ RAW_CHECK(!g_instance_);
+
+ is_capturing_logs_ = true;
+ g_instance_ = this;
+ previous_handler_ = logging::GetLogMessageHandler();
+ logging::SetLogMessageHandler(LogMessageHandler);
+}
+
+void MockLog::StopCapturingLogs() {
+ AutoLock scoped_lock(g_lock);
+
+ // We don't use CHECK(), which can generate a new LOG message, and
+ // thus can confuse MockLog objects or other registered
+ // LogSinks.
+ RAW_CHECK(is_capturing_logs_);
+ RAW_CHECK(g_instance_ == this);
+
+ is_capturing_logs_ = false;
+ logging::SetLogMessageHandler(previous_handler_);
+ g_instance_ = nullptr;
+}
+
+// static
+bool MockLog::LogMessageHandler(int severity,
+ const char* file,
+ int line,
+ size_t message_start,
+ const std::string& str) {
+ // gMock guarantees thread-safety for calling a mocked method
+ // (https://code.google.com/p/googlemock/wiki/CookBook#Using_Google_Mock_and_Threads)
+ // but we also need to make sure that Start/StopCapturingLogs are synchronized
+ // with LogMessageHandler.
+ AutoLock scoped_lock(g_lock);
+
+ return g_instance_->Log(severity, file, line, message_start, str);
+}
+
+} // namespace test
+} // namespace base
diff --git a/test/mock_log.h b/test/mock_log.h
new file mode 100644
index 0000000..315ef1f
--- /dev/null
+++ b/test/mock_log.h
@@ -0,0 +1,98 @@
+// Copyright 2015 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_TEST_MOCK_LOG_H_
+#define BASE_TEST_MOCK_LOG_H_
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/synchronization/lock.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace base {
+namespace test {
+
+// A MockLog object intercepts LOG() messages issued during its lifespan. Using
+// this together with gMock, it's very easy to test how a piece of code calls
+// LOG(). The typical usage:
+//
+// TEST(FooTest, LogsCorrectly) {
+// MockLog log;
+//
+// // We expect the WARNING "Something bad!" exactly twice.
+// EXPECT_CALL(log, Log(WARNING, _, "Something bad!"))
+// .Times(2);
+//
+// // We allow foo.cc to call LOG(INFO) any number of times.
+// EXPECT_CALL(log, Log(INFO, HasSubstr("/foo.cc"), _))
+// .Times(AnyNumber());
+//
+// log.StartCapturingLogs(); // Call this after done setting expectations.
+// Foo(); // Exercises the code under test.
+// }
+//
+// CAVEAT: base/logging does not allow a thread to call LOG() again when it's
+// already inside a LOG() call. Doing so will cause a deadlock. Therefore,
+// it's the user's responsibility to not call LOG() in an action triggered by
+// MockLog::Log(). You may call RAW_LOG() instead.
+class MockLog {
+ public:
+ // Creates a MockLog object that is not capturing logs. If it were to start
+ // to capture logs, it could be a problem if some other threads already exist
+ // and are logging, as the user hasn't had a chance to set up expectation on
+ // this object yet (calling a mock method before setting the expectation is
+ // UNDEFINED behavior).
+ MockLog();
+
+ // When the object is destructed, it stops intercepting logs.
+ ~MockLog();
+
+ // Starts log capturing if the object isn't already doing so.
+ // Otherwise crashes.
+ void StartCapturingLogs();
+
+ // Stops log capturing if the object is capturing logs. Otherwise crashes.
+ void StopCapturingLogs();
+
+ // Log method is invoked for every log message before it's sent to other log
+ // destinations (if any). The method should return true to signal that it
+ // handled the message and the message should not be sent to other log
+ // destinations.
+ MOCK_METHOD5(Log,
+ bool(int severity,
+ const char* file,
+ int line,
+ size_t message_start,
+ const std::string& str));
+
+ private:
+ // The currently active mock log.
+ static MockLog* g_instance_;
+
+ // Lock protecting access to g_instance_.
+ static Lock g_lock;
+
+ // Static function which is set as the logging message handler.
+ // Called once for each message.
+ static bool LogMessageHandler(int severity,
+ const char* file,
+ int line,
+ size_t message_start,
+ const std::string& str);
+
+ // True if this object is currently capturing logs.
+ bool is_capturing_logs_;
+
+ // The previous handler to restore when the MockLog is destroyed.
+ logging::LogMessageHandlerFunction previous_handler_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockLog);
+};
+
+} // namespace test
+} // namespace base
+
+#endif // BASE_TEST_MOCK_LOG_H_
diff --git a/test/test_mock_time_task_runner.cc b/test/test_mock_time_task_runner.cc
index a878280..8e65ccf 100644
--- a/test/test_mock_time_task_runner.cc
+++ b/test/test_mock_time_task_runner.cc
@@ -154,8 +154,7 @@
const tracked_objects::Location& from_here,
const Closure& task,
TimeDelta delay) {
- NOTREACHED();
- return false;
+ return PostDelayedTask(from_here, task, delay);
}
void TestMockTimeTaskRunner::OnBeforeSelectingTask() {
diff --git a/test/test_mock_time_task_runner.h b/test/test_mock_time_task_runner.h
index 1a651f6..5f06013 100644
--- a/test/test_mock_time_task_runner.h
+++ b/test/test_mock_time_task_runner.h
@@ -32,13 +32,12 @@
// - It allows for reentrancy, in that it handles the running of tasks that in
// turn call back into it (e.g., to post more tasks).
// - Tasks are stored in a priority queue, and executed in the increasing
-// order of post time + delay.
+// order of post time + delay, but ignoring nestability.
// - It does not check for overflow when doing time arithmetic. A sufficient
// condition for preventing overflows is to make sure that the sum of all
// posted task delays and fast-forward increments is still representable by
// a TimeDelta, and that adding this delta to the starting values of Time
// and TickTime is still within their respective range.
-// - Non-nestable tasks are not supported.
// - Tasks aren't guaranteed to be destroyed immediately after they're run.
//
// This is a slightly more sophisticated version of TestSimpleTaskRunner, in
diff --git a/third_party/nspr/BUILD.gn b/third_party/nspr/BUILD.gn
index c0b2ec2..a67e168 100644
--- a/third_party/nspr/BUILD.gn
+++ b/third_party/nspr/BUILD.gn
@@ -3,9 +3,7 @@
# found in the LICENSE file.
source_set("nspr") {
- visibility = [
- "//base/*",
- ]
+ visibility = [ "//base/*" ]
sources = [
"prtime.cc",
diff --git a/threading/sequenced_worker_pool.cc b/threading/sequenced_worker_pool.cc
index 89224f7..19b81b7 100644
--- a/threading/sequenced_worker_pool.cc
+++ b/threading/sequenced_worker_pool.cc
@@ -798,9 +798,11 @@
// to run them. Also, there may be some tasks stuck behind running
// ones with the same sequence token, but additional threads won't
// help this case.
- if (shutdown_called_ &&
- blocking_shutdown_pending_task_count_ == 0)
+ if (shutdown_called_ && blocking_shutdown_pending_task_count_ == 0) {
+ AutoUnlock unlock(lock_);
+ delete_these_outside_lock.clear();
break;
+ }
waiting_thread_count_++;
switch (status) {
diff --git a/threading/sequenced_worker_pool_unittest.cc b/threading/sequenced_worker_pool_unittest.cc
index ed5f896..9d0f607 100644
--- a/threading/sequenced_worker_pool_unittest.cc
+++ b/threading/sequenced_worker_pool_unittest.cc
@@ -67,6 +67,23 @@
size_t unblock_counter_;
};
+class DestructionDeadlockChecker
+ : public base::RefCountedThreadSafe<DestructionDeadlockChecker> {
+ public:
+ DestructionDeadlockChecker(const scoped_refptr<SequencedWorkerPool>& pool)
+ : pool_(pool) {}
+
+ protected:
+ virtual ~DestructionDeadlockChecker() {
+ // This method should not deadlock.
+ pool_->RunsTasksOnCurrentThread();
+ }
+
+ private:
+ scoped_refptr<SequencedWorkerPool> pool_;
+ friend class base::RefCountedThreadSafe<DestructionDeadlockChecker>;
+};
+
class TestTracker : public base::RefCountedThreadSafe<TestTracker> {
public:
TestTracker()
@@ -117,6 +134,20 @@
SignalWorkerDone(id);
}
+ // This task posts itself back onto the SequencedWorkerPool before it
+ // finishes running. Each instance of the task maintains a strong reference
+ // to a DestructionDeadlockChecker. The DestructionDeadlockChecker is only
+ // destroyed when the task is destroyed without being run, which only happens
+ // during destruction of the SequencedWorkerPool.
+ void PostRepostingTask(
+ const scoped_refptr<SequencedWorkerPool>& pool,
+ const scoped_refptr<DestructionDeadlockChecker>& checker) {
+ Closure reposting_task =
+ base::Bind(&TestTracker::PostRepostingTask, this, pool, checker);
+ pool->PostWorkerTaskWithShutdownBehavior(
+ FROM_HERE, reposting_task, SequencedWorkerPool::SKIP_ON_SHUTDOWN);
+ }
+
// Waits until the given number of tasks have started executing.
void WaitUntilTasksBlocked(size_t count) {
{
@@ -749,6 +780,21 @@
unused_pool->Shutdown();
}
+// Checks that tasks are destroyed in the right context during shutdown. If a
+// task is destroyed while SequencedWorkerPool's global lock is held,
+// SequencedWorkerPool might deadlock.
+TEST_F(SequencedWorkerPoolTest, AvoidsDeadlockOnShutdown) {
+ for (int i = 0; i < 4; ++i) {
+ scoped_refptr<DestructionDeadlockChecker> checker(
+ new DestructionDeadlockChecker(pool()));
+ tracker()->PostRepostingTask(pool(), checker);
+ }
+
+ // Shutting down the pool should destroy the DestructionDeadlockCheckers,
+ // which in turn should not deadlock in their destructors.
+ pool()->Shutdown();
+}
+
// Verify that FlushForTesting works as intended.
TEST_F(SequencedWorkerPoolTest, FlushForTesting) {
// Should be fine to call on a new instance.
diff --git a/threading/thread_restrictions.h b/threading/thread_restrictions.h
index 7c46fd2..1e5f510 100644
--- a/threading/thread_restrictions.h
+++ b/threading/thread_restrictions.h
@@ -22,6 +22,7 @@
namespace cc {
class CompletionEvent;
+class TaskGraphRunner;
}
namespace chromeos {
class BlockingMethodCaller;
@@ -177,6 +178,7 @@
friend class ::HistogramSynchronizer;
friend class ::ScopedAllowWaitForLegacyWebViewApi;
friend class cc::CompletionEvent;
+ friend class cc::TaskGraphRunner;
friend class mojo::common::WatcherThreadManager;
friend class remoting::AutoThread;
friend class MessagePumpDefault;
diff --git a/timer/timer.cc b/timer/timer.cc
index 11f73ca..fa6b8cd 100644
--- a/timer/timer.cc
+++ b/timer/timer.cc
@@ -163,8 +163,10 @@
}
// Remember the thread ID that posts the first task -- this will be verified
// later when the task is abandoned to detect misuse from multiple threads.
- if (!thread_id_)
+ if (!thread_id_) {
+ DCHECK(GetTaskRunner()->BelongsToCurrentThread());
thread_id_ = static_cast<int>(PlatformThread::CurrentId());
+ }
}
scoped_refptr<SingleThreadTaskRunner> Timer::GetTaskRunner() {
diff --git a/timer/timer.h b/timer/timer.h
index ea34a9f..1ef58a3 100644
--- a/timer/timer.h
+++ b/timer/timer.h
@@ -89,7 +89,8 @@
virtual TimeDelta GetCurrentDelay() const;
// Set the task runner on which the task should be scheduled. This method can
- // only be called before any tasks have been scheduled.
+ // only be called before any tasks have been scheduled. The task runner must
+ // run tasks on the same thread the timer is used on.
virtual void SetTaskRunner(scoped_refptr<SingleThreadTaskRunner> task_runner);
// Start the timer to run at the given |delay| from now. If the timer is
diff --git a/trace_event/memory_dump_manager.cc b/trace_event/memory_dump_manager.cc
index c8be8f8..cbed238 100644
--- a/trace_event/memory_dump_manager.cc
+++ b/trace_event/memory_dump_manager.cc
@@ -6,6 +6,7 @@
#include <algorithm>
+#include "base/atomic_sequence_num.h"
#include "base/compiler_specific.h"
#include "base/trace_event/memory_dump_provider.h"
#include "base/trace_event/process_memory_dump.h"
@@ -15,7 +16,27 @@
namespace trace_event {
namespace {
+
MemoryDumpManager* g_instance_for_testing = nullptr;
+const int kTraceEventNumArgs = 1;
+const char* kTraceEventArgNames[] = {"dumps"};
+const unsigned char kTraceEventArgTypes[] = {TRACE_VALUE_TYPE_CONVERTABLE};
+StaticAtomicSequenceNumber g_next_guid;
+
+const char* DumpPointTypeToString(const DumpPointType& dump_point_type) {
+ switch (dump_point_type) {
+ case DumpPointType::TASK_BEGIN:
+ return "TASK_BEGIN";
+ case DumpPointType::TASK_END:
+ return "TASK_END";
+ case DumpPointType::PERIODIC_INTERVAL:
+ return "PERIODIC_INTERVAL";
+ case DumpPointType::EXPLICITLY_TRIGGERED:
+ return "EXPLICITLY_TRIGGERED";
+ }
+ NOTREACHED();
+ return "UNKNOWN";
+}
}
// TODO(primiano): this should be smarter and should do something similar to
@@ -76,13 +97,18 @@
dump_providers_enabled_.erase(it);
}
-void MemoryDumpManager::RequestDumpPoint(DumpPointType type) {
- // TODO(primiano): this will have more logic, IPC broadcast & co.
+void MemoryDumpManager::RequestDumpPoint(DumpPointType dump_point_type) {
+ // TODO(primiano): this will have more logic to coordinate dump points across
+ // multiple processes via IPC. See crbug.com/462930.
+
// Bail out immediately if tracing is not enabled at all.
if (!UNLIKELY(subtle::NoBarrier_Load(&memory_tracing_enabled_)))
return;
- CreateLocalDumpPoint();
+ // TODO(primiano): Make guid actually unique (cross-process) by hashing it
+ // with the PID. See crbug.com/462931 for details.
+ const uint64 guid = g_next_guid.GetNext();
+ CreateLocalDumpPoint(dump_point_type, guid);
}
void MemoryDumpManager::BroadcastDumpRequest() {
@@ -90,17 +116,44 @@
}
// Creates a dump point for the current process and appends it to the trace.
-void MemoryDumpManager::CreateLocalDumpPoint() {
- AutoLock lock(lock_);
+void MemoryDumpManager::CreateLocalDumpPoint(DumpPointType dump_point_type,
+ uint64 guid) {
+ bool did_any_provider_dump = false;
scoped_ptr<ProcessMemoryDump> pmd(new ProcessMemoryDump());
- for (MemoryDumpProvider* dump_provider : dump_providers_enabled_) {
- dump_provider->DumpInto(pmd.get());
+ // Serialize dump point generation so that memory dump providers don't have to
+ // deal with thread safety.
+ {
+ AutoLock lock(lock_);
+ for (auto it = dump_providers_enabled_.begin();
+ it != dump_providers_enabled_.end();) {
+ MemoryDumpProvider* dump_provider = *it;
+ if (dump_provider->DumpInto(pmd.get())) {
+ did_any_provider_dump = true;
+ ++it;
+ } else {
+ LOG(ERROR) << "The memory dumper " << dump_provider->GetFriendlyName()
+ << " failed, possibly due to sandboxing (crbug.com/461788), "
+ "disabling it for current process. Try restarting chrome "
+ "with the --no-sandbox switch.";
+ it = dump_providers_enabled_.erase(it);
+ }
+ }
}
- scoped_refptr<TracedValue> value(new TracedValue());
- pmd->AsValueInto(value.get());
- // TODO(primiano): add the dump point to the trace at this point.
+ // Don't create a dump point if all the dumpers failed.
+ if (!did_any_provider_dump)
+ return;
+
+ scoped_refptr<ConvertableToTraceFormat> event_value(new TracedValue());
+ pmd->AsValueInto(static_cast<TracedValue*>(event_value.get()));
+ const char* const event_name = DumpPointTypeToString(dump_point_type);
+
+ TRACE_EVENT_API_ADD_TRACE_EVENT(
+ TRACE_EVENT_PHASE_MEMORY_DUMP,
+ TraceLog::GetCategoryGroupEnabled(kTraceCategory), event_name, guid,
+ kTraceEventNumArgs, kTraceEventArgNames, kTraceEventArgTypes,
+ NULL /* arg_values */, &event_value, TRACE_EVENT_FLAG_HAS_ID);
}
void MemoryDumpManager::OnTraceLogEnabled() {
diff --git a/trace_event/memory_dump_manager.h b/trace_event/memory_dump_manager.h
index 1a22e61..8a9b3b7 100644
--- a/trace_event/memory_dump_manager.h
+++ b/trace_event/memory_dump_manager.h
@@ -43,7 +43,7 @@
// Requests a memory dump. The dump might happen or not depending on the
// filters and categories specified when enabling tracing.
- void RequestDumpPoint(DumpPointType type);
+ void RequestDumpPoint(DumpPointType dump_point_type);
// TraceLog::EnabledStateObserver implementation.
void OnTraceLogEnabled() override;
@@ -65,7 +65,7 @@
void BroadcastDumpRequest();
// Creates a dump point for the current process and appends it to the trace.
- void CreateLocalDumpPoint();
+ void CreateLocalDumpPoint(DumpPointType dump_point_type, uint64 guid);
std::vector<MemoryDumpProvider*> dump_providers_registered_; // Not owned.
std::vector<MemoryDumpProvider*> dump_providers_enabled_; // Not owned.
diff --git a/trace_event/memory_dump_manager_unittest.cc b/trace_event/memory_dump_manager_unittest.cc
index 1ba73e6..78be377 100644
--- a/trace_event/memory_dump_manager_unittest.cc
+++ b/trace_event/memory_dump_manager_unittest.cc
@@ -10,6 +10,7 @@
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
+using testing::Return;
namespace base {
namespace trace_event {
@@ -48,7 +49,9 @@
class MockDumpProvider : public MemoryDumpProvider {
public:
- MOCK_METHOD1(DumpInto, void(ProcessMemoryDump* pmd));
+ MOCK_METHOD1(DumpInto, bool(ProcessMemoryDump* pmd));
+
+ const char* GetFriendlyName() const override { return "MockDumpProvider"; }
};
TEST_F(MemoryDumpManagerTest, SingleDumper) {
@@ -64,7 +67,7 @@
// Now repeat enabling the memory category and check that the dumper is
// invoked this time.
EnableTracing(kTraceCategory);
- EXPECT_CALL(mdp, DumpInto(_)).Times(3);
+ EXPECT_CALL(mdp, DumpInto(_)).Times(3).WillRepeatedly(Return(true));
for (int i = 0; i < 3; ++i)
mdm_->RequestDumpPoint(DumpPointType::EXPLICITLY_TRIGGERED);
DisableTracing();
@@ -83,7 +86,7 @@
mdm_->RegisterDumpProvider(&mdp);
EnableTracing(kTraceCategory);
- EXPECT_CALL(mdp, DumpInto(_)).Times(1);
+ EXPECT_CALL(mdp, DumpInto(_)).Times(1).WillRepeatedly(Return(true));
mdm_->RequestDumpPoint(DumpPointType::EXPLICITLY_TRIGGERED);
mdm_->UnregisterDumpProvider(&mdp);
@@ -100,7 +103,7 @@
// Enable only mdp1.
mdm_->RegisterDumpProvider(&mdp1);
EnableTracing(kTraceCategory);
- EXPECT_CALL(mdp1, DumpInto(_)).Times(1);
+ EXPECT_CALL(mdp1, DumpInto(_)).Times(1).WillRepeatedly(Return(true));
EXPECT_CALL(mdp2, DumpInto(_)).Times(0);
mdm_->RequestDumpPoint(DumpPointType::EXPLICITLY_TRIGGERED);
DisableTracing();
@@ -110,18 +113,40 @@
mdm_->RegisterDumpProvider(&mdp2);
EnableTracing(kTraceCategory);
EXPECT_CALL(mdp1, DumpInto(_)).Times(0);
- EXPECT_CALL(mdp2, DumpInto(_)).Times(1);
+ EXPECT_CALL(mdp2, DumpInto(_)).Times(1).WillRepeatedly(Return(true));
mdm_->RequestDumpPoint(DumpPointType::EXPLICITLY_TRIGGERED);
DisableTracing();
// Enable both mdp1 and mdp2.
mdm_->RegisterDumpProvider(&mdp1);
EnableTracing(kTraceCategory);
- EXPECT_CALL(mdp1, DumpInto(_)).Times(1);
- EXPECT_CALL(mdp2, DumpInto(_)).Times(1);
+ EXPECT_CALL(mdp1, DumpInto(_)).Times(1).WillRepeatedly(Return(true));
+ EXPECT_CALL(mdp2, DumpInto(_)).Times(1).WillRepeatedly(Return(true));
mdm_->RequestDumpPoint(DumpPointType::EXPLICITLY_TRIGGERED);
DisableTracing();
}
+// Enable both dump providers, make mdp1 fail and assert that only mdp2 is
+// invoked the 2nd time.
+// FIXME(primiano): remove once crbug.com/461788 gets fixed.
+TEST_F(MemoryDumpManagerTest, DisableFailingDumpers) {
+ MockDumpProvider mdp1;
+ MockDumpProvider mdp2;
+
+ mdm_->RegisterDumpProvider(&mdp1);
+ mdm_->RegisterDumpProvider(&mdp2);
+ EnableTracing(kTraceCategory);
+
+ EXPECT_CALL(mdp1, DumpInto(_)).Times(1).WillRepeatedly(Return(false));
+ EXPECT_CALL(mdp2, DumpInto(_)).Times(1).WillRepeatedly(Return(true));
+ mdm_->RequestDumpPoint(DumpPointType::EXPLICITLY_TRIGGERED);
+
+ EXPECT_CALL(mdp1, DumpInto(_)).Times(0);
+ EXPECT_CALL(mdp2, DumpInto(_)).Times(1).WillRepeatedly(Return(false));
+ mdm_->RequestDumpPoint(DumpPointType::EXPLICITLY_TRIGGERED);
+
+ DisableTracing();
+}
+
} // namespace trace_Event
} // namespace base
diff --git a/trace_event/memory_dump_provider.h b/trace_event/memory_dump_provider.h
index 18363c5..1c5bbb1 100644
--- a/trace_event/memory_dump_provider.h
+++ b/trace_event/memory_dump_provider.h
@@ -17,7 +17,10 @@
class BASE_EXPORT MemoryDumpProvider {
public:
// Called by the MemoryDumpManager when generating dump points.
- virtual void DumpInto(ProcessMemoryDump* pmd) = 0;
+ // Returns: true if the |pmd| was successfully populated, false otherwise.
+ virtual bool DumpInto(ProcessMemoryDump* pmd) = 0;
+
+ virtual const char* GetFriendlyName() const = 0;
protected:
MemoryDumpProvider() {}
diff --git a/trace_event/process_memory_maps_dump_provider.cc b/trace_event/process_memory_maps_dump_provider.cc
index e1cefc3..93feded 100644
--- a/trace_event/process_memory_maps_dump_provider.cc
+++ b/trace_event/process_memory_maps_dump_provider.cc
@@ -15,6 +15,10 @@
namespace base {
namespace trace_event {
+namespace {
+const char kDumperFriendlyName[] = "ProcessMemoryMaps";
+}
+
#if defined(OS_LINUX) || defined(OS_ANDROID)
// static
std::istream* ProcessMemoryMapsDumpProvider::proc_smaps_for_testing = nullptr;
@@ -104,10 +108,9 @@
}
uint32 ReadLinuxProcSmapsFile(std::istream* smaps, ProcessMemoryMaps* pmm) {
- if (!smaps->good()) {
- LOG(ERROR) << "Could not read smaps file.";
+ if (!smaps->good())
return 0;
- }
+
const uint32 kNumExpectedCountersPerRegion = 2;
uint32 counters_parsed_for_current_region = 0;
uint32 num_valid_regions = 0;
@@ -154,7 +157,7 @@
// Called at trace dump point time. Creates a snapshot the memory maps for the
// current process.
-void ProcessMemoryMapsDumpProvider::DumpInto(ProcessMemoryDump* pmd) {
+bool ProcessMemoryMapsDumpProvider::DumpInto(ProcessMemoryDump* pmd) {
uint32 res = 0;
#if defined(OS_LINUX) || defined(OS_ANDROID)
@@ -168,8 +171,16 @@
LOG(ERROR) << "ProcessMemoryMaps dump provider is supported only on Linux";
#endif
- if (res > 0)
+ if (res > 0) {
pmd->set_has_process_mmaps();
+ return true;
+ }
+
+ return false;
+}
+
+const char* ProcessMemoryMapsDumpProvider::GetFriendlyName() const {
+ return kDumperFriendlyName;
}
} // namespace trace_event
diff --git a/trace_event/process_memory_maps_dump_provider.h b/trace_event/process_memory_maps_dump_provider.h
index 543f7fd..0d30db2 100644
--- a/trace_event/process_memory_maps_dump_provider.h
+++ b/trace_event/process_memory_maps_dump_provider.h
@@ -20,7 +20,8 @@
static ProcessMemoryMapsDumpProvider* GetInstance();
// MemoryDumpProvider implementation.
- void DumpInto(ProcessMemoryDump* pmd) override;
+ bool DumpInto(ProcessMemoryDump* pmd) override;
+ const char* GetFriendlyName() const override;
private:
friend struct DefaultSingletonTraits<ProcessMemoryMapsDumpProvider>;
diff --git a/trace_event/process_memory_totals_dump_provider.cc b/trace_event/process_memory_totals_dump_provider.cc
index cda0ff1..125be38 100644
--- a/trace_event/process_memory_totals_dump_provider.cc
+++ b/trace_event/process_memory_totals_dump_provider.cc
@@ -15,6 +15,9 @@
uint64 ProcessMemoryTotalsDumpProvider::rss_bytes_for_testing = 0;
namespace {
+
+const char kDumperFriendlyName[] = "ProcessMemoryTotals";
+
ProcessMetrics* CreateProcessMetricsForCurrentProcess() {
#if !defined(OS_MACOSX) || defined(OS_IOS)
return ProcessMetrics::CreateProcessMetrics(GetCurrentProcessHandle());
@@ -41,12 +44,22 @@
// Called at trace dump point time. Creates a snapshot the memory counters for
// the current process.
-void ProcessMemoryTotalsDumpProvider::DumpInto(ProcessMemoryDump* pmd) {
+bool ProcessMemoryTotalsDumpProvider::DumpInto(ProcessMemoryDump* pmd) {
const uint64 rss_bytes = rss_bytes_for_testing
? rss_bytes_for_testing
: process_metrics_->GetWorkingSetSize();
- pmd->process_totals()->set_resident_set_bytes(rss_bytes);
- pmd->set_has_process_totals();
+
+ if (rss_bytes > 0) {
+ pmd->process_totals()->set_resident_set_bytes(rss_bytes);
+ pmd->set_has_process_totals();
+ return true;
+ }
+
+ return false;
+}
+
+const char* ProcessMemoryTotalsDumpProvider::GetFriendlyName() const {
+ return kDumperFriendlyName;
}
} // namespace trace_event
diff --git a/trace_event/process_memory_totals_dump_provider.h b/trace_event/process_memory_totals_dump_provider.h
index 45917a8..8dae966 100644
--- a/trace_event/process_memory_totals_dump_provider.h
+++ b/trace_event/process_memory_totals_dump_provider.h
@@ -22,7 +22,8 @@
static ProcessMemoryTotalsDumpProvider* GetInstance();
// MemoryDumpProvider implementation.
- void DumpInto(ProcessMemoryDump* pmd) override;
+ bool DumpInto(ProcessMemoryDump* pmd) override;
+ const char* GetFriendlyName() const override;
private:
friend struct DefaultSingletonTraits<ProcessMemoryTotalsDumpProvider>;
diff --git a/trace_event/trace_event.h b/trace_event/trace_event.h
index e12d8f4..c30a84a 100644
--- a/trace_event/trace_event.h
+++ b/trace_event/trace_event.h
@@ -1039,6 +1039,7 @@
#define TRACE_EVENT_PHASE_CREATE_OBJECT ('N')
#define TRACE_EVENT_PHASE_SNAPSHOT_OBJECT ('O')
#define TRACE_EVENT_PHASE_DELETE_OBJECT ('D')
+#define TRACE_EVENT_PHASE_MEMORY_DUMP ('v')
// Flags for changing the behavior of TRACE_EVENT_API_ADD_TRACE_EVENT.
#define TRACE_EVENT_FLAG_NONE (static_cast<unsigned char>(0))
diff --git a/values.cc b/values.cc
index 061b7a1..52876cf 100644
--- a/values.cc
+++ b/values.cc
@@ -1143,6 +1143,9 @@
ValueSerializer::~ValueSerializer() {
}
+ValueDeserializer::~ValueDeserializer() {
+}
+
std::ostream& operator<<(std::ostream& out, const Value& value) {
std::string json;
JSONWriter::WriteWithOptions(&value,
diff --git a/values.h b/values.h
index 4648283..1e1cae3 100644
--- a/values.h
+++ b/values.h
@@ -492,13 +492,20 @@
DISALLOW_COPY_AND_ASSIGN(ListValue);
};
-// This interface is implemented by classes that know how to serialize and
-// deserialize Value objects.
+// This interface is implemented by classes that know how to serialize
+// Value objects.
class BASE_EXPORT ValueSerializer {
public:
virtual ~ValueSerializer();
virtual bool Serialize(const Value& root) = 0;
+};
+
+// This interface is implemented by classes that know how to deserialize Value
+// objects.
+class BASE_EXPORT ValueDeserializer {
+ public:
+ virtual ~ValueDeserializer();
// This method deserializes the subclass-specific format into a Value object.
// If the return value is non-NULL, the caller takes ownership of returned
diff --git a/win/pe_image_unittest.cc b/win/pe_image_unittest.cc
index 4134741..28b65a4 100644
--- a/win/pe_image_unittest.cc
+++ b/win/pe_image_unittest.cc
@@ -6,129 +6,10 @@
#include <algorithm>
#include <vector>
-#include "testing/gtest/include/gtest/gtest.h"
+#include "base/files/file_path.h"
+#include "base/path_service.h"
#include "base/win/pe_image.h"
-#include "base/win/windows_version.h"
-
-namespace {
-
-class Expectations {
- public:
- enum Value {
- SECTIONS = 0,
- IMPORTS_DLLS,
- DELAY_DLLS,
- EXPORTS,
- IMPORTS,
- DELAY_IMPORTS,
- RELOCS
- };
-
- enum Arch {
- ARCH_X86 = 0,
- ARCH_X64,
- ARCH_ALL
- };
-
- Expectations();
-
- void SetDefault(Value value, int count);
- void SetOverride(Value value, base::win::Version version,
- Arch arch, int count);
- void SetOverride(Value value, base::win::Version version, int count);
- void SetOverride(Value value, Arch arch, int count);
-
- // returns -1 on failure.
- int GetExpectation(Value value);
-
- private:
- class Override {
- public:
- enum MatchType { MATCH_VERSION, MATCH_ARCH, MATCH_BOTH, MATCH_NONE };
-
- Override(Value value, base::win::Version version, Arch arch, int count)
- : value_(value), version_(version), arch_(arch), count_(count) {
- };
-
- bool Matches(Value value, base::win::Version version,
- Arch arch, MatchType type) {
- if (value_ != value)
- return false;
-
- switch (type) {
- case MATCH_BOTH:
- return (arch == arch_ && version == version_);
- case MATCH_ARCH:
- return (arch == arch_ && version_ == base::win::VERSION_WIN_LAST);
- case MATCH_VERSION:
- return (arch_ == ARCH_ALL && version == version_);
- case MATCH_NONE:
- return (arch_ == ARCH_ALL && version_ == base::win::VERSION_WIN_LAST);
- }
- return false;
- }
-
- int GetCount() { return count_; }
-
- private:
- Value value_;
- base::win::Version version_;
- Arch arch_;
- int count_;
- };
-
- bool MatchesMyArch(Arch arch);
-
- std::vector<Override> overrides_;
- Arch my_arch_;
- base::win::Version my_version_;
-};
-
-Expectations::Expectations() {
- my_version_ = base::win::GetVersion();
-#if defined(ARCH_CPU_64_BITS)
- my_arch_ = ARCH_X64;
-#else
- my_arch_ = ARCH_X86;
-#endif
-}
-
-int Expectations::GetExpectation(Value value) {
- // Prefer OS version specificity over Arch specificity.
- for (auto type : { Override::MATCH_BOTH,
- Override::MATCH_VERSION,
- Override::MATCH_ARCH,
- Override::MATCH_NONE }) {
- for (auto override : overrides_) {
- if (override.Matches(value, my_version_, my_arch_, type))
- return override.GetCount();
- }
- }
- return -1;
-}
-
-void Expectations::SetDefault(Value value, int count) {
- SetOverride(value, base::win::VERSION_WIN_LAST, ARCH_ALL, count);
-}
-
-void Expectations::SetOverride(Value value,
- base::win::Version version,
- Arch arch,
- int count) {
- overrides_.push_back(Override(value, version, arch, count));
-}
-
-void Expectations::SetOverride(Value value,
- base::win::Version version,
- int count) {
- SetOverride(value, version, ARCH_ALL, count);
-}
-
-void Expectations::SetOverride(Value value, Arch arch, int count) {
- SetOverride(value, base::win::VERSION_WIN_LAST, arch, count);
-}
-
-} // namespace
+#include "testing/gtest/include/gtest/gtest.h"
namespace base {
namespace win {
@@ -210,41 +91,35 @@
} // namespace
// Tests that we are able to enumerate stuff from a PE file, and that
-// the actual number of items found is within the expected range.
+// the actual number of items found matches an expected value.
TEST(PEImageTest, EnumeratesPE) {
- Expectations expectations;
+ base::FilePath pe_image_test_path;
+ ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &pe_image_test_path));
+ pe_image_test_path = pe_image_test_path.Append(FILE_PATH_LITERAL("pe_image"));
-#ifndef NDEBUG
- // Default Debug expectations.
- expectations.SetDefault(Expectations::SECTIONS, 7);
- expectations.SetDefault(Expectations::IMPORTS_DLLS, 3);
- expectations.SetDefault(Expectations::DELAY_DLLS, 2);
- expectations.SetDefault(Expectations::EXPORTS, 2);
- expectations.SetDefault(Expectations::IMPORTS, 49);
- expectations.SetDefault(Expectations::DELAY_IMPORTS, 2);
- expectations.SetDefault(Expectations::RELOCS, 438);
-
- // 64-bit Debug expectations.
- expectations.SetOverride(Expectations::SECTIONS, Expectations::ARCH_X64, 8);
- expectations.SetOverride(Expectations::IMPORTS, Expectations::ARCH_X64, 69);
- expectations.SetOverride(Expectations::RELOCS, Expectations::ARCH_X64, 632);
+#if defined(ARCH_CPU_64_BITS)
+ pe_image_test_path =
+ pe_image_test_path.Append(FILE_PATH_LITERAL("pe_image_test_64.dll"));
+ const int sections = 6;
+ const int imports_dlls = 2;
+ const int delay_dlls = 2;
+ const int exports = 2;
+ const int imports = 69;
+ const int delay_imports = 2;
+ const int relocs = 632;
#else
- // Default Release expectations.
- expectations.SetDefault(Expectations::SECTIONS, 5);
- expectations.SetDefault(Expectations::IMPORTS_DLLS, 2);
- expectations.SetDefault(Expectations::DELAY_DLLS, 2);
- expectations.SetDefault(Expectations::EXPORTS, 2);
- expectations.SetDefault(Expectations::IMPORTS, 66);
- expectations.SetDefault(Expectations::DELAY_IMPORTS, 2);
- expectations.SetDefault(Expectations::RELOCS, 1586);
-
- // 64-bit Release expectations.
- expectations.SetOverride(Expectations::SECTIONS, Expectations::ARCH_X64, 6);
- expectations.SetOverride(Expectations::IMPORTS, Expectations::ARCH_X64, 69);
- expectations.SetOverride(Expectations::RELOCS, Expectations::ARCH_X64, 632);
+ pe_image_test_path =
+ pe_image_test_path.Append(FILE_PATH_LITERAL("pe_image_test_32.dll"));
+ const int sections = 5;
+ const int imports_dlls = 2;
+ const int delay_dlls = 2;
+ const int exports = 2;
+ const int imports = 66;
+ const int delay_imports = 2;
+ const int relocs = 1586;
#endif
- HMODULE module = LoadLibrary(L"pe_image_test.dll");
+ HMODULE module = LoadLibrary(pe_image_test_path.value().c_str());
ASSERT_TRUE(NULL != module);
PEImage pe(module);
@@ -252,31 +127,31 @@
EXPECT_TRUE(pe.VerifyMagic());
pe.EnumSections(SectionsCallback, &count);
- EXPECT_EQ(expectations.GetExpectation(Expectations::SECTIONS), count);
+ EXPECT_EQ(sections, count);
count = 0;
pe.EnumImportChunks(ImportChunksCallback, &count);
- EXPECT_EQ(expectations.GetExpectation(Expectations::IMPORTS_DLLS), count);
+ EXPECT_EQ(imports_dlls, count);
count = 0;
pe.EnumDelayImportChunks(DelayImportChunksCallback, &count);
- EXPECT_EQ(expectations.GetExpectation(Expectations::DELAY_DLLS), count);
+ EXPECT_EQ(delay_dlls, count);
count = 0;
pe.EnumExports(ExportsCallback, &count);
- EXPECT_EQ(expectations.GetExpectation(Expectations::EXPORTS), count);
+ EXPECT_EQ(exports, count);
count = 0;
pe.EnumAllImports(ImportsCallback, &count);
- EXPECT_EQ(expectations.GetExpectation(Expectations::IMPORTS), count);
+ EXPECT_EQ(imports, count);
count = 0;
pe.EnumAllDelayImports(ImportsCallback, &count);
- EXPECT_EQ(expectations.GetExpectation(Expectations::DELAY_IMPORTS), count);
+ EXPECT_EQ(delay_imports, count);
count = 0;
pe.EnumRelocs(RelocsCallback, &count);
- EXPECT_EQ(expectations.GetExpectation(Expectations::RELOCS), count);
+ EXPECT_EQ(relocs, count);
FreeLibrary(module);
}
diff --git a/win/scoped_process_information_unittest.cc b/win/scoped_process_information_unittest.cc
index ccfa729..614504d 100644
--- a/win/scoped_process_information_unittest.cc
+++ b/win/scoped_process_information_unittest.cc
@@ -8,6 +8,7 @@
#include "base/command_line.h"
#include "base/process/kill.h"
+#include "base/process/process.h"
#include "base/test/multiprocess_test.h"
#include "base/win/scoped_process_information.h"
#include "testing/multiprocess_func_list.h"
@@ -135,13 +136,13 @@
// Validate that we have separate handles that are good.
int exit_code = 0;
- ASSERT_TRUE(base::WaitForExitCode(process_info.TakeProcessHandle(),
- &exit_code));
+ base::Process process(process_info.TakeProcessHandle());
+ ASSERT_TRUE(process.WaitForExit(&exit_code));
ASSERT_EQ(7, exit_code);
exit_code = 0;
- ASSERT_TRUE(base::WaitForExitCode(duplicate.TakeProcessHandle(),
- &exit_code));
+ base::Process dup_process(duplicate.TakeProcessHandle());
+ ASSERT_TRUE(dup_process.WaitForExit(&exit_code));
ASSERT_EQ(7, exit_code);
ASSERT_TRUE(::CloseHandle(process_info.TakeThreadHandle()));
diff --git a/win/win_util.cc b/win/win_util.cc
index a3c9ece..957f937 100644
--- a/win/win_util.cc
+++ b/win/win_util.cc
@@ -57,7 +57,7 @@
// Returns true if a physical keyboard is detected on Windows 8 and up.
// Uses the Setup APIs to enumerate the attached keyboards and returns true
-// if the keyboard count is more than 1. While this will work in most cases
+// if the keyboard count is 1 or more.. While this will work in most cases
// it won't work if there are devices which expose keyboard interfaces which
// are attached to the machine.
bool IsKeyboardPresentOnSlate() {
@@ -89,6 +89,7 @@
device_info_data.cbSize = sizeof(device_info_data);
if (!SetupDiEnumDeviceInfo(device_info, i, &device_info_data))
break;
+
// Get the device ID.
wchar_t device_id[MAX_DEVICE_ID_LEN];
CONFIGRET status = CM_Get_Device_ID(device_info_data.DevInst,
@@ -96,20 +97,19 @@
MAX_DEVICE_ID_LEN,
0);
if (status == CR_SUCCESS) {
- // To reduce the scope of the hack we only look for PNP and HID
+ // To reduce the scope of the hack we only look for PNP, MSF and HID
// keyboards.
- if (StartsWith(L"ACPI\\PNP", device_id, false) ||
- StartsWith(L"HID\\VID", device_id, false)) {
+ if (StartsWith(device_id, L"ACPI\\PNP", false) ||
+ StartsWith(device_id, L"ACPI\\MSF", false) ||
+ StartsWith(device_id, L"HID\\VID", false)) {
keyboard_count++;
}
}
}
- // On a Windows machine, the API's always report 1 keyboard at least
- // regardless of whether the machine has a keyboard attached or not.
- // The heuristic we are using is to check the count and return true
- // if the API's report more than one keyboard. Please note that this
+ // The heuristic we are using is to check the count of keyboards and return
+ // true if the API's report one or more keyboards. Please note that this
// will break for non keyboard devices which expose a keyboard PDO.
- return keyboard_count > 1;
+ return keyboard_count >= 1;
}
} // namespace