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