Update //base to chromium 9659b08ea5a34f889dc4166217f438095ddc10d2
This updates our copy of //base to be based off of commit 9659b08ea by
performing the following commands:
1.) Check out //base at 9659b08ea
2.) Apply the //base portion of the following commits from mojo:
57ca940d8410a27fdf517b6194d7ecb0f29ecc2a
c3b05c507e712f4ee65522e0a00e38c735c85244
d3482c6d25faabd04dffcea32d4dee18af74d600
18a6ce7215ece72e4359600852bb0cf5fd87339f
a67383613f17083d918c31cf377932f5b43b91ab
0204e1a3a6679a0e634b66f661e23f424f044152
d423877725e8606e5ebac142e5c4b5c8c2dc4810
7a690c5c0344946ed74402d61d473502bc03ad77
cfeba3cce284f8a4d382337201d479768bb0eb77
df7f866ce3da1ef3c212cde977a6a77dc832ce22
57be778fd4d820b026165969352d7b40d476730b
34776a703d8cce41052e5a3867a23d8970fbb549
0f5eacecbe12aae15b114a8511ae718506431b37
c4ebd590b6e2749a4ab3f97fd49bbd3a4d510759
3.) Fix up the rest of the repo for //base API changes:
*) StartsWith/EndsWith string functions are in base:: namespace and have
slightly different options
*) Tokenize replaced with base::SplitString
*) MessageLoopProxy removed, use TaskRunner instead
4.) git cl format, gn format
5.) Delete OWNERS file import from //base/mac to //chrome/...
This puts mojo's copy at base very close to parity with the flutter engine's
copy.
R=viettrungluu@chromium.org
Review URL: https://codereview.chromium.org/1641513004 .
diff --git a/apps/benchmark/benchmark_app.cc b/apps/benchmark/benchmark_app.cc
index c467d4d..3f5c364 100644
--- a/apps/benchmark/benchmark_app.cc
+++ b/apps/benchmark/benchmark_app.cc
@@ -78,7 +78,7 @@
}
std::vector<std::string> unique_categories(category_set.begin(),
category_set.end());
- return JoinString(unique_categories, ',');
+ return base::JoinString(unique_categories, ",");
}
void StartTracedApplication(mojo::ApplicationImpl* app) {
diff --git a/apps/benchmark/event_unittest.cc b/apps/benchmark/event_unittest.cc
index 9bb60f7..442d78d 100644
--- a/apps/benchmark/event_unittest.cc
+++ b/apps/benchmark/event_unittest.cc
@@ -94,7 +94,7 @@
"event\"}";
event_specs[5] = "{\"tid\":1,\"ts\":6,\"ph\":\"E\"}";
- std::string trace_json = "[" + JoinString(event_specs, ',') + "]";
+ std::string trace_json = "[" + base::JoinString(event_specs, ",") + "]";
std::vector<Event> events;
ASSERT_TRUE(GetEvents(trace_json, &events));
ASSERT_EQ(3u, events.size());
@@ -127,7 +127,7 @@
event_specs[2] = "{\"tid\":1,\"ts\":3,\"ph\":\"E\"}";
event_specs[3] = "{\"tid\":2,\"ts\":4,\"ph\":\"E\"}";
- std::string trace_json = "[" + JoinString(event_specs, ',') + "]";
+ std::string trace_json = "[" + base::JoinString(event_specs, ",") + "]";
std::vector<Event> events;
ASSERT_TRUE(GetEvents(trace_json, &events));
ASSERT_EQ(2u, events.size());
@@ -152,7 +152,7 @@
"\"name\":\"t1 event\"}";
event_specs[1] = "{\"tid\":\"1\",\"ts\":3,\"ph\":\"E\"}";
- std::string trace_json = "[" + JoinString(event_specs, ',') + "]";
+ std::string trace_json = "[" + base::JoinString(event_specs, ",") + "]";
std::vector<Event> events;
ASSERT_TRUE(GetEvents(trace_json, &events));
ASSERT_EQ(1u, events.size());
@@ -179,7 +179,7 @@
"{\"tid\":1004,\"id\":2,\"ts\":4,\"ph\":\"F\",\"cat\":\"cc\",\"name\":"
"\"t2 event\"}";
- std::string trace_json = "[" + JoinString(event_specs, ',') + "]";
+ std::string trace_json = "[" + base::JoinString(event_specs, ",") + "]";
std::vector<Event> events;
ASSERT_TRUE(GetEvents(trace_json, &events));
ASSERT_EQ(2u, events.size());
@@ -206,7 +206,7 @@
"{\"tid\":1003,\"id\":\"a\",\"ts\":3,\"ph\":\"F\",\"cat\":\"cc\","
"\"name\":\"t1 event\"}";
- std::string trace_json = "[" + JoinString(event_specs, ',') + "]";
+ std::string trace_json = "[" + base::JoinString(event_specs, ",") + "]";
std::vector<Event> events;
ASSERT_TRUE(GetEvents(trace_json, &events));
ASSERT_EQ(1u, events.size());
diff --git a/base/BUILD.gn b/base/BUILD.gn
index eff163a..02d0bf9 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -39,7 +39,7 @@
"base_paths_win.h",
]
- if (is_android || is_mac) {
+ if (is_android || is_mac || is_ios) {
sources -= [ "base_paths_posix.cc" ]
}
@@ -83,6 +83,7 @@
"android/content_uri_utils.cc",
"android/content_uri_utils.h",
"android/cpu_features.cc",
+ "android/cxa_demangle_stub.cc",
"android/event_log.cc",
"android/event_log.h",
"android/field_trial_list.cc",
@@ -139,11 +140,8 @@
"atomic_ref_count.h",
"atomic_sequence_num.h",
"atomicops.h",
- "atomicops_internals_gcc.h",
"atomicops_internals_mac.h",
"atomicops_internals_portable.h",
- "atomicops_internals_x86_gcc.cc",
- "atomicops_internals_x86_gcc.h",
"atomicops_internals_x86_msvc.h",
"auto_reset.h",
"barrier_closure.cc",
@@ -177,6 +175,7 @@
"containers/linked_list.h",
"containers/mru_cache.h",
"containers/scoped_ptr_hash_map.h",
+ "containers/scoped_ptr_map.h",
"containers/small_map.h",
"containers/stack_container.h",
"cpu.cc",
@@ -270,6 +269,9 @@
"mac/bind_objc_block.h",
"mac/bundle_locations.h",
"mac/bundle_locations.mm",
+ "mac/call_with_eh_frame.cc",
+ "mac/call_with_eh_frame.h",
+ "mac/call_with_eh_frame_asm.S",
"mac/cocoa_protocols.h",
"mac/dispatch_source_mach.cc",
"mac/dispatch_source_mach.h",
@@ -320,10 +322,8 @@
"message_loop/incoming_task_queue.h",
"message_loop/message_loop.cc",
"message_loop/message_loop.h",
- "message_loop/message_loop_proxy.cc",
- "message_loop/message_loop_proxy.h",
- "message_loop/message_loop_proxy_impl.cc",
- "message_loop/message_loop_proxy_impl.h",
+ "message_loop/message_loop_task_runner.cc",
+ "message_loop/message_loop_task_runner.h",
"message_loop/message_pump.cc",
"message_loop/message_pump.h",
"message_loop/message_pump_android.cc",
@@ -364,7 +364,6 @@
"pending_task.h",
"pickle.cc",
"pickle.h",
- "port.h",
"posix/eintr_wrapper.h",
"posix/file_descriptor_shuffle.cc",
"posix/global_descriptors.cc",
@@ -427,6 +426,8 @@
"strings/latin1_string_conversions.h",
"strings/nullable_string16.cc",
"strings/nullable_string16.h",
+ "strings/pattern.cc",
+ "strings/pattern.h",
"strings/safe_sprintf.cc",
"strings/safe_sprintf.h",
"strings/string16.cc",
@@ -642,6 +643,8 @@
"sys_info_openbsd.cc",
]
+ data = []
+
configs += [ ":base_implementation" ]
deps = [
@@ -722,6 +725,11 @@
"sys_info.cc",
"sys_info_posix.cc",
]
+
+ # We build with a 10.9 min sdk version but lots of code in this directory is
+ # written to target a 10.6 min sdk version and thus depends on declarations
+ # marked as deprecated on 10.9+. Suppress these warnings for now.
+ cflags = [ "-Wno-deprecated-declarations" ]
} else {
# Remove NaCl stuff.
sources -= [
@@ -742,6 +750,30 @@
"sha1_win.cc",
]
+ # Required for base/stack_trace_win.cc to symbolize correctly.
+ data += [ "$root_build_dir/dbghelp.dll" ]
+
+ if (is_component_build) {
+ # Copy the VS runtime DLLs into the isolate so that they don't have to be
+ # preinstalled on the target machine. The debug runtimes have a "d" at
+ # the end.
+ if (is_debug) {
+ vcrt_suffix = "d"
+ } else {
+ vcrt_suffix = ""
+ }
+
+ # These runtime files are copied to the output directory by the
+ # vs_toolchain script that runs as part of toolchain configuration.
+ data += [
+ "$root_out_dir/msvcp120${vcrt_suffix}.dll",
+ "$root_out_dir/msvcr120${vcrt_suffix}.dll",
+ ]
+ if (is_asan) {
+ data += [ "//third_party/llvm-build/Release+Asserts/lib/clang/3.7.0/lib/windows/clang_rt.asan_dynamic-i386.dll" ]
+ }
+ }
+
# TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
@@ -758,17 +790,25 @@
}
# Mac.
- if (is_mac) {
+ if (is_mac || is_ios) {
+ # Common Desktop / iOS excludes
sources -= [
"native_library_posix.cc",
"strings/sys_string_conversions_posix.cc",
"threading/platform_thread_internal_posix.cc",
]
- # We build with a 10.9 min sdk version but lots of code in this directory is
- # written to target a 10.6 min sdk version and thus depends on declarations
- # marked as deprecated on 10.9+. Suppress these warnings for now.
- cflags = [ "-Wno-deprecated-declarations" ]
+ if (is_asan) {
+ # TODO(GYP) hook up asan on Mac. GYP has this extra dylib:
+ #data += [ "$root_out_dir/libclang_rt.asan_osx_dynamic.dylib" ]
+ }
+
+ if (is_ios) {
+ sources -= [
+ "files/file_path_watcher_fsevents.cc",
+ "files/file_path_watcher_fsevents.h",
+ ]
+ }
} else {
# Non-Mac.
sources -= [
@@ -781,6 +821,11 @@
# Linux.
if (is_linux) {
+ if (is_asan || is_lsan || is_msan || is_tsan) {
+ # For llvm-sanitizer.
+ data += [ "//third_party/llvm-build/Release+Asserts/lib/libstdc++.so.6" ]
+ }
+
# TODO(brettw) this will need to be parameterized at some point.
linux_configs = []
@@ -810,8 +855,60 @@
}
}
+ # iOS
+ if (is_ios) {
+ set_sources_assignment_filter([])
+
+ sources += [
+ "atomicops_internals_mac.h",
+ "base_paths_mac.h",
+ "base_paths_mac.mm",
+ "file_version_info_mac.h",
+ "file_version_info_mac.mm",
+ "files/file_util_mac.mm",
+ "mac/bundle_locations.h",
+ "mac/bundle_locations.mm",
+ "mac/call_with_eh_frame.cc",
+ "mac/call_with_eh_frame.h",
+ "mac/foundation_util.h",
+ "mac/foundation_util.mm",
+ "mac/mac_logging.cc",
+ "mac/mac_logging.h",
+ "mac/mach_logging.cc",
+ "mac/mach_logging.h",
+ "mac/objc_property_releaser.h",
+ "mac/objc_property_releaser.mm",
+ "mac/scoped_mach_port.cc",
+ "mac/scoped_mach_port.h",
+ "mac/scoped_mach_vm.cc",
+ "mac/scoped_mach_vm.h",
+ "mac/scoped_nsautorelease_pool.h",
+ "mac/scoped_nsautorelease_pool.mm",
+ "mac/scoped_nsobject.h",
+ "mac/scoped_objc_class_swizzler.h",
+ "mac/scoped_objc_class_swizzler.mm",
+ "message_loop/message_pump_mac.h",
+ "message_loop/message_pump_mac.mm",
+ "strings/sys_string_conversions_mac.mm",
+ "threading/platform_thread_mac.mm",
+ "time/time_mac.cc",
+ ]
+
+ set_sources_assignment_filter(sources_assignment_filter)
+ }
+
+ if (is_asan || is_lsan || is_msan || is_tsan) {
+ data += [ "//tools/valgrind/asan/" ]
+ if (is_win) {
+ data +=
+ [ "//third_party/llvm-build/Release+Asserts/bin/llvm-symbolizer.exe" ]
+ } else {
+ data += [ "//third_party/llvm-build/Release+Asserts/bin/llvm-symbolizer" ]
+ }
+ }
+
configs += [ "//build/config/compiler:wexit_time_destructors" ]
- if (is_android && !is_debug) {
+ if (!is_debug) {
configs -= [ "//build/config/compiler:optimize" ]
configs += [ "//build/config/compiler:optimize_max" ]
}
@@ -830,7 +927,7 @@
"win/pe_image.h",
]
- if (is_android && !is_debug) {
+ if (!is_debug) {
configs -= [ "//build/config/compiler:optimize" ]
configs += [ "//build/config/compiler:optimize_max" ]
}
@@ -883,7 +980,7 @@
"//third_party/icu",
]
- if (is_android && !is_debug) {
+ if (!is_debug) {
configs -= [ "//build/config/compiler:optimize" ]
configs += [ "//build/config/compiler:optimize_max" ]
}
@@ -995,7 +1092,7 @@
":base",
]
- if (is_android && !is_debug) {
+ if (!is_debug) {
configs -= [ "//build/config/compiler:optimize" ]
configs += [ "//build/config/compiler:optimize_max" ]
}
@@ -1056,6 +1153,15 @@
}
}
+# TODO(GYP): Delete this after we've converted everything to GN.
+# The _run targets exist only for compatibility w/ GYP.
+group("base_unittests_run") {
+ testonly = true
+ deps = [
+ ":base_unittests",
+ ]
+}
+
test("base_unittests") {
sources = [
"android/application_status_listener_unittest.cc",
@@ -1089,6 +1195,7 @@
"containers/linked_list_unittest.cc",
"containers/mru_cache_unittest.cc",
"containers/scoped_ptr_hash_map_unittest.cc",
+ "containers/scoped_ptr_map_unittest.cc",
"containers/small_map_unittest.cc",
"containers/stack_container_unittest.cc",
"cpu_unittest.cc",
@@ -1136,6 +1243,7 @@
"lazy_instance_unittest.cc",
"logging_unittest.cc",
"mac/bind_objc_block_unittest.mm",
+ "mac/call_with_eh_frame_unittest.mm",
"mac/dispatch_source_mach_unittest.cc",
"mac/foundation_util_unittest.mm",
"mac/libdispatch_task_runner_unittest.cc",
@@ -1159,8 +1267,7 @@
"memory/singleton_unittest.cc",
"memory/weak_ptr_unittest.cc",
"memory/weak_ptr_unittest.nc",
- "message_loop/message_loop_proxy_impl_unittest.cc",
- "message_loop/message_loop_proxy_unittest.cc",
+ "message_loop/message_loop_task_runner_unittest.cc",
"message_loop/message_loop_unittest.cc",
"message_loop/message_pump_io_ios_unittest.cc",
"metrics/bucket_ranges_unittest.cc",
@@ -1211,6 +1318,7 @@
"sha1_unittest.cc",
"stl_util_unittest.cc",
"strings/nullable_string16_unittest.cc",
+ "strings/pattern_unittest.cc",
"strings/safe_sprintf_unittest.cc",
"strings/string16_unittest.cc",
"strings/string_number_conversions_unittest.cc",
@@ -1308,9 +1416,6 @@
data = [
"test/data/",
-
- # TODO(dpranke): Remove when icu declares this directly.
- "$root_out_dir/icudtl.dat",
]
# Allow more direct string conversions on platforms with native utf8
@@ -1324,14 +1429,15 @@
":base_java",
":base_java_unittest_support",
]
+
+ # TODO(brettw) I think this should not be here, we should not be using
+ # isolate files.
isolate_file = "base_unittests.isolate"
}
if (is_ios) {
sources -= [
"process/memory_unittest.cc",
- "process/memory_unittest_mac.h",
- "process/memory_unittest_mac.mm",
"process/process_unittest.cc",
"process/process_util_unittest.cc",
]
@@ -1371,6 +1477,15 @@
# TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
+ # Symbols for crashes when running tests on swarming.
+ if (symbol_level > 0) {
+ if (is_win) {
+ data += [ "$root_out_dir/base_unittests.exe.pdb" ]
+ } else if (is_mac) {
+ data += [ "$root_out_dir/base_unittests.dSYM/" ]
+ }
+ }
}
if (is_android) {
diff --git a/base/OWNERS b/base/OWNERS
index 9890491..bcaa81b 100644
--- a/base/OWNERS
+++ b/base/OWNERS
@@ -1,13 +1,9 @@
mark@chromium.org
-darin@chromium.org
thakis@chromium.org
danakj@chromium.org
rvargas@chromium.org
thestig@chromium.org
-# On extended leave.
-willchan@chromium.org
-
# Chromium is a very mature project, most things that are generally useful are
# already here, and that things not here aren't generally useful.
#
@@ -21,8 +17,9 @@
# writing the stuff you want to log in a regular logging statement, even
# if it makes your calling code longer. Just add it to your own code.
-per-file *.isolate=csharp@chromium.org
per-file *.isolate=maruel@chromium.org
+per-file *.isolate=tandrii@chromium.org
+per-file *.isolate=vadimsh@chromium.org
per-file security_unittest.cc=jln@chromium.org
# For Android-specific changes:
diff --git a/base/allocator/BUILD.gn b/base/allocator/BUILD.gn
index a07a356..c42de1a 100644
--- a/base/allocator/BUILD.gn
+++ b/base/allocator/BUILD.gn
@@ -63,6 +63,17 @@
"$tcmalloc_dir/src/base/abort.cc",
"$tcmalloc_dir/src/base/abort.h",
"$tcmalloc_dir/src/base/arm_instruction_set_select.h",
+ "$tcmalloc_dir/src/base/atomicops-internals-arm-generic.h",
+ "$tcmalloc_dir/src/base/atomicops-internals-arm-v6plus.h",
+ "$tcmalloc_dir/src/base/atomicops-internals-linuxppc.h",
+ "$tcmalloc_dir/src/base/atomicops-internals-macosx.h",
+ "$tcmalloc_dir/src/base/atomicops-internals-windows.h",
+ "$tcmalloc_dir/src/base/atomicops-internals-x86.cc",
+ "$tcmalloc_dir/src/base/atomicops-internals-x86.h",
+ "$tcmalloc_dir/src/base/atomicops.h",
+ "$tcmalloc_dir/src/base/basictypes.h",
+ "$tcmalloc_dir/src/base/commandlineflags.h",
+ "$tcmalloc_dir/src/base/cycleclock.h",
# We don't list dynamic_annotations.c since its copy is already
# present in the dynamic_annotations target.
diff --git a/base/android/build_info.cc b/base/android/build_info.cc
index 4d3cd55..93667ef 100644
--- a/base/android/build_info.cc
+++ b/base/android/build_info.cc
@@ -48,20 +48,21 @@
model_(StrDupJString(Java_BuildInfo_getDeviceModel(env))),
brand_(StrDupJString(Java_BuildInfo_getBrand(env))),
android_build_id_(StrDupJString(Java_BuildInfo_getAndroidBuildId(env))),
- android_build_fp_(StrDupJString(
- Java_BuildInfo_getAndroidBuildFingerprint(env))),
- package_version_code_(StrDupJString(Java_BuildInfo_getPackageVersionCode(
- env, GetApplicationContext()))),
- package_version_name_(StrDupJString(Java_BuildInfo_getPackageVersionName(
- env, GetApplicationContext()))),
- package_label_(StrDupJString(Java_BuildInfo_getPackageLabel(
- env, GetApplicationContext()))),
- package_name_(StrDupJString(Java_BuildInfo_getPackageName(
- env, GetApplicationContext()))),
+ android_build_fp_(
+ StrDupJString(Java_BuildInfo_getAndroidBuildFingerprint(env))),
+ package_version_code_(StrDupJString(
+ Java_BuildInfo_getPackageVersionCode(env, GetApplicationContext()))),
+ package_version_name_(StrDupJString(
+ Java_BuildInfo_getPackageVersionName(env, GetApplicationContext()))),
+ package_label_(StrDupJString(
+ Java_BuildInfo_getPackageLabel(env, GetApplicationContext()))),
+ package_name_(StrDupJString(
+ Java_BuildInfo_getPackageName(env, GetApplicationContext()))),
build_type_(StrDupJString(Java_BuildInfo_getBuildType(env))),
sdk_int_(Java_BuildInfo_getSdkInt(env)),
- java_exception_info_(NULL) {
-}
+ has_language_apk_splits_(
+ Java_BuildInfo_hasLanguageApkSplits(env, GetApplicationContext())),
+ java_exception_info_(NULL) {}
// static
BuildInfo* BuildInfo::GetInstance() {
diff --git a/base/android/build_info.h b/base/android/build_info.h
index 093b832..0d722b5 100644
--- a/base/android/build_info.h
+++ b/base/android/build_info.h
@@ -96,6 +96,8 @@
return sdk_int_;
}
+ int has_language_apk_splits() const { return has_language_apk_splits_; }
+
const char* java_exception_info() const {
return java_exception_info_;
}
@@ -127,6 +129,7 @@
const char* const package_name_;
const char* const build_type_;
const int sdk_int_;
+ const bool has_language_apk_splits_;
// This is set via set_java_exception_info, not at constructor time.
const char* java_exception_info_;
diff --git a/base/android/cxa_demangle_stub.cc b/base/android/cxa_demangle_stub.cc
new file mode 100644
index 0000000..b0d2b87
--- /dev/null
+++ b/base/android/cxa_demangle_stub.cc
@@ -0,0 +1,19 @@
+// 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 <unistd.h>
+
+// LLVM's demangler is large, and we have no need of it. Overriding it with
+// our own stub version here stops a lot of code being pulled in from libc++.
+// More here:
+// https://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_demangle.cpp
+extern "C" char* __cxa_demangle(const char* mangled_name,
+ char* buf,
+ size_t* n,
+ int* status) {
+ static const int kMemoryAllocFailure = -1; // LLVM's memory_alloc_failure.
+ if (status)
+ *status = kMemoryAllocFailure;
+ return nullptr;
+}
diff --git a/base/android/java/src/org/chromium/base/BaseChromiumApplication.java b/base/android/java/src/org/chromium/base/BaseChromiumApplication.java
index d7c7b05..8ec4afa 100644
--- a/base/android/java/src/org/chromium/base/BaseChromiumApplication.java
+++ b/base/android/java/src/org/chromium/base/BaseChromiumApplication.java
@@ -8,7 +8,6 @@
import android.app.Application;
import android.content.Context;
import android.os.Bundle;
-import android.view.KeyEvent;
import android.view.Window;
import java.lang.reflect.InvocationHandler;
@@ -38,9 +37,6 @@
/**
* Intercepts calls to an existing Window.Callback. Most invocations are passed on directly
* to the composed Window.Callback but enables intercepting/manipulating others.
- *
- * This is used to relay window focus changes throughout the app and remedy a bug in the
- * appcompat library.
*/
private class WindowCallbackProxy implements InvocationHandler {
private final Window.Callback mCallback;
@@ -57,9 +53,6 @@
&& args[0] instanceof Boolean) {
onWindowFocusChanged((boolean) args[0]);
return null;
- } else if (method.getName().equals("dispatchKeyEvent") && args.length == 1
- && args[0] instanceof KeyEvent) {
- return dispatchKeyEvent((KeyEvent) args[0]);
} else {
try {
return method.invoke(mCallback, args);
@@ -85,15 +78,6 @@
listener.onWindowFocusChanged(mActivity, hasFocus);
}
}
-
- public boolean dispatchKeyEvent(KeyEvent event) {
- // TODO(aurimas): remove this once AppCompatDelegateImpl no longer steals
- // KEYCODE_MENU. (see b/20529185)
- if (event.getKeyCode() == KeyEvent.KEYCODE_MENU && mActivity.dispatchKeyEvent(event)) {
- return true;
- }
- return mCallback.dispatchKeyEvent(event);
- }
}
@Override
public void onCreate() {
diff --git a/base/android/java/src/org/chromium/base/BuildInfo.java b/base/android/java/src/org/chromium/base/BuildInfo.java
index 54f611d..1b91d39 100644
--- a/base/android/java/src/org/chromium/base/BuildInfo.java
+++ b/base/android/java/src/org/chromium/base/BuildInfo.java
@@ -4,12 +4,14 @@
package org.chromium.base;
+import android.annotation.TargetApi;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Build;
+import android.text.TextUtils;
import android.util.Log;
/**
@@ -122,4 +124,35 @@
public static int getSdkInt() {
return Build.VERSION.SDK_INT;
}
+
+ /**
+ * @return Whether the Android build is M or later.
+ */
+ public static boolean isMncOrLater() {
+ // TODO(bauerb): Update this once the SDK is updated.
+ return Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1
+ || TextUtils.equals("MNC", Build.VERSION.CODENAME);
+ }
+
+ private static boolean isLanguageSplit(String splitName) {
+ // Names look like "config.XX".
+ return splitName.length() == 9 && splitName.startsWith("config.");
+ }
+
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ @CalledByNative
+ public static boolean hasLanguageApkSplits(Context context) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+ return false;
+ }
+ PackageInfo packageInfo = PackageUtils.getOwnPackageInfo(context);
+ if (packageInfo.splitNames != null) {
+ for (int i = 0; i < packageInfo.splitNames.length; ++i) {
+ if (isLanguageSplit(packageInfo.splitNames[i])) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
}
diff --git a/base/android/java/src/org/chromium/base/CollectionUtil.java b/base/android/java/src/org/chromium/base/CollectionUtil.java
index 4a4c754..2672d65 100644
--- a/base/android/java/src/org/chromium/base/CollectionUtil.java
+++ b/base/android/java/src/org/chromium/base/CollectionUtil.java
@@ -31,6 +31,7 @@
return list;
}
+ @VisibleForTesting
public static <E> ArrayList<E> newArrayList(Iterable<E> iterable) {
ArrayList<E> list = new ArrayList<E>();
for (E element : iterable) {
diff --git a/base/android/java/src/org/chromium/base/Log.java b/base/android/java/src/org/chromium/base/Log.java
index c83cfe7..3b46cdc 100644
--- a/base/android/java/src/org/chromium/base/Log.java
+++ b/base/android/java/src/org/chromium/base/Log.java
@@ -4,9 +4,7 @@
package org.chromium.base;
-import android.text.TextUtils;
-
-import org.chromium.base.annotations.NoSideEffects;
+import org.chromium.base.annotations.RemovableInRelease;
import java.util.Locale;
@@ -62,27 +60,6 @@
return "[" + getCallOrigin() + "] " + formatLog(messageTemplate, params);
}
- /**
- * Returns a full tag for the provided group tag. Full tags longer than 23 characters
- * will cause a runtime exception.
- *
- * @param groupTag {@code null} and empty string are allowed.
- *
- * @see android.util.Log#isLoggable(String, int)
- * @throws IllegalArgumentException if the tag is too long.
- * @deprecated Directly use a string (e.g. "cr.Tag") in your class. See http://crbug.com/485772
- */
- @Deprecated
- public static String makeTag(String groupTag) {
- if (TextUtils.isEmpty(groupTag)) return "cr";
- String tag = "cr." + groupTag;
- if (tag.length() > 23) {
- throw new IllegalArgumentException(
- "The full tag (" + tag + ") is longer than 23 characters.");
- }
- return tag;
- }
-
/** Convenience function, forwards to {@link android.util.Log#isLoggable(String, int)}. */
public static boolean isLoggable(String tag, int level) {
return android.util.Log.isLoggable(tag, level);
@@ -93,7 +70,7 @@
*
* For optimization purposes, only the fixed parameters versions are visible. If you need more
* than 7 parameters, consider building your log message using a function annotated with
- * {@link NoSideEffects}.
+ * {@link RemovableInRelease}.
*
* @param tag Used to identify the source of a log message.
* @param messageTemplate The message you would like logged. It is to be specified as a format
@@ -102,7 +79,7 @@
* one is a {@link Throwable}, its trace will be printed.
*/
private static void verbose(String tag, String messageTemplate, Object... args) {
- if (android.util.Log.isLoggable(tag, android.util.Log.VERBOSE)) {
+ if (Log.isLoggable(tag, Log.VERBOSE)) {
String message = formatLogWithStack(messageTemplate, args);
Throwable tr = getThrowableToLog(args);
if (tr != null) {
@@ -113,46 +90,62 @@
}
}
- /** Sends a {@link android.util.Log#VERBOSE} log message. 0 arg version. */
+ /** Sends a {@link android.util.Log#VERBOSE} log message. 0 args version. */
+ @RemovableInRelease
+ @VisibleForTesting
public static void v(String tag, String message) {
verbose(tag, message);
}
/** Sends a {@link android.util.Log#VERBOSE} log message. 1 arg version. */
+ @RemovableInRelease
+ @VisibleForTesting
public static void v(String tag, String messageTemplate, Object arg1) {
verbose(tag, messageTemplate, arg1);
}
/** Sends a {@link android.util.Log#VERBOSE} log message. 2 args version */
+ @RemovableInRelease
+ @VisibleForTesting
public static void v(String tag, String messageTemplate, Object arg1, Object arg2) {
verbose(tag, messageTemplate, arg1, arg2);
}
/** Sends a {@link android.util.Log#VERBOSE} log message. 3 args version */
+ @RemovableInRelease
+ @VisibleForTesting
public static void v(
String tag, String messageTemplate, Object arg1, Object arg2, Object arg3) {
verbose(tag, messageTemplate, arg1, arg2, arg3);
}
/** Sends a {@link android.util.Log#VERBOSE} log message. 4 args version */
+ @RemovableInRelease
+ @VisibleForTesting
public static void v(String tag, String messageTemplate, Object arg1, Object arg2, Object arg3,
Object arg4) {
verbose(tag, messageTemplate, arg1, arg2, arg3, arg4);
}
/** Sends a {@link android.util.Log#VERBOSE} log message. 5 args version */
+ @RemovableInRelease
+ @VisibleForTesting
public static void v(String tag, String messageTemplate, Object arg1, Object arg2, Object arg3,
Object arg4, Object arg5) {
verbose(tag, messageTemplate, arg1, arg2, arg3, arg4, arg5);
}
/** Sends a {@link android.util.Log#VERBOSE} log message. 6 args version */
+ @RemovableInRelease
+ @VisibleForTesting
public static void v(String tag, String messageTemplate, Object arg1, Object arg2, Object arg3,
Object arg4, Object arg5, Object arg6) {
verbose(tag, messageTemplate, arg1, arg2, arg3, arg4, arg5, arg6);
}
/** Sends a {@link android.util.Log#VERBOSE} log message. 7 args version */
+ @RemovableInRelease
+ @VisibleForTesting
public static void v(String tag, String messageTemplate, Object arg1, Object arg2, Object arg3,
Object arg4, Object arg5, Object arg6, Object arg7) {
verbose(tag, messageTemplate, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
@@ -163,7 +156,7 @@
*
* For optimization purposes, only the fixed parameters versions are visible. If you need more
* than 7 parameters, consider building your log message using a function annotated with
- * {@link NoSideEffects}.
+ * {@link RemovableInRelease}.
*
* @param tag Used to identify the source of a log message.
* @param messageTemplate The message you would like logged. It is to be specified as a format
@@ -172,7 +165,7 @@
* one is a {@link Throwable}, its trace will be printed.
*/
private static void debug(String tag, String messageTemplate, Object... args) {
- if (android.util.Log.isLoggable(tag, android.util.Log.VERBOSE)) {
+ if (isLoggable(tag, Log.DEBUG)) {
String message = formatLogWithStack(messageTemplate, args);
Throwable tr = getThrowableToLog(args);
if (tr != null) {
@@ -183,44 +176,60 @@
}
}
- /** Sends a {@link android.util.Log#DEBUG} log message. 0 arg version. */
+ /** Sends a {@link android.util.Log#DEBUG} log message. 0 args version. */
+ @RemovableInRelease
+ @VisibleForTesting
public static void d(String tag, String message) {
debug(tag, message);
}
/** Sends a {@link android.util.Log#DEBUG} log message. 1 arg version. */
+ @RemovableInRelease
+ @VisibleForTesting
public static void d(String tag, String messageTemplate, Object arg1) {
debug(tag, messageTemplate, arg1);
}
/** Sends a {@link android.util.Log#DEBUG} log message. 2 args version */
+ @RemovableInRelease
+ @VisibleForTesting
public static void d(String tag, String messageTemplate, Object arg1, Object arg2) {
debug(tag, messageTemplate, arg1, arg2);
}
/** Sends a {@link android.util.Log#DEBUG} log message. 3 args version */
+ @RemovableInRelease
+ @VisibleForTesting
public static void d(
String tag, String messageTemplate, Object arg1, Object arg2, Object arg3) {
debug(tag, messageTemplate, arg1, arg2, arg3);
}
/** Sends a {@link android.util.Log#DEBUG} log message. 4 args version */
+ @RemovableInRelease
+ @VisibleForTesting
public static void d(String tag, String messageTemplate, Object arg1, Object arg2, Object arg3,
Object arg4) {
debug(tag, messageTemplate, arg1, arg2, arg3, arg4);
}
/** Sends a {@link android.util.Log#DEBUG} log message. 5 args version */
+ @RemovableInRelease
+ @VisibleForTesting
public static void d(String tag, String messageTemplate, Object arg1, Object arg2, Object arg3,
Object arg4, Object arg5) {
debug(tag, messageTemplate, arg1, arg2, arg3, arg4, arg5);
}
/** Sends a {@link android.util.Log#DEBUG} log message. 6 args version */
+ @RemovableInRelease
+ @VisibleForTesting
public static void d(String tag, String messageTemplate, Object arg1, Object arg2, Object arg3,
Object arg4, Object arg5, Object arg6) {
debug(tag, messageTemplate, arg1, arg2, arg3, arg4, arg5, arg6);
}
/** Sends a {@link android.util.Log#DEBUG} log message. 7 args version */
+ @RemovableInRelease
+ @VisibleForTesting
public static void d(String tag, String messageTemplate, Object arg1, Object arg2, Object arg3,
Object arg4, Object arg5, Object arg6, Object arg7) {
debug(tag, messageTemplate, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
@@ -235,8 +244,9 @@
* @param args Arguments referenced by the format specifiers in the format string. If the last
* one is a {@link Throwable}, its trace will be printed.
*/
+ @VisibleForTesting
public static void i(String tag, String messageTemplate, Object... args) {
- if (android.util.Log.isLoggable(tag, android.util.Log.INFO)) {
+ if (Log.isLoggable(tag, Log.INFO)) {
String message = formatLog(messageTemplate, args);
Throwable tr = getThrowableToLog(args);
if (tr != null) {
@@ -256,8 +266,9 @@
* @param args Arguments referenced by the format specifiers in the format string. If the last
* one is a {@link Throwable}, its trace will be printed.
*/
+ @VisibleForTesting
public static void w(String tag, String messageTemplate, Object... args) {
- if (android.util.Log.isLoggable(tag, android.util.Log.WARN)) {
+ if (Log.isLoggable(tag, Log.WARN)) {
String message = formatLog(messageTemplate, args);
Throwable tr = getThrowableToLog(args);
if (tr != null) {
@@ -277,8 +288,9 @@
* @param args Arguments referenced by the format specifiers in the format string. If the last
* one is a {@link Throwable}, its trace will be printed.
*/
+ @VisibleForTesting
public static void e(String tag, String messageTemplate, Object... args) {
- if (android.util.Log.isLoggable(tag, android.util.Log.ERROR)) {
+ if (Log.isLoggable(tag, Log.ERROR)) {
String message = formatLog(messageTemplate, args);
Throwable tr = getThrowableToLog(args);
if (tr != null) {
@@ -302,8 +314,9 @@
* @param args Arguments referenced by the format specifiers in the format string. If the last
* one is a {@link Throwable}, its trace will be printed.
*/
+ @VisibleForTesting
public static void wtf(String tag, String messageTemplate, Object... args) {
- if (android.util.Log.isLoggable(tag, android.util.Log.ERROR)) {
+ if (Log.isLoggable(tag, Log.ASSERT)) {
String message = formatLog(messageTemplate, args);
Throwable tr = getThrowableToLog(args);
if (tr != null) {
diff --git a/base/android/java/src/org/chromium/base/MemoryPressureListener.java b/base/android/java/src/org/chromium/base/MemoryPressureListener.java
index e7c2030..7979287 100644
--- a/base/android/java/src/org/chromium/base/MemoryPressureListener.java
+++ b/base/android/java/src/org/chromium/base/MemoryPressureListener.java
@@ -72,10 +72,10 @@
} else if (ACTION_TRIM_MEMORY.equals(action)) {
simulateTrimMemoryPressureSignal(activity, ComponentCallbacks2.TRIM_MEMORY_COMPLETE);
} else if (ACTION_TRIM_MEMORY_RUNNING_CRITICAL.equals(action)) {
- simulateTrimMemoryPressureSignal(activity, ComponentCallbacks2.TRIM_MEMORY_MODERATE);
- } else if (ACTION_TRIM_MEMORY_MODERATE.equals(action)) {
simulateTrimMemoryPressureSignal(activity,
ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL);
+ } else if (ACTION_TRIM_MEMORY_MODERATE.equals(action)) {
+ simulateTrimMemoryPressureSignal(activity, ComponentCallbacks2.TRIM_MEMORY_MODERATE);
} else {
return false;
}
diff --git a/base/android/java/src/org/chromium/base/PathUtils.java b/base/android/java/src/org/chromium/base/PathUtils.java
index e46fc30..77affe1 100644
--- a/base/android/java/src/org/chromium/base/PathUtils.java
+++ b/base/android/java/src/org/chromium/base/PathUtils.java
@@ -9,12 +9,14 @@
import android.os.AsyncTask;
import android.os.Environment;
+import java.io.File;
import java.util.concurrent.ExecutionException;
/**
* This class provides the path related methods for the native library.
*/
public abstract class PathUtils {
+ private static final String THUMBNAIL_DIRECTORY = "textures";
private static final int DATA_DIRECTORY = 0;
private static final int DATABASE_DIRECTORY = 1;
@@ -22,6 +24,8 @@
private static final int NUM_DIRECTORIES = 3;
private static AsyncTask<String, Void, String[]> sDirPathFetchTask;
+ private static File sThumbnailDirectory;
+
// Prevent instantiation.
private PathUtils() {}
@@ -91,6 +95,18 @@
return getDirectoryPath(CACHE_DIRECTORY);
}
+ public static File getThumbnailCacheDirectory(Context appContext) {
+ if (sThumbnailDirectory == null) {
+ sThumbnailDirectory = appContext.getDir(THUMBNAIL_DIRECTORY, Context.MODE_PRIVATE);
+ }
+ return sThumbnailDirectory;
+ }
+
+ @CalledByNative
+ public static String getThumbnailCacheDirectoryPath(Context appContext) {
+ return getThumbnailCacheDirectory(appContext).getAbsolutePath();
+ }
+
/**
* @return the public downloads directory.
*/
diff --git a/base/android/java/src/org/chromium/base/README_logging.md b/base/android/java/src/org/chromium/base/README_logging.md
index a795b6b..6104bf0 100644
--- a/base/android/java/src/org/chromium/base/README_logging.md
+++ b/base/android/java/src/org/chromium/base/README_logging.md
@@ -43,6 +43,20 @@
Level here is either `VERBOSE`, `DEBUG`, `INFO`, `WARN`, `ERROR`, `ASSERT`, or `SUPPRESS`
By default, the level for all tags is `INFO`.
+### An exception trace is printed when the exception is the last parameter ###
+
+As with `java.util.Log`, putting a throwable as last parameter will dump the corresponding stack
+trace:
+
+ Log.i(TAG, "An error happened: %s", e)
+
+ I/cr.YourModuleTag: ( 999): An error happened: This is the exception's message
+ I/cr.YourModuleTag: ( 999): java.lang.Exception: This is the exception's message
+ I/cr.YourModuleTag: ( 999): at foo.bar.MyClass.test(MyClass.java:42)
+ I/cr.YourModuleTag: ( 999): ...
+
+Having the exception as last parameter doesn't prevent it from being used for string formatting.
+
### Logging Best Practices
#### Rule #1: Never log PII (Personal Identification Information):
@@ -111,7 +125,7 @@
while the previous method can enable or disable debugging for a whole feature without changing
any source file.
-- Annotate debug functions with the `@NoSideEffects` annotation.
+- Annotate debug functions with the `@RemovableInRelease` annotation.
That annotation tells Proguard to assume that a given function has no side effects, and is
called only for its returned value. If this value is unused, the call will be removed. If the
@@ -121,7 +135,7 @@
/* If that function is only used in Log.d calls, proguard should completely remove it from
* the release builds. */
- @NoSideEffects
+ @RemovableInRelease
private static String getSomeDebugLogString(Thing[] things) {
/* Still needs to be guarded to avoid impacting debug builds, or in case it's used for
* some other log levels. But at least it is done only once, inside the function. */
diff --git a/base/android/java/src/org/chromium/base/ResourceExtractor.java b/base/android/java/src/org/chromium/base/ResourceExtractor.java
index d44f2fc..030dcf9 100644
--- a/base/android/java/src/org/chromium/base/ResourceExtractor.java
+++ b/base/android/java/src/org/chromium/base/ResourceExtractor.java
@@ -6,18 +6,17 @@
import android.annotation.TargetApi;
import android.content.Context;
-import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.res.AssetManager;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Trace;
-import android.preference.PreferenceManager;
import android.util.Log;
+import org.chromium.base.annotations.SuppressFBWarnings;
+
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
@@ -28,7 +27,6 @@
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
-import java.util.regex.Pattern;
/**
* Handles extracting the necessary resources bundled in an APK and moving them to a location on
@@ -37,23 +35,25 @@
public class ResourceExtractor {
private static final String LOGTAG = "ResourceExtractor";
- private static final String LAST_LANGUAGE = "Last language";
- private static final String PAK_FILENAMES_LEGACY_NOREUSE = "Pak filenames";
private static final String ICU_DATA_FILENAME = "icudtl.dat";
private static final String V8_NATIVES_DATA_FILENAME = "natives_blob.bin";
private static final String V8_SNAPSHOT_DATA_FILENAME = "snapshot_blob.bin";
- private static String[] sMandatoryPaks = null;
+ private static ResourceEntry[] sResourcesToExtract = new ResourceEntry[0];
- // By default, we attempt to extract a pak file for the users
- // current device locale. Use setExtractImplicitLocale() to
- // change this behavior.
- private static boolean sExtractImplicitLocalePak = true;
+ /**
+ * Holds information about a res/raw file (e.g. locale .pak files).
+ */
+ public static final class ResourceEntry {
+ public final int resourceId;
+ public final String pathWithinApk;
+ public final String extractedFileName;
- private static boolean isAppDataFile(String file) {
- return ICU_DATA_FILENAME.equals(file)
- || V8_NATIVES_DATA_FILENAME.equals(file)
- || V8_SNAPSHOT_DATA_FILENAME.equals(file);
+ public ResourceEntry(int resourceId, String pathWithinApk, String extractedFileName) {
+ this.resourceId = resourceId;
+ this.pathWithinApk = pathWithinApk;
+ this.extractedFileName = extractedFileName;
+ }
}
private class ExtractTask extends AsyncTask<Void, Void, Void> {
@@ -61,12 +61,38 @@
private final List<Runnable> mCompletionCallbacks = new ArrayList<Runnable>();
- public ExtractTask() {
+ private void extractResourceHelper(InputStream is, File outFile, byte[] buffer)
+ throws IOException {
+ OutputStream os = null;
+ try {
+ os = new FileOutputStream(outFile);
+ Log.i(LOGTAG, "Extracting resource " + outFile);
+
+ int count = 0;
+ while ((count = is.read(buffer, 0, BUFFER_SIZE)) != -1) {
+ os.write(buffer, 0, count);
+ }
+ os.flush();
+
+ // Ensure something reasonable was written.
+ if (outFile.length() == 0) {
+ throw new IOException(outFile + " extracted with 0 length!");
+ }
+ } finally {
+ try {
+ if (is != null) {
+ is.close();
+ }
+ } finally {
+ if (os != null) {
+ os.close();
+ }
+ }
+ }
}
private void doInBackgroundImpl() {
final File outputDir = getOutputDir();
- final File appDataDir = getAppDataDir();
if (!outputDir.exists() && !outputDir.mkdirs()) {
Log.e(LOGTAG, "Unable to create pak resources directory!");
return;
@@ -83,97 +109,22 @@
deleteFiles();
}
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
- String currentLocale = LocaleUtils.getDefaultLocale();
- String currentLanguage = currentLocale.split("-", 2)[0];
- // If everything we need is already there (and the locale hasn't
- // changed), quick exit.
- if (prefs.getString(LAST_LANGUAGE, "").equals(currentLanguage)) {
- boolean filesPresent = true;
- for (String file : sMandatoryPaks) {
- File directory = isAppDataFile(file) ? appDataDir : outputDir;
- if (!new File(directory, file).exists()) {
- filesPresent = false;
- break;
- }
- }
- if (filesPresent) return;
- } else {
- prefs.edit().putString(LAST_LANGUAGE, currentLanguage).apply();
- }
-
- StringBuilder p = new StringBuilder();
- for (String mandatoryPak : sMandatoryPaks) {
- if (p.length() > 0) p.append('|');
- p.append("\\Q" + mandatoryPak + "\\E");
- }
-
- if (sExtractImplicitLocalePak) {
- if (p.length() > 0) p.append('|');
- // As well as the minimum required set of .paks above, we'll
- // also add all .paks that we have for the user's currently
- // selected language.
- p.append(currentLanguage);
- p.append("(-\\w+)?\\.pak");
- }
-
- Pattern paksToInstall = Pattern.compile(p.toString());
-
- AssetManager manager = mContext.getResources().getAssets();
beginTraceSection("WalkAssets");
+ byte[] buffer = new byte[BUFFER_SIZE];
try {
- // Loop through every asset file that we have in the APK, and look for the
- // ones that we need to extract by trying to match the Patterns that we
- // created above.
- byte[] buffer = null;
- String[] files = manager.list("");
- for (String file : files) {
- if (!paksToInstall.matcher(file).matches()) {
- continue;
- }
- File output = new File(isAppDataFile(file) ? appDataDir : outputDir, file);
+ // Extract all files that don't already exist.
+ for (ResourceEntry entry : sResourcesToExtract) {
+ File output = new File(outputDir, entry.extractedFileName);
if (output.exists()) {
continue;
}
-
- InputStream is = null;
- OutputStream os = null;
beginTraceSection("ExtractResource");
+ InputStream inputStream =
+ mContext.getResources().openRawResource(entry.resourceId);
try {
- is = manager.open(file);
- os = new FileOutputStream(output);
- Log.i(LOGTAG, "Extracting resource " + file);
- if (buffer == null) {
- buffer = new byte[BUFFER_SIZE];
- }
-
- int count = 0;
- while ((count = is.read(buffer, 0, BUFFER_SIZE)) != -1) {
- os.write(buffer, 0, count);
- }
- os.flush();
-
- // Ensure something reasonable was written.
- if (output.length() == 0) {
- throw new IOException(file + " extracted with 0 length!");
- }
-
- if (isAppDataFile(file)) {
- // icu and V8 data need to be accessed by a renderer
- // process.
- output.setReadable(true, false);
- }
+ extractResourceHelper(inputStream, output, buffer);
} finally {
- try {
- if (is != null) {
- is.close();
- }
- } finally {
- if (os != null) {
- os.close();
- }
- endTraceSection(); // ExtractResource
- }
+ endTraceSection(); // ExtractResource
}
}
} catch (IOException e) {
@@ -303,53 +254,19 @@
}
/**
- * Specifies the .pak files that should be extracted from the APK's asset resources directory
- * and moved to {@link #getOutputDirFromContext(Context)}.
- * @param mandatoryPaks The list of pak files to be loaded. If no pak files are
- * required, pass a single empty string.
+ * Specifies the files that should be extracted from the APK.
+ * and moved to {@link #getOutputDir()}.
*/
- public static void setMandatoryPaksToExtract(String... mandatoryPaks) {
+ @SuppressFBWarnings("EI_EXPOSE_STATIC_REP2")
+ public static void setResourcesToExtract(ResourceEntry[] entries) {
assert (sInstance == null || sInstance.mExtractTask == null)
: "Must be called before startExtractingResources is called";
- sMandatoryPaks = mandatoryPaks;
-
+ sResourcesToExtract = entries;
}
- /**
- * By default the ResourceExtractor will attempt to extract a pak resource for the users
- * currently specified locale. This behavior can be changed with this function and is
- * only needed by tests.
- * @param extract False if we should not attempt to extract a pak file for
- * the users currently selected locale and try to extract only the
- * pak files specified in sMandatoryPaks.
- */
- @VisibleForTesting
- public static void setExtractImplicitLocaleForTesting(boolean extract) {
- assert (sInstance == null || sInstance.mExtractTask == null)
- : "Must be called before startExtractingResources is called";
- sExtractImplicitLocalePak = extract;
- }
-
- /**
- * Marks all the 'pak' resources, packaged as assets, for extraction during
- * running the tests.
- */
- @VisibleForTesting
- public void setExtractAllPaksAndV8SnapshotForTesting() {
- List<String> pakAndSnapshotFileAssets = new ArrayList<String>();
- AssetManager manager = mContext.getResources().getAssets();
- try {
- String[] files = manager.list("");
- for (String file : files) {
- if (file.endsWith(".pak")) pakAndSnapshotFileAssets.add(file);
- }
- } catch (IOException e) {
- Log.w(LOGTAG, "Exception while accessing assets: " + e.getMessage(), e);
- }
- pakAndSnapshotFileAssets.add("natives_blob.bin");
- pakAndSnapshotFileAssets.add("snapshot_blob.bin");
- setMandatoryPaksToExtract(pakAndSnapshotFileAssets.toArray(
- new String[pakAndSnapshotFileAssets.size()]));
+ // TODO(agrieve): Delete this method ones all usages of it are updated.
+ public static void setMandatoryPaksToExtract(String... paths) {
+ assert paths.length == 1 && "".equals(paths[0]);
}
private ResourceExtractor(Context context) {
@@ -421,6 +338,10 @@
return;
}
+ // If a previous release extracted resources, and the current release does not,
+ // deleteFiles() will not run and some files will be left. This currently
+ // can happen for ContentShell, but not for Chrome proper, since we always extract
+ // locale pak files.
if (shouldSkipPakExtraction()) {
return;
}
@@ -473,12 +394,9 @@
}
/**
- * Pak extraction not necessarily required by the embedder; we allow them to skip
- * this process if they call setMandatoryPaksToExtract with a single empty String.
+ * Pak extraction not necessarily required by the embedder.
*/
private static boolean shouldSkipPakExtraction() {
- // Must call setMandatoryPaksToExtract before beginning resource extraction.
- assert sMandatoryPaks != null;
- return sMandatoryPaks.length == 1 && "".equals(sMandatoryPaks[0]);
+ return sResourcesToExtract.length == 0;
}
}
diff --git a/base/android/java/src/org/chromium/base/StreamUtil.java b/base/android/java/src/org/chromium/base/StreamUtil.java
new file mode 100644
index 0000000..f8cbfee
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/StreamUtil.java
@@ -0,0 +1,28 @@
+// Copyright 2013 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 java.io.Closeable;
+import java.io.IOException;
+
+/**
+ * Helper methods to deal with stream related tasks.
+ */
+public class StreamUtil {
+ /**
+ * Handle closing a {@link java.io.Closeable} via {@link java.io.Closeable#close()} and catch
+ * the potentially thrown {@link java.io.IOException}.
+ * @param closeable The Closeable to be closed.
+ */
+ public static void closeQuietly(Closeable closeable) {
+ if (closeable == null) return;
+
+ try {
+ closeable.close();
+ } catch (IOException ex) {
+ // Ignore the exception on close.
+ }
+ }
+}
diff --git a/base/android/java/src/org/chromium/base/ThreadUtils.java b/base/android/java/src/org/chromium/base/ThreadUtils.java
index c0b9172..647e33e 100644
--- a/base/android/java/src/org/chromium/base/ThreadUtils.java
+++ b/base/android/java/src/org/chromium/base/ThreadUtils.java
@@ -29,6 +29,7 @@
}
}
+ @VisibleForTesting
public static void setUiThread(Looper looper) {
synchronized (sLock) {
if (sUiThreadHandler != null && sUiThreadHandler.getLooper() != looper) {
diff --git a/base/android/java/src/org/chromium/base/TraceEvent.java b/base/android/java/src/org/chromium/base/TraceEvent.java
index 3d3b11a..9ace4a1 100644
--- a/base/android/java/src/org/chromium/base/TraceEvent.java
+++ b/base/android/java/src/org/chromium/base/TraceEvent.java
@@ -20,6 +20,7 @@
public class TraceEvent {
private static volatile boolean sEnabled = false;
+ private static volatile boolean sATraceEnabled = false; // True when taking an Android systrace.
private static class BasicLooperMonitor implements Printer {
@Override
@@ -176,6 +177,8 @@
@CalledByNative
public static void setEnabled(boolean enabled) {
sEnabled = enabled;
+ // Android M+ systrace logs this on its own. Only log it if not writing to Android systrace.
+ if (sATraceEnabled) return;
ThreadUtils.getUiThreadLooper().setMessageLogging(
enabled ? LooperMonitorHolder.sInstance : null);
}
@@ -186,10 +189,15 @@
* systrace, this is for WebView only.
*/
public static void setATraceEnabled(boolean enabled) {
- if (sEnabled == enabled) return;
+ if (sATraceEnabled == enabled) return;
+ sATraceEnabled = enabled;
if (enabled) {
+ // Calls TraceEvent.setEnabled(true) via
+ // TraceLog::EnabledStateObserver::OnTraceLogEnabled
nativeStartATrace();
} else {
+ // Calls TraceEvent.setEnabled(false) via
+ // TraceLog::EnabledStateObserver::OnTraceLogDisabled
nativeStopATrace();
}
}
diff --git a/base/android/java/src/org/chromium/base/annotations/NoSideEffects.java b/base/android/java/src/org/chromium/base/annotations/NoSideEffects.java
deleted file mode 100644
index 803c3f9..0000000
--- a/base/android/java/src/org/chromium/base/annotations/NoSideEffects.java
+++ /dev/null
@@ -1,17 +0,0 @@
-// 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.annotations;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Target;
-
-/**
- * Annotation used to indicate to proguard methods that have no side effects and can be
- * safely removed if their return value is not used. This is to be used with
- * {@link org.chromium.base.Log}'s method, that can also be removed by proguard. That way
- * expensive calls can be left in debug builds but removed in release.
- */
-@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
-public @interface NoSideEffects {}
diff --git a/base/android/java/src/org/chromium/base/annotations/RemovableInRelease.java b/base/android/java/src/org/chromium/base/annotations/RemovableInRelease.java
new file mode 100644
index 0000000..2191334
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/annotations/RemovableInRelease.java
@@ -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.
+
+package org.chromium.base.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * The annotated function can be removed in release builds.
+ *
+ * Calls to this function will be removed if its return value is not used. If all calls are removed,
+ * the function definition itself will be candidate for removal.
+ * It works by indicating to Proguard that the function has no side effects.
+ */
+@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
+public @interface RemovableInRelease {}
diff --git a/base/android/java/src/org/chromium/base/library_loader/LegacyLinker.java b/base/android/java/src/org/chromium/base/library_loader/LegacyLinker.java
new file mode 100644
index 0000000..81ac754
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/library_loader/LegacyLinker.java
@@ -0,0 +1,736 @@
+// 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.library_loader;
+
+import android.os.Bundle;
+import android.os.Parcel;
+
+import org.chromium.base.CalledByNative;
+import org.chromium.base.Log;
+import org.chromium.base.SysUtils;
+import org.chromium.base.ThreadUtils;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.annotation.Nullable;
+
+/*
+ * For more, see Technical note, Security considerations, and the explanation
+ * of how this class is supposed to be used in Linker.java.
+ */
+
+/**
+ * Provides a concrete implementation of the Chromium Linker.
+ *
+ * This Linker implementation uses the crazy linker to map and then run Chrome
+ * for Android.
+ *
+ * For more on the operations performed by the Linker, see {@link Linker}.
+ */
+class LegacyLinker extends Linker {
+ // Log tag for this class.
+ private static final String TAG = "cr.library_loader";
+
+ // Name of the library that contains our JNI code.
+ private static final String LINKER_JNI_LIBRARY = "chromium_android_linker";
+
+ // Becomes true after linker initialization.
+ private boolean mInitialized = false;
+
+ // Set to true to indicate that the system supports safe sharing of RELRO sections.
+ private boolean mRelroSharingSupported = false;
+
+ // Set to true if this runs in the browser process. Disabled by initServiceProcess().
+ // TODO(petrcermak): This flag can be incorrectly set to false (even though this might run in
+ // the browser process) on low-memory devices.
+ private boolean mInBrowserProcess = true;
+
+ // Becomes true to indicate this process needs to wait for a shared RELRO in
+ // finishLibraryLoad().
+ private boolean mWaitForSharedRelros = false;
+
+ // Becomes true when initialization determines that the browser process can use the
+ // shared RELRO.
+ private boolean mBrowserUsesSharedRelro = false;
+
+ // The map of all RELRO sections either created or used in this process.
+ private Bundle mSharedRelros = null;
+
+ // Current common random base load address.
+ private long mBaseLoadAddress = 0;
+
+ // Current fixed-location load address for the next library called by loadLibrary().
+ private long mCurrentLoadAddress = 0;
+
+ // Becomes true once prepareLibraryLoad() has been called.
+ private boolean mPrepareLibraryLoadCalled = false;
+
+ // The map of libraries that are currently loaded in this process.
+ protected HashMap<String, LibInfo> mLoadedLibraries = null;
+
+ // Used internally to initialize the linker's static data. Assume lock is held.
+ private void ensureInitializedLocked() {
+ assert Thread.holdsLock(mLock);
+
+ if (mInitialized) {
+ return;
+ }
+
+ mRelroSharingSupported = false;
+ if (NativeLibraries.sUseLinker) {
+ if (DEBUG) {
+ Log.i(TAG, "Loading lib" + LINKER_JNI_LIBRARY + ".so");
+ }
+ try {
+ System.loadLibrary(LINKER_JNI_LIBRARY);
+ } catch (UnsatisfiedLinkError e) {
+ // In a component build, the ".cr" suffix is added to each library name.
+ Log.w(TAG, "Couldn't load lib" + LINKER_JNI_LIBRARY + ".so, "
+ + "trying lib" + LINKER_JNI_LIBRARY + ".cr.so");
+ System.loadLibrary(LINKER_JNI_LIBRARY + ".cr");
+ }
+ mRelroSharingSupported = nativeCanUseSharedRelro();
+ if (!mRelroSharingSupported) {
+ Log.w(TAG, "This system cannot safely share RELRO sections");
+ } else {
+ if (DEBUG) {
+ Log.i(TAG, "This system supports safe shared RELRO sections");
+ }
+ }
+
+ if (mMemoryDeviceConfig == MEMORY_DEVICE_CONFIG_INIT) {
+ if (SysUtils.isLowEndDevice()) {
+ mMemoryDeviceConfig = MEMORY_DEVICE_CONFIG_LOW;
+ } else {
+ mMemoryDeviceConfig = MEMORY_DEVICE_CONFIG_NORMAL;
+ }
+ }
+
+ switch (BROWSER_SHARED_RELRO_CONFIG) {
+ case BROWSER_SHARED_RELRO_CONFIG_NEVER:
+ mBrowserUsesSharedRelro = false;
+ break;
+ case BROWSER_SHARED_RELRO_CONFIG_LOW_RAM_ONLY:
+ if (mMemoryDeviceConfig == MEMORY_DEVICE_CONFIG_LOW) {
+ mBrowserUsesSharedRelro = true;
+ Log.w(TAG, "Low-memory device: shared RELROs used in all processes");
+ } else {
+ mBrowserUsesSharedRelro = false;
+ }
+ break;
+ case BROWSER_SHARED_RELRO_CONFIG_ALWAYS:
+ Log.w(TAG, "Beware: shared RELROs used in all processes!");
+ mBrowserUsesSharedRelro = true;
+ break;
+ default:
+ assert false : "Unreached";
+ break;
+ }
+ } else {
+ if (DEBUG) {
+ Log.i(TAG, "Linker disabled");
+ }
+ }
+
+ if (!mRelroSharingSupported) {
+ // Sanity.
+ mBrowserUsesSharedRelro = false;
+ mWaitForSharedRelros = false;
+ }
+
+ mInitialized = true;
+ }
+
+ /**
+ * Call this method to determine if this chromium project must
+ * use this linker. If not, System.loadLibrary() should be used to load
+ * libraries instead.
+ */
+ @Override
+ public boolean isUsed() {
+ // Only GYP targets that are APKs and have the 'use_chromium_linker' variable
+ // defined as 1 will use this linker. For all others (the default), the
+ // auto-generated NativeLibraries.sUseLinker variable will be false.
+ if (!NativeLibraries.sUseLinker) return false;
+
+ synchronized (mLock) {
+ ensureInitializedLocked();
+ // At the moment, there is also no point in using this linker if the
+ // system does not support RELRO sharing safely.
+ return mRelroSharingSupported;
+ }
+ }
+
+ /**
+ * Call this method to determine if the linker will try to use shared RELROs
+ * for the browser process.
+ */
+ @Override
+ public boolean isUsingBrowserSharedRelros() {
+ synchronized (mLock) {
+ ensureInitializedLocked();
+ return mBrowserUsesSharedRelro;
+ }
+ }
+
+ /**
+ * Call this method to determine if the chromium project must load
+ * the library directly from the zip file.
+ */
+ @Override
+ public boolean isInZipFile() {
+ return NativeLibraries.sUseLibraryInZipFile;
+ }
+
+ /**
+ * Call this method just before loading any native shared libraries in this process.
+ */
+ @Override
+ public void prepareLibraryLoad() {
+ if (DEBUG) {
+ Log.i(TAG, "prepareLibraryLoad() called");
+ }
+ synchronized (mLock) {
+ mPrepareLibraryLoadCalled = true;
+
+ if (mInBrowserProcess) {
+ // Force generation of random base load address, as well
+ // as creation of shared RELRO sections in this process.
+ setupBaseLoadAddressLocked();
+ }
+ }
+ }
+
+ /**
+ * Call this method just after loading all native shared libraries in this process.
+ * Note that when in a service process, this will block until the RELRO bundle is
+ * received, i.e. when another thread calls useSharedRelros().
+ */
+ @Override
+ public void finishLibraryLoad() {
+ if (DEBUG) {
+ Log.i(TAG, "finishLibraryLoad() called");
+ }
+ synchronized (mLock) {
+ if (DEBUG) {
+ Log.i(TAG,
+ String.format(Locale.US,
+ "mInBrowserProcess=%s mBrowserUsesSharedRelro=%s mWaitForSharedRelros=%s",
+ mInBrowserProcess ? "true" : "false",
+ mBrowserUsesSharedRelro ? "true" : "false",
+ mWaitForSharedRelros ? "true" : "false"));
+ }
+
+ if (mLoadedLibraries == null) {
+ if (DEBUG) {
+ Log.i(TAG, "No libraries loaded");
+ }
+ } else {
+ if (mInBrowserProcess) {
+ // Create new Bundle containing RELRO section information
+ // for all loaded libraries. Make it available to getSharedRelros().
+ mSharedRelros = createBundleFromLibInfoMap(mLoadedLibraries);
+ if (DEBUG) {
+ Log.i(TAG, "Shared RELRO created");
+ dumpBundle(mSharedRelros);
+ }
+
+ if (mBrowserUsesSharedRelro) {
+ useSharedRelrosLocked(mSharedRelros);
+ }
+ }
+
+ if (mWaitForSharedRelros) {
+ assert !mInBrowserProcess;
+
+ // Wait until the shared relro bundle is received from useSharedRelros().
+ while (mSharedRelros == null) {
+ try {
+ mLock.wait();
+ } catch (InterruptedException ie) {
+ // no-op
+ }
+ }
+ useSharedRelrosLocked(mSharedRelros);
+ // Clear the Bundle to ensure its file descriptor references can't be reused.
+ mSharedRelros.clear();
+ mSharedRelros = null;
+ }
+ }
+
+ if (NativeLibraries.sEnableLinkerTests && mTestRunnerClassName != null) {
+ // The TestRunner implementation must be instantiated _after_
+ // all libraries are loaded to ensure that its native methods
+ // are properly registered.
+ if (DEBUG) {
+ Log.i(TAG, "Instantiating " + mTestRunnerClassName);
+ }
+ TestRunner testRunner = null;
+ try {
+ testRunner = (TestRunner) Class.forName(mTestRunnerClassName).newInstance();
+ } catch (Exception e) {
+ Log.e(TAG, "Could not extract test runner class name", e);
+ testRunner = null;
+ }
+ if (testRunner != null) {
+ if (!testRunner.runChecks(mMemoryDeviceConfig, mInBrowserProcess)) {
+ Log.wtf(TAG, "Linker runtime tests failed in this process!!");
+ assert false;
+ } else {
+ Log.i(TAG, "All linker tests passed!");
+ }
+ }
+ }
+ }
+ if (DEBUG) {
+ Log.i(TAG, "finishLibraryLoad() exiting");
+ }
+ }
+
+ /**
+ * Call this to send a Bundle containing the shared RELRO sections to be
+ * used in this process. If initServiceProcess() was previously called,
+ * finishLibraryLoad() will not exit until this method is called in another
+ * thread with a non-null value.
+ * @param bundle The Bundle instance containing a map of shared RELRO sections
+ * to use in this process.
+ */
+ @Override
+ public void useSharedRelros(Bundle bundle) {
+ // Ensure the bundle uses the application's class loader, not the framework
+ // one which doesn't know anything about LibInfo.
+ // Also, hold a fresh copy of it so the caller can't recycle it.
+ Bundle clonedBundle = null;
+ if (bundle != null) {
+ bundle.setClassLoader(LibInfo.class.getClassLoader());
+ clonedBundle = new Bundle(LibInfo.class.getClassLoader());
+ Parcel parcel = Parcel.obtain();
+ bundle.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ clonedBundle.readFromParcel(parcel);
+ parcel.recycle();
+ }
+ if (DEBUG) {
+ Log.i(TAG, "useSharedRelros() called with " + bundle + ", cloned " + clonedBundle);
+ }
+ synchronized (mLock) {
+ // Note that in certain cases, this can be called before
+ // initServiceProcess() in service processes.
+ mSharedRelros = clonedBundle;
+ // Tell any listener blocked in finishLibraryLoad() about it.
+ mLock.notifyAll();
+ }
+ }
+
+ /**
+ * Call this to retrieve the shared RELRO sections created in this process,
+ * after loading all libraries.
+ * @return a new Bundle instance, or null if RELRO sharing is disabled on
+ * this system, or if initServiceProcess() was called previously.
+ */
+ @Override
+ public Bundle getSharedRelros() {
+ if (DEBUG) {
+ Log.i(TAG, "getSharedRelros() called");
+ }
+ synchronized (mLock) {
+ if (!mInBrowserProcess) {
+ if (DEBUG) {
+ Log.i(TAG, "... returning null Bundle");
+ }
+ return null;
+ }
+
+ // Return the Bundle created in finishLibraryLoad().
+ if (DEBUG) {
+ Log.i(TAG, "... returning " + mSharedRelros);
+ }
+ return mSharedRelros;
+ }
+ }
+
+ /**
+ * Call this method before loading any libraries to indicate that this
+ * process shall neither create or reuse shared RELRO sections.
+ */
+ @Override
+ public void disableSharedRelros() {
+ if (DEBUG) {
+ Log.i(TAG, "disableSharedRelros() called");
+ }
+ synchronized (mLock) {
+ mInBrowserProcess = false;
+ mWaitForSharedRelros = false;
+ mBrowserUsesSharedRelro = false;
+ }
+ }
+
+ /**
+ * Call this method before loading any libraries to indicate that this
+ * process is ready to reuse shared RELRO sections from another one.
+ * Typically used when starting service processes.
+ * @param baseLoadAddress the base library load address to use.
+ */
+ @Override
+ public void initServiceProcess(long baseLoadAddress) {
+ if (DEBUG) {
+ Log.i(TAG,
+ String.format(Locale.US, "initServiceProcess(0x%x) called", baseLoadAddress));
+ }
+ synchronized (mLock) {
+ ensureInitializedLocked();
+ mInBrowserProcess = false;
+ mBrowserUsesSharedRelro = false;
+ if (mRelroSharingSupported) {
+ mWaitForSharedRelros = true;
+ mBaseLoadAddress = baseLoadAddress;
+ mCurrentLoadAddress = baseLoadAddress;
+ }
+ }
+ }
+
+ /**
+ * Retrieve the base load address of all shared RELRO sections.
+ * This also enforces the creation of shared RELRO sections in
+ * prepareLibraryLoad(), which can later be retrieved with getSharedRelros().
+ * @return a common, random base load address, or 0 if RELRO sharing is
+ * disabled.
+ */
+ @Override
+ public long getBaseLoadAddress() {
+ synchronized (mLock) {
+ ensureInitializedLocked();
+ if (!mInBrowserProcess) {
+ Log.w(TAG, "Shared RELRO sections are disabled in this process!");
+ return 0;
+ }
+
+ setupBaseLoadAddressLocked();
+ if (DEBUG) {
+ Log.i(TAG, String.format(Locale.US, "getBaseLoadAddress() returns 0x%x",
+ mBaseLoadAddress));
+ }
+ return mBaseLoadAddress;
+ }
+ }
+
+ // Used internally to lazily setup the common random base load address.
+ private void setupBaseLoadAddressLocked() {
+ assert Thread.holdsLock(mLock);
+ if (mBaseLoadAddress == 0) {
+ long address = computeRandomBaseLoadAddress();
+ mBaseLoadAddress = address;
+ mCurrentLoadAddress = address;
+ if (address == 0) {
+ // If the computed address is 0, there are issues with finding enough
+ // free address space, so disable RELRO shared / fixed load addresses.
+ Log.w(TAG, "Disabling shared RELROs due address space pressure");
+ mBrowserUsesSharedRelro = false;
+ mWaitForSharedRelros = false;
+ }
+ }
+ }
+
+ /**
+ * Compute a random base load address at which to place loaded libraries.
+ * @return new base load address, or 0 if the system does not support
+ * RELRO sharing.
+ */
+ private long computeRandomBaseLoadAddress() {
+ // nativeGetRandomBaseLoadAddress() returns an address at which it has previously
+ // successfully mapped an area of the given size, on the basis that we will be
+ // able, with high probability, to map our library into it.
+ //
+ // One issue with this is that we do not yet know the size of the library that
+ // we will load is. So here we pass a value that we expect will always be larger
+ // than that needed. If it is smaller the library mapping may still succeed. The
+ // other issue is that although highly unlikely, there is no guarantee that
+ // something else does not map into the area we are going to use between here and
+ // when we try to map into it.
+ //
+ // The above notes mean that all of this is probablistic. It is however okay to do
+ // because if, worst case and unlikely, we get unlucky in our choice of address,
+ // the back-out and retry without the shared RELRO in the ChildProcessService will
+ // keep things running.
+ final long maxExpectedBytes = 192 * 1024 * 1024;
+ final long address = nativeGetRandomBaseLoadAddress(maxExpectedBytes);
+ if (DEBUG) {
+ Log.i(TAG, String.format(Locale.US, "Random native base load address: 0x%x", address));
+ }
+ return address;
+ }
+
+ // Used for debugging only.
+ private void dumpBundle(Bundle bundle) {
+ if (DEBUG) {
+ Log.i(TAG, "Bundle has " + bundle.size() + " items: " + bundle);
+ }
+ }
+
+ /**
+ * Use the shared RELRO section from a Bundle received form another process.
+ * Call this after calling setBaseLoadAddress() then loading all libraries
+ * with loadLibrary().
+ * @param bundle Bundle instance generated with createSharedRelroBundle() in
+ * another process.
+ */
+ private void useSharedRelrosLocked(Bundle bundle) {
+ assert Thread.holdsLock(mLock);
+
+ if (DEBUG) {
+ Log.i(TAG, "Linker.useSharedRelrosLocked() called");
+ }
+
+ if (bundle == null) {
+ if (DEBUG) {
+ Log.i(TAG, "null bundle!");
+ }
+ return;
+ }
+
+ if (!mRelroSharingSupported) {
+ if (DEBUG) {
+ Log.i(TAG, "System does not support RELRO sharing");
+ }
+ return;
+ }
+
+ if (mLoadedLibraries == null) {
+ if (DEBUG) {
+ Log.i(TAG, "No libraries loaded!");
+ }
+ return;
+ }
+
+ if (DEBUG) {
+ dumpBundle(bundle);
+ }
+ HashMap<String, LibInfo> relroMap = createLibInfoMapFromBundle(bundle);
+
+ // Apply the RELRO section to all libraries that were already loaded.
+ for (Map.Entry<String, LibInfo> entry : relroMap.entrySet()) {
+ String libName = entry.getKey();
+ LibInfo libInfo = entry.getValue();
+ if (!nativeUseSharedRelro(libName, libInfo)) {
+ Log.w(TAG, "Could not use shared RELRO section for " + libName);
+ } else {
+ if (DEBUG) {
+ Log.i(TAG, "Using shared RELRO section for " + libName);
+ }
+ }
+ }
+
+ // In service processes, close all file descriptors from the map now.
+ if (!mInBrowserProcess) closeLibInfoMap(relroMap);
+
+ if (DEBUG) {
+ Log.i(TAG, "Linker.useSharedRelrosLocked() exiting");
+ }
+ }
+
+ /**
+ * Load a native shared library with the Chromium linker. If the zip file
+ * is not null, the shared library must be uncompressed and page aligned
+ * inside the zipfile. Note the crazy linker treats libraries and files as
+ * equivalent, so you can only open one library in a given zip file. The
+ * library must not be the Chromium linker library.
+ *
+ * @param zipFilePath The path of the zip file containing the library (or null).
+ * @param libFilePath The path of the library (possibly in the zip file).
+ */
+ @Override
+ public void loadLibrary(@Nullable String zipFilePath, String libFilePath) {
+ if (DEBUG) {
+ Log.i(TAG, "loadLibrary: " + zipFilePath + ", " + libFilePath);
+ }
+
+ synchronized (mLock) {
+ ensureInitializedLocked();
+
+ // Security: Ensure prepareLibraryLoad() was called before.
+ // In theory, this can be done lazily here, but it's more consistent
+ // to use a pair of functions (i.e. prepareLibraryLoad() + finishLibraryLoad())
+ // that wrap all calls to loadLibrary() in the library loader.
+ assert mPrepareLibraryLoadCalled;
+
+ if (mLoadedLibraries == null) {
+ mLoadedLibraries = new HashMap<String, LibInfo>();
+ }
+
+ if (mLoadedLibraries.containsKey(libFilePath)) {
+ if (DEBUG) {
+ Log.i(TAG, "Not loading " + libFilePath + " twice");
+ }
+ return;
+ }
+
+ LibInfo libInfo = new LibInfo();
+ long loadAddress = 0;
+ if ((mInBrowserProcess && mBrowserUsesSharedRelro) || mWaitForSharedRelros) {
+ // Load the library at a fixed address.
+ loadAddress = mCurrentLoadAddress;
+ }
+
+ String sharedRelRoName = libFilePath;
+ if (zipFilePath != null) {
+ if (!nativeLoadLibraryInZipFile(zipFilePath, libFilePath, loadAddress, libInfo)) {
+ String errorMessage =
+ "Unable to load library: " + libFilePath + ", in: " + zipFilePath;
+ Log.e(TAG, errorMessage);
+ throw new UnsatisfiedLinkError(errorMessage);
+ }
+ sharedRelRoName = zipFilePath;
+ } else {
+ if (!nativeLoadLibrary(libFilePath, loadAddress, libInfo)) {
+ String errorMessage = "Unable to load library: " + libFilePath;
+ Log.e(TAG, errorMessage);
+ throw new UnsatisfiedLinkError(errorMessage);
+ }
+ }
+
+ // Print the load address to the logcat when testing the linker. The format
+ // of the string is expected by the Python test_runner script as one of:
+ // BROWSER_LIBRARY_ADDRESS: <library-name> <address>
+ // RENDERER_LIBRARY_ADDRESS: <library-name> <address>
+ // Where <library-name> is the library name, and <address> is the hexadecimal load
+ // address.
+ if (NativeLibraries.sEnableLinkerTests) {
+ Log.i(TAG, String.format(Locale.US, "%s_LIBRARY_ADDRESS: %s %x",
+ mInBrowserProcess ? "BROWSER" : "RENDERER", libFilePath,
+ libInfo.mLoadAddress));
+ }
+
+ if (mInBrowserProcess) {
+ // Create a new shared RELRO section at the 'current' fixed load address.
+ if (!nativeCreateSharedRelro(sharedRelRoName, mCurrentLoadAddress, libInfo)) {
+ Log.w(TAG,
+ String.format(Locale.US, "Could not create shared RELRO for %s at %x",
+ libFilePath, mCurrentLoadAddress));
+ } else {
+ if (DEBUG) {
+ Log.i(TAG,
+ String.format(Locale.US, "Created shared RELRO for %s at %x: %s",
+ sharedRelRoName, mCurrentLoadAddress, libInfo.toString()));
+ }
+ }
+ }
+
+ if (mCurrentLoadAddress != 0) {
+ // Compute the next current load address. If mBaseLoadAddress
+ // is not 0, this is an explicit library load address. Otherwise,
+ // this is an explicit load address for relocated RELRO sections
+ // only.
+ mCurrentLoadAddress = libInfo.mLoadAddress + libInfo.mLoadSize;
+ }
+
+ mLoadedLibraries.put(sharedRelRoName, libInfo);
+ if (DEBUG) {
+ Log.i(TAG, "Library details " + libInfo.toString());
+ }
+ }
+ }
+
+ /**
+ * Determine whether a library is the linker library. Also deal with the
+ * component build that adds a .cr suffix to the name.
+ */
+ @Override
+ public boolean isChromiumLinkerLibrary(String library) {
+ return library.equals(LINKER_JNI_LIBRARY) || library.equals(LINKER_JNI_LIBRARY + ".cr");
+ }
+
+ /**
+ * Move activity from the native thread to the main UI thread.
+ * Called from native code on its own thread. Posts a callback from
+ * the UI thread back to native code.
+ *
+ * @param opaque Opaque argument.
+ */
+ @CalledByNative
+ public static void postCallbackOnMainThread(final long opaque) {
+ ThreadUtils.postOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ nativeRunCallbackOnUiThread(opaque);
+ }
+ });
+ }
+
+ /**
+ * Native method to run callbacks on the main UI thread.
+ * Supplied by the crazy linker and called by postCallbackOnMainThread.
+ * @param opaque Opaque crazy linker arguments.
+ */
+ private static native void nativeRunCallbackOnUiThread(long opaque);
+
+ /**
+ * Native method used to load a library.
+ * @param library Platform specific library name (e.g. libfoo.so)
+ * @param loadAddress Explicit load address, or 0 for randomized one.
+ * @param libInfo If not null, the mLoadAddress and mLoadSize fields
+ * of this LibInfo instance will set on success.
+ * @return true for success, false otherwise.
+ */
+ private static native boolean nativeLoadLibrary(
+ String library, long loadAddress, LibInfo libInfo);
+
+ /**
+ * Native method used to load a library which is inside a zipfile.
+ * @param zipfileName Filename of the zip file containing the library.
+ * @param library Platform specific library name (e.g. libfoo.so)
+ * @param loadAddress Explicit load address, or 0 for randomized one.
+ * @param libInfo If not null, the mLoadAddress and mLoadSize fields
+ * of this LibInfo instance will set on success.
+ * @return true for success, false otherwise.
+ */
+ private static native boolean nativeLoadLibraryInZipFile(
+ String zipfileName, String libraryName, long loadAddress, LibInfo libInfo);
+
+ /**
+ * Native method used to create a shared RELRO section.
+ * If the library was already loaded at the same address using
+ * nativeLoadLibrary(), this creates the RELRO for it. Otherwise,
+ * this loads a new temporary library at the specified address,
+ * creates and extracts the RELRO section from it, then unloads it.
+ * @param library Library name.
+ * @param loadAddress load address, which can be different from the one
+ * used to load the library in the current process!
+ * @param libInfo libInfo instance. On success, the mRelroStart, mRelroSize
+ * and mRelroFd will be set.
+ * @return true on success, false otherwise.
+ */
+ private static native boolean nativeCreateSharedRelro(
+ String library, long loadAddress, LibInfo libInfo);
+
+ /**
+ * Native method used to use a shared RELRO section.
+ * @param library Library name.
+ * @param libInfo A LibInfo instance containing valid RELRO information
+ * @return true on success.
+ */
+ private static native boolean nativeUseSharedRelro(String library, LibInfo libInfo);
+
+ /**
+ * Checks that the system supports shared RELROs. Old Android kernels
+ * have a bug in the way they check Ashmem region protection flags, which
+ * makes using shared RELROs unsafe. This method performs a simple runtime
+ * check for this misfeature, even though nativeEnableSharedRelro() will
+ * always fail if this returns false.
+ */
+ private static native boolean nativeCanUseSharedRelro();
+
+ /**
+ * Return a random address that should be free to be mapped with the given size.
+ * Maps an area of size bytes, and if successful then unmaps it and returns
+ * the address of the area allocated by the system (with ASLR). The idea is
+ * that this area should remain free of other mappings until we map our library
+ * into it.
+ * @param sizeBytes Size of area in bytes to search for.
+ * @return address to pass to future mmap, or 0 on error.
+ */
+ private static native long nativeGetRandomBaseLoadAddress(long sizeBytes);
+}
diff --git a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
index a789eff..5dfc2ba 100644
--- a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
+++ b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
@@ -11,16 +11,17 @@
import android.os.AsyncTask;
import android.os.Build;
import android.os.SystemClock;
-import android.util.Log;
import org.chromium.base.CalledByNative;
import org.chromium.base.CommandLine;
import org.chromium.base.JNINamespace;
+import org.chromium.base.Log;
import org.chromium.base.PackageUtils;
import org.chromium.base.TraceEvent;
-import org.chromium.base.VisibleForTesting;
import org.chromium.base.metrics.RecordHistogram;
+import java.util.concurrent.atomic.AtomicBoolean;
+
import javax.annotation.Nullable;
/**
@@ -39,7 +40,7 @@
*/
@JNINamespace("base::android")
public class LibraryLoader {
- private static final String TAG = "LibraryLoader";
+ private static final String TAG = "cr.library_loader";
// Set to true to enable debug logs.
private static final boolean DEBUG = false;
@@ -73,15 +74,19 @@
// APK file directly.
private boolean mLibraryWasLoadedFromApk;
- // One-way switch becomes false if the Chromium library should be loaded
- // directly from the APK file but it was compressed or not aligned.
- private boolean mLibraryIsMappableInApk = true;
-
// The type of process the shared library is loaded in.
// This member can be accessed from multiple threads simultaneously, so it have to be
// final (like now) or be protected in some way (volatile of synchronized).
private final int mLibraryProcessType;
+ // One-way switch that becomes true once
+ // {@link asyncPrefetchLibrariesToMemory} has been called.
+ private final AtomicBoolean mPrefetchLibraryHasBeenCalled;
+
+ // The number of milliseconds it took to load all the native libraries, which
+ // will be reported via UMA. Set once when the libraries are done loading.
+ private long mLibraryLoadTimeMs;
+
/**
* @param libraryProcessType the process the shared library is loaded in. refer to
* LibraryProcessType for possible values.
@@ -101,38 +106,21 @@
private LibraryLoader(int libraryProcessType) {
mLibraryProcessType = libraryProcessType;
- }
-
- /**
- * The same as ensureInitialized(null, false), should only be called
- * by non-browser processes.
- *
- * @throws ProcessInitException
- */
- @VisibleForTesting
- public void ensureInitialized() throws ProcessInitException {
- ensureInitialized(null, false);
+ mPrefetchLibraryHasBeenCalled = new AtomicBoolean();
}
/**
* This method blocks until the library is fully loaded and initialized.
*
- * @param context The context in which the method is called, the caller
- * may pass in a null context if it doesn't know in which context it
- * is running.
- *
- * @param shouldDeleteFallbackLibraries The flag tells whether the method
- * should delete the fallback libraries or not.
+ * @param context The context in which the method is called.
*/
- public void ensureInitialized(
- Context context, boolean shouldDeleteFallbackLibraries)
- throws ProcessInitException {
+ public void ensureInitialized(Context context) throws ProcessInitException {
synchronized (sLock) {
if (mInitialized) {
// Already initialized, nothing to do.
return;
}
- loadAlreadyLocked(context, shouldDeleteFallbackLibraries);
+ loadAlreadyLocked(context);
initializeAlreadyLocked();
}
}
@@ -145,32 +133,19 @@
}
/**
- * The same as loadNow(null, false), should only be called by
- * non-browser process.
- *
- * @throws ProcessInitException
- */
- public void loadNow() throws ProcessInitException {
- loadNow(null, false);
- }
-
- /**
* Loads the library and blocks until the load completes. The caller is responsible
* for subsequently calling ensureInitialized().
* May be called on any thread, but should only be called once. Note the thread
* this is called on will be the thread that runs the native code's static initializers.
* See the comment in doInBackground() for more considerations on this.
*
- * @param context The context the code is running, or null if it doesn't have one.
- * @param shouldDeleteFallbackLibraries The flag tells whether the method
- * should delete the old fallback libraries or not.
+ * @param context The context the code is running.
*
* @throws ProcessInitException if the native library failed to load.
*/
- public void loadNow(Context context, boolean shouldDeleteFallbackLibraries)
- throws ProcessInitException {
+ public void loadNow(Context context) throws ProcessInitException {
synchronized (sLock) {
- loadAlreadyLocked(context, shouldDeleteFallbackLibraries);
+ loadAlreadyLocked(context);
}
}
@@ -193,10 +168,9 @@
*
* This is done this way, as testing shows that fadvise(FADV_WILLNEED) is
* detrimental to the startup time.
- *
- * @param context the application context.
*/
- public void asyncPrefetchLibrariesToMemory(final Context context) {
+ public void asyncPrefetchLibrariesToMemory() {
+ if (!mPrefetchLibraryHasBeenCalled.compareAndSet(false, true)) return;
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
@@ -213,34 +187,26 @@
}
// Invoke System.loadLibrary(...), triggering JNI_OnLoad in native code
- private void loadAlreadyLocked(
- Context context, boolean shouldDeleteFallbackLibraries)
- throws ProcessInitException {
+ private void loadAlreadyLocked(Context context) throws ProcessInitException {
try {
if (!mLoaded) {
assert !mInitialized;
long startTime = SystemClock.uptimeMillis();
- boolean useChromiumLinker = Linker.isUsed();
- boolean fallbackWasUsed = false;
+ Linker linker = Linker.getInstance();
+ boolean useChromiumLinker = linker.isUsed();
if (useChromiumLinker) {
// Determine the APK file path.
- String apkFilePath = null;
- if (context != null) {
- apkFilePath = getLibraryApkPath(context);
- } else {
- Log.w(TAG, "could not check load from APK support due to null context");
- }
-
+ String apkFilePath = getLibraryApkPath(context);
// Load libraries using the Chromium linker.
- Linker.prepareLibraryLoad();
+ linker.prepareLibraryLoad();
for (String library : NativeLibraries.LIBRARIES) {
// Don't self-load the linker. This is because the build system is
// not clever enough to understand that all the libraries packaged
// in the final .apk don't need to be explicitly loaded.
- if (Linker.isChromiumLinkerLibrary(library)) {
+ if (linker.isChromiumLinkerLibrary(library)) {
if (DEBUG) Log.i(TAG, "ignoring self-linker load");
continue;
}
@@ -248,26 +214,11 @@
// Determine where the library should be loaded from.
String zipFilePath = null;
String libFilePath = System.mapLibraryName(library);
- if (apkFilePath != null && Linker.isInZipFile()) {
- // The library is in the APK file.
- if (!Linker.checkLibraryIsMappableInApk(apkFilePath, libFilePath)) {
- mLibraryIsMappableInApk = false;
- }
- if (mLibraryIsMappableInApk) {
- // Load directly from the APK.
- zipFilePath = apkFilePath;
- Log.i(TAG, "Loading " + library + " directly from within "
- + apkFilePath);
- } else {
- // Unpack library fallback.
- Log.i(TAG, "Loading " + library
- + " using unpack library fallback from within "
- + apkFilePath);
- libFilePath = LibraryLoaderHelper.buildFallbackLibrary(
- context, library);
- fallbackWasUsed = true;
- Log.i(TAG, "Built fallback library " + libFilePath);
- }
+ if (linker.isInZipFile()) {
+ // Load directly from the APK.
+ zipFilePath = apkFilePath;
+ Log.i(TAG,
+ "Loading " + library + " directly from within " + apkFilePath);
} else {
// The library is in its own file.
Log.i(TAG, "Loading " + library);
@@ -275,7 +226,7 @@
// Load the library.
boolean isLoaded = false;
- if (Linker.isUsingBrowserSharedRelros()) {
+ if (linker.isUsingBrowserSharedRelros()) {
mIsUsingBrowserSharedRelros = true;
try {
loadLibrary(zipFilePath, libFilePath);
@@ -283,7 +234,7 @@
} catch (UnsatisfiedLinkError e) {
Log.w(TAG, "Failed to load native library with shared RELRO, "
+ "retrying without");
- Linker.disableSharedRelros();
+ linker.disableSharedRelros();
mLoadAtFixedAddressFailed = true;
}
}
@@ -292,7 +243,7 @@
}
}
- Linker.finishLibraryLoad();
+ linker.finishLibraryLoad();
} else {
// Load libraries using the system linker.
for (String library : NativeLibraries.LIBRARIES) {
@@ -300,17 +251,10 @@
}
}
- if (!fallbackWasUsed && context != null
- && shouldDeleteFallbackLibraries) {
- LibraryLoaderHelper.deleteLibrariesAsynchronously(
- context, LibraryLoaderHelper.LOAD_FROM_APK_FALLBACK_DIR);
- }
-
long stopTime = SystemClock.uptimeMillis();
+ mLibraryLoadTimeMs = stopTime - startTime;
Log.i(TAG, String.format("Time to load native libraries: %d ms (timestamps %d-%d)",
- stopTime - startTime,
- startTime % 10000,
- stopTime % 10000));
+ mLibraryLoadTimeMs, startTime % 10000, stopTime % 10000));
mLoaded = true;
}
@@ -318,11 +262,9 @@
throw new ProcessInitException(LoaderErrors.LOADER_ERROR_NATIVE_LIBRARY_LOAD_FAILED, e);
}
// Check that the version of the library we have loaded matches the version we expect
- Log.i(TAG, String.format(
- "Expected native library version number \"%s\","
- + "actual native library version number \"%s\"",
- NativeLibraries.sVersionNumber,
- nativeGetVersionNumber()));
+ Log.i(TAG, String.format("Expected native library version number \"%s\", "
+ + "actual native library version number \"%s\"",
+ NativeLibraries.sVersionNumber, nativeGetVersionNumber()));
if (!NativeLibraries.sVersionNumber.equals(nativeGetVersionNumber())) {
throw new ProcessInitException(LoaderErrors.LOADER_ERROR_NATIVE_LIBRARY_WRONG_VERSION);
}
@@ -356,7 +298,7 @@
// Load a native shared library with the Chromium linker. If the zip file
// path is not null, the library is loaded directly from the zip file.
private void loadLibrary(@Nullable String zipFilePath, String libFilePath) {
- Linker.loadLibrary(zipFilePath, libFilePath);
+ Linker.getInstance().loadLibrary(zipFilePath, libFilePath);
if (zipFilePath != null) {
mLibraryWasLoadedFromApk = true;
}
@@ -426,26 +368,22 @@
// Record Chromium linker histogram state for the main browser process. Called from
// onNativeInitializationComplete().
private void recordBrowserProcessHistogram(Context context) {
- if (Linker.isUsed()) {
+ if (Linker.getInstance().isUsed()) {
nativeRecordChromiumAndroidLinkerBrowserHistogram(mIsUsingBrowserSharedRelros,
- mLoadAtFixedAddressFailed,
- getLibraryLoadFromApkStatus(context));
+ mLoadAtFixedAddressFailed, getLibraryLoadFromApkStatus(context),
+ mLibraryLoadTimeMs);
}
}
// Returns the device's status for loading a library directly from the APK file.
// This method can only be called when the Chromium linker is used.
private int getLibraryLoadFromApkStatus(Context context) {
- assert Linker.isUsed();
+ assert Linker.getInstance().isUsed();
if (mLibraryWasLoadedFromApk) {
return LibraryLoadFromApkStatusCodes.SUCCESSFUL;
}
- if (!mLibraryIsMappableInApk) {
- return LibraryLoadFromApkStatusCodes.USED_UNPACK_LIBRARY_FALLBACK;
- }
-
// There were no libraries to be loaded directly from the APK file.
return LibraryLoadFromApkStatusCodes.UNKNOWN;
}
@@ -456,9 +394,9 @@
// RecordChromiumAndroidLinkerRendererHistogram() will record it correctly.
public void registerRendererProcessHistogram(boolean requestedSharedRelro,
boolean loadAtFixedAddressFailed) {
- if (Linker.isUsed()) {
- nativeRegisterChromiumAndroidLinkerRendererHistogram(requestedSharedRelro,
- loadAtFixedAddressFailed);
+ if (Linker.getInstance().isUsed()) {
+ nativeRegisterChromiumAndroidLinkerRendererHistogram(
+ requestedSharedRelro, loadAtFixedAddressFailed, mLibraryLoadTimeMs);
}
}
@@ -485,18 +423,18 @@
// Method called to record statistics about the Chromium linker operation for the main
// browser process. Indicates whether the linker attempted relro sharing for the browser,
// and if it did, whether the library failed to load at a fixed address. Also records
- // support for loading a library directly from the APK file.
+ // support for loading a library directly from the APK file, and the number of milliseconds
+ // it took to load the libraries.
private native void nativeRecordChromiumAndroidLinkerBrowserHistogram(
- boolean isUsingBrowserSharedRelros,
- boolean loadAtFixedAddressFailed,
- int libraryLoadFromApkStatus);
+ boolean isUsingBrowserSharedRelros, boolean loadAtFixedAddressFailed,
+ int libraryLoadFromApkStatus, long libraryLoadTime);
// Method called to register (for later recording) statistics about the Chromium linker
// operation for a renderer process. Indicates whether the linker attempted relro sharing,
- // and if it did, whether the library failed to load at a fixed address.
+ // and if it did, whether the library failed to load at a fixed address. Also records the
+ // number of milliseconds it took to load the libraries.
private native void nativeRegisterChromiumAndroidLinkerRendererHistogram(
- boolean requestedSharedRelro,
- boolean loadAtFixedAddressFailed);
+ boolean requestedSharedRelro, boolean loadAtFixedAddressFailed, long libraryLoadTime);
// Get the version of the native library. This is needed so that we can check we
// have the right version before initializing the (rest of the) JNI.
diff --git a/base/android/java/src/org/chromium/base/library_loader/LibraryLoaderHelper.java b/base/android/java/src/org/chromium/base/library_loader/LibraryLoaderHelper.java
deleted file mode 100644
index 7dd1a29..0000000
--- a/base/android/java/src/org/chromium/base/library_loader/LibraryLoaderHelper.java
+++ /dev/null
@@ -1,338 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-
-package org.chromium.base.library_loader;
-
-import android.content.Context;
-import android.util.Log;
-
-import java.io.BufferedOutputStream;
-import java.io.Closeable;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipException;
-import java.util.zip.ZipFile;
-
-/**
- * Class representing an exception which occured during the unpacking process.
- */
-class UnpackingException extends Exception {
- public UnpackingException(String message, Throwable cause) {
- super(message, cause);
- }
-
- public UnpackingException(String message) {
- super(message);
- }
-}
-
-/**
- * The class provides helper functions to extract native libraries from APK,
- * and load libraries from there.
- */
-class LibraryLoaderHelper {
- private static final String TAG = "LibraryLoaderHelper";
-
- // Fallback directories.
- static final String LOAD_FROM_APK_FALLBACK_DIR = "fallback";
-
- private static final int BUFFER_SIZE = 16384;
-
- /**
- * Returns the directory for holding extracted native libraries.
- * It may create the directory if it doesn't exist.
- *
- * @param context The context the code is running.
- * @param dirName The name of the directory containing the libraries.
- * @return The directory file object.
- */
- static File getLibDir(Context context, String dirName) {
- return context.getDir(dirName, Context.MODE_PRIVATE);
- }
-
- /**
- * Delete libraries and their directory synchronously.
- */
- private static void deleteLibrariesSynchronously(Context context, String dirName) {
- File libDir = getLibDir(context, dirName);
- deleteObsoleteLibraries(libDir, Collections.<File>emptyList());
- }
-
- /**
- * Delete libraries and their directory asynchronously.
- * The actual deletion is done in a background thread.
- */
- static void deleteLibrariesAsynchronously(
- final Context context, final String dirName) {
- // Child process should not reach here.
- new Thread() {
- @Override
- public void run() {
- deleteLibrariesSynchronously(context, dirName);
- }
- }.start();
- }
-
- /**
- * Copy a library from a zip file to the application's private directory.
- * This is used as a fallback when we are unable to load the library
- * directly from the APK file (crbug.com/390618).
- *
- * @param context The context the code is running in.
- * @param library Library name.
- * @return name of the fallback copy of the library.
- */
- static String buildFallbackLibrary(Context context, String library) {
- try {
- String libName = System.mapLibraryName(library);
- File fallbackLibDir = getLibDir(context, LOAD_FROM_APK_FALLBACK_DIR);
- File fallbackLibFile = new File(fallbackLibDir, libName);
- String pathInZipFile = Linker.getLibraryFilePathInZipFile(libName);
- Map<String, File> dstFiles = Collections.singletonMap(pathInZipFile, fallbackLibFile);
-
- deleteObsoleteLibraries(fallbackLibDir, dstFiles.values());
- unpackLibraries(context, dstFiles);
-
- return fallbackLibFile.getAbsolutePath();
- } catch (Exception e) {
- String errorMessage = "Unable to load fallback for library " + library
- + " (" + (e.getMessage() == null ? e.toString() : e.getMessage()) + ")";
- Log.e(TAG, errorMessage, e);
- throw new UnsatisfiedLinkError(errorMessage);
- }
- }
-
- // Delete obsolete libraries from a library folder.
- private static void deleteObsoleteLibraries(File libDir, Collection<File> keptFiles) {
- try {
- // Build a list of libraries that should NOT be deleted.
- Set<String> keptFileNames = new HashSet<String>();
- for (File k : keptFiles) {
- keptFileNames.add(k.getName());
- }
-
- // Delete the obsolete libraries.
- Log.i(TAG, "Deleting obsolete libraries in " + libDir.getPath());
- File[] files = libDir.listFiles();
- if (files != null) {
- for (File f : files) {
- if (!keptFileNames.contains(f.getName())) {
- delete(f);
- }
- }
- } else {
- Log.e(TAG, "Failed to list files in " + libDir.getPath());
- }
-
- // Delete the folder if no libraries were kept.
- if (keptFileNames.isEmpty()) {
- delete(libDir);
- }
- } catch (Exception e) {
- Log.e(TAG, "Failed to remove obsolete libraries from " + libDir.getPath());
- }
- }
-
- // Unpack libraries from a zip file to the file system.
- private static void unpackLibraries(Context context,
- Map<String, File> dstFiles) throws UnpackingException {
- String zipFilePath = context.getApplicationInfo().sourceDir;
- Log.i(TAG, "Opening zip file " + zipFilePath);
- File zipFile = new File(zipFilePath);
- ZipFile zipArchive = openZipFile(zipFile);
-
- try {
- for (Entry<String, File> d : dstFiles.entrySet()) {
- String pathInZipFile = d.getKey();
- File dstFile = d.getValue();
- Log.i(TAG, "Unpacking " + pathInZipFile
- + " to " + dstFile.getAbsolutePath());
- ZipEntry packedLib = zipArchive.getEntry(pathInZipFile);
-
- if (needToUnpackLibrary(zipFile, packedLib, dstFile)) {
- unpackLibraryFromZipFile(zipArchive, packedLib, dstFile);
- setLibraryFilePermissions(dstFile);
- }
- }
- } finally {
- closeZipFile(zipArchive);
- }
- }
-
- // Open a zip file.
- private static ZipFile openZipFile(File zipFile) throws UnpackingException {
- try {
- return new ZipFile(zipFile);
- } catch (ZipException e) {
- throw new UnpackingException("Failed to open zip file " + zipFile.getPath());
- } catch (IOException e) {
- throw new UnpackingException("Failed to open zip file " + zipFile.getPath());
- }
- }
-
- // Determine whether it is necessary to unpack a library from a zip file.
- private static boolean needToUnpackLibrary(
- File zipFile, ZipEntry packedLib, File dstFile) {
- // Check if the fallback library already exists.
- if (!dstFile.exists()) {
- Log.i(TAG, "File " + dstFile.getPath() + " does not exist yet");
- return true;
- }
-
- // Check last modification dates.
- long zipTime = zipFile.lastModified();
- long fallbackLibTime = dstFile.lastModified();
- if (zipTime > fallbackLibTime) {
- Log.i(TAG, "Not using existing fallback file because "
- + "the APK file " + zipFile.getPath()
- + " (timestamp=" + zipTime + ") is newer than "
- + "the fallback library " + dstFile.getPath()
- + "(timestamp=" + fallbackLibTime + ")");
- return true;
- }
-
- // Check file sizes.
- long packedLibSize = packedLib.getSize();
- long fallbackLibSize = dstFile.length();
- if (fallbackLibSize != packedLibSize) {
- Log.i(TAG, "Not using existing fallback file because "
- + "the library in the APK " + zipFile.getPath()
- + " (" + packedLibSize + "B) has a different size than "
- + "the fallback library " + dstFile.getPath()
- + "(" + fallbackLibSize + "B)");
- return true;
- }
-
- Log.i(TAG, "Reusing existing file " + dstFile.getPath());
- return false;
- }
-
- // Unpack a library from a zip file to the filesystem.
- private static void unpackLibraryFromZipFile(ZipFile zipArchive, ZipEntry packedLib,
- File dstFile) throws UnpackingException {
- // Open input stream for the library file inside the zip file.
- InputStream in;
- try {
- in = zipArchive.getInputStream(packedLib);
- } catch (IOException e) {
- throw new UnpackingException(
- "IO exception when locating library in the zip file", e);
- }
-
- // Ensure that the input stream is closed at the end.
- try {
- // Delete existing file if it exists.
- if (dstFile.exists()) {
- Log.i(TAG, "Deleting existing unpacked library file " + dstFile.getPath());
- if (!dstFile.delete()) {
- throw new UnpackingException(
- "Failed to delete existing unpacked library file " + dstFile.getPath());
- }
- }
-
- // Ensure that the library folder exists. Since this is added
- // for increased robustness, we log errors and carry on.
- try {
- dstFile.getParentFile().mkdirs();
- } catch (Exception e) {
- Log.e(TAG, "Failed to make library folder", e);
- }
-
- // Create the destination file.
- try {
- if (!dstFile.createNewFile()) {
- throw new UnpackingException("existing unpacked library file was not deleted");
- }
- } catch (IOException e) {
- throw new UnpackingException("failed to create unpacked library file", e);
- }
-
- // Open the output stream for the destination file.
- OutputStream out;
- try {
- out = new BufferedOutputStream(new FileOutputStream(dstFile));
- } catch (FileNotFoundException e) {
- throw new UnpackingException(
- "failed to open output stream for unpacked library file", e);
- }
-
- // Ensure that the output stream is closed at the end.
- try {
- // Copy the library from the zip file to the destination file.
- Log.i(TAG, "Copying " + packedLib.getName() + " from " + zipArchive.getName()
- + " to " + dstFile.getPath());
- byte[] buffer = new byte[BUFFER_SIZE];
- int len;
- while ((len = in.read(buffer)) != -1) {
- out.write(buffer, 0, len);
- }
- } catch (IOException e) {
- throw new UnpackingException(
- "failed to copy the library from the zip file", e);
- } finally {
- close(out, "output stream");
- }
- } finally {
- close(in, "input stream");
- }
- }
-
- // Set up library file permissions.
- private static void setLibraryFilePermissions(File libFile) {
- // Change permission to rwxr-xr-x
- Log.i(TAG, "Setting file permissions for " + libFile.getPath());
- if (!libFile.setReadable(/* readable */ true, /* ownerOnly */ false)) {
- Log.e(TAG, "failed to chmod a+r the temporary file");
- }
- if (!libFile.setExecutable(/* executable */ true, /* ownerOnly */ false)) {
- Log.e(TAG, "failed to chmod a+x the temporary file");
- }
- if (!libFile.setWritable(/* writable */ true)) {
- Log.e(TAG, "failed to chmod +w the temporary file");
- }
- }
-
- // Close a closable and log a warning if it fails.
- private static void close(Closeable closeable, String name) {
- try {
- closeable.close();
- } catch (IOException e) {
- // Warn and ignore.
- Log.w(TAG, "IO exception when closing " + name, e);
- }
- }
-
- // Close a zip file and log a warning if it fails.
- // This needs to be a separate method because ZipFile is not Closeable in
- // Java 6 (used on some older devices).
- private static void closeZipFile(ZipFile file) {
- try {
- file.close();
- } catch (IOException e) {
- // Warn and ignore.
- Log.w(TAG, "IO exception when closing zip file", e);
- }
- }
-
- // Delete a file and log it.
- private static void delete(File file) {
- if (file.delete()) {
- Log.i(TAG, "Deleted " + file.getPath());
- } else {
- Log.w(TAG, "Failed to delete " + file.getPath());
- }
- }
-}
diff --git a/base/android/java/src/org/chromium/base/library_loader/Linker.java b/base/android/java/src/org/chromium/base/library_loader/Linker.java
index 9a64a3f..c769339 100644
--- a/base/android/java/src/org/chromium/base/library_loader/Linker.java
+++ b/base/android/java/src/org/chromium/base/library_loader/Linker.java
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// 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.
@@ -8,14 +8,10 @@
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
-import android.util.Log;
-import org.chromium.base.CalledByNative;
-import org.chromium.base.SysUtils;
-import org.chromium.base.ThreadUtils;
+import org.chromium.base.Log;
import org.chromium.base.annotations.AccessedByNative;
-import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
@@ -153,13 +149,19 @@
*
* This method also ensures the process uses the shared RELROs.
*/
-public class Linker {
-
- // Log tag for this class. This must match the name of the linker's native library.
- private static final String TAG = "chromium_android_linker";
+public abstract class Linker {
+ // Log tag for this class.
+ private static final String TAG = "cr.library_loader";
// Set to true to enable debug logs.
- private static final boolean DEBUG = false;
+ protected static final boolean DEBUG = false;
+
+ // Used to pass the shared RELRO Bundle through Binder.
+ public static final String EXTRA_LINKER_SHARED_RELROS =
+ "org.chromium.base.android.linker.shared_relros";
+
+ // Guards all access to the linker.
+ protected final Object mLock = new Object();
// Constants used to control the behaviour of the browser process with
// regards to the shared RELRO section.
@@ -188,96 +190,24 @@
// Indicates if this is a low-memory device or not. The default is to
// determine this by probing the system at runtime, but this can be forced
// for testing by calling setMemoryDeviceConfig().
- private static int sMemoryDeviceConfig = MEMORY_DEVICE_CONFIG_INIT;
+ protected int mMemoryDeviceConfig = MEMORY_DEVICE_CONFIG_INIT;
- // Becomes true after linker initialization.
- private static boolean sInitialized = false;
+ // Singleton.
+ private static Linker sSingleton = null;
+ private static Object sSingletonLock = new Object();
- // Set to true to indicate that the system supports safe sharing of RELRO sections.
- private static boolean sRelroSharingSupported = false;
+ // Protected singleton constructor.
+ protected Linker() {}
- // Set to true if this runs in the browser process. Disabled by initServiceProcess().
- // TODO(petrcermak): This flag can be incorrectly set to false (even though this might run in
- // the browser process) on low-memory devices.
- private static boolean sInBrowserProcess = true;
-
- // Becomes true to indicate this process needs to wait for a shared RELRO in
- // finishLibraryLoad().
- private static boolean sWaitForSharedRelros = false;
-
- // Becomes true when initialization determines that the browser process can use the
- // shared RELRO.
- private static boolean sBrowserUsesSharedRelro = false;
-
- // The map of all RELRO sections either created or used in this process.
- private static Bundle sSharedRelros = null;
-
- // Current common random base load address.
- private static long sBaseLoadAddress = 0;
-
- // Current fixed-location load address for the next library called by loadLibrary().
- private static long sCurrentLoadAddress = 0;
-
- // Becomes true once prepareLibraryLoad() has been called.
- private static boolean sPrepareLibraryLoadCalled = false;
-
- // Used internally to initialize the linker's static data. Assume lock is held.
- private static void ensureInitializedLocked() {
- assert Thread.holdsLock(Linker.class);
-
- if (!sInitialized) {
- sRelroSharingSupported = false;
- if (NativeLibraries.sUseLinker) {
- if (DEBUG) Log.i(TAG, "Loading lib" + TAG + ".so");
- try {
- System.loadLibrary(TAG);
- } catch (UnsatisfiedLinkError e) {
- // In a component build, the ".cr" suffix is added to each library name.
- Log.w(TAG, "Couldn't load lib" + TAG + ".so, trying lib" + TAG + ".cr.so");
- System.loadLibrary(TAG + ".cr");
- }
- sRelroSharingSupported = nativeCanUseSharedRelro();
- if (!sRelroSharingSupported) {
- Log.w(TAG, "This system cannot safely share RELRO sections");
- } else {
- if (DEBUG) Log.i(TAG, "This system supports safe shared RELRO sections");
- }
-
- if (sMemoryDeviceConfig == MEMORY_DEVICE_CONFIG_INIT) {
- sMemoryDeviceConfig = SysUtils.isLowEndDevice()
- ? MEMORY_DEVICE_CONFIG_LOW : MEMORY_DEVICE_CONFIG_NORMAL;
- }
-
- switch (BROWSER_SHARED_RELRO_CONFIG) {
- case BROWSER_SHARED_RELRO_CONFIG_NEVER:
- sBrowserUsesSharedRelro = false;
- break;
- case BROWSER_SHARED_RELRO_CONFIG_LOW_RAM_ONLY:
- sBrowserUsesSharedRelro =
- (sMemoryDeviceConfig == MEMORY_DEVICE_CONFIG_LOW);
- if (sBrowserUsesSharedRelro) {
- Log.w(TAG, "Low-memory device: shared RELROs used in all processes");
- }
- break;
- case BROWSER_SHARED_RELRO_CONFIG_ALWAYS:
- Log.w(TAG, "Beware: shared RELROs used in all processes!");
- sBrowserUsesSharedRelro = true;
- break;
- default:
- assert false : "Unreached";
- break;
- }
- } else {
- if (DEBUG) Log.i(TAG, "Linker disabled");
+ // Get singleton instance.
+ public static final Linker getInstance() {
+ synchronized (sSingletonLock) {
+ if (sSingleton == null) {
+ // TODO(simonb): Extend later to return either a LegacyLinker
+ // or a ModernLinker instance.
+ sSingleton = new LegacyLinker();
}
-
- if (!sRelroSharingSupported) {
- // Sanity.
- sBrowserUsesSharedRelro = false;
- sWaitForSharedRelros = false;
- }
-
- sInitialized = true;
+ return sSingleton;
}
}
@@ -297,7 +227,7 @@
}
// The name of a class that implements TestRunner.
- static String sTestRunnerClassName = null;
+ String mTestRunnerClassName = null;
/**
* Set the TestRunner by its class name. It will be instantiated at
@@ -305,17 +235,18 @@
* @param testRunnerClassName null or a String for the class name of the
* TestRunner to use.
*/
- public static void setTestRunnerClassName(String testRunnerClassName) {
- if (DEBUG) Log.i(TAG, "setTestRunnerByClassName(" + testRunnerClassName + ") called");
-
+ public void setTestRunnerClassName(String testRunnerClassName) {
+ if (DEBUG) {
+ Log.i(TAG, "setTestRunnerByClassName(" + testRunnerClassName + ") called");
+ }
if (!NativeLibraries.sEnableLinkerTests) {
- // Ignore this in production code to prevent malvolent runtime injection.
+ // Ignore this in production code to prevent malevolent runtime injection.
return;
}
- synchronized (Linker.class) {
- assert sTestRunnerClassName == null;
- sTestRunnerClassName = testRunnerClassName;
+ synchronized (mLock) {
+ assert mTestRunnerClassName == null;
+ mTestRunnerClassName = testRunnerClassName;
}
}
@@ -326,9 +257,9 @@
* @return null or a String holding the name of the class implementing
* the TestRunner set by calling setTestRunnerClassName() previously.
*/
- public static String getTestRunnerClassName() {
- synchronized (Linker.class) {
- return sTestRunnerClassName;
+ public String getTestRunnerClassName() {
+ synchronized (mLock) {
+ return mTestRunnerClassName;
}
}
@@ -337,12 +268,14 @@
* memory device configuration. Should only be used for testing.
* @param memoryDeviceConfig either MEMORY_DEVICE_CONFIG_LOW or MEMORY_DEVICE_CONFIG_NORMAL.
*/
- public static void setMemoryDeviceConfig(int memoryDeviceConfig) {
- if (DEBUG) Log.i(TAG, "setMemoryDeviceConfig(" + memoryDeviceConfig + ") called");
+ public void setMemoryDeviceConfig(int memoryDeviceConfig) {
+ if (DEBUG) {
+ Log.i(TAG, "setMemoryDeviceConfig(" + memoryDeviceConfig + ") called");
+ }
// Sanity check. This method should only be called during tests.
assert NativeLibraries.sEnableLinkerTests;
- synchronized (Linker.class) {
- assert sMemoryDeviceConfig == MEMORY_DEVICE_CONFIG_INIT;
+ synchronized (mLock) {
+ assert mMemoryDeviceConfig == MEMORY_DEVICE_CONFIG_INIT;
assert memoryDeviceConfig == MEMORY_DEVICE_CONFIG_LOW
|| memoryDeviceConfig == MEMORY_DEVICE_CONFIG_NORMAL;
if (DEBUG) {
@@ -352,7 +285,7 @@
Log.i(TAG, "Simulating a regular-memory device");
}
}
- sMemoryDeviceConfig = memoryDeviceConfig;
+ mMemoryDeviceConfig = memoryDeviceConfig;
}
}
@@ -361,130 +294,31 @@
* use this linker. If not, System.loadLibrary() should be used to load
* libraries instead.
*/
- public static boolean isUsed() {
- // Only GYP targets that are APKs and have the 'use_chromium_linker' variable
- // defined as 1 will use this linker. For all others (the default), the
- // auto-generated NativeLibraries.sUseLinker variable will be false.
- if (!NativeLibraries.sUseLinker) return false;
-
- synchronized (Linker.class) {
- ensureInitializedLocked();
- // At the moment, there is also no point in using this linker if the
- // system does not support RELRO sharing safely.
- return sRelroSharingSupported;
- }
- }
+ public abstract boolean isUsed();
/**
* Call this method to determine if the linker will try to use shared RELROs
* for the browser process.
*/
- public static boolean isUsingBrowserSharedRelros() {
- synchronized (Linker.class) {
- ensureInitializedLocked();
- return sBrowserUsesSharedRelro;
- }
- }
+ public abstract boolean isUsingBrowserSharedRelros();
/**
* Call this method to determine if the chromium project must load
* the library directly from the zip file.
*/
- public static boolean isInZipFile() {
- return NativeLibraries.sUseLibraryInZipFile;
- }
+ public abstract boolean isInZipFile();
/**
* Call this method just before loading any native shared libraries in this process.
*/
- public static void prepareLibraryLoad() {
- if (DEBUG) Log.i(TAG, "prepareLibraryLoad() called");
- synchronized (Linker.class) {
- sPrepareLibraryLoadCalled = true;
-
- if (sInBrowserProcess) {
- // Force generation of random base load address, as well
- // as creation of shared RELRO sections in this process.
- setupBaseLoadAddressLocked();
- }
- }
- }
+ public abstract void prepareLibraryLoad();
/**
* Call this method just after loading all native shared libraries in this process.
* Note that when in a service process, this will block until the RELRO bundle is
* received, i.e. when another thread calls useSharedRelros().
*/
- public static void finishLibraryLoad() {
- if (DEBUG) Log.i(TAG, "finishLibraryLoad() called");
- synchronized (Linker.class) {
- if (DEBUG) Log.i(TAG, String.format(
- Locale.US,
- "sInBrowserProcess=%s sBrowserUsesSharedRelro=%s sWaitForSharedRelros=%s",
- sInBrowserProcess ? "true" : "false",
- sBrowserUsesSharedRelro ? "true" : "false",
- sWaitForSharedRelros ? "true" : "false"));
-
- if (sLoadedLibraries == null) {
- if (DEBUG) Log.i(TAG, "No libraries loaded");
- } else {
- if (sInBrowserProcess) {
- // Create new Bundle containing RELRO section information
- // for all loaded libraries. Make it available to getSharedRelros().
- sSharedRelros = createBundleFromLibInfoMap(sLoadedLibraries);
- if (DEBUG) {
- Log.i(TAG, "Shared RELRO created");
- dumpBundle(sSharedRelros);
- }
-
- if (sBrowserUsesSharedRelro) {
- useSharedRelrosLocked(sSharedRelros);
- }
- }
-
- if (sWaitForSharedRelros) {
- assert !sInBrowserProcess;
-
- // Wait until the shared relro bundle is received from useSharedRelros().
- while (sSharedRelros == null) {
- try {
- Linker.class.wait();
- } catch (InterruptedException ie) {
- // no-op
- }
- }
- useSharedRelrosLocked(sSharedRelros);
- // Clear the Bundle to ensure its file descriptor references can't be reused.
- sSharedRelros.clear();
- sSharedRelros = null;
- }
- }
-
- if (NativeLibraries.sEnableLinkerTests && sTestRunnerClassName != null) {
- // The TestRunner implementation must be instantiated _after_
- // all libraries are loaded to ensure that its native methods
- // are properly registered.
- if (DEBUG) Log.i(TAG, "Instantiating " + sTestRunnerClassName);
- TestRunner testRunner = null;
- try {
- testRunner = (TestRunner)
- Class.forName(sTestRunnerClassName).newInstance();
- } catch (Exception e) {
- Log.e(TAG, "Could not extract test runner class name", e);
- testRunner = null;
- }
- if (testRunner != null) {
- if (!testRunner.runChecks(sMemoryDeviceConfig, sInBrowserProcess)) {
- Log.wtf(TAG, "Linker runtime tests failed in this process!!");
- assert false;
- } else {
- Log.i(TAG, "All linker tests passed!");
- }
- }
- }
- }
- if (DEBUG) Log.i(TAG, "finishLibraryLoad() exiting");
- }
+ public abstract void finishLibraryLoad();
/**
* Call this to send a Bundle containing the shared RELRO sections to be
@@ -494,32 +328,7 @@
* @param bundle The Bundle instance containing a map of shared RELRO sections
* to use in this process.
*/
- public static void useSharedRelros(Bundle bundle) {
- // Ensure the bundle uses the application's class loader, not the framework
- // one which doesn't know anything about LibInfo.
- // Also, hold a fresh copy of it so the caller can't recycle it.
- Bundle clonedBundle = null;
- if (bundle != null) {
- bundle.setClassLoader(LibInfo.class.getClassLoader());
- clonedBundle = new Bundle(LibInfo.class.getClassLoader());
- Parcel parcel = Parcel.obtain();
- bundle.writeToParcel(parcel, 0);
- parcel.setDataPosition(0);
- clonedBundle.readFromParcel(parcel);
- parcel.recycle();
- }
- if (DEBUG) {
- Log.i(TAG, "useSharedRelros() called with " + bundle
- + ", cloned " + clonedBundle);
- }
- synchronized (Linker.class) {
- // Note that in certain cases, this can be called before
- // initServiceProcess() in service processes.
- sSharedRelros = clonedBundle;
- // Tell any listener blocked in finishLibraryLoad() about it.
- Linker.class.notifyAll();
- }
- }
+ public abstract void useSharedRelros(Bundle bundle);
/**
* Call this to retrieve the shared RELRO sections created in this process,
@@ -527,33 +336,13 @@
* @return a new Bundle instance, or null if RELRO sharing is disabled on
* this system, or if initServiceProcess() was called previously.
*/
- public static Bundle getSharedRelros() {
- if (DEBUG) Log.i(TAG, "getSharedRelros() called");
- synchronized (Linker.class) {
- if (!sInBrowserProcess) {
- if (DEBUG) Log.i(TAG, "... returning null Bundle");
- return null;
- }
-
- // Return the Bundle created in finishLibraryLoad().
- if (DEBUG) Log.i(TAG, "... returning " + sSharedRelros);
- return sSharedRelros;
- }
- }
-
+ public abstract Bundle getSharedRelros();
/**
* Call this method before loading any libraries to indicate that this
* process shall neither create or reuse shared RELRO sections.
*/
- public static void disableSharedRelros() {
- if (DEBUG) Log.i(TAG, "disableSharedRelros() called");
- synchronized (Linker.class) {
- sInBrowserProcess = false;
- sWaitForSharedRelros = false;
- sBrowserUsesSharedRelro = false;
- }
- }
+ public abstract void disableSharedRelros();
/**
* Call this method before loading any libraries to indicate that this
@@ -561,22 +350,7 @@
* Typically used when starting service processes.
* @param baseLoadAddress the base library load address to use.
*/
- public static void initServiceProcess(long baseLoadAddress) {
- if (DEBUG) {
- Log.i(TAG, String.format(
- Locale.US, "initServiceProcess(0x%x) called", baseLoadAddress));
- }
- synchronized (Linker.class) {
- ensureInitializedLocked();
- sInBrowserProcess = false;
- sBrowserUsesSharedRelro = false;
- if (sRelroSharingSupported) {
- sWaitForSharedRelros = true;
- sBaseLoadAddress = baseLoadAddress;
- sCurrentLoadAddress = baseLoadAddress;
- }
- }
- }
+ public abstract void initServiceProcess(long baseLoadAddress);
/**
* Retrieve the base load address of all shared RELRO sections.
@@ -585,119 +359,7 @@
* @return a common, random base load address, or 0 if RELRO sharing is
* disabled.
*/
- public static long getBaseLoadAddress() {
- synchronized (Linker.class) {
- ensureInitializedLocked();
- if (!sInBrowserProcess) {
- Log.w(TAG, "Shared RELRO sections are disabled in this process!");
- return 0;
- }
-
- setupBaseLoadAddressLocked();
- if (DEBUG) Log.i(TAG, String.format(Locale.US, "getBaseLoadAddress() returns 0x%x",
- sBaseLoadAddress));
- return sBaseLoadAddress;
- }
- }
-
- // Used internally to lazily setup the common random base load address.
- private static void setupBaseLoadAddressLocked() {
- assert Thread.holdsLock(Linker.class);
- if (sBaseLoadAddress == 0) {
- long address = computeRandomBaseLoadAddress();
- sBaseLoadAddress = address;
- sCurrentLoadAddress = address;
- if (address == 0) {
- // If the computed address is 0, there are issues with finding enough
- // free address space, so disable RELRO shared / fixed load addresses.
- Log.w(TAG, "Disabling shared RELROs due address space pressure");
- sBrowserUsesSharedRelro = false;
- sWaitForSharedRelros = false;
- }
- }
- }
-
-
- /**
- * Compute a random base load address at which to place loaded libraries.
- * @return new base load address, or 0 if the system does not support
- * RELRO sharing.
- */
- private static long computeRandomBaseLoadAddress() {
- // nativeGetRandomBaseLoadAddress() returns an address at which it has previously
- // successfully mapped an area of the given size, on the basis that we will be
- // able, with high probability, to map our library into it.
- //
- // One issue with this is that we do not yet know the size of the library that
- // we will load is. So here we pass a value that we expect will always be larger
- // than that needed. If it is smaller the library mapping may still succeed. The
- // other issue is that although highly unlikely, there is no guarantee that
- // something else does not map into the area we are going to use between here and
- // when we try to map into it.
- //
- // The above notes mean that all of this is probablistic. It is however okay to do
- // because if, worst case and unlikely, we get unlucky in our choice of address,
- // the back-out and retry without the shared RELRO in the ChildProcessService will
- // keep things running.
- final long maxExpectedBytes = 192 * 1024 * 1024;
- final long address = nativeGetRandomBaseLoadAddress(maxExpectedBytes);
- if (DEBUG) {
- Log.i(TAG, String.format(Locale.US, "Random native base load address: 0x%x", address));
- }
- return address;
- }
-
- // Used for debugging only.
- private static void dumpBundle(Bundle bundle) {
- if (DEBUG) Log.i(TAG, "Bundle has " + bundle.size() + " items: " + bundle);
- }
-
- /**
- * Use the shared RELRO section from a Bundle received form another process.
- * Call this after calling setBaseLoadAddress() then loading all libraries
- * with loadLibrary().
- * @param bundle Bundle instance generated with createSharedRelroBundle() in
- * another process.
- */
- private static void useSharedRelrosLocked(Bundle bundle) {
- assert Thread.holdsLock(Linker.class);
-
- if (DEBUG) Log.i(TAG, "Linker.useSharedRelrosLocked() called");
-
- if (bundle == null) {
- if (DEBUG) Log.i(TAG, "null bundle!");
- return;
- }
-
- if (!sRelroSharingSupported) {
- if (DEBUG) Log.i(TAG, "System does not support RELRO sharing");
- return;
- }
-
- if (sLoadedLibraries == null) {
- if (DEBUG) Log.i(TAG, "No libraries loaded!");
- return;
- }
-
- if (DEBUG) dumpBundle(bundle);
- HashMap<String, LibInfo> relroMap = createLibInfoMapFromBundle(bundle);
-
- // Apply the RELRO section to all libraries that were already loaded.
- for (Map.Entry<String, LibInfo> entry : relroMap.entrySet()) {
- String libName = entry.getKey();
- LibInfo libInfo = entry.getValue();
- if (!nativeUseSharedRelro(libName, libInfo)) {
- Log.w(TAG, "Could not use shared RELRO section for " + libName);
- } else {
- if (DEBUG) Log.i(TAG, "Using shared RELRO section for " + libName);
- }
- }
-
- // In service processes, close all file descriptors from the map now.
- if (!sInBrowserProcess) closeLibInfoMap(relroMap);
-
- if (DEBUG) Log.i(TAG, "Linker.useSharedRelrosLocked() exiting");
- }
+ public abstract long getBaseLoadAddress();
/**
* Load a native shared library with the Chromium linker. If the zip file
@@ -709,254 +371,13 @@
* @param zipFilePath The path of the zip file containing the library (or null).
* @param libFilePath The path of the library (possibly in the zip file).
*/
- public static void loadLibrary(@Nullable String zipFilePath, String libFilePath) {
- if (DEBUG) Log.i(TAG, "loadLibrary: " + zipFilePath + ", " + libFilePath);
-
- synchronized (Linker.class) {
- ensureInitializedLocked();
-
- // Security: Ensure prepareLibraryLoad() was called before.
- // In theory, this can be done lazily here, but it's more consistent
- // to use a pair of functions (i.e. prepareLibraryLoad() + finishLibraryLoad())
- // that wrap all calls to loadLibrary() in the library loader.
- assert sPrepareLibraryLoadCalled;
-
- if (sLoadedLibraries == null) sLoadedLibraries = new HashMap<String, LibInfo>();
-
- if (sLoadedLibraries.containsKey(libFilePath)) {
- if (DEBUG) Log.i(TAG, "Not loading " + libFilePath + " twice");
- return;
- }
-
- LibInfo libInfo = new LibInfo();
- long loadAddress = 0;
- if ((sInBrowserProcess && sBrowserUsesSharedRelro) || sWaitForSharedRelros) {
- // Load the library at a fixed address.
- loadAddress = sCurrentLoadAddress;
- }
-
- String sharedRelRoName = libFilePath;
- if (zipFilePath != null) {
- if (!nativeLoadLibraryInZipFile(zipFilePath, libFilePath, loadAddress, libInfo)) {
- String errorMessage = "Unable to load library: " + libFilePath
- + ", in: " + zipFilePath;
- Log.e(TAG, errorMessage);
- throw new UnsatisfiedLinkError(errorMessage);
- }
- sharedRelRoName = zipFilePath;
- } else {
- if (!nativeLoadLibrary(libFilePath, loadAddress, libInfo)) {
- String errorMessage = "Unable to load library: " + libFilePath;
- Log.e(TAG, errorMessage);
- throw new UnsatisfiedLinkError(errorMessage);
- }
- }
-
- // Print the load address to the logcat when testing the linker. The format
- // of the string is expected by the Python test_runner script as one of:
- // BROWSER_LIBRARY_ADDRESS: <library-name> <address>
- // RENDERER_LIBRARY_ADDRESS: <library-name> <address>
- // Where <library-name> is the library name, and <address> is the hexadecimal load
- // address.
- if (NativeLibraries.sEnableLinkerTests) {
- Log.i(TAG, String.format(
- Locale.US,
- "%s_LIBRARY_ADDRESS: %s %x",
- sInBrowserProcess ? "BROWSER" : "RENDERER",
- libFilePath,
- libInfo.mLoadAddress));
- }
-
- if (sInBrowserProcess) {
- // Create a new shared RELRO section at the 'current' fixed load address.
- if (!nativeCreateSharedRelro(sharedRelRoName, sCurrentLoadAddress, libInfo)) {
- Log.w(TAG, String.format(Locale.US,
- "Could not create shared RELRO for %s at %x", libFilePath,
- sCurrentLoadAddress));
- } else {
- if (DEBUG) Log.i(TAG,
- String.format(
- Locale.US,
- "Created shared RELRO for %s at %x: %s",
- sharedRelRoName,
- sCurrentLoadAddress,
- libInfo.toString()));
- }
- }
-
- if (sCurrentLoadAddress != 0) {
- // Compute the next current load address. If sBaseLoadAddress
- // is not 0, this is an explicit library load address. Otherwise,
- // this is an explicit load address for relocated RELRO sections
- // only.
- sCurrentLoadAddress = libInfo.mLoadAddress + libInfo.mLoadSize;
- }
-
- sLoadedLibraries.put(sharedRelRoName, libInfo);
- if (DEBUG) Log.i(TAG, "Library details " + libInfo.toString());
- }
- }
+ public abstract void loadLibrary(@Nullable String zipFilePath, String libFilePath);
/**
* Determine whether a library is the linker library. Also deal with the
* component build that adds a .cr suffix to the name.
*/
- public static boolean isChromiumLinkerLibrary(String library) {
- return library.equals(TAG) || library.equals(TAG + ".cr");
- }
-
- /**
- * Get the full library path in zip file (lib/<abi>/crazy.<lib_name>).
- *
- * @param library The library's base name.
- * @return the library path.
- */
- public static String getLibraryFilePathInZipFile(String library) throws FileNotFoundException {
- synchronized (Linker.class) {
- ensureInitializedLocked();
-
- String path = nativeGetLibraryFilePathInZipFile(library);
- if (path.equals("")) {
- throw new FileNotFoundException(
- "Failed to retrieve path in zip file for library " + library);
- }
- return path;
- }
- }
-
- /**
- * Check whether a library is page aligned and uncompressed in the APK file.
- *
- * @param apkFile Filename of the APK.
- * @param library The library's base name.
- * @return true if page aligned and uncompressed.
- */
- public static boolean checkLibraryIsMappableInApk(String apkFile, String library) {
- synchronized (Linker.class) {
- ensureInitializedLocked();
-
- if (DEBUG) Log.i(TAG, "checkLibraryIsMappableInApk: " + apkFile + ", " + library);
- boolean aligned = nativeCheckLibraryIsMappableInApk(apkFile, library);
- if (DEBUG) Log.i(TAG, library + " is " + (aligned ? "" : "NOT ")
- + "page aligned in " + apkFile);
- return aligned;
- }
- }
-
- /**
- * Move activity from the native thread to the main UI thread.
- * Called from native code on its own thread. Posts a callback from
- * the UI thread back to native code.
- *
- * @param opaque Opaque argument.
- */
- @CalledByNative
- public static void postCallbackOnMainThread(final long opaque) {
- ThreadUtils.postOnUiThread(new Runnable() {
- @Override
- public void run() {
- nativeRunCallbackOnUiThread(opaque);
- }
- });
- }
-
- /**
- * Native method to run callbacks on the main UI thread.
- * Supplied by the crazy linker and called by postCallbackOnMainThread.
- * @param opaque Opaque crazy linker arguments.
- */
- private static native void nativeRunCallbackOnUiThread(long opaque);
-
- /**
- * Native method used to load a library.
- * @param library Platform specific library name (e.g. libfoo.so)
- * @param loadAddress Explicit load address, or 0 for randomized one.
- * @param libInfo If not null, the mLoadAddress and mLoadSize fields
- * of this LibInfo instance will set on success.
- * @return true for success, false otherwise.
- */
- private static native boolean nativeLoadLibrary(String library,
- long loadAddress,
- LibInfo libInfo);
-
- /**
- * Native method used to load a library which is inside a zipfile.
- * @param zipfileName Filename of the zip file containing the library.
- * @param library Platform specific library name (e.g. libfoo.so)
- * @param loadAddress Explicit load address, or 0 for randomized one.
- * @param libInfo If not null, the mLoadAddress and mLoadSize fields
- * of this LibInfo instance will set on success.
- * @return true for success, false otherwise.
- */
- private static native boolean nativeLoadLibraryInZipFile(String zipfileName,
- String libraryName,
- long loadAddress,
- LibInfo libInfo);
-
- /**
- * Native method used to create a shared RELRO section.
- * If the library was already loaded at the same address using
- * nativeLoadLibrary(), this creates the RELRO for it. Otherwise,
- * this loads a new temporary library at the specified address,
- * creates and extracts the RELRO section from it, then unloads it.
- * @param library Library name.
- * @param loadAddress load address, which can be different from the one
- * used to load the library in the current process!
- * @param libInfo libInfo instance. On success, the mRelroStart, mRelroSize
- * and mRelroFd will be set.
- * @return true on success, false otherwise.
- */
- private static native boolean nativeCreateSharedRelro(String library,
- long loadAddress,
- LibInfo libInfo);
-
- /**
- * Native method used to use a shared RELRO section.
- * @param library Library name.
- * @param libInfo A LibInfo instance containing valid RELRO information
- * @return true on success.
- */
- private static native boolean nativeUseSharedRelro(String library,
- LibInfo libInfo);
-
- /**
- * Checks that the system supports shared RELROs. Old Android kernels
- * have a bug in the way they check Ashmem region protection flags, which
- * makes using shared RELROs unsafe. This method performs a simple runtime
- * check for this misfeature, even though nativeEnableSharedRelro() will
- * always fail if this returns false.
- */
- private static native boolean nativeCanUseSharedRelro();
-
- /**
- * Return a random address that should be free to be mapped with the given size.
- * Maps an area of size bytes, and if successful then unmaps it and returns
- * the address of the area allocated by the system (with ASLR). The idea is
- * that this area should remain free of other mappings until we map our library
- * into it.
- * @param sizeBytes Size of area in bytes to search for.
- * @return address to pass to future mmap, or 0 on error.
- */
- private static native long nativeGetRandomBaseLoadAddress(long sizeBytes);
-
- /**
- * Native method used to get the full library path in zip file
- * (lib/<abi>/crazy.<lib_name>).
- *
- * @param library The library's base name.
- * @return the library path (or empty string on failure).
- */
- private static native String nativeGetLibraryFilePathInZipFile(String library);
-
- /**
- * Native method which checks whether a library is page aligned and
- * uncompressed in the APK file.
- *
- * @param apkFile Filename of the APK.
- * @param library The library's base name.
- * @return true if page aligned and uncompressed.
- */
- private static native boolean nativeCheckLibraryIsMappableInApk(String apkFile, String library);
+ public abstract boolean isChromiumLinkerLibrary(String library);
/**
* Record information for a given library.
@@ -979,7 +400,9 @@
try {
ParcelFileDescriptor.adoptFd(mRelroFd).close();
} catch (java.io.IOException e) {
- if (DEBUG) Log.e(TAG, "Failed to close fd: " + mRelroFd);
+ if (DEBUG) {
+ Log.e(TAG, "Failed to close fd: " + mRelroFd);
+ }
}
mRelroFd = -1;
}
@@ -1060,7 +483,7 @@
}
// Create a Bundle from a map of LibInfo objects.
- private static Bundle createBundleFromLibInfoMap(HashMap<String, LibInfo> map) {
+ protected Bundle createBundleFromLibInfoMap(HashMap<String, LibInfo> map) {
Bundle bundle = new Bundle(map.size());
for (Map.Entry<String, LibInfo> entry : map.entrySet()) {
bundle.putParcelable(entry.getKey(), entry.getValue());
@@ -1070,7 +493,7 @@
}
// Create a new LibInfo map from a Bundle.
- private static HashMap<String, LibInfo> createLibInfoMapFromBundle(Bundle bundle) {
+ protected HashMap<String, LibInfo> createLibInfoMapFromBundle(Bundle bundle) {
HashMap<String, LibInfo> map = new HashMap<String, LibInfo>();
for (String library : bundle.keySet()) {
LibInfo libInfo = bundle.getParcelable(library);
@@ -1080,16 +503,9 @@
}
// Call the close() method on all values of a LibInfo map.
- private static void closeLibInfoMap(HashMap<String, LibInfo> map) {
+ protected void closeLibInfoMap(HashMap<String, LibInfo> map) {
for (Map.Entry<String, LibInfo> entry : map.entrySet()) {
entry.getValue().close();
}
}
-
- // The map of libraries that are currently loaded in this process.
- private static HashMap<String, LibInfo> sLoadedLibraries = null;
-
- // Used to pass the shared RELRO Bundle through Binder.
- public static final String EXTRA_LINKER_SHARED_RELROS =
- "org.chromium.base.android.linker.shared_relros";
}
diff --git a/base/android/javatests/src/org/chromium/base/metrics/RecordHistogramTest.java b/base/android/javatests/src/org/chromium/base/metrics/RecordHistogramTest.java
index 24af056..1bf9c21 100644
--- a/base/android/javatests/src/org/chromium/base/metrics/RecordHistogramTest.java
+++ b/base/android/javatests/src/org/chromium/base/metrics/RecordHistogramTest.java
@@ -20,7 +20,8 @@
@Override
protected void setUp() throws Exception {
super.setUp();
- LibraryLoader.get(LibraryProcessType.PROCESS_BROWSER).ensureInitialized();
+ LibraryLoader.get(LibraryProcessType.PROCESS_BROWSER)
+ .ensureInitialized(getInstrumentation().getTargetContext());
RecordHistogram.initialize();
}
diff --git a/base/android/jni_generator/java/src/org/chromium/example/jni_generator/SampleForTests.java b/base/android/jni_generator/java/src/org/chromium/example/jni_generator/SampleForTests.java
index f54944b..dad59ce 100644
--- a/base/android/jni_generator/java/src/org/chromium/example/jni_generator/SampleForTests.java
+++ b/base/android/jni_generator/java/src/org/chromium/example/jni_generator/SampleForTests.java
@@ -125,7 +125,7 @@
// This is triggered by the @CalledByNative annotation; the methods may be named as you wish.
// Exported to C++ as:
- // Java_Example_javaMethod(JNIEnv* env, jobject obj, jint foo, jint bar)
+ // Java_Example_javaMethod(JNIEnv* env, jobject caller, jint foo, jint bar)
// Typically the C++ code would have obtained the jobject via the Init() call described above.
@CalledByNative
public int javaMethod(int foo, int bar) {
@@ -177,7 +177,7 @@
// signatures. Besides these constraints the methods can be freely named.
// This declares a C++ function which the application code must implement:
- // static jint Init(JNIEnv* env, jobject obj);
+ // static jint Init(JNIEnv* env, jobject caller);
// The jobject parameter refers back to this java side object instance.
// The implementation must return the pointer to the C++ object cast to jint.
// The caller of this method should store it, and supply it as a the nativeCPPClass param to
@@ -195,7 +195,7 @@
private native void nativeDestroy(long nativeCPPClass);
// This declares a C++ function which the application code must implement:
- // static jdouble GetDoubleFunction(JNIEnv* env, jobject obj);
+ // static jdouble GetDoubleFunction(JNIEnv* env, jobject caller);
// The jobject parameter refers back to this java side object instance.
private native double nativeGetDoubleFunction();
@@ -209,7 +209,7 @@
private native void nativeSetNonPODDatatype(Rect rect);
// This declares a C++ function which the application code must implement:
- // static ScopedJavaLocalRef<jobject> GetNonPODDatatype(JNIEnv* env, jobject obj);
+ // static ScopedJavaLocalRef<jobject> GetNonPODDatatype(JNIEnv* env, jobject caller);
// The jobject parameter refers back to this java side object instance.
// Note that it returns a ScopedJavaLocalRef<jobject> so that you don' have to worry about
// deleting the JNI local reference. This is similar with Strings and arrays.
diff --git a/base/android/jni_generator/jni_generator.py b/base/android/jni_generator/jni_generator.py
index d1f14ba..74d0117 100755
--- a/base/android/jni_generator/jni_generator.py
+++ b/base/android/jni_generator/jni_generator.py
@@ -236,6 +236,9 @@
'Ljava/lang/Object',
'Ljava/lang/String',
'Ljava/lang/Class',
+ 'Ljava/lang/CharSequence',
+ 'Ljava/lang/Runnable',
+ 'Ljava/lang/Throwable',
]
prefix = ''
diff --git a/base/android/jni_generator/jni_generator_tests.py b/base/android/jni_generator/jni_generator_tests.py
index 6014847..21534ef 100755
--- a/base/android/jni_generator/jni_generator_tests.py
+++ b/base/android/jni_generator/jni_generator_tests.py
@@ -1085,29 +1085,6 @@
TestOptions())
self.assertRaises(SyntaxError, willRaise)
- def testImplicitImport(self):
- test_data = """
- package org.chromium.android_webview;
-
- %(IMPORT)s
-
- @CalledByNative
- private static void clientCertificatesCleared(Runnable callback) {
- if (callbaback == null) return;
- callback.run();
- }
- """
- def generate(import_clause):
- jni_generator.JNIFromJavaSource(
- test_data % {'IMPORT': import_clause},
- 'org/chromium/android_webview/AwContentStatics',
- TestOptions())
- # Ensure it raises without the import.
- self.assertRaises(SyntaxError, lambda: generate(''))
-
- # Ensure it's fine with the import.
- generate('import java.lang.Runnable;')
-
def testSingleJNIAdditionalImport(self):
test_data = """
package org.chromium.foo;
diff --git a/base/android/jni_generator/sample_for_tests.cc b/base/android/jni_generator/sample_for_tests.cc
index 3c5ca02..8ba77c6 100644
--- a/base/android/jni_generator/sample_for_tests.cc
+++ b/base/android/jni_generator/sample_for_tests.cc
@@ -18,7 +18,7 @@
namespace base {
namespace android {
-jdouble CPPClass::InnerClass::MethodOtherP0(JNIEnv* env, jobject obj) {
+jdouble CPPClass::InnerClass::MethodOtherP0(JNIEnv* env, jobject caller) {
return 0.0;
}
@@ -33,22 +33,22 @@
return RegisterNativesImpl(env); // Generated in SampleForTests_jni.h
}
-void CPPClass::Destroy(JNIEnv* env, jobject obj) {
+void CPPClass::Destroy(JNIEnv* env, jobject caller) {
delete this;
}
-jint CPPClass::Method(JNIEnv* env, jobject obj) {
+jint CPPClass::Method(JNIEnv* env, jobject caller) {
return 0;
}
-void CPPClass::AddStructB(JNIEnv* env, jobject obj, jobject structb) {
+void CPPClass::AddStructB(JNIEnv* env, jobject caller, jobject structb) {
long key = Java_InnerStructB_getKey(env, structb);
std::string value = ConvertJavaStringToUTF8(
env, Java_InnerStructB_getValue(env, structb).obj());
map_[key] = value;
}
-void CPPClass::IterateAndDoSomethingWithStructB(JNIEnv* env, jobject obj) {
+void CPPClass::IterateAndDoSomethingWithStructB(JNIEnv* env, jobject caller) {
// Iterate over the elements and do something with them.
for (std::map<long, std::string>::const_iterator it = map_.begin();
it != map_.end(); ++it) {
@@ -59,14 +59,15 @@
}
base::android::ScopedJavaLocalRef<jstring> CPPClass::ReturnAString(
- JNIEnv* env, jobject obj) {
+ JNIEnv* env,
+ jobject caller) {
base::android::ScopedJavaLocalRef<jstring> ret = ConvertUTF8ToJavaString(
env, "test");
return ret;
}
// Static free functions declared and called directly from java.
-static jlong Init(JNIEnv* env, jobject obj, jstring param) {
+static jlong Init(JNIEnv* env, jobject caller, jstring param) {
return 0;
}
diff --git a/base/android/jni_generator/sample_for_tests.h b/base/android/jni_generator/sample_for_tests.h
index e878e56..d183c16 100644
--- a/base/android/jni_generator/sample_for_tests.h
+++ b/base/android/jni_generator/sample_for_tests.h
@@ -148,19 +148,19 @@
class InnerClass {
public:
- jdouble MethodOtherP0(JNIEnv* env, jobject obj);
+ jdouble MethodOtherP0(JNIEnv* env, jobject caller);
};
- void Destroy(JNIEnv* env, jobject obj);
+ void Destroy(JNIEnv* env, jobject caller);
- jint Method(JNIEnv* env, jobject obj);
+ jint Method(JNIEnv* env, jobject caller);
- void AddStructB(JNIEnv* env, jobject obj, jobject structb);
+ void AddStructB(JNIEnv* env, jobject caller, jobject structb);
- void IterateAndDoSomethingWithStructB(JNIEnv* env, jobject obj);
+ void IterateAndDoSomethingWithStructB(JNIEnv* env, jobject caller);
- base::android::ScopedJavaLocalRef<jstring> ReturnAString(
- JNIEnv* env, jobject obj);
+ base::android::ScopedJavaLocalRef<jstring> ReturnAString(JNIEnv* env,
+ jobject caller);
private:
std::map<long, std::string> map_;
diff --git a/base/android/junit/src/org/chromium/base/BaseChromiumApplicationTest.java b/base/android/junit/src/org/chromium/base/BaseChromiumApplicationTest.java
index d3441f7..0aa9ccd 100644
--- a/base/android/junit/src/org/chromium/base/BaseChromiumApplicationTest.java
+++ b/base/android/junit/src/org/chromium/base/BaseChromiumApplicationTest.java
@@ -8,7 +8,6 @@
import static org.mockito.Mockito.verify;
import android.app.Activity;
-import android.view.KeyEvent;
import junit.framework.Assert;
@@ -39,12 +38,6 @@
public void onWindowFocusChanged(@SuppressWarnings("unused") boolean hasFocus) {
mWindowFocusCalls++;
}
-
- @Implementation
- public boolean dispatchKeyEvent(@SuppressWarnings("unused") KeyEvent event) {
- mDispatchKeyEventCalls++;
- return mReturnValueForKeyDispatch;
- }
}
@Test
@@ -65,30 +58,4 @@
// Also ensure that the original activity is forwarded the notification.
Assert.assertEquals(1, shadow.mWindowFocusCalls);
}
-
- @Test
- public void testDispatchKeyEvent() throws Exception {
- ActivityController<Activity> controller =
- Robolectric.buildActivity(Activity.class).create().start().visible();
- TrackingShadowActivity shadow =
- (TrackingShadowActivity) Robolectric.shadowOf(controller.get());
-
- final KeyEvent menuKey = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU);
-
- // Ensure that key events are forwarded.
- Assert.assertFalse(controller.get().getWindow().getCallback().dispatchKeyEvent(menuKey));
- // This gets called twice - once to see if the activity is swallowing it, and again to
- // dispatch it.
- Assert.assertEquals(2, shadow.mDispatchKeyEventCalls);
-
- // Ensure that our activity can swallow the event.
- shadow.mReturnValueForKeyDispatch = true;
- Assert.assertTrue(controller.get().getWindow().getCallback().dispatchKeyEvent(menuKey));
- Assert.assertEquals(3, shadow.mDispatchKeyEventCalls);
-
- // A non-enter key only dispatches once.
- Assert.assertTrue(controller.get().getWindow().getCallback().dispatchKeyEvent(
- new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_SPACE)));
- Assert.assertEquals(4, shadow.mDispatchKeyEventCalls);
- }
}
diff --git a/base/android/junit/src/org/chromium/base/LogTest.java b/base/android/junit/src/org/chromium/base/LogTest.java
index 46bdc67..e5ce239 100644
--- a/base/android/junit/src/org/chromium/base/LogTest.java
+++ b/base/android/junit/src/org/chromium/base/LogTest.java
@@ -9,6 +9,7 @@
import static org.junit.Assert.assertTrue;
import org.chromium.testing.local.LocalRobolectricTestRunner;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.annotation.Config;
@@ -22,20 +23,6 @@
@RunWith(LocalRobolectricTestRunner.class)
@Config(manifest = Config.NONE, shadows = {LogTest.PermissiveShadowLog.class})
public class LogTest {
- /** Test method for {@link Log#makeTag(String)} */
- @Test
- public void testMakeTag() {
- assertEquals("cr.Foo", Log.makeTag("Foo"));
- assertEquals("cr", Log.makeTag(null));
- assertEquals("cr", Log.makeTag(""));
- }
-
- /** Test method for {@link Log#makeTag(String)} */
- @Test(expected = IllegalArgumentException.class)
- public void testMakeTagFailure() {
- Log.makeTag("ThisIs21Char.....Long");
- }
-
/** Tests that the computed call origin is the correct one. */
@Test
public void callOriginTest() {
@@ -88,12 +75,138 @@
assertEquals("Bar MyThrowable MyOtherThrowable", logs.get(logs.size() - 1).msg);
}
+ public void verboseLoggingTest() {
+ PermissiveShadowLog.setLevel(Log.VERBOSE);
+ List<ShadowLog.LogItem> logs = ShadowLog.getLogs();
+
+ Log.wtf("Foo", "Bar");
+ Log.e("Foo", "Bar");
+ Log.w("Foo", "Bar");
+ Log.i("Foo", "Bar");
+ Log.d("Foo", "Bar");
+ Log.v("Foo", "Bar");
+
+ assertEquals(Log.ASSERT, logs.get(0).type);
+ assertEquals(Log.ERROR, logs.get(1).type);
+ assertEquals(Log.WARN, logs.get(2).type);
+ assertEquals(Log.INFO, logs.get(3).type);
+ assertEquals(Log.DEBUG, logs.get(4).type);
+ assertEquals(Log.VERBOSE, logs.get(5).type);
+ assertEquals(6, logs.size());
+ }
+
+ @Test
+ public void debugLoggingTest() {
+ PermissiveShadowLog.setLevel(Log.DEBUG);
+ List<ShadowLog.LogItem> logs = ShadowLog.getLogs();
+
+ Log.wtf("Foo", "Bar");
+ Log.e("Foo", "Bar");
+ Log.w("Foo", "Bar");
+ Log.i("Foo", "Bar");
+ Log.d("Foo", "Bar");
+ Log.v("Foo", "Bar");
+
+ assertEquals(Log.ASSERT, logs.get(0).type);
+ assertEquals(Log.ERROR, logs.get(1).type);
+ assertEquals(Log.WARN, logs.get(2).type);
+ assertEquals(Log.INFO, logs.get(3).type);
+ assertEquals(Log.DEBUG, logs.get(4).type);
+ assertEquals(5, logs.size());
+ }
+
+ @Test
+ public void infoLoggingTest() {
+ PermissiveShadowLog.setLevel(Log.INFO);
+ List<ShadowLog.LogItem> logs = ShadowLog.getLogs();
+
+ Log.wtf("Foo", "Bar");
+ Log.e("Foo", "Bar");
+ Log.w("Foo", "Bar");
+ Log.i("Foo", "Bar");
+ Log.d("Foo", "Bar");
+ Log.v("Foo", "Bar");
+
+ assertEquals(Log.ASSERT, logs.get(0).type);
+ assertEquals(Log.ERROR, logs.get(1).type);
+ assertEquals(Log.WARN, logs.get(2).type);
+ assertEquals(Log.INFO, logs.get(3).type);
+ assertEquals(4, logs.size());
+ }
+
+ @Test
+ public void warnLoggingTest() {
+ PermissiveShadowLog.setLevel(Log.WARN);
+ List<ShadowLog.LogItem> logs = ShadowLog.getLogs();
+
+ Log.wtf("Foo", "Bar");
+ Log.e("Foo", "Bar");
+ Log.w("Foo", "Bar");
+ Log.i("Foo", "Bar");
+ Log.d("Foo", "Bar");
+ Log.v("Foo", "Bar");
+
+ assertEquals(Log.ASSERT, logs.get(0).type);
+ assertEquals(Log.ERROR, logs.get(1).type);
+ assertEquals(Log.WARN, logs.get(2).type);
+ assertEquals(3, logs.size());
+ }
+
+ @Test
+ public void errorLoggingTest() {
+ PermissiveShadowLog.setLevel(Log.ERROR);
+ List<ShadowLog.LogItem> logs = ShadowLog.getLogs();
+
+ Log.wtf("Foo", "Bar");
+ Log.e("Foo", "Bar");
+ Log.w("Foo", "Bar");
+ Log.i("Foo", "Bar");
+ Log.d("Foo", "Bar");
+ Log.v("Foo", "Bar");
+
+ assertEquals(Log.ASSERT, logs.get(0).type);
+ assertEquals(Log.ERROR, logs.get(1).type);
+ assertEquals(2, logs.size());
+ }
+
+ @Test
+ public void assertLoggingTest() {
+ PermissiveShadowLog.setLevel(Log.ASSERT);
+ List<ShadowLog.LogItem> logs = ShadowLog.getLogs();
+
+ Log.wtf("Foo", "Bar");
+ Log.e("Foo", "Bar");
+ Log.w("Foo", "Bar");
+ Log.i("Foo", "Bar");
+ Log.d("Foo", "Bar");
+ Log.v("Foo", "Bar");
+
+ assertEquals(Log.ASSERT, logs.get(0).type);
+ assertEquals(1, logs.size());
+ }
+
+ @Before
+ public void beforeTest() {
+ PermissiveShadowLog.reset();
+ }
+
/** Needed to allow debug/verbose logging that is disabled by default. */
@Implements(android.util.Log.class)
public static class PermissiveShadowLog extends ShadowLog {
+ private static int sLevel = Log.VERBOSE;
+
+ /** Sets the log level for all tags. */
+ public static void setLevel(int level) {
+ sLevel = level;
+ }
+
@Implementation
public static boolean isLoggable(String tag, int level) {
- return true;
+ return level >= sLevel;
+ }
+
+ public static void reset() {
+ sLevel = Log.VERBOSE;
}
}
}
diff --git a/base/android/library_loader/library_loader_hooks.cc b/base/android/library_loader/library_loader_hooks.cc
index 0b59a30..0313f70 100644
--- a/base/android/library_loader/library_loader_hooks.cc
+++ b/base/android/library_loader/library_loader_hooks.cc
@@ -48,13 +48,19 @@
RendererHistogramCode g_renderer_histogram_code = NO_PENDING_HISTOGRAM_CODE;
+// The amount of time, in milliseconds, that it took to load the shared
+// libraries in the renderer. Set in
+// RegisterChromiumAndroidLinkerRendererHistogram.
+long g_renderer_library_load_time_ms = 0;
+
} // namespace
static void RegisterChromiumAndroidLinkerRendererHistogram(
JNIEnv* env,
jobject jcaller,
jboolean requested_shared_relro,
- jboolean load_at_fixed_address_failed) {
+ jboolean load_at_fixed_address_failed,
+ jlong library_load_time_ms) {
// Note a pending histogram value for later recording.
if (requested_shared_relro) {
g_renderer_histogram_code = load_at_fixed_address_failed
@@ -62,6 +68,8 @@
} else {
g_renderer_histogram_code = LFA_NOT_ATTEMPTED;
}
+
+ g_renderer_library_load_time_ms = library_load_time_ms;
}
void RecordChromiumAndroidLinkerRendererHistogram() {
@@ -72,6 +80,11 @@
g_renderer_histogram_code,
MAX_RENDERER_HISTOGRAM_CODE);
g_renderer_histogram_code = NO_PENDING_HISTOGRAM_CODE;
+
+ // Record how long it took to load the shared libraries.
+ UMA_HISTOGRAM_TIMES(
+ "ChromiumAndroidLinker.RendererLoadTime",
+ base::TimeDelta::FromMilliseconds(g_renderer_library_load_time_ms));
}
static void RecordChromiumAndroidLinkerBrowserHistogram(
@@ -79,7 +92,8 @@
jobject jcaller,
jboolean is_using_browser_shared_relros,
jboolean load_at_fixed_address_failed,
- jint library_load_from_apk_status) {
+ jint library_load_from_apk_status,
+ jlong library_load_time_ms) {
// For low-memory devices, record whether or not we successfully loaded the
// browser at a fixed address. Otherwise just record a normal invocation.
BrowserHistogramCode histogram_code;
@@ -97,6 +111,10 @@
UMA_HISTOGRAM_ENUMERATION("ChromiumAndroidLinker.LibraryLoadFromApkStatus",
library_load_from_apk_status,
LIBRARY_LOAD_FROM_APK_STATUS_CODES_MAX);
+
+ // Record how long it took to load the shared libraries.
+ UMA_HISTOGRAM_TIMES("ChromiumAndroidLinker.BrowserLoadTime",
+ base::TimeDelta::FromMilliseconds(library_load_time_ms));
}
void SetLibraryLoadedHook(LibraryLoadedHook* func) {
diff --git a/base/android/library_loader/library_prefetcher.cc b/base/android/library_loader/library_prefetcher.cc
index 798a283..9b54843 100644
--- a/base/android/library_loader/library_prefetcher.cc
+++ b/base/android/library_loader/library_prefetcher.cc
@@ -36,7 +36,7 @@
bool PathMatchesSuffix(const std::string& path) {
for (size_t i = 0; i < arraysize(kSuffixesToMatch); i++) {
- if (EndsWith(path, kSuffixesToMatch[i], true)) {
+ if (EndsWith(path, kSuffixesToMatch[i], CompareCase::SENSITIVE)) {
return true;
}
}
@@ -82,14 +82,14 @@
std::vector<AddressRange>* ranges) {
bool has_libchrome_region = false;
for (const base::debug::MappedMemoryRegion& region : regions) {
- if (EndsWith(region.path, kLibchromeSuffix, true)) {
+ if (EndsWith(region.path, kLibchromeSuffix, CompareCase::SENSITIVE)) {
has_libchrome_region = true;
break;
}
}
for (const base::debug::MappedMemoryRegion& region : regions) {
if (has_libchrome_region &&
- !EndsWith(region.path, kLibchromeSuffix, true)) {
+ !EndsWith(region.path, kLibchromeSuffix, CompareCase::SENSITIVE)) {
continue;
}
ranges->push_back(std::make_pair(region.start, region.end));
diff --git a/base/android/linker/BUILD.gn b/base/android/linker/BUILD.gn
index 190ea47..043bfc6 100644
--- a/base/android/linker/BUILD.gn
+++ b/base/android/linker/BUILD.gn
@@ -9,7 +9,7 @@
# GYP: //base/base.gyp:chromium_android_linker
shared_library("chromium_android_linker") {
sources = [
- "linker_jni.cc",
+ "legacy_linker_jni.cc",
]
# The NDK contains the crazy_linker here:
diff --git a/base/android/linker/linker_jni.cc b/base/android/linker/legacy_linker_jni.cc
similarity index 75%
rename from base/android/linker/linker_jni.cc
rename to base/android/linker/legacy_linker_jni.cc
index 2bc480c..4612fac 100644
--- a/base/android/linker/linker_jni.cc
+++ b/base/android/linker/legacy_linker_jni.cc
@@ -22,6 +22,13 @@
#include <sys/mman.h>
#include <unistd.h>
+// See commentary in crazy_linker_elf_loader.cpp for the effect of setting
+// this. If changing there, change here also.
+//
+// For more, see:
+// https://crbug.com/504410
+#define RESERVE_BREAKPAD_GUARD_REGION 1
+
// Set this to 1 to enable debug traces to the Android log.
// Note that LOG() from "base/logging.h" cannot be used, since it is
// in base/ which hasn't been loaded yet.
@@ -104,8 +111,8 @@
LOG_ERROR("Could not find ID for field '%s'", field_name);
return false;
}
- LOG_INFO(
- "%s: Found ID %p for field '%s'", __FUNCTION__, *field_id, field_name);
+ LOG_INFO("%s: Found ID %p for field '%s'", __FUNCTION__, *field_id,
+ field_name);
return true;
}
@@ -123,8 +130,8 @@
LOG_ERROR("Could not find ID for static method '%s'", method_name);
return false;
}
- LOG_INFO("%s: Found ID %p for static method '%s'",
- __FUNCTION__, *method_id, method_name);
+ LOG_INFO("%s: Found ID %p for static method '%s'", __FUNCTION__, *method_id,
+ method_name);
return true;
}
@@ -142,9 +149,8 @@
LOG_ERROR("Could not find ID for static field '%s'", field_name);
return false;
}
- LOG_INFO(
- "%s: Found ID %p for static field '%s'",
- __FUNCTION__, *field_id, field_name);
+ LOG_INFO("%s: Found ID %p for static field '%s'", __FUNCTION__, *field_id,
+ field_name);
return true;
}
@@ -165,9 +171,8 @@
return false;
*value = env->GetStaticIntField(clazz, field_id);
- LOG_INFO(
- "%s: Found value %d for class '%s', static field '%s'",
- __FUNCTION__, *value, class_name, field_name);
+ LOG_INFO("%s: Found value %d for class '%s', static field '%s'", __FUNCTION__,
+ *value, class_name, field_name);
return true;
}
@@ -186,7 +191,7 @@
bool Init(JNIEnv* env) {
jclass clazz;
if (!InitClassReference(
- env, "org/chromium/base/library_loader/Linker$LibInfo", &clazz)) {
+ env, "org/chromium/base/library_loader/Linker$LibInfo", &clazz)) {
return false;
}
@@ -245,8 +250,8 @@
return false;
crazy_set_sdk_build_version(static_cast<int>(value));
- LOG_INFO("%s: Set SDK build version to %d",
- __FUNCTION__, static_cast<int>(value));
+ LOG_INFO("%s: Set SDK build version to %d", __FUNCTION__,
+ static_cast<int>(value));
return true;
}
@@ -298,10 +303,11 @@
namespace {
template <class LibraryOpener>
-bool GenericLoadLibrary(
- JNIEnv* env,
- const char* library_name, jlong load_address, jobject lib_info_obj,
- const LibraryOpener& opener) {
+bool GenericLoadLibrary(JNIEnv* env,
+ const char* library_name,
+ jlong load_address,
+ jobject lib_info_obj,
+ const LibraryOpener& opener) {
crazy_context_t* context = GetCrazyContext();
if (!IsValidAddress(load_address)) {
@@ -319,18 +325,16 @@
crazy_library_info_t info;
if (!crazy_library_get_info(library.Get(), context, &info)) {
- LOG_ERROR("%s: Could not get library information for %s: %s",
- __FUNCTION__,
- library_name,
- crazy_context_get_error(context));
+ LOG_ERROR("%s: Could not get library information for %s: %s", __FUNCTION__,
+ library_name, crazy_context_get_error(context));
return false;
}
// Release library object to keep it alive after the function returns.
library.Release();
- s_lib_info_fields.SetLoadInfo(
- env, lib_info_obj, info.load_address, info.load_size);
+ s_lib_info_fields.SetLoadInfo(env, lib_info_obj, info.load_address,
+ info.load_size);
LOG_INFO("%s: Success loading library %s", __FUNCTION__, library_name);
return true;
}
@@ -338,20 +342,16 @@
// Used for opening the library in a regular file.
class FileLibraryOpener {
public:
- bool Open(
- crazy_library_t** library,
- const char* library_name,
- crazy_context_t* context) const;
+ bool Open(crazy_library_t** library,
+ const char* library_name,
+ crazy_context_t* context) const;
};
-bool FileLibraryOpener::Open(
- crazy_library_t** library,
- const char* library_name,
- crazy_context_t* context) const {
+bool FileLibraryOpener::Open(crazy_library_t** library,
+ const char* library_name,
+ crazy_context_t* context) const {
if (!crazy_library_open(library, library_name, context)) {
- LOG_ERROR("%s: Could not open %s: %s",
- __FUNCTION__,
- library_name,
+ LOG_ERROR("%s: Could not open %s: %s", __FUNCTION__, library_name,
crazy_context_get_error(context));
return false;
}
@@ -362,24 +362,22 @@
class ZipLibraryOpener {
public:
explicit ZipLibraryOpener(const char* zip_file) : zip_file_(zip_file) {}
- bool Open(
- crazy_library_t** library,
- const char* library_name,
- crazy_context_t* context) const;
+ bool Open(crazy_library_t** library,
+ const char* library_name,
+ crazy_context_t* context) const;
+
private:
const char* zip_file_;
};
-bool ZipLibraryOpener::Open(
- crazy_library_t** library,
- const char* library_name,
- crazy_context_t* context) const {
- if (!crazy_library_open_in_zip_file(
- library, zip_file_, library_name, context)) {
- LOG_ERROR("%s: Could not open %s in zip file %s: %s",
- __FUNCTION__, library_name, zip_file_,
- crazy_context_get_error(context));
- return false;
+bool ZipLibraryOpener::Open(crazy_library_t** library,
+ const char* library_name,
+ crazy_context_t* context) const {
+ if (!crazy_library_open_in_zip_file(library, zip_file_, library_name,
+ context)) {
+ LOG_ERROR("%s: Could not open %s in zip file %s: %s", __FUNCTION__,
+ library_name, zip_file_, crazy_context_get_error(context));
+ return false;
}
return true;
}
@@ -406,9 +404,9 @@
jobject lib_info_obj) {
String lib_name(env, library_name);
FileLibraryOpener opener;
- return GenericLoadLibrary(
- env, lib_name.c_str(),
- static_cast<size_t>(load_address), lib_info_obj, opener);
+ return GenericLoadLibrary(env, lib_name.c_str(),
+ static_cast<size_t>(load_address), lib_info_obj,
+ opener);
}
// Load a library from a zipfile with the chromium linker. The
@@ -442,9 +440,9 @@
String zipfile_name_str(env, zipfile_name);
String lib_name(env, library_name);
ZipLibraryOpener opener(zipfile_name_str.c_str());
- return GenericLoadLibrary(
- env, lib_name.c_str(),
- static_cast<size_t>(load_address), lib_info_obj, opener);
+ return GenericLoadLibrary(env, lib_name.c_str(),
+ static_cast<size_t>(load_address), lib_info_obj,
+ opener);
}
// Class holding the Java class and method ID for the Java side Linker
@@ -456,11 +454,8 @@
// Initialize an instance.
bool Init(JNIEnv* env, jclass linker_class) {
clazz = reinterpret_cast<jclass>(env->NewGlobalRef(linker_class));
- return InitStaticMethodId(env,
- linker_class,
- "postCallbackOnMainThread",
- "(J)V",
- &method_id);
+ return InitStaticMethodId(env, linker_class, "postCallbackOnMainThread",
+ "(J)V", &method_id);
}
};
@@ -475,8 +470,8 @@
void RunCallbackOnUiThread(JNIEnv* env, jclass clazz, jlong arg) {
crazy_callback_t* callback = reinterpret_cast<crazy_callback_t*>(arg);
- LOG_INFO("%s: Called back from java with handler %p, opaque %p",
- __FUNCTION__, callback->handler, callback->opaque);
+ LOG_INFO("%s: Called back from java with handler %p, opaque %p", __FUNCTION__,
+ callback->handler, callback->opaque);
crazy_callback_run(callback);
delete callback;
@@ -496,14 +491,13 @@
JavaVM* vm;
int minimum_jni_version;
- crazy_context_get_java_vm(context,
- reinterpret_cast<void**>(&vm),
+ crazy_context_get_java_vm(context, reinterpret_cast<void**>(&vm),
&minimum_jni_version);
// Do not reuse JNIEnv from JNI_OnLoad, but retrieve our own.
JNIEnv* env;
- if (JNI_OK != vm->GetEnv(
- reinterpret_cast<void**>(&env), minimum_jni_version)) {
+ if (JNI_OK !=
+ vm->GetEnv(reinterpret_cast<void**>(&env), minimum_jni_version)) {
LOG_ERROR("Could not create JNIEnv");
return false;
}
@@ -512,13 +506,13 @@
crazy_callback_t* callback = new crazy_callback_t();
*callback = *callback_request;
- LOG_INFO("%s: Calling back to java with handler %p, opaque %p",
- __FUNCTION__, callback->handler, callback->opaque);
+ LOG_INFO("%s: Calling back to java with handler %p, opaque %p", __FUNCTION__,
+ callback->handler, callback->opaque);
jlong arg = static_cast<jlong>(reinterpret_cast<uintptr_t>(callback));
- env->CallStaticVoidMethod(
- s_java_callback_bindings.clazz, s_java_callback_bindings.method_id, arg);
+ env->CallStaticVoidMethod(s_java_callback_bindings.clazz,
+ s_java_callback_bindings.method_id, arg);
// Back out and return false if we encounter a JNI exception.
if (env->ExceptionCheck() == JNI_TRUE) {
@@ -556,21 +550,16 @@
size_t relro_size = 0;
int relro_fd = -1;
- if (!crazy_library_create_shared_relro(library.Get(),
- context,
- static_cast<size_t>(load_address),
- &relro_start,
- &relro_size,
- &relro_fd)) {
+ if (!crazy_library_create_shared_relro(
+ library.Get(), context, static_cast<size_t>(load_address),
+ &relro_start, &relro_size, &relro_fd)) {
LOG_ERROR("%s: Could not create shared RELRO sharing for %s: %s\n",
- __FUNCTION__,
- lib_name.c_str(),
- crazy_context_get_error(context));
+ __FUNCTION__, lib_name.c_str(), crazy_context_get_error(context));
return false;
}
- s_lib_info_fields.SetRelroInfo(
- env, lib_info_obj, relro_start, relro_size, relro_fd);
+ s_lib_info_fields.SetRelroInfo(env, lib_info_obj, relro_start, relro_size,
+ relro_fd);
return true;
}
@@ -580,9 +569,7 @@
jobject lib_info_obj) {
String lib_name(env, library_name);
- LOG_INFO("%s: called for %s, lib_info_ref=%p",
- __FUNCTION__,
- lib_name.c_str(),
+ LOG_INFO("%s: called for %s, lib_info_ref=%p", __FUNCTION__, lib_name.c_str(),
lib_info_obj);
ScopedLibrary library;
@@ -595,27 +582,20 @@
size_t relro_start = 0;
size_t relro_size = 0;
int relro_fd = -1;
- s_lib_info_fields.GetRelroInfo(
- env, lib_info_obj, &relro_start, &relro_size, &relro_fd);
+ s_lib_info_fields.GetRelroInfo(env, lib_info_obj, &relro_start, &relro_size,
+ &relro_fd);
- LOG_INFO("%s: library=%s relro start=%p size=%p fd=%d",
- __FUNCTION__,
- lib_name.c_str(),
- (void*)relro_start,
- (void*)relro_size,
- relro_fd);
+ LOG_INFO("%s: library=%s relro start=%p size=%p fd=%d", __FUNCTION__,
+ lib_name.c_str(), (void*)relro_start, (void*)relro_size, relro_fd);
- if (!crazy_library_use_shared_relro(
- library.Get(), context, relro_start, relro_size, relro_fd)) {
- LOG_ERROR("%s: Could not use shared RELRO for %s: %s",
- __FUNCTION__,
- lib_name.c_str(),
- crazy_context_get_error(context));
+ if (!crazy_library_use_shared_relro(library.Get(), context, relro_start,
+ relro_size, relro_fd)) {
+ LOG_ERROR("%s: Could not use shared RELRO for %s: %s", __FUNCTION__,
+ lib_name.c_str(), crazy_context_get_error(context));
return false;
}
- LOG_INFO("%s: Library %s using shared RELRO section!",
- __FUNCTION__,
+ LOG_INFO("%s: Library %s using shared RELRO section!", __FUNCTION__,
lib_name.c_str());
return true;
@@ -626,6 +606,13 @@
}
jlong GetRandomBaseLoadAddress(JNIEnv* env, jclass clazz, jlong bytes) {
+#if RESERVE_BREAKPAD_GUARD_REGION
+ // Add a Breakpad guard region. 16Mb should be comfortably larger than
+ // the largest relocation packer saving we expect to encounter.
+ static const size_t kBreakpadGuardRegionBytes = 16 * 1024 * 1024;
+ bytes += kBreakpadGuardRegionBytes;
+#endif
+
void* address =
mmap(NULL, bytes, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (address == MAP_FAILED) {
@@ -633,56 +620,17 @@
return 0;
}
munmap(address, bytes);
+
+#if RESERVE_BREAKPAD_GUARD_REGION
+ // Allow for a Breakpad guard region ahead of the returned address.
+ address = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(address) +
+ kBreakpadGuardRegionBytes);
+#endif
+
LOG_INFO("%s: Random base load address is %p\n", __FUNCTION__, address);
return static_cast<jlong>(reinterpret_cast<uintptr_t>(address));
}
-// Get the full path of a library in the zip file
-// (lib/<abi>/crazy.<lib_name>).
-//
-// |env| is the current JNI environment handle.
-// |clazz| is the static class handle which is not used here.
-// |lib_name| is the library base name.
-// Returns the full path (or empty string on failure).
-jstring GetLibraryFilePathInZipFile(JNIEnv* env,
- jclass clazz,
- jstring lib_name) {
- String lib_name_str(env, lib_name);
- const char* lib_name_c_str = lib_name_str.c_str();
- char buffer[kMaxFilePathLengthInZip + 1];
- if (crazy_library_file_path_in_zip_file(
- lib_name_c_str, buffer, sizeof(buffer)) == CRAZY_STATUS_FAILURE) {
- LOG_ERROR("%s: Failed to get full filename for library '%s'",
- __FUNCTION__, lib_name_c_str);
- buffer[0] = '\0';
- }
- return env->NewStringUTF(buffer);
-}
-
-// Check whether a library is page aligned and uncompressed in the APK file.
-//
-// |env| is the current JNI environment handle.
-// |clazz| is the static class handle which is not used here.
-// |apkfile_name| is the filename of the APK.
-// |library_name| is the library base name.
-// Returns true if page aligned and uncompressed.
-jboolean CheckLibraryIsMappableInApk(JNIEnv* env, jclass clazz,
- jstring apkfile_name,
- jstring library_name) {
- String apkfile_name_str(env, apkfile_name);
- const char* apkfile_name_c_str = apkfile_name_str.c_str();
- String library_name_str(env, library_name);
- const char* library_name_c_str = library_name_str.c_str();
-
- LOG_INFO("%s: Checking if %s is page-aligned and uncompressed in %s\n",
- __FUNCTION__, library_name_c_str, apkfile_name_c_str);
- jboolean mappable = crazy_linker_check_library_is_mappable_in_zip_file(
- apkfile_name_c_str, library_name_c_str) == CRAZY_STATUS_SUCCESS;
- LOG_INFO("%s: %s\n", __FUNCTION__, mappable ? "Mappable" : "NOT mappable");
-
- return mappable;
-}
-
const JNINativeMethod kNativeMethods[] = {
{"nativeLoadLibrary",
"("
@@ -733,19 +681,7 @@
")"
"J",
reinterpret_cast<void*>(&GetRandomBaseLoadAddress)},
- {"nativeGetLibraryFilePathInZipFile",
- "("
- "Ljava/lang/String;"
- ")"
- "Ljava/lang/String;",
- reinterpret_cast<void*>(&GetLibraryFilePathInZipFile)},
- {"nativeCheckLibraryIsMappableInApk",
- "("
- "Ljava/lang/String;"
- "Ljava/lang/String;"
- ")"
- "Z",
- reinterpret_cast<void*>(&CheckLibraryIsMappableInApk)}, };
+};
} // namespace
@@ -768,14 +704,12 @@
// Register native methods.
jclass linker_class;
- if (!InitClassReference(env,
- "org/chromium/base/library_loader/Linker",
+ if (!InitClassReference(env, "org/chromium/base/library_loader/LegacyLinker",
&linker_class))
return -1;
LOG_INFO("%s: Registering native methods", __FUNCTION__);
- env->RegisterNatives(linker_class,
- kNativeMethods,
+ env->RegisterNatives(linker_class, kNativeMethods,
sizeof(kNativeMethods) / sizeof(kNativeMethods[0]));
// Find LibInfo field ids.
diff --git a/base/android/path_utils.cc b/base/android/path_utils.cc
index c98007c..caad53a 100644
--- a/base/android/path_utils.cc
+++ b/base/android/path_utils.cc
@@ -41,6 +41,16 @@
return true;
}
+bool GetThumbnailCacheDirectory(FilePath* result) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jstring> path =
+ Java_PathUtils_getThumbnailCacheDirectoryPath(env,
+ GetApplicationContext());
+ FilePath thumbnail_cache_path(ConvertJavaStringToUTF8(path));
+ *result = thumbnail_cache_path;
+ return true;
+}
+
bool GetDownloadsDirectory(FilePath* result) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jstring> path =
diff --git a/base/android/path_utils.h b/base/android/path_utils.h
index d3421b3..6501f1b 100644
--- a/base/android/path_utils.h
+++ b/base/android/path_utils.h
@@ -31,6 +31,10 @@
// cache dir.
BASE_EXPORT bool GetCacheDirectory(FilePath* result);
+// Retrieves the path to the thumbnail cache directory. The result is placed
+// in the FilePath pointed to by 'result'.
+BASE_EXPORT bool GetThumbnailCacheDirectory(FilePath* result);
+
// Retrieves the path to the public downloads directory. The result is placed
// in the FilePath pointed to by 'result'.
BASE_EXPORT bool GetDownloadsDirectory(FilePath* result);
diff --git a/base/android/trace_event_binding.cc b/base/android/trace_event_binding.cc
index 791b67f..3c5ee17 100644
--- a/base/android/trace_event_binding.cc
+++ b/base/android/trace_event_binding.cc
@@ -8,6 +8,7 @@
#include <set>
+#include "base/android/jni_string.h"
#include "base/lazy_instance.h"
#include "base/trace_event/trace_event.h"
#include "base/trace_event/trace_event_impl.h"
@@ -25,32 +26,28 @@
// Boilerplate for safely converting Java data to TRACE_EVENT data.
class TraceEventDataConverter {
public:
- TraceEventDataConverter(JNIEnv* env,
- jstring jname,
- jstring jarg)
+ TraceEventDataConverter(JNIEnv* env, jstring jname, jstring jarg)
: env_(env),
jname_(jname),
jarg_(jarg),
- name_(env->GetStringUTFChars(jname, NULL)),
- arg_(jarg ? env->GetStringUTFChars(jarg, NULL) : NULL) {
- }
+ name_(ConvertJavaStringToUTF8(env, jname)),
+ has_arg_(jarg != nullptr),
+ arg_(jarg ? ConvertJavaStringToUTF8(env, jarg) : "") {}
~TraceEventDataConverter() {
- env_->ReleaseStringUTFChars(jname_, name_);
- if (jarg_)
- env_->ReleaseStringUTFChars(jarg_, arg_);
}
// Return saves values to pass to TRACE_EVENT macros.
- const char* name() { return name_; }
- const char* arg_name() { return arg_ ? "arg" : NULL; }
- const char* arg() { return arg_; }
+ const char* name() { return name_.c_str(); }
+ const char* arg_name() { return has_arg_ ? "arg" : nullptr; }
+ const char* arg() { return has_arg_ ? arg_.c_str() : nullptr; }
private:
JNIEnv* env_;
jstring jname_;
jstring jarg_;
- const char* name_;
- const char* arg_;
+ std::string name_;
+ bool has_arg_;
+ std::string arg_;
DISALLOW_COPY_AND_ASSIGN(TraceEventDataConverter);
};
diff --git a/base/atomicops.h b/base/atomicops.h
index 6a5371c..f983b45 100644
--- a/base/atomicops.h
+++ b/base/atomicops.h
@@ -144,33 +144,6 @@
} // namespace subtle
} // namespace base
-// The following x86 CPU features are used in atomicops_internals_x86_gcc.h, but
-// this file is duplicated inside of Chrome: protobuf and tcmalloc rely on the
-// struct being present at link time. Some parts of Chrome can currently use the
-// portable interface whereas others still use GCC one. The include guards are
-// the same as in atomicops_internals_x86_gcc.cc.
-#if defined(__i386__) || defined(__x86_64__)
-// This struct is not part of the public API of this module; clients may not
-// use it. (However, it's exported via BASE_EXPORT because clients implicitly
-// do use it at link time by inlining these functions.)
-// Features of this x86. Values may not be correct before main() is run,
-// but are set conservatively.
-struct AtomicOps_x86CPUFeatureStruct {
- bool has_amd_lock_mb_bug; // Processor has AMD memory-barrier bug; do lfence
- // after acquire compare-and-swap.
- // The following fields are unused by Chrome's base implementation but are
- // still used by copies of the same code in other parts of the code base. This
- // causes an ODR violation, and the other code is likely reading invalid
- // memory.
- // TODO(jfb) Delete these fields once the rest of the Chrome code base doesn't
- // depend on them.
- bool has_sse2; // Processor has SSE2.
- bool has_cmpxchg16b; // Processor supports cmpxchg16b instruction.
-};
-BASE_EXPORT extern struct AtomicOps_x86CPUFeatureStruct
- AtomicOps_Internalx86CPUFeatures;
-#endif
-
// Try to use a portable implementation based on C++11 atomics.
//
// Some toolchains support C++11 language features without supporting library
@@ -188,17 +161,6 @@
# include "base/atomicops_internals_x86_msvc.h"
# elif defined(OS_MACOSX)
# include "base/atomicops_internals_mac.h"
-# elif defined(OS_NACL)
-# include "base/atomicops_internals_gcc.h"
-# elif defined(COMPILER_GCC) && defined(ARCH_CPU_ARMEL)
-# include "base/atomicops_internals_arm_gcc.h"
-# elif defined(COMPILER_GCC) && defined(ARCH_CPU_ARM64)
-# include "base/atomicops_internals_arm64_gcc.h"
-# elif defined(COMPILER_GCC) && defined(ARCH_CPU_X86_FAMILY)
-# include "base/atomicops_internals_x86_gcc.h"
-# elif (defined(COMPILER_GCC) && \
- (defined(ARCH_CPU_MIPS_FAMILY) || defined(ARCH_CPU_MIPS64_FAMILY)))
-# include "base/atomicops_internals_mips_gcc.h"
# else
# error "Atomic operations are not supported on your platform"
# endif
diff --git a/base/atomicops_internals_arm64_gcc.h b/base/atomicops_internals_arm64_gcc.h
deleted file mode 100644
index ddcfec9..0000000
--- a/base/atomicops_internals_arm64_gcc.h
+++ /dev/null
@@ -1,307 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// This file is an internal atomic implementation, use base/atomicops.h instead.
-
-// TODO(rmcilroy): Investigate whether we can use __sync__ intrinsics instead of
-// the hand coded assembly without introducing perf regressions.
-// TODO(rmcilroy): Investigate whether we can use acquire / release versions of
-// exclusive load / store assembly instructions and do away with
-// the barriers.
-
-#ifndef BASE_ATOMICOPS_INTERNALS_ARM64_GCC_H_
-#define BASE_ATOMICOPS_INTERNALS_ARM64_GCC_H_
-
-#if defined(OS_QNX)
-#include <sys/cpuinline.h>
-#endif
-
-namespace base {
-namespace subtle {
-
-inline void MemoryBarrier() {
- __asm__ __volatile__ ("dmb ish" ::: "memory"); // NOLINT
-}
-
-// NoBarrier versions of the operation include "memory" in the clobber list.
-// This is not required for direct usage of the NoBarrier versions of the
-// operations. However this is required for correctness when they are used as
-// part of the Acquire or Release versions, to ensure that nothing from outside
-// the call is reordered between the operation and the memory barrier. This does
-// not change the code generated, so has no or minimal impact on the
-// NoBarrier operations.
-
-inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
- Atomic32 old_value,
- Atomic32 new_value) {
- Atomic32 prev;
- int32_t temp;
-
- __asm__ __volatile__ ( // NOLINT
- "0: \n\t"
- "ldxr %w[prev], %[ptr] \n\t" // Load the previous value.
- "cmp %w[prev], %w[old_value] \n\t"
- "bne 1f \n\t"
- "stxr %w[temp], %w[new_value], %[ptr] \n\t" // Try to store the new value.
- "cbnz %w[temp], 0b \n\t" // Retry if it did not work.
- "1: \n\t"
- : [prev]"=&r" (prev),
- [temp]"=&r" (temp),
- [ptr]"+Q" (*ptr)
- : [old_value]"IJr" (old_value),
- [new_value]"r" (new_value)
- : "cc", "memory"
- ); // NOLINT
-
- return prev;
-}
-
-inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
- Atomic32 new_value) {
- Atomic32 result;
- int32_t temp;
-
- __asm__ __volatile__ ( // NOLINT
- "0: \n\t"
- "ldxr %w[result], %[ptr] \n\t" // Load the previous value.
- "stxr %w[temp], %w[new_value], %[ptr] \n\t" // Try to store the new value.
- "cbnz %w[temp], 0b \n\t" // Retry if it did not work.
- : [result]"=&r" (result),
- [temp]"=&r" (temp),
- [ptr]"+Q" (*ptr)
- : [new_value]"r" (new_value)
- : "memory"
- ); // NOLINT
-
- return result;
-}
-
-inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
- Atomic32 increment) {
- Atomic32 result;
- int32_t temp;
-
- __asm__ __volatile__ ( // NOLINT
- "0: \n\t"
- "ldxr %w[result], %[ptr] \n\t" // Load the previous value.
- "add %w[result], %w[result], %w[increment]\n\t"
- "stxr %w[temp], %w[result], %[ptr] \n\t" // Try to store the result.
- "cbnz %w[temp], 0b \n\t" // Retry on failure.
- : [result]"=&r" (result),
- [temp]"=&r" (temp),
- [ptr]"+Q" (*ptr)
- : [increment]"IJr" (increment)
- : "memory"
- ); // NOLINT
-
- return result;
-}
-
-inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
- Atomic32 increment) {
- MemoryBarrier();
- Atomic32 result = NoBarrier_AtomicIncrement(ptr, increment);
- MemoryBarrier();
-
- return result;
-}
-
-inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
- Atomic32 old_value,
- Atomic32 new_value) {
- Atomic32 prev = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
- MemoryBarrier();
-
- return prev;
-}
-
-inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
- Atomic32 old_value,
- Atomic32 new_value) {
- MemoryBarrier();
- Atomic32 prev = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
-
- return prev;
-}
-
-inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
- *ptr = value;
-}
-
-inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
- *ptr = value;
- MemoryBarrier();
-}
-
-inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
- __asm__ __volatile__ ( // NOLINT
- "stlr %w[value], %[ptr] \n\t"
- : [ptr]"=Q" (*ptr)
- : [value]"r" (value)
- : "memory"
- ); // NOLINT
-}
-
-inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
- return *ptr;
-}
-
-inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
- Atomic32 value;
-
- __asm__ __volatile__ ( // NOLINT
- "ldar %w[value], %[ptr] \n\t"
- : [value]"=r" (value)
- : [ptr]"Q" (*ptr)
- : "memory"
- ); // NOLINT
-
- return value;
-}
-
-inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
- MemoryBarrier();
- return *ptr;
-}
-
-// 64-bit versions of the operations.
-// See the 32-bit versions for comments.
-
-inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
- Atomic64 old_value,
- Atomic64 new_value) {
- Atomic64 prev;
- int32_t temp;
-
- __asm__ __volatile__ ( // NOLINT
- "0: \n\t"
- "ldxr %[prev], %[ptr] \n\t"
- "cmp %[prev], %[old_value] \n\t"
- "bne 1f \n\t"
- "stxr %w[temp], %[new_value], %[ptr] \n\t"
- "cbnz %w[temp], 0b \n\t"
- "1: \n\t"
- : [prev]"=&r" (prev),
- [temp]"=&r" (temp),
- [ptr]"+Q" (*ptr)
- : [old_value]"IJr" (old_value),
- [new_value]"r" (new_value)
- : "cc", "memory"
- ); // NOLINT
-
- return prev;
-}
-
-inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
- Atomic64 new_value) {
- Atomic64 result;
- int32_t temp;
-
- __asm__ __volatile__ ( // NOLINT
- "0: \n\t"
- "ldxr %[result], %[ptr] \n\t"
- "stxr %w[temp], %[new_value], %[ptr] \n\t"
- "cbnz %w[temp], 0b \n\t"
- : [result]"=&r" (result),
- [temp]"=&r" (temp),
- [ptr]"+Q" (*ptr)
- : [new_value]"r" (new_value)
- : "memory"
- ); // NOLINT
-
- return result;
-}
-
-inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr,
- Atomic64 increment) {
- Atomic64 result;
- int32_t temp;
-
- __asm__ __volatile__ ( // NOLINT
- "0: \n\t"
- "ldxr %[result], %[ptr] \n\t"
- "add %[result], %[result], %[increment] \n\t"
- "stxr %w[temp], %[result], %[ptr] \n\t"
- "cbnz %w[temp], 0b \n\t"
- : [result]"=&r" (result),
- [temp]"=&r" (temp),
- [ptr]"+Q" (*ptr)
- : [increment]"IJr" (increment)
- : "memory"
- ); // NOLINT
-
- return result;
-}
-
-inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr,
- Atomic64 increment) {
- MemoryBarrier();
- Atomic64 result = NoBarrier_AtomicIncrement(ptr, increment);
- MemoryBarrier();
-
- return result;
-}
-
-inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
- Atomic64 old_value,
- Atomic64 new_value) {
- Atomic64 prev = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
- MemoryBarrier();
-
- return prev;
-}
-
-inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
- Atomic64 old_value,
- Atomic64 new_value) {
- MemoryBarrier();
- Atomic64 prev = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
-
- return prev;
-}
-
-inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
- *ptr = value;
-}
-
-inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
- *ptr = value;
- MemoryBarrier();
-}
-
-inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
- __asm__ __volatile__ ( // NOLINT
- "stlr %x[value], %[ptr] \n\t"
- : [ptr]"=Q" (*ptr)
- : [value]"r" (value)
- : "memory"
- ); // NOLINT
-}
-
-inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
- return *ptr;
-}
-
-inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
- Atomic64 value;
-
- __asm__ __volatile__ ( // NOLINT
- "ldar %x[value], %[ptr] \n\t"
- : [value]"=r" (value)
- : [ptr]"Q" (*ptr)
- : "memory"
- ); // NOLINT
-
- return value;
-}
-
-inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
- MemoryBarrier();
- return *ptr;
-}
-
-} // namespace subtle
-} // namespace base
-
-#endif // BASE_ATOMICOPS_INTERNALS_ARM64_GCC_H_
diff --git a/base/atomicops_internals_arm_gcc.h b/base/atomicops_internals_arm_gcc.h
deleted file mode 100644
index 44c91c8..0000000
--- a/base/atomicops_internals_arm_gcc.h
+++ /dev/null
@@ -1,294 +0,0 @@
-// Copyright 2013 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.
-
-// This file is an internal atomic implementation, use base/atomicops.h instead.
-//
-// LinuxKernelCmpxchg and Barrier_AtomicIncrement are from Google Gears.
-
-#ifndef BASE_ATOMICOPS_INTERNALS_ARM_GCC_H_
-#define BASE_ATOMICOPS_INTERNALS_ARM_GCC_H_
-
-#if defined(OS_QNX)
-#include <sys/cpuinline.h>
-#endif
-
-namespace base {
-namespace subtle {
-
-// Memory barriers on ARM are funky, but the kernel is here to help:
-//
-// * ARMv5 didn't support SMP, there is no memory barrier instruction at
-// all on this architecture, or when targeting its machine code.
-//
-// * Some ARMv6 CPUs support SMP. A full memory barrier can be produced by
-// writing a random value to a very specific coprocessor register.
-//
-// * On ARMv7, the "dmb" instruction is used to perform a full memory
-// barrier (though writing to the co-processor will still work).
-// However, on single core devices (e.g. Nexus One, or Nexus S),
-// this instruction will take up to 200 ns, which is huge, even though
-// it's completely un-needed on these devices.
-//
-// * There is no easy way to determine at runtime if the device is
-// single or multi-core. However, the kernel provides a useful helper
-// function at a fixed memory address (0xffff0fa0), which will always
-// perform a memory barrier in the most efficient way. I.e. on single
-// core devices, this is an empty function that exits immediately.
-// On multi-core devices, it implements a full memory barrier.
-//
-// * This source could be compiled to ARMv5 machine code that runs on a
-// multi-core ARMv6 or ARMv7 device. In this case, memory barriers
-// are needed for correct execution. Always call the kernel helper, even
-// when targeting ARMv5TE.
-//
-
-inline void MemoryBarrier() {
-#if defined(OS_LINUX) || defined(OS_ANDROID)
- // Note: This is a function call, which is also an implicit compiler barrier.
- typedef void (*KernelMemoryBarrierFunc)();
- ((KernelMemoryBarrierFunc)0xffff0fa0)();
-#elif defined(OS_QNX)
- __cpu_membarrier();
-#else
-#error MemoryBarrier() is not implemented on this platform.
-#endif
-}
-
-// An ARM toolchain would only define one of these depending on which
-// variant of the target architecture is being used. This tests against
-// any known ARMv6 or ARMv7 variant, where it is possible to directly
-// use ldrex/strex instructions to implement fast atomic operations.
-#if defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || \
- defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || \
- defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || \
- defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || \
- defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__)
-
-inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
- Atomic32 old_value,
- Atomic32 new_value) {
- Atomic32 prev_value;
- int reloop;
- do {
- // The following is equivalent to:
- //
- // prev_value = LDREX(ptr)
- // reloop = 0
- // if (prev_value != old_value)
- // reloop = STREX(ptr, new_value)
- __asm__ __volatile__(" ldrex %0, [%3]\n"
- " mov %1, #0\n"
- " cmp %0, %4\n"
-#ifdef __thumb2__
- " it eq\n"
-#endif
- " strexeq %1, %5, [%3]\n"
- : "=&r"(prev_value), "=&r"(reloop), "+m"(*ptr)
- : "r"(ptr), "r"(old_value), "r"(new_value)
- : "cc", "memory");
- } while (reloop != 0);
- return prev_value;
-}
-
-inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
- Atomic32 old_value,
- Atomic32 new_value) {
- Atomic32 result = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
- MemoryBarrier();
- return result;
-}
-
-inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
- Atomic32 old_value,
- Atomic32 new_value) {
- MemoryBarrier();
- return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
-}
-
-inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
- Atomic32 increment) {
- Atomic32 value;
- int reloop;
- do {
- // Equivalent to:
- //
- // value = LDREX(ptr)
- // value += increment
- // reloop = STREX(ptr, value)
- //
- __asm__ __volatile__(" ldrex %0, [%3]\n"
- " add %0, %0, %4\n"
- " strex %1, %0, [%3]\n"
- : "=&r"(value), "=&r"(reloop), "+m"(*ptr)
- : "r"(ptr), "r"(increment)
- : "cc", "memory");
- } while (reloop);
- return value;
-}
-
-inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
- Atomic32 increment) {
- // TODO(digit): Investigate if it's possible to implement this with
- // a single MemoryBarrier() operation between the LDREX and STREX.
- // See http://crbug.com/246514
- MemoryBarrier();
- Atomic32 result = NoBarrier_AtomicIncrement(ptr, increment);
- MemoryBarrier();
- return result;
-}
-
-inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
- Atomic32 new_value) {
- Atomic32 old_value;
- int reloop;
- do {
- // old_value = LDREX(ptr)
- // reloop = STREX(ptr, new_value)
- __asm__ __volatile__(" ldrex %0, [%3]\n"
- " strex %1, %4, [%3]\n"
- : "=&r"(old_value), "=&r"(reloop), "+m"(*ptr)
- : "r"(ptr), "r"(new_value)
- : "cc", "memory");
- } while (reloop != 0);
- return old_value;
-}
-
-// This tests against any known ARMv5 variant.
-#elif defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5T__) || \
- defined(__ARM_ARCH_5TE__) || defined(__ARM_ARCH_5TEJ__)
-
-// The kernel also provides a helper function to perform an atomic
-// compare-and-swap operation at the hard-wired address 0xffff0fc0.
-// On ARMv5, this is implemented by a special code path that the kernel
-// detects and treats specially when thread pre-emption happens.
-// On ARMv6 and higher, it uses LDREX/STREX instructions instead.
-//
-// Note that this always perform a full memory barrier, there is no
-// need to add calls MemoryBarrier() before or after it. It also
-// returns 0 on success, and 1 on exit.
-//
-// Available and reliable since Linux 2.6.24. Both Android and ChromeOS
-// use newer kernel revisions, so this should not be a concern.
-namespace {
-
-inline int LinuxKernelCmpxchg(Atomic32 old_value,
- Atomic32 new_value,
- volatile Atomic32* ptr) {
- typedef int (*KernelCmpxchgFunc)(Atomic32, Atomic32, volatile Atomic32*);
- return ((KernelCmpxchgFunc)0xffff0fc0)(old_value, new_value, ptr);
-}
-
-} // namespace
-
-inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
- Atomic32 old_value,
- Atomic32 new_value) {
- Atomic32 prev_value;
- for (;;) {
- prev_value = *ptr;
- if (prev_value != old_value)
- return prev_value;
- if (!LinuxKernelCmpxchg(old_value, new_value, ptr))
- return old_value;
- }
-}
-
-inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
- Atomic32 new_value) {
- Atomic32 old_value;
- do {
- old_value = *ptr;
- } while (LinuxKernelCmpxchg(old_value, new_value, ptr));
- return old_value;
-}
-
-inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
- Atomic32 increment) {
- return Barrier_AtomicIncrement(ptr, increment);
-}
-
-inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
- Atomic32 increment) {
- for (;;) {
- // Atomic exchange the old value with an incremented one.
- Atomic32 old_value = *ptr;
- Atomic32 new_value = old_value + increment;
- if (!LinuxKernelCmpxchg(old_value, new_value, ptr)) {
- // The exchange took place as expected.
- return new_value;
- }
- // Otherwise, *ptr changed mid-loop and we need to retry.
- }
-}
-
-inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
- Atomic32 old_value,
- Atomic32 new_value) {
- Atomic32 prev_value;
- for (;;) {
- prev_value = *ptr;
- if (prev_value != old_value) {
- // Always ensure acquire semantics.
- MemoryBarrier();
- return prev_value;
- }
- if (!LinuxKernelCmpxchg(old_value, new_value, ptr))
- return old_value;
- }
-}
-
-inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
- Atomic32 old_value,
- Atomic32 new_value) {
- // This could be implemented as:
- // MemoryBarrier();
- // return NoBarrier_CompareAndSwap();
- //
- // But would use 3 barriers per succesful CAS. To save performance,
- // use Acquire_CompareAndSwap(). Its implementation guarantees that:
- // - A succesful swap uses only 2 barriers (in the kernel helper).
- // - An early return due to (prev_value != old_value) performs
- // a memory barrier with no store, which is equivalent to the
- // generic implementation above.
- return Acquire_CompareAndSwap(ptr, old_value, new_value);
-}
-
-#else
-# error "Your CPU's ARM architecture is not supported yet"
-#endif
-
-// NOTE: Atomicity of the following load and store operations is only
-// guaranteed in case of 32-bit alignement of |ptr| values.
-
-inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
- *ptr = value;
-}
-
-inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
- *ptr = value;
- MemoryBarrier();
-}
-
-inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
- MemoryBarrier();
- *ptr = value;
-}
-
-inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) { return *ptr; }
-
-inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
- Atomic32 value = *ptr;
- MemoryBarrier();
- return value;
-}
-
-inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
- MemoryBarrier();
- return *ptr;
-}
-
-} // namespace subtle
-} // namespace base
-
-#endif // BASE_ATOMICOPS_INTERNALS_ARM_GCC_H_
diff --git a/base/atomicops_internals_gcc.h b/base/atomicops_internals_gcc.h
deleted file mode 100644
index 35c95fe..0000000
--- a/base/atomicops_internals_gcc.h
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// This file is an internal atomic implementation, include base/atomicops.h
-// instead. This file is for platforms that use GCC intrinsics rather than
-// platform-specific assembly code for atomic operations.
-
-#ifndef BASE_ATOMICOPS_INTERNALS_GCC_H_
-#define BASE_ATOMICOPS_INTERNALS_GCC_H_
-
-namespace base {
-namespace subtle {
-
-inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
- Atomic32 old_value,
- Atomic32 new_value) {
- Atomic32 prev_value;
- do {
- if (__sync_bool_compare_and_swap(ptr, old_value, new_value))
- return old_value;
- prev_value = *ptr;
- } while (prev_value == old_value);
- return prev_value;
-}
-
-inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
- Atomic32 new_value) {
- Atomic32 old_value;
- do {
- old_value = *ptr;
- } while (!__sync_bool_compare_and_swap(ptr, old_value, new_value));
- return old_value;
-}
-
-inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
- Atomic32 increment) {
- return Barrier_AtomicIncrement(ptr, increment);
-}
-
-inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
- Atomic32 increment) {
- for (;;) {
- // Atomic exchange the old value with an incremented one.
- Atomic32 old_value = *ptr;
- Atomic32 new_value = old_value + increment;
- if (__sync_bool_compare_and_swap(ptr, old_value, new_value)) {
- // The exchange took place as expected.
- return new_value;
- }
- // Otherwise, *ptr changed mid-loop and we need to retry.
- }
-}
-
-inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
- Atomic32 old_value,
- Atomic32 new_value) {
- // Since NoBarrier_CompareAndSwap uses __sync_bool_compare_and_swap, which
- // is a full memory barrier, none is needed here or below in Release.
- return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
-}
-
-inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
- Atomic32 old_value,
- Atomic32 new_value) {
- return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
-}
-
-inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
- *ptr = value;
-}
-
-inline void MemoryBarrier() {
- __sync_synchronize();
-}
-
-inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
- *ptr = value;
- MemoryBarrier();
-}
-
-inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
- MemoryBarrier();
- *ptr = value;
-}
-
-inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
- return *ptr;
-}
-
-inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
- Atomic32 value = *ptr;
- MemoryBarrier();
- return value;
-}
-
-inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
- MemoryBarrier();
- return *ptr;
-}
-
-} // namespace subtle
-} // namespace base
-
-#endif // BASE_ATOMICOPS_INTERNALS_GCC_H_
-
diff --git a/base/atomicops_internals_mips_gcc.h b/base/atomicops_internals_mips_gcc.h
deleted file mode 100644
index b4551b8..0000000
--- a/base/atomicops_internals_mips_gcc.h
+++ /dev/null
@@ -1,280 +0,0 @@
-// Copyright (c) 2012 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.
-
-// This file is an internal atomic implementation, use base/atomicops.h instead.
-//
-// LinuxKernelCmpxchg and Barrier_AtomicIncrement are from Google Gears.
-
-#ifndef BASE_ATOMICOPS_INTERNALS_MIPS_GCC_H_
-#define BASE_ATOMICOPS_INTERNALS_MIPS_GCC_H_
-
-namespace base {
-namespace subtle {
-
-// Atomically execute:
-// result = *ptr;
-// if (*ptr == old_value)
-// *ptr = new_value;
-// return result;
-//
-// I.e., replace "*ptr" with "new_value" if "*ptr" used to be "old_value".
-// Always return the old value of "*ptr"
-//
-// This routine implies no memory barriers.
-inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
- Atomic32 old_value,
- Atomic32 new_value) {
- Atomic32 prev, tmp;
- __asm__ __volatile__(".set push\n"
- ".set noreorder\n"
- "1:\n"
- "ll %0, %5\n" // prev = *ptr
- "bne %0, %3, 2f\n" // if (prev != old_value) goto 2
- "move %2, %4\n" // tmp = new_value
- "sc %2, %1\n" // *ptr = tmp (with atomic check)
- "beqz %2, 1b\n" // start again on atomic error
- "nop\n" // delay slot nop
- "2:\n"
- ".set pop\n"
- : "=&r" (prev), "=m" (*ptr), "=&r" (tmp)
- : "r" (old_value), "r" (new_value), "m" (*ptr)
- : "memory");
- return prev;
-}
-
-// Atomically store new_value into *ptr, returning the previous value held in
-// *ptr. This routine implies no memory barriers.
-inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
- Atomic32 new_value) {
- Atomic32 temp, old;
- __asm__ __volatile__(".set push\n"
- ".set noreorder\n"
- "1:\n"
- "ll %1, %4\n" // old = *ptr
- "move %0, %3\n" // temp = new_value
- "sc %0, %2\n" // *ptr = temp (with atomic check)
- "beqz %0, 1b\n" // start again on atomic error
- "nop\n" // delay slot nop
- ".set pop\n"
- : "=&r" (temp), "=&r" (old), "=m" (*ptr)
- : "r" (new_value), "m" (*ptr)
- : "memory");
-
- return old;
-}
-
-// Atomically increment *ptr by "increment". Returns the new value of
-// *ptr with the increment applied. This routine implies no memory barriers.
-inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
- Atomic32 increment) {
- Atomic32 temp, temp2;
-
- __asm__ __volatile__(".set push\n"
- ".set noreorder\n"
- "1:\n"
- "ll %0, %4\n" // temp = *ptr
- "addu %1, %0, %3\n" // temp2 = temp + increment
- "sc %1, %2\n" // *ptr = temp2 (with atomic check)
- "beqz %1, 1b\n" // start again on atomic error
- "addu %1, %0, %3\n" // temp2 = temp + increment
- ".set pop\n"
- : "=&r" (temp), "=&r" (temp2), "=m" (*ptr)
- : "Ir" (increment), "m" (*ptr)
- : "memory");
- // temp2 now holds the final value.
- return temp2;
-}
-
-inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
- Atomic32 increment) {
- MemoryBarrier();
- Atomic32 res = NoBarrier_AtomicIncrement(ptr, increment);
- MemoryBarrier();
- return res;
-}
-
-// "Acquire" operations
-// ensure that no later memory access can be reordered ahead of the operation.
-// "Release" operations ensure that no previous memory access can be reordered
-// after the operation. "Barrier" operations have both "Acquire" and "Release"
-// semantics. A MemoryBarrier() has "Barrier" semantics, but does no memory
-// access.
-inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
- Atomic32 old_value,
- Atomic32 new_value) {
- Atomic32 res = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
- MemoryBarrier();
- return res;
-}
-
-inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
- Atomic32 old_value,
- Atomic32 new_value) {
- MemoryBarrier();
- return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
-}
-
-inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
- *ptr = value;
-}
-
-inline void MemoryBarrier() {
- __asm__ __volatile__("sync" : : : "memory");
-}
-
-inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
- *ptr = value;
- MemoryBarrier();
-}
-
-inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
- MemoryBarrier();
- *ptr = value;
-}
-
-inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
- return *ptr;
-}
-
-inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
- Atomic32 value = *ptr;
- MemoryBarrier();
- return value;
-}
-
-inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
- MemoryBarrier();
- return *ptr;
-}
-
-#if defined(__LP64__)
-// 64-bit versions of the atomic ops.
-
-inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
- Atomic64 old_value,
- Atomic64 new_value) {
- Atomic64 prev, tmp;
- __asm__ __volatile__(".set push\n"
- ".set noreorder\n"
- "1:\n"
- "lld %0, %5\n" // prev = *ptr
- "bne %0, %3, 2f\n" // if (prev != old_value) goto 2
- "move %2, %4\n" // tmp = new_value
- "scd %2, %1\n" // *ptr = tmp (with atomic check)
- "beqz %2, 1b\n" // start again on atomic error
- "nop\n" // delay slot nop
- "2:\n"
- ".set pop\n"
- : "=&r" (prev), "=m" (*ptr), "=&r" (tmp)
- : "r" (old_value), "r" (new_value), "m" (*ptr)
- : "memory");
- return prev;
-}
-
-// Atomically store new_value into *ptr, returning the previous value held in
-// *ptr. This routine implies no memory barriers.
-inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
- Atomic64 new_value) {
- Atomic64 temp, old;
- __asm__ __volatile__(".set push\n"
- ".set noreorder\n"
- "1:\n"
- "lld %1, %4\n" // old = *ptr
- "move %0, %3\n" // temp = new_value
- "scd %0, %2\n" // *ptr = temp (with atomic check)
- "beqz %0, 1b\n" // start again on atomic error
- "nop\n" // delay slot nop
- ".set pop\n"
- : "=&r" (temp), "=&r" (old), "=m" (*ptr)
- : "r" (new_value), "m" (*ptr)
- : "memory");
-
- return old;
-}
-
-// Atomically increment *ptr by "increment". Returns the new value of
-// *ptr with the increment applied. This routine implies no memory barriers.
-inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr,
- Atomic64 increment) {
- Atomic64 temp, temp2;
-
- __asm__ __volatile__(".set push\n"
- ".set noreorder\n"
- "1:\n"
- "lld %0, %4\n" // temp = *ptr
- "daddu %1, %0, %3\n" // temp2 = temp + increment
- "scd %1, %2\n" // *ptr = temp2 (with atomic check)
- "beqz %1, 1b\n" // start again on atomic error
- "daddu %1, %0, %3\n" // temp2 = temp + increment
- ".set pop\n"
- : "=&r" (temp), "=&r" (temp2), "=m" (*ptr)
- : "Ir" (increment), "m" (*ptr)
- : "memory");
- // temp2 now holds the final value.
- return temp2;
-}
-
-inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr,
- Atomic64 increment) {
- MemoryBarrier();
- Atomic64 res = NoBarrier_AtomicIncrement(ptr, increment);
- MemoryBarrier();
- return res;
-}
-
-// "Acquire" operations
-// ensure that no later memory access can be reordered ahead of the operation.
-// "Release" operations ensure that no previous memory access can be reordered
-// after the operation. "Barrier" operations have both "Acquire" and "Release"
-// semantics. A MemoryBarrier() has "Barrier" semantics, but does no memory
-// access.
-inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
- Atomic64 old_value,
- Atomic64 new_value) {
- Atomic64 res = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
- MemoryBarrier();
- return res;
-}
-
-inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
- Atomic64 old_value,
- Atomic64 new_value) {
- MemoryBarrier();
- return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
-}
-
-inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
- *ptr = value;
-}
-
-inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
- *ptr = value;
- MemoryBarrier();
-}
-
-inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
- MemoryBarrier();
- *ptr = value;
-}
-
-inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
- return *ptr;
-}
-
-inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
- Atomic64 value = *ptr;
- MemoryBarrier();
- return value;
-}
-
-inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
- MemoryBarrier();
- return *ptr;
-}
-#endif
-
-} // namespace subtle
-} // namespace base
-
-#endif // BASE_ATOMICOPS_INTERNALS_MIPS_GCC_H_
diff --git a/base/atomicops_internals_x86_gcc.cc b/base/atomicops_internals_x86_gcc.cc
deleted file mode 100644
index c21e96d..0000000
--- a/base/atomicops_internals_x86_gcc.cc
+++ /dev/null
@@ -1,103 +0,0 @@
-// Copyright (c) 2006-2008 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.
-
-// This module gets enough CPU information to optimize the
-// atomicops module on x86.
-
-#include <stdint.h>
-#include <string.h>
-
-#include "base/atomicops.h"
-
-// Inline cpuid instruction. In PIC compilations, %ebx contains the address
-// of the global offset table. To avoid breaking such executables, this code
-// must preserve that register's value across cpuid instructions.
-//
-// The include guards are the same as in atomicops.h.
-#if defined(__i386__)
-#define cpuid(a, b, c, d, inp) \
- asm("mov %%ebx, %%edi\n" \
- "cpuid\n" \
- "xchg %%edi, %%ebx\n" \
- : "=a" (a), "=D" (b), "=c" (c), "=d" (d) : "a" (inp))
-#elif defined(__x86_64__)
-#define cpuid(a, b, c, d, inp) \
- asm("mov %%rbx, %%rdi\n" \
- "cpuid\n" \
- "xchg %%rdi, %%rbx\n" \
- : "=a" (a), "=D" (b), "=c" (c), "=d" (d) : "a" (inp))
-#endif
-
-#if defined(cpuid) // initialize the struct only on x86
-
-// Set the flags so that code will run correctly and conservatively, so even
-// if we haven't been initialized yet, we're probably single threaded, and our
-// default values should hopefully be pretty safe.
-struct AtomicOps_x86CPUFeatureStruct AtomicOps_Internalx86CPUFeatures = {
- false, // bug can't exist before process spawns multiple threads
- false, // Chrome requires SSE2, but for transition assume not and initialize
- // this properly.
- false, // cmpxchg16b isn't present on early AMD64 CPUs.
-};
-
-namespace {
-
-// Initialize the AtomicOps_Internalx86CPUFeatures struct.
-void AtomicOps_Internalx86CPUFeaturesInit() {
- uint32_t eax;
- uint32_t ebx;
- uint32_t ecx;
- uint32_t edx;
-
- // Get vendor string (issue CPUID with eax = 0)
- cpuid(eax, ebx, ecx, edx, 0);
- char vendor[13];
- memcpy(vendor, &ebx, 4);
- memcpy(vendor + 4, &edx, 4);
- memcpy(vendor + 8, &ecx, 4);
- vendor[12] = 0;
-
- // get feature flags in ecx/edx, and family/model in eax
- cpuid(eax, ebx, ecx, edx, 1);
-
- int family = (eax >> 8) & 0xf; // family and model fields
- int model = (eax >> 4) & 0xf;
- if (family == 0xf) { // use extended family and model fields
- family += (eax >> 20) & 0xff;
- model += ((eax >> 16) & 0xf) << 4;
- }
-
- // Opteron Rev E has a bug in which on very rare occasions a locked
- // instruction doesn't act as a read-acquire barrier if followed by a
- // non-locked read-modify-write instruction. Rev F has this bug in
- // pre-release versions, but not in versions released to customers,
- // so we test only for Rev E, which is family 15, model 32..63 inclusive.
- if (strcmp(vendor, "AuthenticAMD") == 0 && // AMD
- family == 15 &&
- 32 <= model && model <= 63) {
- AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug = true;
- } else {
- AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug = false;
- }
-
- // edx bit 26 is SSE2 which we use to tell use whether we can use mfence
- AtomicOps_Internalx86CPUFeatures.has_sse2 = ((edx >> 26) & 1);
-
- // ecx bit 13 indicates whether the cmpxchg16b instruction is supported
- AtomicOps_Internalx86CPUFeatures.has_cmpxchg16b = ((ecx >> 13) & 1);
-}
-
-class AtomicOpsx86Initializer {
- public:
- AtomicOpsx86Initializer() {
- AtomicOps_Internalx86CPUFeaturesInit();
- }
-};
-
-// A global to get use initialized on startup via static initialization :/
-AtomicOpsx86Initializer g_initer;
-
-} // namespace
-
-#endif // if x86
diff --git a/base/atomicops_internals_x86_gcc.h b/base/atomicops_internals_x86_gcc.h
deleted file mode 100644
index f0d2242..0000000
--- a/base/atomicops_internals_x86_gcc.h
+++ /dev/null
@@ -1,228 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// This file is an internal atomic implementation, use base/atomicops.h instead.
-
-#ifndef BASE_ATOMICOPS_INTERNALS_X86_GCC_H_
-#define BASE_ATOMICOPS_INTERNALS_X86_GCC_H_
-
-#define ATOMICOPS_COMPILER_BARRIER() __asm__ __volatile__("" : : : "memory")
-
-namespace base {
-namespace subtle {
-
-// 32-bit low-level operations on any platform.
-
-inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
- Atomic32 old_value,
- Atomic32 new_value) {
- Atomic32 prev;
- __asm__ __volatile__("lock; cmpxchgl %1,%2"
- : "=a" (prev)
- : "q" (new_value), "m" (*ptr), "0" (old_value)
- : "memory");
- return prev;
-}
-
-inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
- Atomic32 new_value) {
- __asm__ __volatile__("xchgl %1,%0" // The lock prefix is implicit for xchg.
- : "=r" (new_value)
- : "m" (*ptr), "0" (new_value)
- : "memory");
- return new_value; // Now it's the previous value.
-}
-
-inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
- Atomic32 increment) {
- Atomic32 temp = increment;
- __asm__ __volatile__("lock; xaddl %0,%1"
- : "+r" (temp), "+m" (*ptr)
- : : "memory");
- // temp now holds the old value of *ptr
- return temp + increment;
-}
-
-inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
- Atomic32 increment) {
- Atomic32 temp = increment;
- __asm__ __volatile__("lock; xaddl %0,%1"
- : "+r" (temp), "+m" (*ptr)
- : : "memory");
- // temp now holds the old value of *ptr
- if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) {
- __asm__ __volatile__("lfence" : : : "memory");
- }
- return temp + increment;
-}
-
-inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
- Atomic32 old_value,
- Atomic32 new_value) {
- Atomic32 x = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
- if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) {
- __asm__ __volatile__("lfence" : : : "memory");
- }
- return x;
-}
-
-inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
- Atomic32 old_value,
- Atomic32 new_value) {
- return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
-}
-
-inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
- *ptr = value;
-}
-
-inline void MemoryBarrier() {
- __asm__ __volatile__("mfence" : : : "memory");
-}
-
-inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
- *ptr = value;
- MemoryBarrier();
-}
-
-inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
- ATOMICOPS_COMPILER_BARRIER();
- *ptr = value; // An x86 store acts as a release barrier.
- // See comments in Atomic64 version of Release_Store(), below.
-}
-
-inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
- return *ptr;
-}
-
-inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
- Atomic32 value = *ptr; // An x86 load acts as a acquire barrier.
- // See comments in Atomic64 version of Release_Store(), below.
- ATOMICOPS_COMPILER_BARRIER();
- return value;
-}
-
-inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
- MemoryBarrier();
- return *ptr;
-}
-
-#if defined(__x86_64__)
-
-// 64-bit low-level operations on 64-bit platform.
-
-inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
- Atomic64 old_value,
- Atomic64 new_value) {
- Atomic64 prev;
- __asm__ __volatile__("lock; cmpxchgq %1,%2"
- : "=a" (prev)
- : "q" (new_value), "m" (*ptr), "0" (old_value)
- : "memory");
- return prev;
-}
-
-inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
- Atomic64 new_value) {
- __asm__ __volatile__("xchgq %1,%0" // The lock prefix is implicit for xchg.
- : "=r" (new_value)
- : "m" (*ptr), "0" (new_value)
- : "memory");
- return new_value; // Now it's the previous value.
-}
-
-inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr,
- Atomic64 increment) {
- Atomic64 temp = increment;
- __asm__ __volatile__("lock; xaddq %0,%1"
- : "+r" (temp), "+m" (*ptr)
- : : "memory");
- // temp now contains the previous value of *ptr
- return temp + increment;
-}
-
-inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr,
- Atomic64 increment) {
- Atomic64 temp = increment;
- __asm__ __volatile__("lock; xaddq %0,%1"
- : "+r" (temp), "+m" (*ptr)
- : : "memory");
- // temp now contains the previous value of *ptr
- if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) {
- __asm__ __volatile__("lfence" : : : "memory");
- }
- return temp + increment;
-}
-
-inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
- *ptr = value;
-}
-
-inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
- *ptr = value;
- MemoryBarrier();
-}
-
-inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
- ATOMICOPS_COMPILER_BARRIER();
-
- *ptr = value; // An x86 store acts as a release barrier
- // for current AMD/Intel chips as of Jan 2008.
- // See also Acquire_Load(), below.
-
- // When new chips come out, check:
- // IA-32 Intel Architecture Software Developer's Manual, Volume 3:
- // System Programming Guide, Chatper 7: Multiple-processor management,
- // Section 7.2, Memory Ordering.
- // Last seen at:
- // http://developer.intel.com/design/pentium4/manuals/index_new.htm
- //
- // x86 stores/loads fail to act as barriers for a few instructions (clflush
- // maskmovdqu maskmovq movntdq movnti movntpd movntps movntq) but these are
- // not generated by the compiler, and are rare. Users of these instructions
- // need to know about cache behaviour in any case since all of these involve
- // either flushing cache lines or non-temporal cache hints.
-}
-
-inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
- return *ptr;
-}
-
-inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
- Atomic64 value = *ptr; // An x86 load acts as a acquire barrier,
- // for current AMD/Intel chips as of Jan 2008.
- // See also Release_Store(), above.
- ATOMICOPS_COMPILER_BARRIER();
- return value;
-}
-
-inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
- MemoryBarrier();
- return *ptr;
-}
-
-inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
- Atomic64 old_value,
- Atomic64 new_value) {
- Atomic64 x = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
- if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) {
- __asm__ __volatile__("lfence" : : : "memory");
- }
- return x;
-}
-
-inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
- Atomic64 old_value,
- Atomic64 new_value) {
- return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
-}
-
-#endif // defined(__x86_64__)
-
-} // namespace subtle
-} // namespace base
-
-#undef ATOMICOPS_COMPILER_BARRIER
-
-#endif // BASE_ATOMICOPS_INTERNALS_X86_GCC_H_
diff --git a/base/base.isolate b/base/base.isolate
index c7ba651..948c600 100644
--- a/base/base.isolate
+++ b/base/base.isolate
@@ -36,7 +36,9 @@
['OS=="win" and asan==1 and component=="shared_library"', {
'variables': {
'files': [
- '../third_party/llvm-build/Release+Asserts/lib/clang/3.7.0/lib/windows/clang_rt.asan_dynamic-i386.dll',
+ # We only need x.y.z/lib/windows/clang_rt.asan_dynamic-i386.dll,
+ # but since the version (x.y.z) changes, just grab the whole dir.
+ '../third_party/llvm-build/Release+Asserts/lib/clang/',
],
},
}],
diff --git a/base/base_paths_mac.mm b/base/base_paths_mac.mm
index 9864eb3..a9d01f2 100644
--- a/base/base_paths_mac.mm
+++ b/base/base_paths_mac.mm
@@ -29,8 +29,8 @@
_NSGetExecutablePath(NULL, &executable_length);
DCHECK_GT(executable_length, 1u);
std::string executable_path;
- int rv = _NSGetExecutablePath(WriteInto(&executable_path, executable_length),
- &executable_length);
+ int rv = _NSGetExecutablePath(
+ base::WriteInto(&executable_path, executable_length), &executable_length);
DCHECK_EQ(rv, 0);
// _NSGetExecutablePath may return paths containing ./ or ../ which makes
diff --git a/base/base_paths_win.cc b/base/base_paths_win.cc
index 4ecb59d..58f925f 100644
--- a/base/base_paths_win.cc
+++ b/base/base_paths_win.cc
@@ -32,14 +32,16 @@
FilePath cur;
switch (key) {
case base::FILE_EXE:
- GetModuleFileName(NULL, system_buffer, MAX_PATH);
+ if (GetModuleFileName(NULL, system_buffer, MAX_PATH) == 0)
+ return false;
cur = FilePath(system_buffer);
break;
case base::FILE_MODULE: {
// the resource containing module is assumed to be the one that
// this code lives in, whether that's a dll or exe
HMODULE this_module = reinterpret_cast<HMODULE>(&__ImageBase);
- GetModuleFileName(this_module, system_buffer, MAX_PATH);
+ if (GetModuleFileName(this_module, system_buffer, MAX_PATH) == 0)
+ return false;
cur = FilePath(system_buffer);
break;
}
diff --git a/base/base_switches.cc b/base/base_switches.cc
index 3076540..6d517e5 100644
--- a/base/base_switches.cc
+++ b/base/base_switches.cc
@@ -67,6 +67,11 @@
// chrome://profiler.
const char kProfilerTimingDisabledValue[] = "0";
+#if defined(OS_WIN)
+// Disables the USB keyboard detection for blocking the OSK on Win8+.
+const char kDisableUsbKeyboardDetect[] = "disable-usb-keyboard-detect";
+#endif
+
#if defined(OS_POSIX)
// Used for turning on Breakpad crash reporting in a debug environment where
// crash reporting is typically compiled but disabled.
diff --git a/base/base_switches.h b/base/base_switches.h
index c579f6a..bbd590b 100644
--- a/base/base_switches.h
+++ b/base/base_switches.h
@@ -27,6 +27,10 @@
extern const char kVModule[];
extern const char kWaitForDebugger[];
+#if defined(OS_WIN)
+extern const char kDisableUsbKeyboardDetect[];
+#endif
+
#if defined(OS_POSIX)
extern const char kEnableCrashReporterForTesting[];
#endif
diff --git a/base/basictypes.h b/base/basictypes.h
index bf75e67..d71abd9 100644
--- a/base/basictypes.h
+++ b/base/basictypes.h
@@ -16,7 +16,7 @@
#include <stdint.h> // For intptr_t.
#include "base/macros.h"
-#include "base/port.h" // Types that only need exist on certain systems.
+#include "build/build_config.h"
// DEPRECATED: Please use (u)int{8,16,32,64}_t instead (and include <stdint.h>).
typedef int8_t int8;
diff --git a/base/compiler_specific.h b/base/compiler_specific.h
index 63297dc..66dc80d 100644
--- a/base/compiler_specific.h
+++ b/base/compiler_specific.h
@@ -140,7 +140,7 @@
// Annotate a function indicating the caller must examine the return value.
// Use like:
// int foo() WARN_UNUSED_RESULT;
-// To explicitly ignore a result, see |ignore_result()| in <base/basictypes.h>.
+// To explicitly ignore a result, see |ignore_result()| in base/macros.h.
#if defined(COMPILER_GCC)
#define WARN_UNUSED_RESULT __attribute__((warn_unused_result))
#else
diff --git a/base/containers/scoped_ptr_map.h b/base/containers/scoped_ptr_map.h
new file mode 100644
index 0000000..a4605e3
--- /dev/null
+++ b/base/containers/scoped_ptr_map.h
@@ -0,0 +1,144 @@
+// 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_CONTAINERS_SCOPED_PTR_MAP_H_
+#define BASE_CONTAINERS_SCOPED_PTR_MAP_H_
+
+#include <functional>
+#include <map>
+#include <utility>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/move.h"
+#include "base/stl_util.h"
+
+namespace base {
+
+// ScopedPtrMap provides a std::map that supports scoped_ptr values. It ensures
+// that the map's values are properly deleted when removed from the map, or when
+// the map is destroyed.
+//
+// |ScopedPtr| must be a type scoped_ptr<T>. This is for compatibility with
+// std::map in C++11.
+template <class Key, class ScopedPtr, class Compare = std::less<Key>>
+class ScopedPtrMap {
+ MOVE_ONLY_TYPE_WITH_MOVE_CONSTRUCTOR_FOR_CPP_03(ScopedPtrMap)
+
+ using Container = std::map<Key, typename ScopedPtr::element_type*, Compare>;
+
+ public:
+ using allocator_type = typename Container::allocator_type;
+ using size_type = typename Container::size_type;
+ using difference_type = typename Container::difference_type;
+ using reference = typename Container::reference;
+ using const_reference = typename Container::const_reference;
+ using key_type = typename Container::key_type;
+ using mapped_type = ScopedPtr;
+ using key_compare = typename Container::key_compare;
+ using const_iterator = typename Container::const_iterator;
+ using const_reverse_iterator = typename Container::const_reverse_iterator;
+
+ ScopedPtrMap() {}
+ ~ScopedPtrMap() { clear(); }
+ ScopedPtrMap(ScopedPtrMap<Key, ScopedPtr>&& other) { swap(other); }
+
+ ScopedPtrMap& operator=(ScopedPtrMap<Key, ScopedPtr>&& rhs) {
+ swap(rhs);
+ return *this;
+ }
+
+ const_iterator find(const Key& k) const { return data_.find(k); }
+ size_type count(const Key& k) const { return data_.count(k); }
+
+ bool empty() const { return data_.empty(); }
+ size_t size() const { return data_.size(); }
+
+ const_reverse_iterator rbegin() const { return data_.rbegin(); }
+ const_reverse_iterator rend() const { return data_.rend(); }
+
+ const_iterator begin() const { return data_.begin(); }
+ const_iterator end() const { return data_.end(); }
+
+ void swap(ScopedPtrMap<Key, ScopedPtr>& other) { data_.swap(other.data_); }
+
+ void clear() { STLDeleteValues(&data_); }
+
+ // Inserts |val| into the map, associated with |key|.
+ std::pair<const_iterator, bool> insert(const Key& key, ScopedPtr val) {
+ auto result = data_.insert(std::make_pair(key, val.get()));
+ if (result.second)
+ ignore_result(val.release());
+ return result;
+ }
+
+ // Inserts |val| into the map, associated with |key|. Overwrites any existing
+ // element at |key|.
+ void set(const Key& key, ScopedPtr val) {
+ typename ScopedPtr::element_type*& val_ref = data_[key];
+ delete val_ref;
+ val_ref = val.release();
+ }
+
+ void erase(const_iterator position) {
+ DCHECK(position != end());
+ delete position->second;
+ // Key-based lookup (cannot use const_iterator overload in C++03 library).
+ data_.erase(position->first);
+ }
+
+ size_type erase(const Key& k) {
+ typename Container::iterator it = data_.find(k);
+ if (it == end())
+ return 0;
+
+ delete it->second;
+ data_.erase(it);
+ return 1;
+ }
+
+ void erase(const_iterator first, const_iterator last) {
+ STLDeleteContainerPairSecondPointers(first, last);
+ // Need non-const iterators as required by the C++03 library.
+ data_.erase(ConstIteratorToIterator(first), ConstIteratorToIterator(last));
+ }
+
+ // Like |erase()|, but returns the element instead of deleting it.
+ ScopedPtr take_and_erase(const_iterator position) {
+ DCHECK(position != end());
+ if (position == end())
+ return ScopedPtr();
+
+ ScopedPtr ret(position->second);
+ // Key-based lookup (cannot use const_iterator overload in C++03 library).
+ data_.erase(position->first);
+ return ret.Pass();
+ }
+
+ // Like |erase()|, but returns the element instead of deleting it.
+ ScopedPtr take_and_erase(const Key& k) {
+ typename Container::iterator it = data_.find(k);
+ if (it == end())
+ return ScopedPtr();
+
+ ScopedPtr ret(it->second);
+ data_.erase(it);
+ return ret.Pass();
+ }
+
+ private:
+ Container data_;
+
+ typename Container::iterator ConstIteratorToIterator(const_iterator it) {
+ // This is the only way to convert a const iterator to a non-const iterator
+ // in C++03 (get the key and do the lookup again).
+ if (it == data_.end())
+ return data_.end();
+ return data_.find(it->first);
+ };
+};
+
+} // namespace base
+
+#endif // BASE_CONTAINERS_SCOPED_PTR_MAP_H_
diff --git a/base/containers/scoped_ptr_map_unittest.cc b/base/containers/scoped_ptr_map_unittest.cc
new file mode 100644
index 0000000..46843b3
--- /dev/null
+++ b/base/containers/scoped_ptr_map_unittest.cc
@@ -0,0 +1,240 @@
+// 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/containers/scoped_ptr_map.h"
+
+#include <functional>
+#include <map>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+// A ScopedDestroyer sets a Boolean to true upon destruction.
+class ScopedDestroyer {
+ public:
+ ScopedDestroyer(bool* destroyed) : destroyed_(destroyed) {
+ *destroyed_ = false;
+ }
+
+ ~ScopedDestroyer() { *destroyed_ = true; }
+
+ private:
+ bool* destroyed_;
+};
+
+TEST(ScopedPtrMapTest, Insert) {
+ bool destroyed1 = false;
+ bool destroyed2 = false;
+ {
+ ScopedPtrMap<int, scoped_ptr<ScopedDestroyer>> scoped_map;
+
+ // Insert to new key.
+ ScopedDestroyer* elem1 = new ScopedDestroyer(&destroyed1);
+ EXPECT_FALSE(destroyed1);
+ EXPECT_TRUE(scoped_map.insert(0, make_scoped_ptr(elem1)).second);
+ EXPECT_EQ(elem1, scoped_map.find(0)->second);
+ EXPECT_FALSE(destroyed1);
+
+ // Insert to existing key.
+ ScopedDestroyer* elem2 = new ScopedDestroyer(&destroyed2);
+ EXPECT_FALSE(destroyed2);
+ EXPECT_FALSE(scoped_map.insert(0, make_scoped_ptr(elem2)).second);
+ EXPECT_EQ(elem1, scoped_map.find(0)->second);
+
+ EXPECT_FALSE(destroyed1);
+ EXPECT_TRUE(destroyed2);
+ }
+ EXPECT_TRUE(destroyed1);
+}
+
+TEST(ScopedPtrMapTest, Set) {
+ bool destroyed1 = false;
+ bool destroyed2 = false;
+ {
+ ScopedPtrMap<int, scoped_ptr<ScopedDestroyer>> scoped_map;
+
+ // Set a new key.
+ ScopedDestroyer* elem1 = new ScopedDestroyer(&destroyed1);
+ EXPECT_FALSE(destroyed1);
+ scoped_map.set(0, make_scoped_ptr(elem1));
+ EXPECT_EQ(elem1, scoped_map.find(0)->second);
+ EXPECT_FALSE(destroyed1);
+
+ // Set to replace an existing key.
+ ScopedDestroyer* elem2 = new ScopedDestroyer(&destroyed2);
+ EXPECT_FALSE(destroyed2);
+ scoped_map.set(0, make_scoped_ptr(elem2));
+ EXPECT_EQ(elem2, scoped_map.find(0)->second);
+
+ EXPECT_TRUE(destroyed1);
+ EXPECT_FALSE(destroyed2);
+ }
+ EXPECT_TRUE(destroyed1);
+ EXPECT_TRUE(destroyed2);
+}
+
+TEST(ScopedPtrMapTest, EraseIterator) {
+ bool destroyed = false;
+ ScopedPtrMap<int, scoped_ptr<ScopedDestroyer>> scoped_map;
+ scoped_map.insert(0, make_scoped_ptr(new ScopedDestroyer(&destroyed)));
+ EXPECT_FALSE(destroyed);
+ scoped_map.erase(scoped_map.find(0));
+ EXPECT_TRUE(destroyed);
+ EXPECT_TRUE(scoped_map.empty());
+}
+
+TEST(ScopedPtrMapTest, EraseKey) {
+ bool destroyed = false;
+ ScopedPtrMap<int, scoped_ptr<ScopedDestroyer>> scoped_map;
+ scoped_map.insert(0, make_scoped_ptr(new ScopedDestroyer(&destroyed)));
+ EXPECT_FALSE(destroyed);
+ EXPECT_EQ(1u, scoped_map.erase(0));
+ EXPECT_TRUE(destroyed);
+ EXPECT_TRUE(scoped_map.empty());
+
+ // Test erase of a non-existent key.
+ EXPECT_EQ(0u, scoped_map.erase(7));
+}
+
+TEST(ScopedPtrMapTest, EraseRange) {
+ bool destroyed1 = false;
+ bool destroyed2 = false;
+ ScopedPtrMap<int, scoped_ptr<ScopedDestroyer>> scoped_map;
+
+ scoped_map.insert(0, make_scoped_ptr(new ScopedDestroyer(&destroyed1)));
+ EXPECT_FALSE(destroyed1);
+
+ scoped_map.insert(1, make_scoped_ptr(new ScopedDestroyer(&destroyed2)));
+ EXPECT_FALSE(destroyed2);
+
+ scoped_map.erase(scoped_map.find(0), scoped_map.end());
+ EXPECT_TRUE(destroyed1);
+ EXPECT_TRUE(destroyed2);
+ EXPECT_TRUE(scoped_map.empty());
+}
+
+TEST(ScopedPtrMapTest, TakeAndErase) {
+ bool destroyed = false;
+ ScopedPtrMap<int, scoped_ptr<ScopedDestroyer>> scoped_map;
+ ScopedDestroyer* elem = new ScopedDestroyer(&destroyed);
+ scoped_map.insert(0, make_scoped_ptr(elem));
+ EXPECT_EQ(elem, scoped_map.find(0)->second);
+ EXPECT_FALSE(destroyed);
+ scoped_ptr<ScopedDestroyer> object = scoped_map.take_and_erase(0);
+ EXPECT_EQ(elem, object.get());
+ EXPECT_FALSE(destroyed);
+ EXPECT_TRUE(scoped_map.empty());
+ object.reset();
+ EXPECT_TRUE(destroyed);
+}
+
+TEST(ScopedPtrMapTest, Clear) {
+ bool destroyed = false;
+ ScopedPtrMap<int, scoped_ptr<ScopedDestroyer>> scoped_map;
+ scoped_map.insert(0, make_scoped_ptr(new ScopedDestroyer(&destroyed)));
+ EXPECT_FALSE(destroyed);
+ scoped_map.clear();
+ EXPECT_TRUE(destroyed);
+ EXPECT_TRUE(scoped_map.empty());
+}
+
+TEST(ScopedPtrMapTest, Compare) {
+ // Construct a ScopedPtrMap with a custom comparison function.
+ bool destroyed = false;
+ ScopedPtrMap<int, scoped_ptr<ScopedDestroyer>, std::greater<int>> scoped_map;
+ scoped_map.insert(0, make_scoped_ptr(new ScopedDestroyer(&destroyed)));
+ scoped_map.insert(1, make_scoped_ptr(new ScopedDestroyer(&destroyed)));
+
+ auto it = scoped_map.begin();
+ EXPECT_EQ(1, it->first);
+ ++it;
+ EXPECT_EQ(0, it->first);
+}
+
+TEST(ScopedPtrMapTest, Scope) {
+ bool destroyed = false;
+ {
+ ScopedPtrMap<int, scoped_ptr<ScopedDestroyer>> scoped_map;
+ scoped_map.insert(0, make_scoped_ptr(new ScopedDestroyer(&destroyed)));
+ EXPECT_FALSE(destroyed);
+ }
+ EXPECT_TRUE(destroyed);
+}
+
+TEST(ScopedPtrMapTest, MoveConstruct) {
+ bool destroyed = false;
+ {
+ ScopedPtrMap<int, scoped_ptr<ScopedDestroyer>> scoped_map;
+ ScopedDestroyer* elem = new ScopedDestroyer(&destroyed);
+ scoped_map.insert(0, make_scoped_ptr(elem));
+ EXPECT_EQ(elem, scoped_map.find(0)->second);
+ EXPECT_FALSE(destroyed);
+ EXPECT_FALSE(scoped_map.empty());
+
+ ScopedPtrMap<int, scoped_ptr<ScopedDestroyer>> scoped_map_copy(
+ scoped_map.Pass());
+ EXPECT_TRUE(scoped_map.empty());
+ EXPECT_FALSE(scoped_map_copy.empty());
+ EXPECT_EQ(elem, scoped_map_copy.find(0)->second);
+ EXPECT_FALSE(destroyed);
+ }
+ EXPECT_TRUE(destroyed);
+}
+
+TEST(ScopedPtrMapTest, MoveAssign) {
+ bool destroyed = false;
+ {
+ ScopedPtrMap<int, scoped_ptr<ScopedDestroyer>> scoped_map;
+ ScopedDestroyer* elem = new ScopedDestroyer(&destroyed);
+ scoped_map.insert(0, make_scoped_ptr(elem));
+ EXPECT_EQ(elem, scoped_map.find(0)->second);
+ EXPECT_FALSE(destroyed);
+ EXPECT_FALSE(scoped_map.empty());
+
+ ScopedPtrMap<int, scoped_ptr<ScopedDestroyer>> scoped_map_assign;
+ scoped_map_assign = scoped_map.Pass();
+ EXPECT_TRUE(scoped_map.empty());
+ EXPECT_FALSE(scoped_map_assign.empty());
+ EXPECT_EQ(elem, scoped_map_assign.find(0)->second);
+ EXPECT_FALSE(destroyed);
+ }
+ EXPECT_TRUE(destroyed);
+}
+
+template <typename Key, typename ScopedPtr>
+ScopedPtrMap<Key, ScopedPtr> PassThru(ScopedPtrMap<Key, ScopedPtr> scoper) {
+ return scoper;
+}
+
+TEST(ScopedPtrMapTest, Passed) {
+ bool destroyed = false;
+ ScopedPtrMap<int, scoped_ptr<ScopedDestroyer>> scoped_map;
+ ScopedDestroyer* elem = new ScopedDestroyer(&destroyed);
+ scoped_map.insert(0, make_scoped_ptr(elem));
+ EXPECT_EQ(elem, scoped_map.find(0)->second);
+ EXPECT_FALSE(destroyed);
+
+ base::Callback<ScopedPtrMap<int, scoped_ptr<ScopedDestroyer>>(void)>
+ callback = base::Bind(&PassThru<int, scoped_ptr<ScopedDestroyer>>,
+ base::Passed(&scoped_map));
+ EXPECT_TRUE(scoped_map.empty());
+ EXPECT_FALSE(destroyed);
+
+ ScopedPtrMap<int, scoped_ptr<ScopedDestroyer>> result = callback.Run();
+ EXPECT_TRUE(scoped_map.empty());
+ EXPECT_EQ(elem, result.find(0)->second);
+ EXPECT_FALSE(destroyed);
+
+ result.clear();
+ EXPECT_TRUE(destroyed);
+};
+
+} // namespace
+} // namespace base
diff --git a/base/debug/crash_logging.cc b/base/debug/crash_logging.cc
index f9b4449..058b476 100644
--- a/base/debug/crash_logging.cc
+++ b/base/debug/crash_logging.cc
@@ -119,7 +119,7 @@
hex_backtrace.push_back(s);
}
- value = JoinString(hex_backtrace, ' ');
+ value = base::JoinString(hex_backtrace, " ");
// Warn if this exceeds the breakpad limits.
DCHECK_LE(value.length(), kBreakpadValueMax);
diff --git a/base/debug/profiler.cc b/base/debug/profiler.cc
index ed553cd..924c769 100644
--- a/base/debug/profiler.cc
+++ b/base/debug/profiler.cc
@@ -7,8 +7,8 @@
#include <string>
#include "base/process/process_handle.h"
+#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
#if defined(OS_WIN)
#include "base/win/pe_image.h"
@@ -30,8 +30,8 @@
void StartProfiling(const std::string& name) {
++profile_count;
std::string full_name(name);
- std::string pid = StringPrintf("%d", GetCurrentProcId());
- std::string count = StringPrintf("%d", profile_count);
+ std::string pid = IntToString(GetCurrentProcId());
+ std::string count = IntToString(profile_count);
ReplaceSubstringsAfterOffset(&full_name, 0, "{pid}", pid);
ReplaceSubstringsAfterOffset(&full_name, 0, "{count}", count);
ProfilerStart(full_name.c_str());
diff --git a/base/debug/stack_trace_posix.cc b/base/debug/stack_trace_posix.cc
index 76cb524..8749bed 100644
--- a/base/debug/stack_trace_posix.cc
+++ b/base/debug/stack_trace_posix.cc
@@ -788,7 +788,8 @@
// Handle negative numbers (only for base 10).
if (i < 0 && base == 10) {
- j = -i;
+ // This does "j = -i" while avoiding integer overflow.
+ j = static_cast<uintptr_t>(-(i + 1)) + 1;
// Make sure we can write the '-' character.
if (++n > sz) {
diff --git a/base/debug/task_annotator.cc b/base/debug/task_annotator.cc
index 19df8cb..b74d390 100644
--- a/base/debug/task_annotator.cc
+++ b/base/debug/task_annotator.cc
@@ -26,7 +26,6 @@
}
void TaskAnnotator::RunTask(const char* queue_function,
- const char* run_function,
const PendingTask& pending_task) {
tracked_objects::TaskStopwatch stopwatch;
stopwatch.Start();
@@ -39,18 +38,6 @@
"queue_duration",
queue_duration.InMilliseconds());
- // When tracing memory for posted tasks it's more valuable to attribute the
- // memory allocations to the source function than generically to the task
- // runner.
- TRACE_EVENT_WITH_MEMORY_TAG2(
- "toplevel",
- run_function,
- pending_task.posted_from.function_name(), // Name for memory tracking.
- "src_file",
- pending_task.posted_from.file_name(),
- "src_func",
- pending_task.posted_from.function_name());
-
// Before running the task, store the program counter where it was posted
// and deliberately alias it to ensure it is on the stack if the task
// crashes. Be careful not to assume that the variable itself will have the
diff --git a/base/debug/task_annotator.h b/base/debug/task_annotator.h
index aa5f17b..74068d9 100644
--- a/base/debug/task_annotator.h
+++ b/base/debug/task_annotator.h
@@ -25,11 +25,8 @@
const PendingTask& pending_task);
// Run a previously queued task. |queue_function| should match what was
- // passed into |DidQueueTask| for this task. |run_function| is used as the
- // name for the trace event that surrounds the task's execution.
- void RunTask(const char* queue_function,
- const char* run_function,
- const PendingTask& pending_task);
+ // passed into |DidQueueTask| for this task.
+ void RunTask(const char* queue_function, const PendingTask& pending_task);
private:
// Creates a process-wide unique ID to represent this task in trace events.
@@ -40,6 +37,13 @@
DISALLOW_COPY_AND_ASSIGN(TaskAnnotator);
};
+#define TRACE_TASK_EXECUTION(run_function, task) \
+ TRACE_EVENT_WITH_MEMORY_TAG2( \
+ "toplevel", (run_function), \
+ (task).posted_from.function_name(), /* Name for memory tracking. */ \
+ "src_file", (task).posted_from.file_name(), "src_func", \
+ (task).posted_from.function_name());
+
} // namespace debug
} // namespace base
diff --git a/base/debug/task_annotator_unittest.cc b/base/debug/task_annotator_unittest.cc
index ddffc21..9f5c442 100644
--- a/base/debug/task_annotator_unittest.cc
+++ b/base/debug/task_annotator_unittest.cc
@@ -24,8 +24,7 @@
TaskAnnotator annotator;
annotator.DidQueueTask("TaskAnnotatorTest::Queue", pending_task);
EXPECT_EQ(0, result);
- annotator.RunTask(
- "TaskAnnotatorTest::Queue", "TaskAnnotatorTest::Run", pending_task);
+ annotator.RunTask("TaskAnnotatorTest::Queue", pending_task);
EXPECT_EQ(123, result);
}
diff --git a/base/file_version_info_unittest.cc b/base/file_version_info_unittest.cc
index 9b10d04..c5e9859 100644
--- a/base/file_version_info_unittest.cc
+++ b/base/file_version_info_unittest.cc
@@ -32,53 +32,49 @@
#if defined(OS_WIN)
TEST(FileVersionInfoTest, HardCodedProperties) {
- const wchar_t* kDLLNames[] = {
- L"FileVersionInfoTest1.dll"
- };
+ const wchar_t kDLLName[] = {L"FileVersionInfoTest1.dll"};
- const wchar_t* kExpectedValues[1][15] = {
+ const wchar_t* const kExpectedValues[15] = {
// FileVersionInfoTest.dll
- L"Goooooogle", // company_name
- L"Google", // company_short_name
- L"This is the product name", // product_name
- L"This is the product short name", // product_short_name
- L"The Internal Name", // internal_name
- L"4.3.2.1", // product_version
- L"Private build property", // private_build
- L"Special build property", // special_build
+ L"Goooooogle", // company_name
+ L"Google", // company_short_name
+ L"This is the product name", // product_name
+ L"This is the product short name", // product_short_name
+ L"The Internal Name", // internal_name
+ L"4.3.2.1", // product_version
+ L"Private build property", // private_build
+ L"Special build property", // special_build
L"This is a particularly interesting comment", // comments
- L"This is the original filename", // original_filename
- L"This is my file description", // file_description
- L"1.2.3.4", // file_version
- L"This is the legal copyright", // legal_copyright
- L"This is the legal trademarks", // legal_trademarks
- L"This is the last change", // last_change
+ L"This is the original filename", // original_filename
+ L"This is my file description", // file_description
+ L"1.2.3.4", // file_version
+ L"This is the legal copyright", // legal_copyright
+ L"This is the legal trademarks", // legal_trademarks
+ L"This is the last change", // last_change
};
- for (int i = 0; i < arraysize(kDLLNames); ++i) {
- FilePath dll_path = GetTestDataPath();
- dll_path = dll_path.Append(kDLLNames[i]);
+ FilePath dll_path = GetTestDataPath();
+ dll_path = dll_path.Append(kDLLName);
- scoped_ptr<FileVersionInfo> version_info(
- FileVersionInfo::CreateFileVersionInfo(dll_path));
+ scoped_ptr<FileVersionInfo> version_info(
+ FileVersionInfo::CreateFileVersionInfo(dll_path));
- int j = 0;
- EXPECT_EQ(kExpectedValues[i][j++], version_info->company_name());
- EXPECT_EQ(kExpectedValues[i][j++], version_info->company_short_name());
- EXPECT_EQ(kExpectedValues[i][j++], version_info->product_name());
- EXPECT_EQ(kExpectedValues[i][j++], version_info->product_short_name());
- EXPECT_EQ(kExpectedValues[i][j++], version_info->internal_name());
- EXPECT_EQ(kExpectedValues[i][j++], version_info->product_version());
- EXPECT_EQ(kExpectedValues[i][j++], version_info->private_build());
- EXPECT_EQ(kExpectedValues[i][j++], version_info->special_build());
- EXPECT_EQ(kExpectedValues[i][j++], version_info->comments());
- EXPECT_EQ(kExpectedValues[i][j++], version_info->original_filename());
- EXPECT_EQ(kExpectedValues[i][j++], version_info->file_description());
- EXPECT_EQ(kExpectedValues[i][j++], version_info->file_version());
- EXPECT_EQ(kExpectedValues[i][j++], version_info->legal_copyright());
- EXPECT_EQ(kExpectedValues[i][j++], version_info->legal_trademarks());
- EXPECT_EQ(kExpectedValues[i][j++], version_info->last_change());
- }
+ int j = 0;
+ EXPECT_EQ(kExpectedValues[j++], version_info->company_name());
+ EXPECT_EQ(kExpectedValues[j++], version_info->company_short_name());
+ EXPECT_EQ(kExpectedValues[j++], version_info->product_name());
+ EXPECT_EQ(kExpectedValues[j++], version_info->product_short_name());
+ EXPECT_EQ(kExpectedValues[j++], version_info->internal_name());
+ EXPECT_EQ(kExpectedValues[j++], version_info->product_version());
+ EXPECT_EQ(kExpectedValues[j++], version_info->private_build());
+ EXPECT_EQ(kExpectedValues[j++], version_info->special_build());
+ EXPECT_EQ(kExpectedValues[j++], version_info->comments());
+ EXPECT_EQ(kExpectedValues[j++], version_info->original_filename());
+ EXPECT_EQ(kExpectedValues[j++], version_info->file_description());
+ EXPECT_EQ(kExpectedValues[j++], version_info->file_version());
+ EXPECT_EQ(kExpectedValues[j++], version_info->legal_copyright());
+ EXPECT_EQ(kExpectedValues[j++], version_info->legal_trademarks());
+ EXPECT_EQ(kExpectedValues[j++], version_info->last_change());
}
#endif
diff --git a/base/files/file.cc b/base/files/file.cc
index 58f80c5..9a4ddb1 100644
--- a/base/files/file.cc
+++ b/base/files/file.cc
@@ -52,22 +52,30 @@
File::File(RValue other)
: file_(other.object->TakePlatformFile()),
- path_(other.object->path_),
+ tracing_path_(other.object->tracing_path_),
error_details_(other.object->error_details()),
created_(other.object->created()),
- async_(other.object->async_) {
-}
+ async_(other.object->async_) {}
File::~File() {
// Go through the AssertIOAllowed logic.
Close();
}
+// static
+File File::CreateForAsyncHandle(PlatformFile platform_file) {
+ File file(platform_file);
+ // It would be nice if we could validate that |platform_file| was opened with
+ // FILE_FLAG_OVERLAPPED on Windows but this doesn't appear to be possible.
+ file.async_ = true;
+ return file.Pass();
+}
+
File& File::operator=(RValue other) {
if (this != other.object) {
Close();
SetPlatformFile(other.object->TakePlatformFile());
- path_ = other.object->path_;
+ tracing_path_ = other.object->tracing_path_;
error_details_ = other.object->error_details();
created_ = other.object->created();
async_ = other.object->async_;
@@ -81,9 +89,10 @@
error_details_ = FILE_ERROR_ACCESS_DENIED;
return;
}
- path_ = path;
+ if (FileTracing::IsCategoryEnabled())
+ tracing_path_ = path;
SCOPED_FILE_TRACE("Initialize");
- DoInitialize(flags);
+ DoInitialize(path, flags);
}
#endif
diff --git a/base/files/file.h b/base/files/file.h
index b21b159..cba4353 100644
--- a/base/files/file.h
+++ b/base/files/file.h
@@ -176,6 +176,9 @@
~File();
+ // Takes ownership of |platform_file|.
+ static File CreateForAsyncHandle(PlatformFile platform_file);
+
// Move operator= for C++03 move emulation of this type.
File& operator=(RValue other);
@@ -352,9 +355,9 @@
};
#endif
- // Creates or opens the given file. Only called if |path_| has no
+ // Creates or opens the given file. Only called if |path| has no
// traversal ('..') components.
- void DoInitialize(uint32 flags);
+ void DoInitialize(const FilePath& path, uint32 flags);
// TODO(tnagel): Reintegrate into Flush() once histogram isn't needed anymore,
// cf. issue 473337.
@@ -368,8 +371,9 @@
MemoryCheckingScopedFD file_;
#endif
- // Path that |Initialize()| was called with. Only set if safe (i.e. no '..').
- FilePath path_;
+ // A path to use for tracing purposes. Set if file tracing is enabled during
+ // |Initialize()|.
+ FilePath tracing_path_;
// Object tied to the lifetime of |this| that enables/disables tracing.
FileTracing::ScopedEnabler trace_enabler_;
diff --git a/base/files/file_enumerator_win.cc b/base/files/file_enumerator_win.cc
index 931d154..ae41a46 100644
--- a/base/files/file_enumerator_win.cc
+++ b/base/files/file_enumerator_win.cc
@@ -43,10 +43,10 @@
FileEnumerator::FileEnumerator(const FilePath& root_path,
bool recursive,
int file_type)
- : recursive_(recursive),
- file_type_(file_type),
- has_find_data_(false),
- find_handle_(INVALID_HANDLE_VALUE) {
+ : has_find_data_(false),
+ find_handle_(INVALID_HANDLE_VALUE),
+ recursive_(recursive),
+ file_type_(file_type) {
// INCLUDE_DOT_DOT must not be specified if recursive.
DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
memset(&find_data_, 0, sizeof(find_data_));
@@ -57,11 +57,11 @@
bool recursive,
int file_type,
const FilePath::StringType& pattern)
- : recursive_(recursive),
+ : has_find_data_(false),
+ find_handle_(INVALID_HANDLE_VALUE),
+ recursive_(recursive),
file_type_(file_type),
- has_find_data_(false),
- pattern_(pattern),
- find_handle_(INVALID_HANDLE_VALUE) {
+ pattern_(pattern) {
// INCLUDE_DOT_DOT must not be specified if recursive.
DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
memset(&find_data_, 0, sizeof(find_data_));
diff --git a/base/files/file_path.cc b/base/files/file_path.cc
index de8927a..10dcf94 100644
--- a/base/files/file_path.cc
+++ b/base/files/file_path.cc
@@ -10,9 +10,6 @@
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/pickle.h"
-
-// These includes are just for the *Hack functions, and should be removed
-// when those functions are removed.
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "base/strings/sys_string_conversions.h"
@@ -31,7 +28,8 @@
namespace base {
-typedef FilePath::StringType StringType;
+using StringType = FilePath::StringType;
+using StringPieceType = FilePath::StringPieceType;
namespace {
@@ -45,7 +43,7 @@
// otherwise returns npos. This can only be true on Windows, when a pathname
// begins with a letter followed by a colon. On other platforms, this always
// returns npos.
-StringType::size_type FindDriveLetter(const StringType& path) {
+StringPieceType::size_type FindDriveLetter(StringPieceType path) {
#if defined(FILE_PATH_USES_DRIVE_LETTERS)
// This is dependent on an ASCII-based character set, but that's a
// reasonable assumption. iswalpha can be too inclusive here.
@@ -59,26 +57,25 @@
}
#if defined(FILE_PATH_USES_DRIVE_LETTERS)
-bool EqualDriveLetterCaseInsensitive(const StringType& a,
- const StringType& b) {
+bool EqualDriveLetterCaseInsensitive(StringPieceType a, StringPieceType b) {
size_t a_letter_pos = FindDriveLetter(a);
size_t b_letter_pos = FindDriveLetter(b);
if (a_letter_pos == StringType::npos || b_letter_pos == StringType::npos)
return a == b;
- StringType a_letter(a.substr(0, a_letter_pos + 1));
- StringType b_letter(b.substr(0, b_letter_pos + 1));
- if (!StartsWith(a_letter, b_letter, false))
+ StringPieceType a_letter(a.substr(0, a_letter_pos + 1));
+ StringPieceType b_letter(b.substr(0, b_letter_pos + 1));
+ if (!StartsWith(a_letter, b_letter, CompareCase::INSENSITIVE_ASCII))
return false;
- StringType a_rest(a.substr(a_letter_pos + 1));
- StringType b_rest(b.substr(b_letter_pos + 1));
+ StringPieceType a_rest(a.substr(a_letter_pos + 1));
+ StringPieceType b_rest(b.substr(b_letter_pos + 1));
return a_rest == b_rest;
}
#endif // defined(FILE_PATH_USES_DRIVE_LETTERS)
-bool IsPathAbsolute(const StringType& path) {
+bool IsPathAbsolute(StringPieceType path) {
#if defined(FILE_PATH_USES_DRIVE_LETTERS)
StringType::size_type letter = FindDriveLetter(path);
if (letter != StringType::npos) {
@@ -177,7 +174,8 @@
FilePath::FilePath(const FilePath& that) : path_(that.path_) {
}
-FilePath::FilePath(const StringType& path) : path_(path) {
+FilePath::FilePath(StringPieceType path) {
+ path.CopyToString(&path_);
StringType::size_type nul_pos = path_.find(kStringTerminator);
if (nul_pos != StringType::npos)
path_.erase(nul_pos, StringType::npos);
@@ -279,7 +277,7 @@
// never case sensitive.
if ((FindDriveLetter(*parent_comp) != StringType::npos) &&
(FindDriveLetter(*child_comp) != StringType::npos)) {
- if (!StartsWith(*parent_comp, *child_comp, false))
+ if (!StartsWith(*parent_comp, *child_comp, CompareCase::INSENSITIVE_ASCII))
return false;
++parent_comp;
++child_comp;
@@ -404,7 +402,7 @@
return FilePath(path_.substr(0, dot));
}
-FilePath FilePath::InsertBeforeExtension(const StringType& suffix) const {
+FilePath FilePath::InsertBeforeExtension(StringPieceType suffix) const {
if (suffix.empty())
return FilePath(path_);
@@ -413,27 +411,27 @@
StringType ext = Extension();
StringType ret = RemoveExtension().value();
- ret.append(suffix);
+ suffix.AppendToString(&ret);
ret.append(ext);
return FilePath(ret);
}
-FilePath FilePath::InsertBeforeExtensionASCII(const StringPiece& suffix)
- const {
+FilePath FilePath::InsertBeforeExtensionASCII(StringPiece suffix) const {
DCHECK(IsStringASCII(suffix));
#if defined(OS_WIN)
- return InsertBeforeExtension(ASCIIToUTF16(suffix.as_string()));
+ return InsertBeforeExtension(ASCIIToUTF16(suffix));
#elif defined(OS_POSIX)
- return InsertBeforeExtension(suffix.as_string());
+ return InsertBeforeExtension(suffix);
#endif
}
-FilePath FilePath::AddExtension(const StringType& extension) const {
+FilePath FilePath::AddExtension(StringPieceType extension) const {
if (IsEmptyOrSpecialCase(BaseName().value()))
return FilePath();
// If the new extension is "" or ".", then just return the current FilePath.
- if (extension.empty() || extension == StringType(1, kExtensionSeparator))
+ if (extension.empty() ||
+ (extension.size() == 1 && extension[0] == kExtensionSeparator))
return *this;
StringType str = path_;
@@ -441,27 +439,28 @@
*(str.end() - 1) != kExtensionSeparator) {
str.append(1, kExtensionSeparator);
}
- str.append(extension);
+ extension.AppendToString(&str);
return FilePath(str);
}
-FilePath FilePath::ReplaceExtension(const StringType& extension) const {
+FilePath FilePath::ReplaceExtension(StringPieceType extension) const {
if (IsEmptyOrSpecialCase(BaseName().value()))
return FilePath();
FilePath no_ext = RemoveExtension();
// If the new extension is "" or ".", then just remove the current extension.
- if (extension.empty() || extension == StringType(1, kExtensionSeparator))
+ if (extension.empty() ||
+ (extension.size() == 1 && extension[0] == kExtensionSeparator))
return no_ext;
StringType str = no_ext.value();
if (extension[0] != kExtensionSeparator)
str.append(1, kExtensionSeparator);
- str.append(extension);
+ extension.AppendToString(&str);
return FilePath(str);
}
-bool FilePath::MatchesExtension(const StringType& extension) const {
+bool FilePath::MatchesExtension(StringPieceType extension) const {
DCHECK(extension.empty() || extension[0] == kExtensionSeparator);
StringType current_extension = Extension();
@@ -472,17 +471,17 @@
return FilePath::CompareEqualIgnoreCase(extension, current_extension);
}
-FilePath FilePath::Append(const StringType& component) const {
- const StringType* appended = &component;
+FilePath FilePath::Append(StringPieceType component) const {
+ StringPieceType appended = component;
StringType without_nuls;
StringType::size_type nul_pos = component.find(kStringTerminator);
- if (nul_pos != StringType::npos) {
- without_nuls = component.substr(0, nul_pos);
- appended = &without_nuls;
+ if (nul_pos != StringPieceType::npos) {
+ component.substr(0, nul_pos).CopyToString(&without_nuls);
+ appended = StringPieceType(without_nuls);
}
- DCHECK(!IsPathAbsolute(*appended));
+ DCHECK(!IsPathAbsolute(appended));
if (path_.compare(kCurrentDirectory) == 0) {
// Append normally doesn't do any normalization, but as a special case,
@@ -492,7 +491,7 @@
// it's likely in practice to wind up with FilePath objects containing
// only kCurrentDirectory when calling DirName on a single relative path
// component.
- return FilePath(*appended);
+ return FilePath(appended);
}
FilePath new_path(path_);
@@ -501,7 +500,7 @@
// Don't append a separator if the path is empty (indicating the current
// directory) or if the path component is empty (indicating nothing to
// append).
- if (appended->length() > 0 && new_path.path_.length() > 0) {
+ if (appended.length() > 0 && new_path.path_.length() > 0) {
// Don't append a separator if the path still ends with a trailing
// separator after stripping (indicating the root directory).
if (!IsSeparator(new_path.path_[new_path.path_.length() - 1])) {
@@ -512,7 +511,7 @@
}
}
- new_path.path_.append(*appended);
+ appended.AppendToString(&new_path.path_);
return new_path;
}
@@ -520,12 +519,12 @@
return Append(component.value());
}
-FilePath FilePath::AppendASCII(const StringPiece& component) const {
+FilePath FilePath::AppendASCII(StringPiece component) const {
DCHECK(base::IsStringASCII(component));
#if defined(OS_WIN)
- return Append(ASCIIToUTF16(component.as_string()));
+ return Append(ASCIIToUTF16(component));
#elif defined(OS_POSIX)
- return Append(component.as_string());
+ return Append(component);
#endif
}
@@ -680,17 +679,17 @@
}
#if defined(OS_WIN)
-// Windows specific implementation of file string comparisons
+// Windows specific implementation of file string comparisons.
-int FilePath::CompareIgnoreCase(const StringType& string1,
- const StringType& string2) {
+int FilePath::CompareIgnoreCase(StringPieceType string1,
+ StringPieceType string2) {
// Perform character-wise upper case comparison rather than using the
// fully Unicode-aware CompareString(). For details see:
// http://blogs.msdn.com/michkap/archive/2005/10/17/481600.aspx
- StringType::const_iterator i1 = string1.begin();
- StringType::const_iterator i2 = string2.begin();
- StringType::const_iterator string1end = string1.end();
- StringType::const_iterator string2end = string2.end();
+ StringPieceType::const_iterator i1 = string1.begin();
+ StringPieceType::const_iterator i2 = string2.begin();
+ StringPieceType::const_iterator string1end = string1.end();
+ StringPieceType::const_iterator string2end = string2.end();
for ( ; i1 != string1end && i2 != string2end; ++i1, ++i2) {
wchar_t c1 =
(wchar_t)LOWORD(::CharUpperW((LPWSTR)(DWORD_PTR)MAKELONG(*i1, 0)));
@@ -709,7 +708,7 @@
}
#elif defined(OS_MACOSX)
-// Mac OS X specific implementation of file string comparisons
+// Mac OS X specific implementation of file string comparisons.
// cf. http://developer.apple.com/mac/library/technotes/tn/tn1150.html#UnicodeSubtleties
//
@@ -1153,25 +1152,23 @@
return codepoint;
}
-} // anonymous namespace
+} // namespace
// Special UTF-8 version of FastUnicodeCompare. Cf:
// http://developer.apple.com/mac/library/technotes/tn/tn1150.html#StringComparisonAlgorithm
// The input strings must be in the special HFS decomposed form.
-int FilePath::HFSFastUnicodeCompare(const StringType& string1,
- const StringType& string2) {
+int FilePath::HFSFastUnicodeCompare(StringPieceType string1,
+ StringPieceType string2) {
int length1 = string1.length();
int length2 = string2.length();
int index1 = 0;
int index2 = 0;
for (;;) {
- int codepoint1 = HFSReadNextNonIgnorableCodepoint(string1.c_str(),
- length1,
- &index1);
- int codepoint2 = HFSReadNextNonIgnorableCodepoint(string2.c_str(),
- length2,
- &index2);
+ int codepoint1 =
+ HFSReadNextNonIgnorableCodepoint(string1.data(), length1, &index1);
+ int codepoint2 =
+ HFSReadNextNonIgnorableCodepoint(string2.data(), length2, &index2);
if (codepoint1 != codepoint2)
return (codepoint1 < codepoint2) ? -1 : 1;
if (codepoint1 == 0) {
@@ -1182,15 +1179,10 @@
}
}
-StringType FilePath::GetHFSDecomposedForm(const StringType& string) {
- ScopedCFTypeRef<CFStringRef> cfstring(
- CFStringCreateWithBytesNoCopy(
- NULL,
- reinterpret_cast<const UInt8*>(string.c_str()),
- string.length(),
- kCFStringEncodingUTF8,
- false,
- kCFAllocatorNull));
+StringType FilePath::GetHFSDecomposedForm(StringPieceType string) {
+ ScopedCFTypeRef<CFStringRef> cfstring(CFStringCreateWithBytesNoCopy(
+ NULL, reinterpret_cast<const UInt8*>(string.data()), string.length(),
+ kCFStringEncodingUTF8, false, kCFAllocatorNull));
// Query the maximum length needed to store the result. In most cases this
// will overestimate the required space. The return value also already
// includes the space needed for a terminating 0.
@@ -1215,8 +1207,8 @@
return result;
}
-int FilePath::CompareIgnoreCase(const StringType& string1,
- const StringType& string2) {
+int FilePath::CompareIgnoreCase(StringPieceType string1,
+ StringPieceType string2) {
// Quick checks for empty strings - these speed things up a bit and make the
// following code cleaner.
if (string1.empty())
@@ -1230,22 +1222,12 @@
// GetHFSDecomposedForm() returns an empty string in an error case.
if (hfs1.empty() || hfs2.empty()) {
NOTREACHED();
- ScopedCFTypeRef<CFStringRef> cfstring1(
- CFStringCreateWithBytesNoCopy(
- NULL,
- reinterpret_cast<const UInt8*>(string1.c_str()),
- string1.length(),
- kCFStringEncodingUTF8,
- false,
- kCFAllocatorNull));
- ScopedCFTypeRef<CFStringRef> cfstring2(
- CFStringCreateWithBytesNoCopy(
- NULL,
- reinterpret_cast<const UInt8*>(string2.c_str()),
- string2.length(),
- kCFStringEncodingUTF8,
- false,
- kCFAllocatorNull));
+ ScopedCFTypeRef<CFStringRef> cfstring1(CFStringCreateWithBytesNoCopy(
+ NULL, reinterpret_cast<const UInt8*>(string1.data()), string1.length(),
+ kCFStringEncodingUTF8, false, kCFAllocatorNull));
+ ScopedCFTypeRef<CFStringRef> cfstring2(CFStringCreateWithBytesNoCopy(
+ NULL, reinterpret_cast<const UInt8*>(string2.data()), string2.length(),
+ kCFStringEncodingUTF8, false, kCFAllocatorNull));
return CFStringCompare(cfstring1,
cfstring2,
kCFCompareCaseInsensitive);
@@ -1256,11 +1238,12 @@
#else // << WIN. MACOSX | other (POSIX) >>
-// Generic (POSIX) implementation of file string comparison.
-// TODO(rolandsteiner) check if this is sufficient/correct.
-int FilePath::CompareIgnoreCase(const StringType& string1,
- const StringType& string2) {
- int comparison = strcasecmp(string1.c_str(), string2.c_str());
+// Generic Posix system comparisons.
+int FilePath::CompareIgnoreCase(StringPieceType string1,
+ StringPieceType string2) {
+ // Specifically need null termianted strings for this API call.
+ int comparison =
+ strcasecmp(string1.as_string().c_str(), string2.as_string().c_str());
if (comparison < 0)
return -1;
if (comparison > 0)
diff --git a/base/files/file_path.h b/base/files/file_path.h
index 0c84af6..f10503e 100644
--- a/base/files/file_path.h
+++ b/base/files/file_path.h
@@ -112,7 +112,7 @@
#include "base/compiler_specific.h"
#include "base/containers/hash_tables.h"
#include "base/strings/string16.h"
-#include "base/strings/string_piece.h" // For implicit conversions.
+#include "base/strings/string_piece.h"
#include "build/build_config.h"
// Windows-style drive letter support and pathname separator characters can be
@@ -144,6 +144,7 @@
typedef std::wstring StringType;
#endif // OS_WIN
+ typedef BasicStringPiece<StringType> StringPieceType;
typedef StringType::value_type CharType;
// Null-terminated array of separators used to separate components in
@@ -166,7 +167,7 @@
FilePath();
FilePath(const FilePath& that);
- explicit FilePath(const StringType& path);
+ explicit FilePath(StringPieceType path);
~FilePath();
FilePath& operator=(const FilePath& that);
@@ -267,26 +268,24 @@
// path == "jojo.jpg" suffix == " (1)", returns "jojo (1).jpg"
// path == "C:\pics\jojo" suffix == " (1)", returns "C:\pics\jojo (1)"
// path == "C:\pics.old\jojo" suffix == " (1)", returns "C:\pics.old\jojo (1)"
- FilePath InsertBeforeExtension(
- const StringType& suffix) const WARN_UNUSED_RESULT;
- FilePath InsertBeforeExtensionASCII(
- const base::StringPiece& suffix) const WARN_UNUSED_RESULT;
+ FilePath InsertBeforeExtension(StringPieceType suffix) const
+ WARN_UNUSED_RESULT;
+ FilePath InsertBeforeExtensionASCII(StringPiece suffix) const
+ WARN_UNUSED_RESULT;
// Adds |extension| to |file_name|. Returns the current FilePath if
// |extension| is empty. Returns "" if BaseName() == "." or "..".
- FilePath AddExtension(
- const StringType& extension) const WARN_UNUSED_RESULT;
+ FilePath AddExtension(StringPieceType extension) const WARN_UNUSED_RESULT;
// Replaces the extension of |file_name| with |extension|. If |file_name|
// does not have an extension, then |extension| is added. If |extension| is
// empty, then the extension is removed from |file_name|.
// Returns "" if BaseName() == "." or "..".
- FilePath ReplaceExtension(
- const StringType& extension) const WARN_UNUSED_RESULT;
+ FilePath ReplaceExtension(StringPieceType extension) const WARN_UNUSED_RESULT;
// Returns true if the file path matches the specified extension. The test is
// case insensitive. Don't forget the leading period if appropriate.
- bool MatchesExtension(const StringType& extension) const;
+ bool MatchesExtension(StringPieceType extension) const;
// Returns a FilePath by appending a separator and the supplied path
// component to this object's path. Append takes care to avoid adding
@@ -294,7 +293,7 @@
// If this object's path is kCurrentDirectory, a new FilePath corresponding
// only to |component| is returned. |component| must be a relative path;
// it is an error to pass an absolute path.
- FilePath Append(const StringType& component) const WARN_UNUSED_RESULT;
+ FilePath Append(StringPieceType component) const WARN_UNUSED_RESULT;
FilePath Append(const FilePath& component) const WARN_UNUSED_RESULT;
// Although Windows StringType is std::wstring, since the encoding it uses for
@@ -303,8 +302,7 @@
// On Linux, although it can use any 8-bit encoding for paths, we assume that
// ASCII is a valid subset, regardless of the encoding, since many operating
// system paths will always be ASCII.
- FilePath AppendASCII(const base::StringPiece& component)
- const WARN_UNUSED_RESULT;
+ FilePath AppendASCII(StringPiece component) const WARN_UNUSED_RESULT;
// Returns true if this FilePath contains an absolute path. On Windows, an
// absolute path begins with either a drive letter specification followed by
@@ -388,14 +386,14 @@
// on parts of a file path, e.g., just the extension.
// CompareIgnoreCase() returns -1, 0 or 1 for less-than, equal-to and
// greater-than respectively.
- static int CompareIgnoreCase(const StringType& string1,
- const StringType& string2);
- static bool CompareEqualIgnoreCase(const StringType& string1,
- const StringType& string2) {
+ static int CompareIgnoreCase(StringPieceType string1,
+ StringPieceType string2);
+ static bool CompareEqualIgnoreCase(StringPieceType string1,
+ StringPieceType string2) {
return CompareIgnoreCase(string1, string2) == 0;
}
- static bool CompareLessIgnoreCase(const StringType& string1,
- const StringType& string2) {
+ static bool CompareLessIgnoreCase(StringPieceType string1,
+ StringPieceType string2) {
return CompareIgnoreCase(string1, string2) < 0;
}
@@ -405,14 +403,14 @@
// http://developer.apple.com/mac/library/technotes/tn/tn1150.html#UnicodeSubtleties
// for further comments.
// Returns the epmty string if the conversion failed.
- static StringType GetHFSDecomposedForm(const FilePath::StringType& string);
+ static StringType GetHFSDecomposedForm(StringPieceType string);
// Special UTF-8 version of FastUnicodeCompare. Cf:
// http://developer.apple.com/mac/library/technotes/tn/tn1150.html#StringComparisonAlgorithm
// IMPORTANT: The input strings must be in the special HFS decomposed form!
// (cf. above GetHFSDecomposedForm method)
- static int HFSFastUnicodeCompare(const StringType& string1,
- const StringType& string2);
+ static int HFSFastUnicodeCompare(StringPieceType string1,
+ StringPieceType string2);
#endif
#if defined(OS_ANDROID)
diff --git a/base/files/file_posix.cc b/base/files/file_posix.cc
index bb49d2d..7fb617c 100644
--- a/base/files/file_posix.cc
+++ b/base/files/file_posix.cc
@@ -466,7 +466,7 @@
// NaCl doesn't implement system calls to open files directly.
#if !defined(OS_NACL)
// TODO(erikkay): does it make sense to support FLAG_EXCLUSIVE_* here?
-void File::DoInitialize(uint32 flags) {
+void File::DoInitialize(const FilePath& path, uint32 flags) {
ThreadRestrictions::AssertIOAllowed();
DCHECK(!IsValid());
@@ -521,7 +521,7 @@
mode |= S_IRGRP | S_IROTH;
#endif
- int descriptor = HANDLE_EINTR(open(path_.value().c_str(), open_flags, mode));
+ int descriptor = HANDLE_EINTR(open(path.value().c_str(), open_flags, mode));
if (flags & FLAG_OPEN_ALWAYS) {
if (descriptor < 0) {
@@ -529,7 +529,7 @@
if (flags & FLAG_EXCLUSIVE_READ || flags & FLAG_EXCLUSIVE_WRITE)
open_flags |= O_EXCL; // together with O_CREAT implies O_NOFOLLOW
- descriptor = HANDLE_EINTR(open(path_.value().c_str(), open_flags, mode));
+ descriptor = HANDLE_EINTR(open(path.value().c_str(), open_flags, mode));
if (descriptor >= 0)
created_ = true;
}
@@ -544,7 +544,7 @@
created_ = true;
if (flags & FLAG_DELETE_ON_CLOSE)
- unlink(path_.value().c_str());
+ unlink(path.value().c_str());
async_ = ((flags & FLAG_ASYNC) == FLAG_ASYNC);
error_details_ = FILE_OK;
diff --git a/base/files/file_tracing.cc b/base/files/file_tracing.cc
index c25772d..92a5780 100644
--- a/base/files/file_tracing.cc
+++ b/base/files/file_tracing.cc
@@ -13,6 +13,11 @@
}
// static
+bool FileTracing::IsCategoryEnabled() {
+ return g_provider && g_provider->FileTracingCategoryIsEnabled();
+}
+
+// static
void FileTracing::SetProvider(FileTracing::Provider* provider) {
g_provider = provider;
}
@@ -34,19 +39,11 @@
g_provider->FileTracingEventEnd(name_, id_);
}
-bool FileTracing::ScopedTrace::ShouldInitialize() const {
- return g_provider && g_provider->FileTracingCategoryIsEnabled();
-}
-
void FileTracing::ScopedTrace::Initialize(
const char* name, File* file, int64 size) {
- if (!g_provider)
- return;
-
id_ = &file->trace_enabler_;
name_ = name;
-
- g_provider->FileTracingEventBegin(name_, id_, file->path_, size);
+ g_provider->FileTracingEventBegin(name_, id_, file->tracing_path_, size);
}
} // namespace base
diff --git a/base/files/file_tracing.h b/base/files/file_tracing.h
index 149bd78..373fe0e 100644
--- a/base/files/file_tracing.h
+++ b/base/files/file_tracing.h
@@ -12,9 +12,9 @@
#define FILE_TRACING_PREFIX "File"
#define SCOPED_FILE_TRACE_WITH_SIZE(name, size) \
- FileTracing::ScopedTrace scoped_file_trace; \
- if (scoped_file_trace.ShouldInitialize()) \
- scoped_file_trace.Initialize(FILE_TRACING_PREFIX "::" name, this, size)
+ FileTracing::ScopedTrace scoped_file_trace; \
+ if (FileTracing::IsCategoryEnabled()) \
+ scoped_file_trace.Initialize(FILE_TRACING_PREFIX "::" name, this, size)
#define SCOPED_FILE_TRACE(name) SCOPED_FILE_TRACE_WITH_SIZE(name, 0)
@@ -25,8 +25,13 @@
class BASE_EXPORT FileTracing {
public:
+ // Whether the file tracing category is enabled.
+ static bool IsCategoryEnabled();
+
class Provider {
public:
+ virtual ~Provider() = default;
+
// Whether the file tracing category is currently enabled.
virtual bool FileTracingCategoryIsEnabled() const = 0;
@@ -61,9 +66,6 @@
ScopedTrace();
~ScopedTrace();
- // Whether this trace should be initialized or not.
- bool ShouldInitialize() const;
-
// Called only if the tracing category is enabled. |name| is the name of the
// event to trace (e.g. "Read", "Write") and must have an application
// lifetime (e.g. static or literal). |file| is the file being traced; must
diff --git a/base/files/file_win.cc b/base/files/file_win.cc
index 9792852..ccecb70 100644
--- a/base/files/file_win.cc
+++ b/base/files/file_win.cc
@@ -308,7 +308,7 @@
}
}
-void File::DoInitialize(uint32 flags) {
+void File::DoInitialize(const FilePath& path, uint32 flags) {
ThreadRestrictions::AssertIOAllowed();
DCHECK(!IsValid());
@@ -376,8 +376,8 @@
if (flags & FLAG_BACKUP_SEMANTICS)
create_flags |= FILE_FLAG_BACKUP_SEMANTICS;
- file_.Set(CreateFile(path_.value().c_str(), access, sharing, NULL,
- disposition, create_flags, NULL));
+ file_.Set(CreateFile(path.value().c_str(), access, sharing, NULL, disposition,
+ create_flags, NULL));
if (file_.IsValid()) {
error_details_ = FILE_OK;
diff --git a/base/files/memory_mapped_file.cc b/base/files/memory_mapped_file.cc
index bad1792..227a41f 100644
--- a/base/files/memory_mapped_file.cc
+++ b/base/files/memory_mapped_file.cc
@@ -10,18 +10,7 @@
namespace base {
-const MemoryMappedFile::Region MemoryMappedFile::Region::kWholeFile(
- base::LINKER_INITIALIZED);
-
-MemoryMappedFile::Region::Region(base::LinkerInitialized) : offset(0), size(0) {
-}
-
-MemoryMappedFile::Region::Region() : offset(-1), size(-1) {
-}
-
-MemoryMappedFile::Region::Region(int64 offset, int64 size)
- : offset(offset), size(size) {
-}
+const MemoryMappedFile::Region MemoryMappedFile::Region::kWholeFile = {0, 0};
bool MemoryMappedFile::Region::operator==(
const MemoryMappedFile::Region& other) const {
diff --git a/base/files/memory_mapped_file.h b/base/files/memory_mapped_file.h
index 96d1d91..9ff29b9 100644
--- a/base/files/memory_mapped_file.h
+++ b/base/files/memory_mapped_file.h
@@ -28,9 +28,6 @@
struct BASE_EXPORT Region {
static const Region kWholeFile;
- Region();
- Region(int64 offset, int64 size);
-
bool operator==(const Region& other) const;
bool operator!=(const Region& other) const;
@@ -39,10 +36,6 @@
// Length of the region in bytes.
int64 size;
-
- private:
- // Used by kWholeFile.
- Region(base::LinkerInitialized);
};
// Opens an existing file and maps it into memory. Access is restricted to
diff --git a/base/files/memory_mapped_file_unittest.cc b/base/files/memory_mapped_file_unittest.cc
index d0833b5..05b941c 100644
--- a/base/files/memory_mapped_file_unittest.cc
+++ b/base/files/memory_mapped_file_unittest.cc
@@ -107,7 +107,8 @@
MemoryMappedFile map;
File file(temp_file_path(), File::FLAG_OPEN | File::FLAG_READ);
- map.Initialize(file.Pass(), MemoryMappedFile::Region(0, kPartialSize));
+ MemoryMappedFile::Region region = {0, kPartialSize};
+ map.Initialize(file.Pass(), region);
ASSERT_EQ(kPartialSize, map.length());
ASSERT_TRUE(map.data() != NULL);
EXPECT_TRUE(map.IsValid());
@@ -122,7 +123,8 @@
MemoryMappedFile map;
File file(temp_file_path(), File::FLAG_OPEN | File::FLAG_READ);
- map.Initialize(file.Pass(), MemoryMappedFile::Region(kOffset, kPartialSize));
+ MemoryMappedFile::Region region = {kOffset, kPartialSize};
+ map.Initialize(file.Pass(), region);
ASSERT_EQ(kPartialSize, map.length());
ASSERT_TRUE(map.data() != NULL);
EXPECT_TRUE(map.IsValid());
@@ -138,7 +140,8 @@
MemoryMappedFile map;
File file(temp_file_path(), File::FLAG_OPEN | File::FLAG_READ);
- map.Initialize(file.Pass(), MemoryMappedFile::Region(kOffset, kPartialSize));
+ MemoryMappedFile::Region region = {kOffset, kPartialSize};
+ map.Initialize(file.Pass(), region);
ASSERT_EQ(kPartialSize, map.length());
ASSERT_TRUE(map.data() != NULL);
EXPECT_TRUE(map.IsValid());
@@ -154,7 +157,8 @@
MemoryMappedFile map;
File file(temp_file_path(), File::FLAG_OPEN | File::FLAG_READ);
- map.Initialize(file.Pass(), MemoryMappedFile::Region(kOffset, kPartialSize));
+ MemoryMappedFile::Region region = {kOffset, kPartialSize};
+ map.Initialize(file.Pass(), region);
ASSERT_EQ(kPartialSize, map.length());
ASSERT_TRUE(map.data() != NULL);
EXPECT_TRUE(map.IsValid());
diff --git a/base/files/memory_mapped_file_win.cc b/base/files/memory_mapped_file_win.cc
index 4e7e934..8585906 100644
--- a/base/files/memory_mapped_file_win.cc
+++ b/base/files/memory_mapped_file_win.cc
@@ -32,7 +32,7 @@
if (!file_mapping_.IsValid())
return false;
- LARGE_INTEGER map_start = {0};
+ LARGE_INTEGER map_start = {};
SIZE_T map_size = 0;
int32 data_offset = 0;
diff --git a/base/i18n/bidi_line_iterator.cc b/base/i18n/bidi_line_iterator.cc
index 8c81d85..80da731 100644
--- a/base/i18n/bidi_line_iterator.cc
+++ b/base/i18n/bidi_line_iterator.cc
@@ -9,6 +9,25 @@
namespace base {
namespace i18n {
+namespace {
+UBiDiLevel GetParagraphLevelForDirection(TextDirection direction) {
+ switch (direction) {
+ case UNKNOWN_DIRECTION:
+ return UBIDI_DEFAULT_LTR;
+ break;
+ case RIGHT_TO_LEFT:
+ return 1; // Highest RTL level.
+ break;
+ case LEFT_TO_RIGHT:
+ return 0; // Highest LTR level.
+ break;
+ default:
+ NOTREACHED();
+ return 0;
+ }
+}
+} // namespace
+
BiDiLineIterator::BiDiLineIterator() : bidi_(NULL) {
}
@@ -19,15 +38,14 @@
}
}
-bool BiDiLineIterator::Open(const string16& text, bool right_to_left) {
+bool BiDiLineIterator::Open(const string16& text, TextDirection direction) {
DCHECK(!bidi_);
UErrorCode error = U_ZERO_ERROR;
bidi_ = ubidi_openSized(static_cast<int>(text.length()), 0, &error);
if (U_FAILURE(error))
return false;
ubidi_setPara(bidi_, text.data(), static_cast<int>(text.length()),
- right_to_left ? UBIDI_DEFAULT_RTL : UBIDI_DEFAULT_LTR,
- NULL, &error);
+ GetParagraphLevelForDirection(direction), NULL, &error);
return (U_SUCCESS(error) == TRUE);
}
diff --git a/base/i18n/bidi_line_iterator.h b/base/i18n/bidi_line_iterator.h
index 07d9aa2..0bf1ec6 100644
--- a/base/i18n/bidi_line_iterator.h
+++ b/base/i18n/bidi_line_iterator.h
@@ -7,6 +7,7 @@
#include "base/basictypes.h"
#include "base/i18n/base_i18n_export.h"
+#include "base/i18n/rtl.h"
#include "base/strings/string16.h"
#include "third_party/icu/source/common/unicode/ubidi.h"
@@ -23,7 +24,7 @@
// Initializes the bidirectional iterator with the specified text. Returns
// whether initialization succeeded.
- bool Open(const string16& text, bool right_to_left);
+ bool Open(const string16& text, TextDirection direction);
// Returns the number of visual runs in the text, or zero on error.
int CountRuns();
diff --git a/base/i18n/case_conversion.cc b/base/i18n/case_conversion.cc
index 5debc2e..d0eceb9 100644
--- a/base/i18n/case_conversion.cc
+++ b/base/i18n/case_conversion.cc
@@ -4,22 +4,91 @@
#include "base/i18n/case_conversion.h"
+#include "base/numerics/safe_conversions.h"
#include "base/strings/string16.h"
+#include "base/strings/string_util.h"
+#include "third_party/icu/source/common/unicode/uchar.h"
#include "third_party/icu/source/common/unicode/unistr.h"
+#include "third_party/icu/source/common/unicode/ustring.h"
namespace base {
namespace i18n {
-string16 ToLower(const StringPiece16& string) {
- icu::UnicodeString unicode_string(string.data(), string.size());
- unicode_string.toLower();
- return string16(unicode_string.getBuffer(), unicode_string.length());
+namespace {
+
+// Provides a uniform interface for upper/lower/folding which take take
+// slightly varying parameters.
+typedef int32_t (*CaseMapperFunction)(UChar* dest,
+ int32_t dest_capacity,
+ const UChar* src,
+ int32_t src_length,
+ UErrorCode* error);
+
+int32_t ToUpperMapper(UChar* dest,
+ int32_t dest_capacity,
+ const UChar* src,
+ int32_t src_length,
+ UErrorCode* error) {
+ // Use default locale.
+ return u_strToUpper(dest, dest_capacity, src, src_length, NULL, error);
}
-string16 ToUpper(const StringPiece16& string) {
- icu::UnicodeString unicode_string(string.data(), string.size());
- unicode_string.toUpper();
- return string16(unicode_string.getBuffer(), unicode_string.length());
+int32_t ToLowerMapper(UChar* dest,
+ int32_t dest_capacity,
+ const UChar* src,
+ int32_t src_length,
+ UErrorCode* error) {
+ // Use default locale.
+ return u_strToLower(dest, dest_capacity, src, src_length, NULL, error);
+}
+
+int32_t FoldCaseMapper(UChar* dest,
+ int32_t dest_capacity,
+ const UChar* src,
+ int32_t src_length,
+ UErrorCode* error) {
+ return u_strFoldCase(dest, dest_capacity, src, src_length,
+ U_FOLD_CASE_DEFAULT, error);
+}
+
+// Provides similar functionality as UnicodeString::caseMap but on string16.
+string16 CaseMap(StringPiece16 string, CaseMapperFunction case_mapper) {
+ string16 dest;
+ if (string.empty())
+ return dest;
+
+ // Provide an initial guess that the string length won't change. The typical
+ // strings we use will very rarely change length in this process, so don't
+ // optimize for that case.
+ dest.resize(string.size());
+
+ UErrorCode error;
+ do {
+ error = U_ZERO_ERROR;
+
+ // ICU won't terminate the string if there's not enough room for the null
+ // terminator, but will otherwise. So we don't need to save room for that.
+ // Don't use WriteInto, which assumes null terminators.
+ int32_t new_length = case_mapper(
+ &dest[0], saturated_cast<int32_t>(dest.size()), string.data(),
+ saturated_cast<int32_t>(string.size()), &error);
+ dest.resize(new_length);
+ } while (error == U_BUFFER_OVERFLOW_ERROR);
+ return dest;
+}
+
+} // namespace
+
+string16 ToLower(StringPiece16 string) {
+ return CaseMap(string, &ToLowerMapper);
+}
+
+string16 ToUpper(StringPiece16 string) {
+ return CaseMap(string, &ToUpperMapper);
+}
+
+string16 FoldCase(StringPiece16 string) {
+ return CaseMap(string, &FoldCaseMapper);
}
} // namespace i18n
diff --git a/base/i18n/case_conversion.h b/base/i18n/case_conversion.h
index 5d538cc..0631a80 100644
--- a/base/i18n/case_conversion.h
+++ b/base/i18n/case_conversion.h
@@ -12,11 +12,35 @@
namespace base {
namespace i18n {
-// Returns the lower case equivalent of string. Uses ICU's default locale.
-BASE_I18N_EXPORT string16 ToLower(const StringPiece16& string);
+// UNICODE CASE-HANDLING ADVICE
+//
+// In English it's always safe to convert to upper-case or lower-case text
+// and get a good answer. But some languages have rules specific to those
+// locales. One example is the Turkish I:
+// http://www.i18nguy.com/unicode/turkish-i18n.html
+//
+// ToLower/ToUpper use the current ICU locale which will take into account
+// the user language preference. Use this when dealing with user typing.
+//
+// FoldCase canonicalizes to a standardized form independent of the current
+// locale. Use this when comparing general Unicode strings that don't
+// necessarily belong in the user's current locale (like commands, protocol
+// names, other strings from the web) for case-insensitive equality.
+//
+// Note that case conversions will change the length of the string in some
+// not-uncommon cases. Never assume that the output is the same length as
+// the input.
-// Returns the upper case equivalent of string. Uses ICU's default locale.
-BASE_I18N_EXPORT string16 ToUpper(const StringPiece16& string);
+// Returns the lower case equivalent of string. Uses ICU's current locale.
+BASE_I18N_EXPORT string16 ToLower(StringPiece16 string);
+
+// Returns the upper case equivalent of string. Uses ICU's current locale.
+BASE_I18N_EXPORT string16 ToUpper(StringPiece16 string);
+
+// Convert the given string to a canonical case, independent of the current
+// locale. For ASCII the canonical form is lower case.
+// See http://unicode.org/faq/casemap_charprop.html#2
+BASE_I18N_EXPORT string16 FoldCase(StringPiece16 string);
} // namespace i18n
} // namespace base
diff --git a/base/i18n/case_conversion_unittest.cc b/base/i18n/case_conversion_unittest.cc
index 75e5ad2..dc5bc1f 100644
--- a/base/i18n/case_conversion_unittest.cc
+++ b/base/i18n/case_conversion_unittest.cc
@@ -9,39 +9,47 @@
#include "third_party/icu/source/i18n/unicode/usearch.h"
namespace base {
+namespace i18n {
+
namespace {
+const wchar_t kNonASCIIMixed[] =
+ L"\xC4\xD6\xE4\xF6\x20\xCF\xEF\x20\xF7\x25"
+ L"\xA4\x23\x2A\x5E\x60\x40\xA3\x24\x2030\x201A\x7E\x20\x1F07\x1F0F"
+ L"\x20\x1E00\x1E01";
+const wchar_t kNonASCIILower[] =
+ L"\xE4\xF6\xE4\xF6\x20\xEF\xEF"
+ L"\x20\xF7\x25\xA4\x23\x2A\x5E\x60\x40\xA3\x24\x2030\x201A\x7E\x20\x1F07"
+ L"\x1F07\x20\x1E01\x1E01";
+const wchar_t kNonASCIIUpper[] =
+ L"\xC4\xD6\xC4\xD6\x20\xCF\xCF"
+ L"\x20\xF7\x25\xA4\x23\x2A\x5E\x60\x40\xA3\x24\x2030\x201A\x7E\x20\x1F0F"
+ L"\x1F0F\x20\x1E00\x1E00";
+
+} // namespace
+
// Test upper and lower case string conversion.
TEST(CaseConversionTest, UpperLower) {
const string16 mixed(ASCIIToUTF16("Text with UPPer & lowER casE."));
const string16 expected_lower(ASCIIToUTF16("text with upper & lower case."));
const string16 expected_upper(ASCIIToUTF16("TEXT WITH UPPER & LOWER CASE."));
- string16 result = base::i18n::ToLower(mixed);
+ string16 result = ToLower(mixed);
EXPECT_EQ(expected_lower, result);
- result = base::i18n::ToUpper(mixed);
+ result = ToUpper(mixed);
EXPECT_EQ(expected_upper, result);
}
TEST(CaseConversionTest, NonASCII) {
- const string16 mixed(WideToUTF16(
- L"\xC4\xD6\xE4\xF6\x20\xCF\xEF\x20\xF7\x25"
- L"\xA4\x23\x2A\x5E\x60\x40\xA3\x24\x2030\x201A\x7E\x20\x1F07\x1F0F"
- L"\x20\x1E00\x1E01"));
- const string16 expected_lower(WideToUTF16(
- L"\xE4\xF6\xE4\xF6\x20\xEF\xEF"
- L"\x20\xF7\x25\xA4\x23\x2A\x5E\x60\x40\xA3\x24\x2030\x201A\x7E\x20\x1F07"
- L"\x1F07\x20\x1E01\x1E01"));
- const string16 expected_upper(WideToUTF16(
- L"\xC4\xD6\xC4\xD6\x20\xCF\xCF"
- L"\x20\xF7\x25\xA4\x23\x2A\x5E\x60\x40\xA3\x24\x2030\x201A\x7E\x20\x1F0F"
- L"\x1F0F\x20\x1E00\x1E00"));
+ const string16 mixed(WideToUTF16(kNonASCIIMixed));
+ const string16 expected_lower(WideToUTF16(kNonASCIILower));
+ const string16 expected_upper(WideToUTF16(kNonASCIIUpper));
- string16 result = base::i18n::ToLower(mixed);
+ string16 result = ToLower(mixed);
EXPECT_EQ(expected_lower, result);
- result = base::i18n::ToUpper(mixed);
+ result = ToUpper(mixed);
EXPECT_EQ(expected_upper, result);
}
@@ -53,10 +61,10 @@
std::string default_locale(uloc_getDefault());
i18n::SetICUDefaultLocale("en_US");
- string16 result = base::i18n::ToLower(mixed);
+ string16 result = ToLower(mixed);
EXPECT_EQ(expected_lower, result);
- result = base::i18n::ToUpper(mixed);
+ result = ToUpper(mixed);
EXPECT_EQ(expected_upper, result);
i18n::SetICUDefaultLocale("tr");
@@ -64,16 +72,48 @@
const string16 expected_lower_turkish(WideToUTF16(L"\x131\x131"));
const string16 expected_upper_turkish(WideToUTF16(L"\x49\x49"));
- result = base::i18n::ToLower(mixed);
+ result = ToLower(mixed);
EXPECT_EQ(expected_lower_turkish, result);
- result = base::i18n::ToUpper(mixed);
+ result = ToUpper(mixed);
EXPECT_EQ(expected_upper_turkish, result);
- base::i18n::SetICUDefaultLocale(default_locale.data());
+ SetICUDefaultLocale(default_locale.data());
}
-} // namespace
+TEST(CaseConversionTest, FoldCase) {
+ // Simple ASCII, should lower-case.
+ EXPECT_EQ(ASCIIToUTF16("hello, world"),
+ FoldCase(ASCIIToUTF16("Hello, World")));
+
+ // Non-ASCII cases from above. They should all fold to the same result.
+ EXPECT_EQ(FoldCase(WideToUTF16(kNonASCIIMixed)),
+ FoldCase(WideToUTF16(kNonASCIILower)));
+ EXPECT_EQ(FoldCase(WideToUTF16(kNonASCIIMixed)),
+ FoldCase(WideToUTF16(kNonASCIIUpper)));
+
+ // Turkish cases from above. This is the lower-case expected result from the
+ // US locale. It should be the same even when the current locale is Turkish.
+ const string16 turkish(WideToUTF16(L"\x49\x131"));
+ const string16 turkish_expected(WideToUTF16(L"\x69\x131"));
+
+ std::string default_locale(uloc_getDefault());
+ i18n::SetICUDefaultLocale("en_US");
+ EXPECT_EQ(turkish_expected, FoldCase(turkish));
+
+ i18n::SetICUDefaultLocale("tr");
+ EXPECT_EQ(turkish_expected, FoldCase(turkish));
+
+ // Test a case that gets bigger when processed.
+ // U+130 = LATIN CAPITAL LETTER I WITH DOT ABOVE gets folded to a lower case
+ // "i" followed by U+307 COMBINING DOT ABOVE.
+ EXPECT_EQ(WideToUTF16(L"i\u0307j"), FoldCase(WideToUTF16(L"\u0130j")));
+
+ // U+00DF (SHARP S) and U+1E9E (CAPIRAL SHARP S) are both folded to "ss".
+ EXPECT_EQ(ASCIIToUTF16("ssss"), FoldCase(WideToUTF16(L"\u00DF\u1E9E")));
+}
+
+} // namespace i18n
} // namespace base
diff --git a/base/i18n/icu_util.cc b/base/i18n/icu_util.cc
index a9f0b12..1dd54cd 100644
--- a/base/i18n/icu_util.cc
+++ b/base/i18n/icu_util.cc
@@ -23,6 +23,10 @@
#include "third_party/icu/source/i18n/unicode/timezone.h"
#endif
+#if defined(OS_ANDROID)
+#include "base/android/apk_assets.h"
+#endif
+
#if defined(OS_MACOSX)
#include "base/mac/foundation_util.h"
#endif
@@ -34,11 +38,6 @@
namespace base {
namespace i18n {
-// Use an unversioned file name to simplify a icu version update down the road.
-// No need to change the filename in multiple places (gyp files, windows
-// build pkg configurations, etc). 'l' stands for Little Endian.
-// This variable is exported through the header file.
-const char kIcuDataFileName[] = "icudtl.dat";
#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_SHARED
#define ICU_UTIL_DATA_SYMBOL "icudt" U_ICU_VERSION_SHORT "_dat"
#if defined(OS_WIN)
@@ -47,44 +46,140 @@
#endif
namespace {
-
+#if !defined(OS_NACL)
#if !defined(NDEBUG)
// Assert that we are not called more than once. Even though calling this
// function isn't harmful (ICU can handle it), being called twice probably
// indicates a programming error.
-#if !defined(OS_NACL)
-bool g_called_once = false;
-#endif
bool g_check_called_once = true;
+bool g_called_once = false;
+#endif // !defined(NDEBUG)
+
+#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
+// Use an unversioned file name to simplify a icu version update down the road.
+// No need to change the filename in multiple places (gyp files, windows
+// build pkg configurations, etc). 'l' stands for Little Endian.
+// This variable is exported through the header file.
+const char kIcuDataFileName[] = "icudtl.dat";
+#if defined(OS_ANDROID)
+const char kAndroidAssetsIcuDataFileName[] = "assets/icudtl.dat";
#endif
+
+// File handle intentionally never closed. Not using File here because its
+// Windows implementation guards against two instances owning the same
+// PlatformFile (which we allow since we know it is never freed).
+const PlatformFile kInvalidPlatformFile =
+#if defined(OS_WIN)
+ INVALID_HANDLE_VALUE;
+#else
+ -1;
+#endif
+PlatformFile g_icudtl_pf = kInvalidPlatformFile;
+MemoryMappedFile* g_icudtl_mapped_file = nullptr;
+MemoryMappedFile::Region g_icudtl_region;
+
+void LazyInitIcuDataFile() {
+ if (g_icudtl_pf != kInvalidPlatformFile) {
+ return;
+ }
+#if defined(OS_ANDROID)
+ int fd = base::android::OpenApkAsset(kAndroidAssetsIcuDataFileName,
+ &g_icudtl_region);
+ g_icudtl_pf = fd;
+ if (fd != -1) {
+ return;
+ }
+// For unit tests, data file is located on disk, so try there as a fallback.
+#endif // defined(OS_ANDROID)
+#if !defined(OS_MACOSX)
+ FilePath data_path;
+#if defined(OS_WIN)
+ // The data file will be in the same directory as the current module.
+ bool path_ok = PathService::Get(DIR_MODULE, &data_path);
+ wchar_t tmp_buffer[_MAX_PATH] = {0};
+ wcscpy_s(tmp_buffer, data_path.value().c_str());
+ debug::Alias(tmp_buffer);
+ CHECK(path_ok); // TODO(scottmg): http://crbug.com/445616
+#elif defined(OS_ANDROID)
+ bool path_ok = PathService::Get(DIR_ANDROID_APP_DATA, &data_path);
+#else
+ // For now, expect the data file to be alongside the executable.
+ // This is sufficient while we work on unit tests, but will eventually
+ // likely live in a data directory.
+ bool path_ok = PathService::Get(DIR_EXE, &data_path);
+#endif
+ DCHECK(path_ok);
+ data_path = data_path.AppendASCII(kIcuDataFileName);
+
+#if defined(OS_WIN)
+ // TODO(scottmg): http://crbug.com/445616
+ wchar_t tmp_buffer2[_MAX_PATH] = {0};
+ wcscpy_s(tmp_buffer2, data_path.value().c_str());
+ debug::Alias(tmp_buffer2);
+#endif
+
+#else
+ // Assume it is in the framework bundle's Resources directory.
+ ScopedCFTypeRef<CFStringRef> data_file_name(
+ SysUTF8ToCFStringRef(kIcuDataFileName));
+ FilePath data_path = mac::PathForFrameworkBundleResource(data_file_name);
+ if (data_path.empty()) {
+ LOG(ERROR) << kIcuDataFileName << " not found in bundle";
+ return;
+ }
+#endif // !defined(OS_MACOSX)
+ File file(data_path, File::FLAG_OPEN | File::FLAG_READ);
+ if (file.IsValid()) {
+ g_icudtl_pf = file.TakePlatformFile();
+ g_icudtl_region = MemoryMappedFile::Region::kWholeFile;
+ }
}
+bool InitializeICUWithFileDescriptorInternal(
+ PlatformFile data_fd,
+ const MemoryMappedFile::Region& data_region) {
+ // This can be called multiple times in tests.
+ if (g_icudtl_mapped_file) {
+ return true;
+ }
+ if (data_fd == kInvalidPlatformFile) {
+ return false;
+ }
+
+ scoped_ptr<MemoryMappedFile> icudtl_mapped_file(new MemoryMappedFile());
+ if (!icudtl_mapped_file->Initialize(File(data_fd), data_region)) {
+ LOG(ERROR) << "Couldn't mmap icu data file";
+ return false;
+ }
+ g_icudtl_mapped_file = icudtl_mapped_file.release();
+
+ UErrorCode err = U_ZERO_ERROR;
+ udata_setCommonData(const_cast<uint8*>(g_icudtl_mapped_file->data()), &err);
+ return err == U_ZERO_ERROR;
+}
+#endif // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
+#endif // !defined(OS_NACL)
+
+} // namespace
+
#if !defined(OS_NACL)
+#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
bool InitializeICUWithFileDescriptor(
PlatformFile data_fd,
- MemoryMappedFile::Region data_region) {
+ const MemoryMappedFile::Region& data_region) {
#if !defined(NDEBUG)
DCHECK(!g_check_called_once || !g_called_once);
g_called_once = true;
#endif
-
-#if (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_STATIC)
- // The ICU data is statically linked.
- return true;
-#elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE)
- CR_DEFINE_STATIC_LOCAL(MemoryMappedFile, mapped_file, ());
- if (!mapped_file.IsValid()) {
- if (!mapped_file.Initialize(File(data_fd), data_region)) {
- LOG(ERROR) << "Couldn't mmap icu data file";
- return false;
- }
- }
- UErrorCode err = U_ZERO_ERROR;
- udata_setCommonData(const_cast<uint8*>(mapped_file.data()), &err);
- return err == U_ZERO_ERROR;
-#endif // ICU_UTIL_DATA_FILE
+ return InitializeICUWithFileDescriptorInternal(data_fd, data_region);
}
+PlatformFile GetIcuDataFileHandle(MemoryMappedFile::Region* out_region) {
+ CHECK_NE(g_icudtl_pf, kInvalidPlatformFile);
+ *out_region = g_icudtl_region;
+ return g_icudtl_pf;
+}
+#endif // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
bool InitializeICU() {
#if !defined(NDEBUG)
@@ -123,60 +218,9 @@
// it is needed. This can fail if the process is sandboxed at that time.
// Instead, we map the file in and hand off the data so the sandbox won't
// cause any problems.
-
- // Chrome doesn't normally shut down ICU, so the mapped data shouldn't ever
- // be released.
- CR_DEFINE_STATIC_LOCAL(MemoryMappedFile, mapped_file, ());
- if (!mapped_file.IsValid()) {
-#if !defined(OS_MACOSX)
- FilePath data_path;
-#if defined(OS_WIN)
- // The data file will be in the same directory as the current module.
- bool path_ok = PathService::Get(DIR_MODULE, &data_path);
- wchar_t tmp_buffer[_MAX_PATH] = {0};
- wcscpy_s(tmp_buffer, data_path.value().c_str());
- debug::Alias(tmp_buffer);
- CHECK(path_ok); // TODO(scottmg): http://crbug.com/445616
-#elif defined(OS_ANDROID)
- bool path_ok = PathService::Get(DIR_ANDROID_APP_DATA, &data_path);
-#else
- // For now, expect the data file to be alongside the executable.
- // This is sufficient while we work on unit tests, but will eventually
- // likely live in a data directory.
- bool path_ok = PathService::Get(DIR_EXE, &data_path);
-#endif
- DCHECK(path_ok);
- data_path = data_path.AppendASCII(kIcuDataFileName);
-
-#if defined(OS_WIN)
- // TODO(scottmg): http://crbug.com/445616
- wchar_t tmp_buffer2[_MAX_PATH] = {0};
- wcscpy_s(tmp_buffer2, data_path.value().c_str());
- debug::Alias(tmp_buffer2);
-#endif
-
-#else
- // Assume it is in the framework bundle's Resources directory.
- ScopedCFTypeRef<CFStringRef> data_file_name(
- SysUTF8ToCFStringRef(kIcuDataFileName));
- FilePath data_path =
- mac::PathForFrameworkBundleResource(data_file_name);
- if (data_path.empty()) {
- LOG(ERROR) << kIcuDataFileName << " not found in bundle";
- return false;
- }
-#endif // OS check
- if (!mapped_file.Initialize(data_path)) {
-#if defined(OS_WIN)
- CHECK(false); // TODO(scottmg): http://crbug.com/445616
-#endif
- LOG(ERROR) << "Couldn't mmap " << data_path.AsUTF8Unsafe();
- return false;
- }
- }
- UErrorCode err = U_ZERO_ERROR;
- udata_setCommonData(const_cast<uint8*>(mapped_file.data()), &err);
- result = (err == U_ZERO_ERROR);
+ LazyInitIcuDataFile();
+ result =
+ InitializeICUWithFileDescriptorInternal(g_icudtl_pf, g_icudtl_region);
#if defined(OS_WIN)
CHECK(result); // TODO(scottmg): http://crbug.com/445616
#endif
@@ -193,10 +237,10 @@
#endif
return result;
}
-#endif
+#endif // !defined(OS_NACL)
void AllowMultipleInitializeCallsForTesting() {
-#if !defined(NDEBUG)
+#if !defined(NDEBUG) && !defined(OS_NACL)
g_check_called_once = false;
#endif
}
diff --git a/base/i18n/icu_util.h b/base/i18n/icu_util.h
index 65de0ad..1de4172 100644
--- a/base/i18n/icu_util.h
+++ b/base/i18n/icu_util.h
@@ -12,19 +12,24 @@
namespace base {
namespace i18n {
-BASE_I18N_EXPORT extern const char kIcuDataFileName[];
-
#if !defined(OS_NACL)
// Call this function to load ICU's data tables for the current process. This
// function should be called before ICU is used.
BASE_I18N_EXPORT bool InitializeICU();
+#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
+// Returns the PlatformFile and Region that was initialized by InitializeICU().
+// Use with InitializeICUWithFileDescriptor().
+BASE_I18N_EXPORT PlatformFile
+GetIcuDataFileHandle(MemoryMappedFile::Region* out_region);
+
// Android and html_viewer use a file descriptor passed by browser process to
// initialize ICU in render processes.
BASE_I18N_EXPORT bool InitializeICUWithFileDescriptor(
PlatformFile data_fd,
- MemoryMappedFile::Region data_region);
-#endif
+ const MemoryMappedFile::Region& data_region);
+#endif // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
+#endif // !defined(OS_NACL)
// In a test binary, the call above might occur twice.
BASE_I18N_EXPORT void AllowMultipleInitializeCallsForTesting();
diff --git a/base/i18n/time_formatting.cc b/base/i18n/time_formatting.cc
index 15b34a3..5512111 100644
--- a/base/i18n/time_formatting.cc
+++ b/base/i18n/time_formatting.cc
@@ -45,6 +45,26 @@
static_cast<size_t>(time_string.length()));
}
+icu::SimpleDateFormat CreateSimpleDateFormatter(const char* pattern) {
+ // Generate a locale-dependent format pattern. The generator will take
+ // care of locale-dependent formatting issues like which separator to
+ // use (some locales use '.' instead of ':'), and where to put the am/pm
+ // marker.
+ UErrorCode status = U_ZERO_ERROR;
+ scoped_ptr<icu::DateTimePatternGenerator> generator(
+ icu::DateTimePatternGenerator::createInstance(status));
+ DCHECK(U_SUCCESS(status));
+ icu::UnicodeString generated_pattern =
+ generator->getBestPattern(icu::UnicodeString(pattern), status);
+ DCHECK(U_SUCCESS(status));
+
+ // Then, format the time using the generated pattern.
+ icu::SimpleDateFormat formatter(generated_pattern, status);
+ DCHECK(U_SUCCESS(status));
+
+ return formatter;
+}
+
} // namespace
string16 TimeFormatTimeOfDay(const Time& time) {
@@ -55,6 +75,11 @@
return TimeFormat(formatter.get(), time);
}
+string16 TimeFormatTimeOfDayWithMilliseconds(const Time& time) {
+ icu::SimpleDateFormat formatter = CreateSimpleDateFormatter("HmsSSS");
+ return TimeFormatWithoutAmPm(&formatter, time);
+}
+
string16 TimeFormatTimeOfDayWithHourClockType(const Time& time,
HourClockType type,
AmPmClockType ampm) {
@@ -65,22 +90,9 @@
return TimeFormatTimeOfDay(time);
}
- // Generate a locale-dependent format pattern. The generator will take
- // care of locale-dependent formatting issues like which separator to
- // use (some locales use '.' instead of ':'), and where to put the am/pm
- // marker.
- UErrorCode status = U_ZERO_ERROR;
- scoped_ptr<icu::DateTimePatternGenerator> generator(
- icu::DateTimePatternGenerator::createInstance(status));
- DCHECK(U_SUCCESS(status));
const char* base_pattern = (type == k12HourClock ? "ahm" : "Hm");
- icu::UnicodeString generated_pattern =
- generator->getBestPattern(icu::UnicodeString(base_pattern), status);
- DCHECK(U_SUCCESS(status));
+ icu::SimpleDateFormat formatter = CreateSimpleDateFormatter(base_pattern);
- // Then, format the time using the generated pattern.
- icu::SimpleDateFormat formatter(generated_pattern, status);
- DCHECK(U_SUCCESS(status));
if (ampm == kKeepAmPm) {
return TimeFormat(&formatter, time);
} else {
diff --git a/base/i18n/time_formatting.h b/base/i18n/time_formatting.h
index 2053c0b..df91f41 100644
--- a/base/i18n/time_formatting.h
+++ b/base/i18n/time_formatting.h
@@ -30,6 +30,10 @@
// Returns the time of day, e.g., "3:07 PM".
BASE_I18N_EXPORT string16 TimeFormatTimeOfDay(const Time& time);
+// Returns the time of day in 24-hour clock format with millisecond accuracy,
+// e.g., "15:07:30.568"
+BASE_I18N_EXPORT string16 TimeFormatTimeOfDayWithMilliseconds(const Time& time);
+
// Returns the time of day in the specified hour clock type. e.g.
// "3:07 PM" (type == k12HourClock, ampm == kKeepAmPm).
// "3:07" (type == k12HourClock, ampm == kDropAmPm).
diff --git a/base/i18n/time_formatting_unittest.cc b/base/i18n/time_formatting_unittest.cc
index df0c1ed..9385628 100644
--- a/base/i18n/time_formatting_unittest.cc
+++ b/base/i18n/time_formatting_unittest.cc
@@ -12,6 +12,7 @@
#include "third_party/icu/source/common/unicode/uversion.h"
#include "third_party/icu/source/i18n/unicode/calendar.h"
#include "third_party/icu/source/i18n/unicode/timezone.h"
+#include "third_party/icu/source/i18n/unicode/tzfmt.h"
namespace base {
namespace {
@@ -21,10 +22,19 @@
15, 42, 7, 0 // 15:42:07.000
};
-base::string16 GetShortTimeZone() {
+// Returns difference between the local time and GMT formatted as string.
+// This function gets |time| because the difference depends on time,
+// see https://en.wikipedia.org/wiki/Daylight_saving_time for details.
+base::string16 GetShortTimeZone(const Time& time) {
+ UErrorCode status = U_ZERO_ERROR;
scoped_ptr<icu::TimeZone> zone(icu::TimeZone::createDefault());
+ scoped_ptr<icu::TimeZoneFormat> zone_formatter(
+ icu::TimeZoneFormat::createInstance(icu::Locale::getDefault(), status));
+ EXPECT_TRUE(U_SUCCESS(status));
icu::UnicodeString name;
- zone->getDisplayName(true, icu::TimeZone::SHORT, name);
+ zone_formatter->format(UTZFMT_STYLE_SPECIFIC_SHORT, *zone,
+ static_cast<UDate>(time.ToDoubleT() * 1000), name,
+ nullptr);
return base::string16(name.getBuffer(), name.length());
}
@@ -37,9 +47,11 @@
string16 clock24h(ASCIIToUTF16("15:42"));
string16 clock12h_pm(ASCIIToUTF16("3:42 PM"));
string16 clock12h(ASCIIToUTF16("3:42"));
+ string16 clock24h_millis(ASCIIToUTF16("15:42:07.000"));
// The default is 12h clock.
EXPECT_EQ(clock12h_pm, TimeFormatTimeOfDay(time));
+ EXPECT_EQ(clock24h_millis, TimeFormatTimeOfDayWithMilliseconds(time));
EXPECT_EQ(k12HourClock, GetHourClockType());
// k{Keep,Drop}AmPm should not affect for 24h clock.
EXPECT_EQ(clock24h,
@@ -70,9 +82,11 @@
string16 clock24h(ASCIIToUTF16("15:42"));
string16 clock12h_pm(ASCIIToUTF16("3:42 pm"));
string16 clock12h(ASCIIToUTF16("3:42"));
+ string16 clock24h_millis(ASCIIToUTF16("15:42:07.000"));
// The default is 24h clock.
EXPECT_EQ(clock24h, TimeFormatTimeOfDay(time));
+ EXPECT_EQ(clock24h_millis, TimeFormatTimeOfDayWithMilliseconds(time));
EXPECT_EQ(k24HourClock, GetHourClockType());
// k{Keep,Drop}AmPm should not affect for 24h clock.
EXPECT_EQ(clock24h,
@@ -139,7 +153,7 @@
EXPECT_EQ(ASCIIToUTF16("4/30/11, 3:42:07 PM"),
TimeFormatShortDateAndTime(time));
- EXPECT_EQ(ASCIIToUTF16("4/30/11, 3:42:07 PM ") + GetShortTimeZone(),
+ EXPECT_EQ(ASCIIToUTF16("4/30/11, 3:42:07 PM ") + GetShortTimeZone(time),
TimeFormatShortDateAndTimeWithTimeZone(time));
EXPECT_EQ(ASCIIToUTF16("Saturday, April 30, 2011 at 3:42:07 PM"),
@@ -160,7 +174,7 @@
EXPECT_EQ(ASCIIToUTF16("30/04/2011"), TimeFormatShortDateNumeric(time));
EXPECT_EQ(ASCIIToUTF16("30/04/2011, 15:42:07"),
TimeFormatShortDateAndTime(time));
- EXPECT_EQ(ASCIIToUTF16("30/04/2011, 15:42:07 ") + GetShortTimeZone(),
+ EXPECT_EQ(ASCIIToUTF16("30/04/2011, 15:42:07 ") + GetShortTimeZone(time),
TimeFormatShortDateAndTimeWithTimeZone(time));
EXPECT_EQ(ASCIIToUTF16("Saturday, 30 April 2011 at 15:42:07"),
TimeFormatFriendlyDateAndTime(time));
diff --git a/base/ios/crb_protocol_observers_unittest.mm b/base/ios/crb_protocol_observers_unittest.mm
index 5f11051..b8cf423 100644
--- a/base/ios/crb_protocol_observers_unittest.mm
+++ b/base/ios/crb_protocol_observers_unittest.mm
@@ -4,6 +4,7 @@
#import "base/ios/crb_protocol_observers.h"
#include "base/ios/weak_nsobject.h"
+#include "base/logging.h"
#include "base/mac/scoped_nsautorelease_pool.h"
#include "base/mac/scoped_nsobject.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -36,10 +37,9 @@
@end
@interface TestMutateObserver : TestCompleteObserver
-
- (instancetype)initWithObserver:(CRBProtocolObservers*)observer
NS_DESIGNATED_INITIALIZER;
-
+- (instancetype)init NS_UNAVAILABLE;
@end
namespace {
@@ -266,6 +266,11 @@
return self;
}
+- (instancetype)init {
+ NOTREACHED();
+ return nil;
+}
+
- (void)mutateByAddingObserver:(id<TestObserver>)observer {
[_observers addObserver:observer];
}
diff --git a/base/ios/device_util.mm b/base/ios/device_util.mm
index 1234562..4af8234 100644
--- a/base/ios/device_util.mm
+++ b/base/ios/device_util.mm
@@ -59,7 +59,7 @@
std::string platform;
size_t size = 0;
sysctlbyname("hw.machine", NULL, &size, NULL, 0);
- sysctlbyname("hw.machine", WriteInto(&platform, size), &size, NULL, 0);
+ sysctlbyname("hw.machine", base::WriteInto(&platform, size), &size, NULL, 0);
return platform;
}
diff --git a/base/ios/ios_util.h b/base/ios/ios_util.h
index d9d7e19..688fbf3 100644
--- a/base/ios/ios_util.h
+++ b/base/ios/ios_util.h
@@ -14,6 +14,9 @@
// Returns whether the operating system is iOS 8 or later.
BASE_EXPORT bool IsRunningOnIOS8OrLater();
+// Returns whether the operating system is iOS 9 or later.
+BASE_EXPORT bool IsRunningOnIOS9OrLater();
+
// Returns whether the operating system is at the given version or later.
BASE_EXPORT bool IsRunningOnOrLater(int32 major, int32 minor, int32 bug_fix);
diff --git a/base/ios/ios_util.mm b/base/ios/ios_util.mm
index ca0a24d..d920045 100644
--- a/base/ios/ios_util.mm
+++ b/base/ios/ios_util.mm
@@ -20,10 +20,15 @@
namespace base {
namespace ios {
+// When dropping iOS7 support, also address issues listed in crbug.com/502968.
bool IsRunningOnIOS8OrLater() {
return IsRunningOnOrLater(8, 0, 0);
}
+bool IsRunningOnIOS9OrLater() {
+ return IsRunningOnOrLater(9, 0, 0);
+}
+
bool IsRunningOnOrLater(int32 major, int32 minor, int32 bug_fix) {
static const int32* current_version = OSVersionAsArray();
int32 version[] = { major, minor, bug_fix };
diff --git a/base/ios/ns_error_util.h b/base/ios/ns_error_util.h
new file mode 100644
index 0000000..1012292
--- /dev/null
+++ b/base/ios/ns_error_util.h
@@ -0,0 +1,25 @@
+// 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_IOS_NS_ERROR_UTIL_H_
+#define BASE_IOS_NS_ERROR_UTIL_H_
+
+@class NSError;
+
+namespace base {
+namespace ios {
+
+// Iterates through |error|'s underlying errors and returns the first error for
+// which there is no underlying error.
+NSError* GetFinalUnderlyingErrorFromError(NSError* error);
+
+// Returns a copy of |original_error| with |underlying_error| appended to the
+// end of its underlying error chain.
+NSError* ErrorWithAppendedUnderlyingError(NSError* original_error,
+ NSError* underlying_error);
+
+} // namespace ios
+} // namespace base
+
+#endif // BASE_IOS_NS_ERROR_UTIL_H_
diff --git a/base/ios/ns_error_util.mm b/base/ios/ns_error_util.mm
new file mode 100644
index 0000000..c44d9ee
--- /dev/null
+++ b/base/ios/ns_error_util.mm
@@ -0,0 +1,53 @@
+// 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.
+
+#import "base/ios/ns_error_util.h"
+
+#import <Foundation/Foundation.h>
+
+#include "base/logging.h"
+#include "base/mac/scoped_nsobject.h"
+
+namespace base {
+namespace ios {
+
+namespace {
+// Iterates through |error|'s underlying errors and returns them in an array.
+NSArray* GetFullErrorChainForError(NSError* error) {
+ NSMutableArray* error_chain = [NSMutableArray array];
+ NSError* current_error = error;
+ while (current_error) {
+ DCHECK([current_error isKindOfClass:[NSError class]]);
+ [error_chain addObject:current_error];
+ current_error = current_error.userInfo[NSUnderlyingErrorKey];
+ }
+ return error_chain;
+}
+} // namespace
+
+NSError* GetFinalUnderlyingErrorFromError(NSError* error) {
+ DCHECK(error);
+ return [GetFullErrorChainForError(error) lastObject];
+}
+
+NSError* ErrorWithAppendedUnderlyingError(NSError* original_error,
+ NSError* underlying_error) {
+ DCHECK(original_error);
+ DCHECK(underlying_error);
+ NSArray* error_chain = GetFullErrorChainForError(original_error);
+ NSError* current_error = underlying_error;
+ for (NSInteger idx = error_chain.count - 1; idx >= 0; --idx) {
+ NSError* error = error_chain[idx];
+ scoped_nsobject<NSMutableDictionary> user_info(
+ [error.userInfo mutableCopy]);
+ [user_info setObject:current_error forKey:NSUnderlyingErrorKey];
+ current_error = [NSError errorWithDomain:error.domain
+ code:error.code
+ userInfo:user_info];
+ }
+ return current_error;
+}
+
+} // namespace ios
+} // namespace base
diff --git a/base/json/json_parser.cc b/base/json/json_parser.cc
index 4d79be3..fc972ce 100644
--- a/base/json/json_parser.cc
+++ b/base/json/json_parser.cc
@@ -31,7 +31,7 @@
// optimization avoids about 2/3rds of string memory copies. The constructor
// takes ownership of the input string. The real root value is Swap()ed into
// the new instance.
-class DictionaryHiddenRootValue : public base::DictionaryValue {
+class DictionaryHiddenRootValue : public DictionaryValue {
public:
DictionaryHiddenRootValue(std::string* json, Value* root) : json_(json) {
DCHECK(root->IsType(Value::TYPE_DICTIONARY));
@@ -43,7 +43,7 @@
// First deep copy to convert JSONStringValue to std::string and swap that
// copy with |other|, which contains the new contents of |this|.
- scoped_ptr<base::DictionaryValue> copy(DeepCopy());
+ scoped_ptr<DictionaryValue> copy(DeepCopy());
copy->Swap(other);
// Then erase the contents of the current dictionary and swap in the
@@ -81,7 +81,7 @@
DISALLOW_COPY_AND_ASSIGN(DictionaryHiddenRootValue);
};
-class ListHiddenRootValue : public base::ListValue {
+class ListHiddenRootValue : public ListValue {
public:
ListHiddenRootValue(std::string* json, Value* root) : json_(json) {
DCHECK(root->IsType(Value::TYPE_LIST));
@@ -93,7 +93,7 @@
// First deep copy to convert JSONStringValue to std::string and swap that
// copy with |other|, which contains the new contents of |this|.
- scoped_ptr<base::ListValue> copy(DeepCopy());
+ scoped_ptr<ListValue> copy(DeepCopy());
copy->Swap(other);
// Then erase the contents of the current list and swap in the new contents,
@@ -130,14 +130,12 @@
// A variant on StringValue that uses StringPiece instead of copying the string
// into the Value. This can only be stored in a child of hidden root (above),
// otherwise the referenced string will not be guaranteed to outlive it.
-class JSONStringValue : public base::Value {
+class JSONStringValue : public Value {
public:
- explicit JSONStringValue(const base::StringPiece& piece)
- : Value(TYPE_STRING),
- string_piece_(piece) {
- }
+ explicit JSONStringValue(const StringPiece& piece)
+ : Value(TYPE_STRING), string_piece_(piece) {}
- // Overridden from base::Value:
+ // Overridden from Value:
bool GetAsString(std::string* out_value) const override {
string_piece_.CopyToString(out_value);
return true;
@@ -157,7 +155,7 @@
private:
// The location in the original input stream.
- base::StringPiece string_piece_;
+ StringPiece string_piece_;
DISALLOW_COPY_AND_ASSIGN(JSONStringValue);
};
@@ -776,11 +774,17 @@
uint32 code_point = CBU16_GET_SUPPLEMENTARY(code_unit16_high,
code_unit16_low);
+ if (!IsValidCharacter(code_point))
+ return false;
+
offset = 0;
CBU8_APPEND_UNSAFE(code_unit8, offset, code_point);
} else {
// Not a surrogate.
DCHECK(CBU16_IS_SINGLE(code_unit16_high));
+ if (!IsValidCharacter(code_unit16_high))
+ return false;
+
CBU8_APPEND_UNSAFE(code_unit8, offset, code_unit16_high);
}
@@ -789,6 +793,8 @@
}
void JSONParser::DecodeUTF8(const int32& point, StringBuilder* dest) {
+ DCHECK(IsValidCharacter(point));
+
// Anything outside of the basic ASCII plane will need to be decoded from
// int32 to a multi-byte sequence.
if (point < kExtendedASCIIStart) {
@@ -872,7 +878,7 @@
return new FundamentalValue(num_int);
double num_double;
- if (base::StringToDouble(num_string.as_string(), &num_double) &&
+ if (StringToDouble(num_string.as_string(), &num_double) &&
std::isfinite(num_double)) {
return new FundamentalValue(num_double);
}
diff --git a/base/json/json_parser_unittest.cc b/base/json/json_parser_unittest.cc
index f776ddf..d88f9ea 100644
--- a/base/json/json_parser_unittest.cc
+++ b/base/json/json_parser_unittest.cc
@@ -313,5 +313,13 @@
EXPECT_TRUE(root.get()) << error_message;
}
+TEST_F(JSONParserTest, DecodeUnicodeNonCharacter) {
+ // Tests Unicode code points (encoded as escaped UTF-16) that are not valid
+ // characters.
+ EXPECT_FALSE(JSONReader::Read("[\"\\ufdd0\"]"));
+ EXPECT_FALSE(JSONReader::Read("[\"\\ufffe\"]"));
+ EXPECT_FALSE(JSONReader::Read("[\"\\ud83f\\udffe\"]"));
+}
+
} // namespace internal
} // namespace base
diff --git a/base/logging.cc b/base/logging.cc
index 1a2f774..7654e7f 100644
--- a/base/logging.cc
+++ b/base/logging.cc
@@ -521,6 +521,12 @@
Init(file, line);
}
+LogMessage::LogMessage(const char* file, int line, const char* condition)
+ : severity_(LOG_FATAL), file_(file), line_(line) {
+ Init(file, line);
+ stream_ << "Check failed: " << condition << ". ";
+}
+
LogMessage::LogMessage(const char* file, int line, std::string* result)
: severity_(LOG_FATAL), file_(file), line_(line) {
Init(file, line);
diff --git a/base/logging.h b/base/logging.h
index ea096d1..adc8fb6 100644
--- a/base/logging.h
+++ b/base/logging.h
@@ -436,7 +436,7 @@
#if defined(OFFICIAL_BUILD) && defined(NDEBUG) && !defined(OS_ANDROID)
// Make all CHECK functions discard their log strings to reduce code
-// bloat for official release builds.
+// bloat for official release builds (except Android).
// TODO(akalin): This would be more valuable if there were some way to
// remove BreakDebugger() from the backtrace, perhaps by turning it
@@ -470,9 +470,10 @@
#else // _PREFAST_
-#define CHECK(condition) \
- LAZY_STREAM(LOG_STREAM(FATAL), !(condition)) \
- << "Check failed: " #condition ". "
+// Do as much work as possible out of line to reduce inline code size.
+#define CHECK(condition) \
+ LAZY_STREAM(logging::LogMessage(__FILE__, __LINE__, #condition).stream(), \
+ !(condition))
#define PCHECK(condition) \
LAZY_STREAM(PLOG_STREAM(FATAL), !(condition)) \
@@ -727,6 +728,9 @@
// Used for LOG(severity).
LogMessage(const char* file, int line, LogSeverity severity);
+ // Used for CHECK(). Implied severity = LOG_FATAL.
+ LogMessage(const char* file, int line, const char* condition);
+
// Used for CHECK_EQ(), etc. Takes ownership of the given string.
// Implied severity = LOG_FATAL.
LogMessage(const char* file, int line, std::string* result);
diff --git a/base/mac/OWNERS b/base/mac/OWNERS
index 4aba972..a3fc32f 100644
--- a/base/mac/OWNERS
+++ b/base/mac/OWNERS
@@ -1,14 +1,2 @@
mark@chromium.org
thakis@chromium.org
-
-# sdk_forward_declarations.[h|mm] will likely need to be modified by Cocoa
-# developers in general; keep in sync with OWNERS of //chrome/browser/ui/cocoa.
-per-file sdk_forward_declarations.*=asvitkine@chromium.org
-per-file sdk_forward_declarations.*=avi@chromium.org
-per-file sdk_forward_declarations.*=groby@chromium.org
-per-file sdk_forward_declarations.*=jeremy@chromium.org
-per-file sdk_forward_declarations.*=mark@chromium.org
-per-file sdk_forward_declarations.*=rohitrao@chromium.org
-per-file sdk_forward_declarations.*=rsesek@chromium.org
-per-file sdk_forward_declarations.*=shess@chromium.org
-per-file sdk_forward_declarations.*=thakis@chromium.org
diff --git a/base/mac/call_with_eh_frame.cc b/base/mac/call_with_eh_frame.cc
new file mode 100644
index 0000000..8ea6f75
--- /dev/null
+++ b/base/mac/call_with_eh_frame.cc
@@ -0,0 +1,37 @@
+// 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/mac/call_with_eh_frame.h"
+
+#include <unwind.h>
+
+#include "build/build_config.h"
+
+namespace base {
+namespace mac {
+
+_Unwind_Reason_Code CxxPersonalityRoutine(
+ int version,
+ _Unwind_Action actions,
+ uint64_t exceptionClass,
+ struct _Unwind_Exception* exceptionObject,
+ struct _Unwind_Context* context) {
+ // Tell libunwind that this is the end of the stack. When it encounters the
+ // CallWithEHFrame, it will stop searching for an exception handler. The
+ // result is that no exception handler has been found higher on the stack,
+ // and any that are lower on the stack (e.g. in CFRunLoopRunSpecific), will
+ // now be skipped. Since this is reporting the end of the stack, and no
+ // exception handler will have been found, std::terminate() will be called.
+ return _URC_END_OF_STACK;
+}
+
+#if defined(OS_IOS)
+// No iOS assembly implementation exists, so just call the block directly.
+void CallWithEHFrame(void (^block)(void)) {
+ block();
+}
+#endif
+
+} // namespace mac
+} // namespace base
diff --git a/base/mac/call_with_eh_frame.h b/base/mac/call_with_eh_frame.h
new file mode 100644
index 0000000..1f7d5e0
--- /dev/null
+++ b/base/mac/call_with_eh_frame.h
@@ -0,0 +1,26 @@
+// 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_MAC_CALL_WITH_EH_FRAME_H_
+#define BASE_MAC_CALL_WITH_EH_FRAME_H_
+
+#include "base/base_export.h"
+
+namespace base {
+namespace mac {
+
+// Invokes the specified block in a stack frame with a special exception
+// handler. This function creates an exception handling stack frame that
+// specifies a custom C++ exception personality routine, which terminates the
+// search for an exception handler at this frame.
+//
+// The purpose of this function is to prevent a try/catch statement in system
+// libraries, acting as a global exception handler, from handling exceptions
+// in such a way that disrupts the generation of useful stack traces.
+void BASE_EXPORT CallWithEHFrame(void (^block)(void));
+
+} // namespace mac
+} // namespace base
+
+#endif // BASE_MAC_CALL_WITH_EH_FRAME_H_
diff --git a/base/mac/call_with_eh_frame_asm.S b/base/mac/call_with_eh_frame_asm.S
new file mode 100644
index 0000000..0e399cf
--- /dev/null
+++ b/base/mac/call_with_eh_frame_asm.S
@@ -0,0 +1,89 @@
+// 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.
+
+// base::mac::CallWithEHFrame(void () block_pointer)
+#define CALL_WITH_EH_FRAME __ZN4base3mac15CallWithEHFrameEU13block_pointerFvvE
+
+ .section __TEXT,__text,regular,pure_instructions
+#if !defined(COMPONENT_BUILD)
+ .private_extern CALL_WITH_EH_FRAME
+#endif
+ .globl CALL_WITH_EH_FRAME
+ .align 4
+CALL_WITH_EH_FRAME:
+
+ .cfi_startproc
+
+ // Configure the C++ exception handler personality routine. Normally the
+ // compiler would emit ___gxx_personality_v0 here. The purpose of this
+ // function is to use a custom personality routine.
+ .cfi_personality 155, __ZN4base3mac21CxxPersonalityRoutineEi14_Unwind_ActionyP17_Unwind_ExceptionP15_Unwind_Context
+ .cfi_lsda 16, CallWithEHFrame_exception_table
+
+Lfunction_start:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+
+ // Load the function pointer from the block descriptor.
+ movq 16(%rdi), %rax
+
+ // Execute the block in the context of a C++ try{}.
+Ltry_start:
+ callq *%rax
+Ltry_end:
+ popq %rbp
+ ret
+
+ // Landing pad for the exception handler. This should never be called, since
+ // the personality routine will stop the search for an exception handler,
+ // which will cause the runtime to invoke the default terminate handler.
+Lcatch:
+ movq %rax, %rdi
+ callq ___cxa_begin_catch // The ABI requires a call to the catch handler.
+ ud2 // In the event this is called, make it fatal.
+
+Lfunction_end:
+ .cfi_endproc
+
+// The C++ exception table that is used to identify this frame as an
+// exception handler. See http://llvm.org/docs/ExceptionHandling.html and
+// http://mentorembedded.github.io/cxx-abi/exceptions.pdf.
+ .section __TEXT,__gcc_except_tab
+ .align 2
+CallWithEHFrame_exception_table:
+ .byte 255 // DW_EH_PE_omit
+ .byte 155 // DW_EH_PE_indirect | DW_EH_PE_pcrel | DW_EH_PE_sdata4
+ .asciz "\242\200\200" // LE int128 for the number of bytes in this table.
+ .byte 3 // DW_EH_PE_udata4
+ .byte 26 // Callsite table length.
+
+// First callsite.
+CS1_begin = Ltry_start - Lfunction_start
+ .long CS1_begin
+CS1_end = Ltry_end - Ltry_start
+ .long CS1_end
+
+// First landing pad.
+LP1 = Lcatch - Lfunction_start
+ .long LP1
+ .byte 1 // Action record.
+
+// Second callsite.
+CS2_begin = Ltry_end - Lfunction_start
+ .long CS2_begin
+CS2_end = Lfunction_end - Ltry_end
+ .long CS2_end
+
+// Second landing pad (none).
+ .long 0
+ .byte 0 // No action.
+
+// Action table.
+ .byte 1 // Action record 1.
+ .byte 0 // No further action to take.
+ .long 0 // No type filter for this catch(){} clause.
+ .align 2
diff --git a/base/mac/call_with_eh_frame_unittest.mm b/base/mac/call_with_eh_frame_unittest.mm
new file mode 100644
index 0000000..663dae7
--- /dev/null
+++ b/base/mac/call_with_eh_frame_unittest.mm
@@ -0,0 +1,51 @@
+// 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/mac/call_with_eh_frame.h"
+
+#import <Foundation/Foundation.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace mac {
+namespace {
+
+class CallWithEHFrameTest : public testing::Test {
+ protected:
+ void ThrowException() { [NSArray arrayWithObject:nil]; }
+};
+
+// Catching from within the EHFrame is allowed.
+TEST_F(CallWithEHFrameTest, CatchExceptionHigher) {
+ bool __block saw_exception = false;
+ base::mac::CallWithEHFrame(^{
+ @try {
+ ThrowException();
+ } @catch (NSException* exception) {
+ saw_exception = true;
+ }
+ });
+ EXPECT_TRUE(saw_exception);
+}
+
+// Trying to catch an exception outside the EHFrame is blocked.
+TEST_F(CallWithEHFrameTest, CatchExceptionLower) {
+ auto catch_exception_lower = ^{
+ bool saw_exception = false;
+ @try {
+ base::mac::CallWithEHFrame(^{
+ ThrowException();
+ });
+ } @catch (NSException* exception) {
+ saw_exception = true;
+ }
+ ASSERT_FALSE(saw_exception);
+ };
+ EXPECT_DEATH(catch_exception_lower(), "");
+}
+
+} // namespace
+} // namespace mac
+} // namespace base
diff --git a/base/mac/mac_util.h b/base/mac/mac_util.h
index f8ffa97..af52667 100644
--- a/base/mac/mac_util.h
+++ b/base/mac/mac_util.h
@@ -147,6 +147,10 @@
BASE_EXPORT bool IsOSYosemite();
BASE_EXPORT bool IsOSYosemiteOrLater();
+// El Capitan is Mac OS X 10.11, Darwin 15.
+BASE_EXPORT bool IsOSElCapitan();
+BASE_EXPORT bool IsOSElCapitanOrLater();
+
// This should be infrequently used. It only makes sense to use this to avoid
// codepaths that are very likely to break on future (unreleased, untested,
// unborn) OS releases, or to log when the OS is newer than any known version.
@@ -157,6 +161,9 @@
inline bool IsOSLionOrEarlier() { return !IsOSMountainLionOrLater(); }
inline bool IsOSMountainLionOrEarlier() { return !IsOSMavericksOrLater(); }
inline bool IsOSMavericksOrEarlier() { return !IsOSYosemiteOrLater(); }
+inline bool IsOSYosemiteOrEarlier() {
+ return !IsOSElCapitanOrLater();
+}
// When the deployment target is set, the code produced cannot run on earlier
// OS releases. That enables some of the IsOS* family to be implemented as
@@ -213,6 +220,22 @@
inline bool IsOSLaterThanYosemite_DontCallThis() { return true; }
#endif
+#if defined(MAC_OS_X_VERSION_10_11) && \
+ MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11
+#define BASE_MAC_MAC_UTIL_H_INLINED_GE_10_11
+inline bool IsOSElCapitanOrLater() {
+ return true;
+}
+#endif
+
+#if defined(MAC_OS_X_VERSION_10_11) && \
+ MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_11
+#define BASE_MAC_MAC_UTIL_H_INLINED_GT_10_11
+inline bool IsOSElCapitan() {
+ return false;
+}
+#endif
+
// Retrieve the system's model identifier string from the IOKit registry:
// for example, "MacPro4,1", "MacBookPro6,1". Returns empty string upon
// failure.
diff --git a/base/mac/mac_util.mm b/base/mac/mac_util.mm
index bdf45de..e1e15dc 100644
--- a/base/mac/mac_util.mm
+++ b/base/mac/mac_util.mm
@@ -483,6 +483,7 @@
MOUNTAIN_LION_MINOR_VERSION = 8,
MAVERICKS_MINOR_VERSION = 9,
YOSEMITE_MINOR_VERSION = 10,
+ EL_CAPITAN_MINOR_VERSION = 11,
};
} // namespace
@@ -547,6 +548,18 @@
}
#endif
+#if !defined(BASE_MAC_MAC_UTIL_H_INLINED_GT_10_11)
+bool IsOSElCapitan() {
+ return MacOSXMinorVersion() == EL_CAPITAN_MINOR_VERSION;
+}
+#endif
+
+#if !defined(BASE_MAC_MAC_UTIL_H_INLINED_GE_10_11)
+bool IsOSElCapitanOrLater() {
+ return MacOSXMinorVersion() >= EL_CAPITAN_MINOR_VERSION;
+}
+#endif
+
std::string GetModelIdentifier() {
std::string return_string;
ScopedIOObject<io_service_t> platform_expert(
diff --git a/base/macros.h b/base/macros.h
index 0325e74..53b3926 100644
--- a/base/macros.h
+++ b/base/macros.h
@@ -41,7 +41,7 @@
// that wants to prevent anyone from instantiating it. This is
// especially useful for classes containing only static methods.
#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
- TypeName(); \
+ TypeName() = delete; \
DISALLOW_COPY_AND_ASSIGN(TypeName)
// The arraysize(arr) macro returns the # of elements in an array arr.
diff --git a/base/memory/BUILD.gn b/base/memory/BUILD.gn
index c53cc05..1f1d3f2 100644
--- a/base/memory/BUILD.gn
+++ b/base/memory/BUILD.gn
@@ -35,6 +35,9 @@
"scoped_vector.h",
"shared_memory.h",
"shared_memory_android.cc",
+ "shared_memory_handle.h",
+ "shared_memory_handle_mac.cc",
+ "shared_memory_mac.cc",
"shared_memory_nacl.cc",
"shared_memory_posix.cc",
"shared_memory_win.cc",
@@ -54,6 +57,10 @@
sources -= [ "shared_memory_nacl.cc" ]
}
+ if (is_mac) {
+ sources -= [ "shared_memory_posix.cc" ]
+ }
+
if (is_android) {
deps = [
"//third_party/ashmem",
diff --git a/base/memory/ref_counted_delete_on_message_loop.h b/base/memory/ref_counted_delete_on_message_loop.h
index 6a109e8..d278a44 100644
--- a/base/memory/ref_counted_delete_on_message_loop.h
+++ b/base/memory/ref_counted_delete_on_message_loop.h
@@ -8,8 +8,6 @@
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
-// TODO(ricea): Remove the following include once all callers have been fixed.
-#include "base/message_loop/message_loop_proxy.h"
#include "base/single_thread_task_runner.h"
namespace base {
diff --git a/base/memory/scoped_vector.h b/base/memory/scoped_vector.h
index 173ea5a..e1e5c72 100644
--- a/base/memory/scoped_vector.h
+++ b/base/memory/scoped_vector.h
@@ -106,6 +106,10 @@
return v_.insert(position, x);
}
+ iterator insert(iterator position, scoped_ptr<T> x) {
+ return v_.insert(position, x.release());
+ }
+
// Lets the ScopedVector take ownership of elements in [first,last).
template<typename InputIterator>
void insert(iterator position, InputIterator first, InputIterator last) {
diff --git a/base/memory/shared_memory.h b/base/memory/shared_memory.h
index 008bb01..4326758 100644
--- a/base/memory/shared_memory.h
+++ b/base/memory/shared_memory.h
@@ -17,6 +17,7 @@
#include "base/base_export.h"
#include "base/basictypes.h"
+#include "base/memory/shared_memory_handle.h"
#include "base/process/process_handle.h"
#if defined(OS_POSIX)
@@ -29,14 +30,6 @@
class FilePath;
-// SharedMemoryHandle is a platform specific type which represents
-// the underlying OS handle to a shared memory segment.
-#if defined(OS_WIN)
-typedef HANDLE SharedMemoryHandle;
-#elif defined(OS_POSIX)
-typedef FileDescriptor SharedMemoryHandle;
-#endif
-
// Options for creating a shared memory object.
struct SharedMemoryCreateOptions {
SharedMemoryCreateOptions()
@@ -89,12 +82,13 @@
// only affects how the SharedMemory will be mmapped. Use
// ShareReadOnlyToProcess to drop permissions. TODO(jln,jyasskin): DCHECK
// that |read_only| matches the permissions of the handle.
- SharedMemory(SharedMemoryHandle handle, bool read_only);
+ SharedMemory(const SharedMemoryHandle& handle, bool read_only);
// Create a new SharedMemory object from an existing, open
// shared memory file that was created by a remote process and not shared
// to the current process.
- SharedMemory(SharedMemoryHandle handle, bool read_only,
+ SharedMemory(const SharedMemoryHandle& handle,
+ bool read_only,
ProcessHandle process);
// Closes any open files.
@@ -257,27 +251,11 @@
return ShareToProcessCommon(process, new_handle, true, SHARE_CURRENT_MODE);
}
- // DEPRECATED (crbug.com/345734):
- // Locks the shared memory.
- //
- // WARNING: on POSIX the memory locking primitive only works across
- // processes, not across threads. The LockDeprecated method is not currently
- // used in inner loops, so we protect against multiple threads in a
- // critical section using a class global lock.
- void LockDeprecated();
-
- // DEPRECATED (crbug.com/345734):
- // Releases the shared memory lock.
- void UnlockDeprecated();
-
private:
-#if defined(OS_POSIX) && !defined(OS_NACL)
-#if !defined(OS_ANDROID)
+#if defined(OS_POSIX) && !defined(OS_NACL) && !defined(OS_ANDROID)
bool PrepareMapFile(ScopedFILE fp, ScopedFD readonly);
bool FilePathForMemoryName(const std::string& mem_name, FilePath* path);
-#endif
- void LockOrUnlockCommon(int function);
-#endif // defined(OS_POSIX) && !defined(OS_NACL)
+#endif // defined(OS_POSIX) && !defined(OS_NACL) && !defined(OS_ANDROID)
enum ShareMode {
SHARE_READONLY,
SHARE_CURRENT_MODE,
@@ -298,32 +276,9 @@
void* memory_;
bool read_only_;
size_t requested_size_;
-#if !defined(OS_POSIX)
- HANDLE lock_;
-#endif
DISALLOW_COPY_AND_ASSIGN(SharedMemory);
};
-
-// DEPRECATED (crbug.com/345734):
-// A helper class that acquires the shared memory lock while
-// the SharedMemoryAutoLockDeprecated is in scope.
-class SharedMemoryAutoLockDeprecated {
- public:
- explicit SharedMemoryAutoLockDeprecated(SharedMemory* shared_memory)
- : shared_memory_(shared_memory) {
- shared_memory_->LockDeprecated();
- }
-
- ~SharedMemoryAutoLockDeprecated() {
- shared_memory_->UnlockDeprecated();
- }
-
- private:
- SharedMemory* shared_memory_;
- DISALLOW_COPY_AND_ASSIGN(SharedMemoryAutoLockDeprecated);
-};
-
} // namespace base
#endif // BASE_MEMORY_SHARED_MEMORY_H_
diff --git a/base/memory/shared_memory_handle.h b/base/memory/shared_memory_handle.h
new file mode 100644
index 0000000..7af8729
--- /dev/null
+++ b/base/memory/shared_memory_handle.h
@@ -0,0 +1,97 @@
+// 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_MEMORY_SHARED_MEMORY_HANDLE_H_
+#define BASE_MEMORY_SHARED_MEMORY_HANDLE_H_
+
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#elif defined(OS_MACOSX) && !defined(OS_IOS)
+#include <sys/types.h>
+#include "base/base_export.h"
+#include "base/file_descriptor_posix.h"
+#include "base/macros.h"
+#elif defined(OS_POSIX)
+#include <sys/types.h>
+#include "base/file_descriptor_posix.h"
+#endif
+
+namespace base {
+
+class Pickle;
+
+// SharedMemoryHandle is a platform specific type which represents
+// the underlying OS handle to a shared memory segment.
+#if defined(OS_WIN)
+typedef HANDLE SharedMemoryHandle;
+#elif defined(OS_POSIX) && !(defined(OS_MACOSX) && !defined(OS_IOS))
+typedef FileDescriptor SharedMemoryHandle;
+#else
+class BASE_EXPORT SharedMemoryHandle {
+ public:
+ enum Type {
+ // Indicates that the SharedMemoryHandle is backed by a POSIX fd.
+ POSIX,
+ // Indicates that the SharedMemoryHandle is backed by the Mach primitive
+ // "memory object".
+ MACH,
+ };
+
+ // The format that should be used to transmit |Type| over the wire.
+ typedef int TypeWireFormat;
+
+ // The default constructor returns an invalid SharedMemoryHandle.
+ SharedMemoryHandle();
+
+ // Constructs a SharedMemoryHandle backed by the components of a
+ // FileDescriptor. The newly created instance has the same ownership semantics
+ // as base::FileDescriptor. This typically means that the SharedMemoryHandle
+ // takes ownership of the |fd| if |auto_close| is true. Unfortunately, it's
+ // common for existing code to make shallow copies of SharedMemoryHandle, and
+ // the one that is finally passed into a base::SharedMemory is the one that
+ // "consumes" the fd.
+ explicit SharedMemoryHandle(const base::FileDescriptor& file_descriptor);
+ SharedMemoryHandle(int fd, bool auto_close);
+
+ // Standard copy constructor. The new instance shares the underlying OS
+ // primitives.
+ SharedMemoryHandle(const SharedMemoryHandle& handle);
+
+ // Standard assignment operator. The updated instance shares the underlying
+ // OS primitives.
+ SharedMemoryHandle& operator=(const SharedMemoryHandle& handle);
+
+ // Duplicates the underlying OS resources.
+ SharedMemoryHandle Duplicate() const;
+
+ // Comparison operators.
+ bool operator==(const SharedMemoryHandle& handle) const;
+ bool operator!=(const SharedMemoryHandle& handle) const;
+
+ // Returns the type.
+ Type GetType() const;
+
+ // Whether the underlying OS primitive is valid.
+ bool IsValid() const;
+
+ // Sets the POSIX fd backing the SharedMemoryHandle. Requires that the
+ // SharedMemoryHandle be backed by a POSIX fd.
+ void SetFileHandle(int fd, bool auto_close);
+
+ // This method assumes that the SharedMemoryHandle is backed by a POSIX fd.
+ // This is eventually no longer going to be true, so please avoid adding new
+ // uses of this method.
+ const FileDescriptor GetFileDescriptor() const;
+
+ private:
+ Type type_;
+ FileDescriptor file_descriptor_;
+};
+#endif
+
+} // namespace base
+
+#endif // BASE_MEMORY_SHARED_MEMORY_HANDLE_H_
diff --git a/base/memory/shared_memory_handle_mac.cc b/base/memory/shared_memory_handle_mac.cc
new file mode 100644
index 0000000..86f5c54
--- /dev/null
+++ b/base/memory/shared_memory_handle_mac.cc
@@ -0,0 +1,86 @@
+// 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/memory/shared_memory_handle.h"
+
+#include <unistd.h>
+
+#include "base/posix/eintr_wrapper.h"
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+namespace base {
+
+static_assert(sizeof(SharedMemoryHandle::Type) <=
+ sizeof(SharedMemoryHandle::TypeWireFormat),
+ "Size of enum SharedMemoryHandle::Type exceeds size of type "
+ "transmitted over wire.");
+
+SharedMemoryHandle::SharedMemoryHandle() : type_(POSIX), file_descriptor_() {}
+
+SharedMemoryHandle::SharedMemoryHandle(
+ const base::FileDescriptor& file_descriptor)
+ : type_(POSIX), file_descriptor_(file_descriptor) {}
+
+SharedMemoryHandle::SharedMemoryHandle(int fd, bool auto_close)
+ : type_(POSIX), file_descriptor_(fd, auto_close) {}
+
+SharedMemoryHandle::SharedMemoryHandle(const SharedMemoryHandle& handle)
+ : type_(handle.type_), file_descriptor_(handle.file_descriptor_) {}
+
+SharedMemoryHandle& SharedMemoryHandle::operator=(
+ const SharedMemoryHandle& handle) {
+ if (this == &handle)
+ return *this;
+
+ type_ = handle.type_;
+ file_descriptor_ = handle.file_descriptor_;
+ return *this;
+}
+
+bool SharedMemoryHandle::operator==(const SharedMemoryHandle& handle) const {
+ // Invalid handles are always equal, even if they have different types.
+ if (!IsValid() && !handle.IsValid())
+ return true;
+
+ return type_ == handle.type_ && file_descriptor_ == handle.file_descriptor_;
+}
+
+bool SharedMemoryHandle::operator!=(const SharedMemoryHandle& handle) const {
+ return !(*this == handle);
+}
+
+SharedMemoryHandle::Type SharedMemoryHandle::GetType() const {
+ return type_;
+}
+
+bool SharedMemoryHandle::IsValid() const {
+ switch (type_) {
+ case POSIX:
+ return file_descriptor_.fd >= 0;
+ case MACH:
+ return false;
+ }
+}
+
+void SharedMemoryHandle::SetFileHandle(int fd, bool auto_close) {
+ DCHECK_EQ(type_, POSIX);
+ file_descriptor_.fd = fd;
+ file_descriptor_.auto_close = auto_close;
+}
+
+const FileDescriptor SharedMemoryHandle::GetFileDescriptor() const {
+ DCHECK_EQ(type_, POSIX);
+ return file_descriptor_;
+}
+
+SharedMemoryHandle SharedMemoryHandle::Duplicate() const {
+ DCHECK_EQ(type_, POSIX);
+ int duped_handle = HANDLE_EINTR(dup(file_descriptor_.fd));
+ if (duped_handle < 0)
+ return SharedMemoryHandle();
+ return SharedMemoryHandle(duped_handle, true);
+}
+
+} // namespace base
+#endif // defined(OS_MACOSX) && !defined(OS_IOS)
diff --git a/base/memory/shared_memory_mac.cc b/base/memory/shared_memory_mac.cc
new file mode 100644
index 0000000..8d8a164
--- /dev/null
+++ b/base/memory/shared_memory_mac.cc
@@ -0,0 +1,460 @@
+// Copyright (c) 2012 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/memory/shared_memory.h"
+
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/posix/safe_strerror.h"
+#include "base/process/process_metrics.h"
+#include "base/profiler/scoped_tracker.h"
+#include "base/scoped_generic.h"
+#include "base/strings/utf_string_conversions.h"
+
+#if defined(OS_MACOSX)
+#include "base/mac/foundation_util.h"
+#endif // OS_MACOSX
+
+namespace base {
+
+namespace {
+
+struct ScopedPathUnlinkerTraits {
+ static FilePath* InvalidValue() { return nullptr; }
+
+ static void Free(FilePath* path) {
+ // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466437
+ // is fixed.
+ tracked_objects::ScopedTracker tracking_profile(
+ FROM_HERE_WITH_EXPLICIT_FUNCTION(
+ "466437 SharedMemory::Create::Unlink"));
+ if (unlink(path->value().c_str()))
+ PLOG(WARNING) << "unlink";
+ }
+};
+
+// Unlinks the FilePath when the object is destroyed.
+typedef ScopedGeneric<FilePath*, ScopedPathUnlinkerTraits> ScopedPathUnlinker;
+
+// Makes a temporary file, fdopens it, and then unlinks it. |fp| is populated
+// with the fdopened FILE. |readonly_fd| is populated with the opened fd if
+// options.share_read_only is true. |path| is populated with the location of
+// the file before it was unlinked.
+// Returns false if there's an unhandled failure.
+bool CreateAnonymousSharedMemory(const SharedMemoryCreateOptions& options,
+ ScopedFILE* fp,
+ ScopedFD* readonly_fd,
+ FilePath* path) {
+ // It doesn't make sense to have a open-existing private piece of shmem
+ DCHECK(!options.open_existing_deprecated);
+ // Q: Why not use the shm_open() etc. APIs?
+ // A: Because they're limited to 4mb on OS X. FFFFFFFUUUUUUUUUUU
+ FilePath directory;
+ ScopedPathUnlinker path_unlinker;
+ if (GetShmemTempDir(options.executable, &directory)) {
+ // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466437
+ // is fixed.
+ tracked_objects::ScopedTracker tracking_profile(
+ FROM_HERE_WITH_EXPLICIT_FUNCTION(
+ "466437 SharedMemory::Create::OpenTemporaryFile"));
+ fp->reset(base::CreateAndOpenTemporaryFileInDir(directory, path));
+
+ // Deleting the file prevents anyone else from mapping it in (making it
+ // private), and prevents the need for cleanup (once the last fd is
+ // closed, it is truly freed).
+ if (*fp)
+ path_unlinker.reset(path);
+ }
+
+ if (*fp) {
+ if (options.share_read_only) {
+ // TODO(erikchen): Remove ScopedTracker below once
+ // http://crbug.com/466437 is fixed.
+ tracked_objects::ScopedTracker tracking_profile(
+ FROM_HERE_WITH_EXPLICIT_FUNCTION(
+ "466437 SharedMemory::Create::OpenReadonly"));
+ // Also open as readonly so that we can ShareReadOnlyToProcess.
+ readonly_fd->reset(HANDLE_EINTR(open(path->value().c_str(), O_RDONLY)));
+ if (!readonly_fd->is_valid()) {
+ DPLOG(ERROR) << "open(\"" << path->value() << "\", O_RDONLY) failed";
+ fp->reset();
+ return false;
+ }
+ }
+ }
+ return true;
+}
+}
+
+SharedMemory::SharedMemory()
+ : mapped_file_(-1),
+ readonly_mapped_file_(-1),
+ mapped_size_(0),
+ memory_(NULL),
+ read_only_(false),
+ requested_size_(0) {}
+
+SharedMemory::SharedMemory(const SharedMemoryHandle& handle, bool read_only)
+ : mapped_file_(GetFdFromSharedMemoryHandle(handle)),
+ readonly_mapped_file_(-1),
+ mapped_size_(0),
+ memory_(NULL),
+ read_only_(read_only),
+ requested_size_(0) {}
+
+SharedMemory::SharedMemory(const SharedMemoryHandle& handle,
+ bool read_only,
+ ProcessHandle process)
+ : mapped_file_(GetFdFromSharedMemoryHandle(handle)),
+ readonly_mapped_file_(-1),
+ mapped_size_(0),
+ memory_(NULL),
+ read_only_(read_only),
+ requested_size_(0) {
+ // We don't handle this case yet (note the ignored parameter); let's die if
+ // someone comes calling.
+ NOTREACHED();
+}
+
+SharedMemory::~SharedMemory() {
+ Unmap();
+ Close();
+}
+
+// static
+bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) {
+ return handle.IsValid();
+}
+
+// static
+SharedMemoryHandle SharedMemory::NULLHandle() {
+ return SharedMemoryHandle();
+}
+
+// static
+void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) {
+ DCHECK_GE(GetFdFromSharedMemoryHandle(handle), 0);
+ if (close(GetFdFromSharedMemoryHandle(handle)) < 0)
+ DPLOG(ERROR) << "close";
+}
+
+// static
+size_t SharedMemory::GetHandleLimit() {
+ return base::GetMaxFds();
+}
+
+// static
+SharedMemoryHandle SharedMemory::DuplicateHandle(
+ const SharedMemoryHandle& handle) {
+ return handle.Duplicate();
+}
+
+// static
+int SharedMemory::GetFdFromSharedMemoryHandle(
+ const SharedMemoryHandle& handle) {
+ return handle.GetFileDescriptor().fd;
+}
+
+bool SharedMemory::CreateAndMapAnonymous(size_t size) {
+ return CreateAnonymous(size) && Map(size);
+}
+
+// static
+int SharedMemory::GetSizeFromSharedMemoryHandle(
+ const SharedMemoryHandle& handle) {
+ struct stat st;
+ if (fstat(GetFdFromSharedMemoryHandle(handle), &st) != 0)
+ return -1;
+ return st.st_size;
+}
+
+// Chromium mostly only uses the unique/private shmem as specified by
+// "name == L"". The exception is in the StatsTable.
+// TODO(jrg): there is no way to "clean up" all unused named shmem if
+// we restart from a crash. (That isn't a new problem, but it is a problem.)
+// In case we want to delete it later, it may be useful to save the value
+// of mem_filename after FilePathForMemoryName().
+bool SharedMemory::Create(const SharedMemoryCreateOptions& options) {
+ // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466437
+ // is fixed.
+ tracked_objects::ScopedTracker tracking_profile1(
+ FROM_HERE_WITH_EXPLICIT_FUNCTION("466437 SharedMemory::Create::Start"));
+ DCHECK_EQ(-1, mapped_file_);
+ if (options.size == 0)
+ return false;
+
+ if (options.size > static_cast<size_t>(std::numeric_limits<int>::max()))
+ return false;
+
+ // This function theoretically can block on the disk, but realistically
+ // the temporary files we create will just go into the buffer cache
+ // and be deleted before they ever make it out to disk.
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+
+ ScopedFILE fp;
+ bool fix_size = true;
+ ScopedFD readonly_fd;
+
+ FilePath path;
+ if (options.name_deprecated == NULL || options.name_deprecated->empty()) {
+ bool result =
+ CreateAnonymousSharedMemory(options, &fp, &readonly_fd, &path);
+ if (!result)
+ return false;
+ } else {
+ if (!FilePathForMemoryName(*options.name_deprecated, &path))
+ return false;
+
+ // Make sure that the file is opened without any permission
+ // to other users on the system.
+ const mode_t kOwnerOnly = S_IRUSR | S_IWUSR;
+
+ // First, try to create the file.
+ int fd = HANDLE_EINTR(
+ open(path.value().c_str(), O_RDWR | O_CREAT | O_EXCL, kOwnerOnly));
+ if (fd == -1 && options.open_existing_deprecated) {
+ // If this doesn't work, try and open an existing file in append mode.
+ // Opening an existing file in a world writable directory has two main
+ // security implications:
+ // - Attackers could plant a file under their control, so ownership of
+ // the file is checked below.
+ // - Attackers could plant a symbolic link so that an unexpected file
+ // is opened, so O_NOFOLLOW is passed to open().
+ fd = HANDLE_EINTR(
+ open(path.value().c_str(), O_RDWR | O_APPEND | O_NOFOLLOW));
+
+ // Check that the current user owns the file.
+ // If uid != euid, then a more complex permission model is used and this
+ // API is not appropriate.
+ const uid_t real_uid = getuid();
+ const uid_t effective_uid = geteuid();
+ struct stat sb;
+ if (fd >= 0 && (fstat(fd, &sb) != 0 || sb.st_uid != real_uid ||
+ sb.st_uid != effective_uid)) {
+ LOG(ERROR) << "Invalid owner when opening existing shared memory file.";
+ close(fd);
+ return false;
+ }
+
+ // An existing file was opened, so its size should not be fixed.
+ fix_size = false;
+ }
+
+ if (options.share_read_only) {
+ // Also open as readonly so that we can ShareReadOnlyToProcess.
+ readonly_fd.reset(HANDLE_EINTR(open(path.value().c_str(), O_RDONLY)));
+ if (!readonly_fd.is_valid()) {
+ DPLOG(ERROR) << "open(\"" << path.value() << "\", O_RDONLY) failed";
+ close(fd);
+ fd = -1;
+ return false;
+ }
+ }
+ if (fd >= 0) {
+ // "a+" is always appropriate: if it's a new file, a+ is similar to w+.
+ fp.reset(fdopen(fd, "a+"));
+ }
+ }
+ if (fp && fix_size) {
+ // Get current size.
+ struct stat stat;
+ if (fstat(fileno(fp.get()), &stat) != 0)
+ return false;
+ const size_t current_size = stat.st_size;
+ if (current_size != options.size) {
+ if (HANDLE_EINTR(ftruncate(fileno(fp.get()), options.size)) != 0)
+ return false;
+ }
+ requested_size_ = options.size;
+ }
+ if (fp == NULL) {
+ PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed";
+ return false;
+ }
+
+ return PrepareMapFile(fp.Pass(), readonly_fd.Pass());
+}
+
+// Our current implementation of shmem is with mmap()ing of files.
+// These files need to be deleted explicitly.
+// In practice this call is only needed for unit tests.
+bool SharedMemory::Delete(const std::string& name) {
+ FilePath path;
+ if (!FilePathForMemoryName(name, &path))
+ return false;
+
+ if (PathExists(path))
+ return base::DeleteFile(path, false);
+
+ // Doesn't exist, so success.
+ return true;
+}
+
+bool SharedMemory::Open(const std::string& name, bool read_only) {
+ FilePath path;
+ if (!FilePathForMemoryName(name, &path))
+ return false;
+
+ read_only_ = read_only;
+
+ const char* mode = read_only ? "r" : "r+";
+ ScopedFILE fp(base::OpenFile(path, mode));
+ ScopedFD readonly_fd(HANDLE_EINTR(open(path.value().c_str(), O_RDONLY)));
+ if (!readonly_fd.is_valid()) {
+ DPLOG(ERROR) << "open(\"" << path.value() << "\", O_RDONLY) failed";
+ return false;
+ }
+ return PrepareMapFile(fp.Pass(), readonly_fd.Pass());
+}
+
+bool SharedMemory::MapAt(off_t offset, size_t bytes) {
+ if (mapped_file_ == -1)
+ return false;
+
+ if (bytes > static_cast<size_t>(std::numeric_limits<int>::max()))
+ return false;
+
+ if (memory_)
+ return false;
+
+ memory_ = mmap(NULL, bytes, PROT_READ | (read_only_ ? 0 : PROT_WRITE),
+ MAP_SHARED, mapped_file_, offset);
+
+ bool mmap_succeeded = memory_ != (void*)-1 && memory_ != NULL;
+ if (mmap_succeeded) {
+ mapped_size_ = bytes;
+ DCHECK_EQ(0U, reinterpret_cast<uintptr_t>(memory_) &
+ (SharedMemory::MAP_MINIMUM_ALIGNMENT - 1));
+ } else {
+ memory_ = NULL;
+ }
+
+ return mmap_succeeded;
+}
+
+bool SharedMemory::Unmap() {
+ if (memory_ == NULL)
+ return false;
+
+ munmap(memory_, mapped_size_);
+ memory_ = NULL;
+ mapped_size_ = 0;
+ return true;
+}
+
+SharedMemoryHandle SharedMemory::handle() const {
+ return SharedMemoryHandle(mapped_file_, false);
+}
+
+void SharedMemory::Close() {
+ if (mapped_file_ > 0) {
+ if (close(mapped_file_) < 0)
+ PLOG(ERROR) << "close";
+ mapped_file_ = -1;
+ }
+ if (readonly_mapped_file_ > 0) {
+ if (close(readonly_mapped_file_) < 0)
+ PLOG(ERROR) << "close";
+ readonly_mapped_file_ = -1;
+ }
+}
+
+bool SharedMemory::PrepareMapFile(ScopedFILE fp, ScopedFD readonly_fd) {
+ DCHECK_EQ(-1, mapped_file_);
+ DCHECK_EQ(-1, readonly_mapped_file_);
+ if (fp == NULL)
+ return false;
+
+ // This function theoretically can block on the disk, but realistically
+ // the temporary files we create will just go into the buffer cache
+ // and be deleted before they ever make it out to disk.
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+
+ struct stat st = {};
+ if (fstat(fileno(fp.get()), &st))
+ NOTREACHED();
+ if (readonly_fd.is_valid()) {
+ struct stat readonly_st = {};
+ if (fstat(readonly_fd.get(), &readonly_st))
+ NOTREACHED();
+ if (st.st_dev != readonly_st.st_dev || st.st_ino != readonly_st.st_ino) {
+ LOG(ERROR) << "writable and read-only inodes don't match; bailing";
+ return false;
+ }
+ }
+
+ mapped_file_ = HANDLE_EINTR(dup(fileno(fp.get())));
+ if (mapped_file_ == -1) {
+ if (errno == EMFILE) {
+ LOG(WARNING) << "Shared memory creation failed; out of file descriptors";
+ return false;
+ } else {
+ NOTREACHED() << "Call to dup failed, errno=" << errno;
+ }
+ }
+ readonly_mapped_file_ = readonly_fd.release();
+
+ return true;
+}
+
+// For the given shmem named |mem_name|, return a filename to mmap()
+// (and possibly create). Modifies |filename|. Return false on
+// error, or true of we are happy.
+bool SharedMemory::FilePathForMemoryName(const std::string& mem_name,
+ FilePath* path) {
+ // mem_name will be used for a filename; make sure it doesn't
+ // contain anything which will confuse us.
+ DCHECK_EQ(std::string::npos, mem_name.find('/'));
+ DCHECK_EQ(std::string::npos, mem_name.find('\0'));
+
+ FilePath temp_dir;
+ if (!GetShmemTempDir(false, &temp_dir))
+ return false;
+
+ std::string name_base = std::string(base::mac::BaseBundleID());
+ *path = temp_dir.AppendASCII(name_base + ".shmem." + mem_name);
+ return true;
+}
+
+bool SharedMemory::ShareToProcessCommon(ProcessHandle process,
+ SharedMemoryHandle* new_handle,
+ bool close_self,
+ ShareMode share_mode) {
+ int handle_to_dup = -1;
+ switch (share_mode) {
+ case SHARE_CURRENT_MODE:
+ handle_to_dup = mapped_file_;
+ break;
+ case SHARE_READONLY:
+ // We could imagine re-opening the file from /dev/fd, but that can't make
+ // it readonly on Mac: https://codereview.chromium.org/27265002/#msg10
+ CHECK_GE(readonly_mapped_file_, 0);
+ handle_to_dup = readonly_mapped_file_;
+ break;
+ }
+
+ const int new_fd = HANDLE_EINTR(dup(handle_to_dup));
+ if (new_fd < 0) {
+ DPLOG(ERROR) << "dup() failed.";
+ return false;
+ }
+
+ new_handle->SetFileHandle(new_fd, true);
+
+ if (close_self) {
+ Unmap();
+ Close();
+ }
+
+ return true;
+}
+
+} // namespace base
diff --git a/base/memory/shared_memory_nacl.cc b/base/memory/shared_memory_nacl.cc
index 26dd4a3..1e93cca 100644
--- a/base/memory/shared_memory_nacl.cc
+++ b/base/memory/shared_memory_nacl.cc
@@ -24,15 +24,15 @@
requested_size_(0) {
}
-SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only)
+SharedMemory::SharedMemory(const SharedMemoryHandle& handle, bool read_only)
: mapped_file_(handle.fd),
mapped_size_(0),
memory_(NULL),
read_only_(read_only),
- requested_size_(0) {
-}
+ requested_size_(0) {}
-SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only,
+SharedMemory::SharedMemory(const SharedMemoryHandle& handle,
+ bool read_only,
ProcessHandle process)
: mapped_file_(handle.fd),
mapped_size_(0),
@@ -139,14 +139,6 @@
}
}
-void SharedMemory::LockDeprecated() {
- NOTIMPLEMENTED();
-}
-
-void SharedMemory::UnlockDeprecated() {
- NOTIMPLEMENTED();
-}
-
bool SharedMemory::ShareToProcessCommon(ProcessHandle process,
SharedMemoryHandle *new_handle,
bool close_self,
diff --git a/base/memory/shared_memory_posix.cc b/base/memory/shared_memory_posix.cc
index 35d746e..7bd9ecf 100644
--- a/base/memory/shared_memory_posix.cc
+++ b/base/memory/shared_memory_posix.cc
@@ -4,16 +4,13 @@
#include "base/memory/shared_memory.h"
-#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
-#include <sys/types.h>
#include <unistd.h>
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
-#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
#include "base/posix/safe_strerror.h"
@@ -21,13 +18,6 @@
#include "base/profiler/scoped_tracker.h"
#include "base/scoped_generic.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/synchronization/lock.h"
-#include "base/threading/platform_thread.h"
-#include "base/threading/thread_restrictions.h"
-
-#if defined(OS_MACOSX)
-#include "base/mac/foundation_util.h"
-#endif // OS_MACOSX
#if defined(OS_ANDROID)
#include "base/os_compat_android.h"
@@ -38,8 +28,6 @@
namespace {
-LazyInstance<Lock>::Leaky g_thread_lock_ = LAZY_INSTANCE_INITIALIZER;
-
struct ScopedPathUnlinkerTraits {
static FilePath* InvalidValue() { return nullptr; }
@@ -118,16 +106,16 @@
requested_size_(0) {
}
-SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only)
+SharedMemory::SharedMemory(const SharedMemoryHandle& handle, bool read_only)
: mapped_file_(handle.fd),
readonly_mapped_file_(-1),
mapped_size_(0),
memory_(NULL),
read_only_(read_only),
- requested_size_(0) {
-}
+ requested_size_(0) {}
-SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only,
+SharedMemory::SharedMemory(const SharedMemoryHandle& handle,
+ bool read_only,
ProcessHandle process)
: mapped_file_(handle.fd),
readonly_mapped_file_(-1),
@@ -298,7 +286,6 @@
requested_size_ = options.size;
}
if (fp == NULL) {
-#if !defined(OS_MACOSX)
PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed";
FilePath dir = path.DirName();
if (access(dir.value().c_str(), W_OK | X_OK) < 0) {
@@ -308,9 +295,6 @@
<< "/dev/shm. Try 'sudo chmod 1777 /dev/shm' to fix.";
}
}
-#else
- PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed";
-#endif
return false;
}
@@ -414,16 +398,6 @@
}
}
-void SharedMemory::LockDeprecated() {
- g_thread_lock_.Get().Acquire();
- LockOrUnlockCommon(F_LOCK);
-}
-
-void SharedMemory::UnlockDeprecated() {
- LockOrUnlockCommon(F_ULOCK);
- g_thread_lock_.Get().Release();
-}
-
#if !defined(OS_ANDROID)
bool SharedMemory::PrepareMapFile(ScopedFILE fp, ScopedFD readonly_fd) {
DCHECK_EQ(-1, mapped_file_);
@@ -477,39 +451,16 @@
if (!GetShmemTempDir(false, &temp_dir))
return false;
-#if !defined(OS_MACOSX)
#if defined(GOOGLE_CHROME_BUILD)
std::string name_base = std::string("com.google.Chrome");
#else
std::string name_base = std::string("org.chromium.Chromium");
#endif
-#else // OS_MACOSX
- std::string name_base = std::string(base::mac::BaseBundleID());
-#endif // OS_MACOSX
*path = temp_dir.AppendASCII(name_base + ".shmem." + mem_name);
return true;
}
#endif // !defined(OS_ANDROID)
-void SharedMemory::LockOrUnlockCommon(int function) {
- DCHECK_GE(mapped_file_, 0);
- while (lockf(mapped_file_, function, 0) < 0) {
- if (errno == EINTR) {
- continue;
- } else if (errno == ENOLCK) {
- // temporary kernel resource exaustion
- base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(500));
- continue;
- } else {
- NOTREACHED() << "lockf() failed."
- << " function:" << function
- << " fd:" << mapped_file_
- << " errno:" << errno
- << " msg:" << base::safe_strerror(errno);
- }
- }
-}
-
bool SharedMemory::ShareToProcessCommon(ProcessHandle process,
SharedMemoryHandle* new_handle,
bool close_self,
diff --git a/base/memory/shared_memory_unittest.cc b/base/memory/shared_memory_unittest.cc
index 6fe5706..c129e18 100644
--- a/base/memory/shared_memory_unittest.cc
+++ b/base/memory/shared_memory_unittest.cc
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/atomicops.h"
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/shared_memory.h"
@@ -33,7 +34,7 @@
#endif
static const int kNumThreads = 5;
-#if !defined(OS_IOS) // iOS does not allow multiple processes.
+#if !defined(OS_IOS) && !defined(OS_ANDROID)
static const int kNumTasks = 5;
#endif
@@ -90,56 +91,6 @@
const char* const MultipleThreadMain::s_test_name_ =
"SharedMemoryOpenThreadTest";
-// TODO(port):
-// This test requires the ability to pass file descriptors between processes.
-// We haven't done that yet in Chrome for POSIX.
-#if defined(OS_WIN)
-// Each thread will open the shared memory. Each thread will take the memory,
-// and keep changing it while trying to lock it, with some small pauses in
-// between. Verify that each thread's value in the shared memory is always
-// correct.
-class MultipleLockThread : public PlatformThread::Delegate {
- public:
- explicit MultipleLockThread(int id) : id_(id) {}
- ~MultipleLockThread() override {}
-
- // PlatformThread::Delegate interface.
- void ThreadMain() override {
- const uint32 kDataSize = sizeof(int);
- SharedMemoryHandle handle = NULL;
- {
- SharedMemory memory1;
- EXPECT_TRUE(memory1.CreateNamedDeprecated(
- "SharedMemoryMultipleLockThreadTest", true, kDataSize));
- EXPECT_TRUE(memory1.ShareToProcess(GetCurrentProcess(), &handle));
- // TODO(paulg): Implement this once we have a posix version of
- // SharedMemory::ShareToProcess.
- EXPECT_TRUE(true);
- }
-
- SharedMemory memory2(handle, false);
- EXPECT_TRUE(memory2.Map(kDataSize));
- volatile int* const ptr = static_cast<int*>(memory2.memory());
-
- for (int idx = 0; idx < 20; idx++) {
- memory2.LockDeprecated();
- int i = (id_ << 16) + idx;
- *ptr = i;
- PlatformThread::Sleep(TimeDelta::FromMilliseconds(1));
- EXPECT_EQ(*ptr, i);
- memory2.UnlockDeprecated();
- }
-
- memory2.Close();
- }
-
- private:
- int id_;
-
- DISALLOW_COPY_AND_ASSIGN(MultipleLockThread);
-};
-#endif
-
} // namespace
// Android doesn't support SharedMemory::Open/Delete/
@@ -320,34 +271,6 @@
MultipleThreadMain::CleanUp();
}
-// TODO(port): this test requires the MultipleLockThread class
-// (defined above), which requires the ability to pass file
-// descriptors between processes. We haven't done that yet in Chrome
-// for POSIX.
-#if defined(OS_WIN)
-// Create a set of threads to each open a shared memory segment and write to it
-// with the lock held. Verify that they are always reading/writing consistent
-// data.
-TEST(SharedMemoryTest, Lock) {
- PlatformThreadHandle thread_handles[kNumThreads];
- MultipleLockThread* thread_delegates[kNumThreads];
-
- // Spawn the threads.
- for (int index = 0; index < kNumThreads; ++index) {
- PlatformThreadHandle pth;
- thread_delegates[index] = new MultipleLockThread(index);
- EXPECT_TRUE(PlatformThread::Create(0, thread_delegates[index], &pth));
- thread_handles[index] = pth;
- }
-
- // Wait for the threads to finish.
- for (int index = 0; index < kNumThreads; ++index) {
- PlatformThread::Join(thread_handles[index]);
- delete thread_delegates[index];
- }
-}
-#endif
-
// Allocate private (unique) shared memory with an empty string for a
// name. Make sure several of them don't point to the same thing as
// we might expect if the names are equal.
@@ -437,12 +360,13 @@
// http://crbug.com/320865
(void)handle;
#elif defined(OS_POSIX)
- EXPECT_EQ(O_RDONLY, fcntl(handle.fd, F_GETFL) & O_ACCMODE)
+ int handle_fd = SharedMemory::GetFdFromSharedMemoryHandle(handle);
+ EXPECT_EQ(O_RDONLY, fcntl(handle_fd, F_GETFL) & O_ACCMODE)
<< "The descriptor itself should be read-only.";
errno = 0;
- void* writable = mmap(
- NULL, contents.size(), PROT_READ | PROT_WRITE, MAP_SHARED, handle.fd, 0);
+ void* writable = mmap(NULL, contents.size(), PROT_READ | PROT_WRITE,
+ MAP_SHARED, handle_fd, 0);
int mmap_errno = errno;
EXPECT_EQ(MAP_FAILED, writable)
<< "It shouldn't be possible to re-mmap the descriptor writable.";
@@ -596,7 +520,8 @@
EXPECT_TRUE(shared_memory.Create(options));
- int shm_fd = shared_memory.handle().fd;
+ int shm_fd =
+ SharedMemory::GetFdFromSharedMemoryHandle(shared_memory.handle());
struct stat shm_stat;
EXPECT_EQ(0, fstat(shm_fd, &shm_stat));
// Neither the group, nor others should be able to read the shared memory
@@ -622,7 +547,8 @@
// Clean-up the backing file name immediately, we don't need it.
EXPECT_TRUE(shared_memory.Delete(shared_mem_name));
- int shm_fd = shared_memory.handle().fd;
+ int shm_fd =
+ SharedMemory::GetFdFromSharedMemoryHandle(shared_memory.handle());
struct stat shm_stat;
EXPECT_EQ(0, fstat(shm_fd, &shm_stat));
// Neither the group, nor others should have been able to open the shared
@@ -647,7 +573,9 @@
shared_memory.Close();
}
-#if !defined(OS_IOS) // iOS does not allow multiple processes.
+// iOS does not allow multiple processes.
+// Android ashmem doesn't support named shared memory.
+#if !defined(OS_IOS) && !defined(OS_ANDROID)
// On POSIX it is especially important we test shmem across processes,
// not just across threads. But the test is enabled on all platforms.
@@ -664,53 +592,61 @@
#if defined(OS_MACOSX)
mac::ScopedNSAutoreleasePool pool;
#endif
- const uint32 kDataSize = 1024;
SharedMemory memory;
- bool rv = memory.CreateNamedDeprecated(s_test_name_, true, kDataSize);
+ bool rv = memory.CreateNamedDeprecated(s_test_name_, true, s_data_size_);
EXPECT_TRUE(rv);
if (rv != true)
errors++;
- rv = memory.Map(kDataSize);
+ rv = memory.Map(s_data_size_);
EXPECT_TRUE(rv);
if (rv != true)
errors++;
int *ptr = static_cast<int*>(memory.memory());
- for (int idx = 0; idx < 20; idx++) {
- memory.LockDeprecated();
- int i = (1 << 16) + idx;
- *ptr = i;
- PlatformThread::Sleep(TimeDelta::FromMilliseconds(10));
- if (*ptr != i)
- errors++;
- memory.UnlockDeprecated();
- }
-
+ // This runs concurrently in multiple processes. Writes need to be atomic.
+ base::subtle::Barrier_AtomicIncrement(ptr, 1);
memory.Close();
return errors;
}
- private:
static const char* const s_test_name_;
+ static const uint32 s_data_size_;
};
const char* const SharedMemoryProcessTest::s_test_name_ = "MPMem";
+const uint32 SharedMemoryProcessTest::s_data_size_ = 1024;
-TEST_F(SharedMemoryProcessTest, Tasks) {
+TEST_F(SharedMemoryProcessTest, SharedMemoryAcrossProcesses) {
SharedMemoryProcessTest::CleanUp();
+ // Create a shared memory region. Set the first word to 0.
+ SharedMemory memory;
+ bool rv = memory.CreateNamedDeprecated(s_test_name_, true, s_data_size_);
+ ASSERT_TRUE(rv);
+ rv = memory.Map(s_data_size_);
+ ASSERT_TRUE(rv);
+ int* ptr = static_cast<int*>(memory.memory());
+ *ptr = 0;
+
+ // Start |kNumTasks| processes, each of which atomically increments the first
+ // word by 1.
Process processes[kNumTasks];
for (int index = 0; index < kNumTasks; ++index) {
processes[index] = SpawnChild("SharedMemoryTestMain");
ASSERT_TRUE(processes[index].IsValid());
}
+ // Check that each process exited correctly.
int exit_code = 0;
for (int index = 0; index < kNumTasks; ++index) {
EXPECT_TRUE(processes[index].WaitForExit(&exit_code));
EXPECT_EQ(0, exit_code);
}
+ // Check that the shared memory region reflects |kNumTasks| increments.
+ ASSERT_EQ(kNumTasks, *ptr);
+
+ memory.Close();
SharedMemoryProcessTest::CleanUp();
}
@@ -718,6 +654,6 @@
return SharedMemoryProcessTest::TaskTestMain();
}
-#endif // !OS_IOS
+#endif // !defined(OS_IOS) && !defined(OS_ANDROID)
} // namespace base
diff --git a/base/memory/shared_memory_win.cc b/base/memory/shared_memory_win.cc
index eacf0d6..40dcaae 100644
--- a/base/memory/shared_memory_win.cc
+++ b/base/memory/shared_memory_win.cc
@@ -29,40 +29,34 @@
SharedMemory::SharedMemory()
: mapped_file_(NULL),
+ mapped_size_(0),
memory_(NULL),
read_only_(false),
- mapped_size_(0),
- requested_size_(0),
- lock_(NULL) {
-}
+ requested_size_(0) {}
SharedMemory::SharedMemory(const std::wstring& name)
- : mapped_file_(NULL),
+ : name_(name),
+ mapped_file_(NULL),
+ mapped_size_(0),
memory_(NULL),
read_only_(false),
- requested_size_(0),
- mapped_size_(0),
- lock_(NULL),
- name_(name) {
-}
+ requested_size_(0) {}
-SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only)
+SharedMemory::SharedMemory(const SharedMemoryHandle& handle, bool read_only)
: mapped_file_(handle),
+ mapped_size_(0),
memory_(NULL),
read_only_(read_only),
- requested_size_(0),
- mapped_size_(0),
- lock_(NULL) {
-}
+ requested_size_(0) {}
-SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only,
+SharedMemory::SharedMemory(const SharedMemoryHandle& handle,
+ bool read_only,
ProcessHandle process)
: mapped_file_(NULL),
+ mapped_size_(0),
memory_(NULL),
read_only_(read_only),
- requested_size_(0),
- mapped_size_(0),
- lock_(NULL) {
+ requested_size_(0) {
::DuplicateHandle(process, handle,
GetCurrentProcess(), &mapped_file_,
read_only_ ? FILE_MAP_READ : FILE_MAP_READ |
@@ -73,8 +67,6 @@
SharedMemory::~SharedMemory() {
Unmap();
Close();
- if (lock_ != NULL)
- CloseHandle(lock_);
}
// static
@@ -270,26 +262,6 @@
}
}
-void SharedMemory::LockDeprecated() {
- if (lock_ == NULL) {
- std::wstring name = name_;
- name.append(L"lock");
- lock_ = CreateMutex(NULL, FALSE, name.c_str());
- if (lock_ == NULL) {
- DPLOG(ERROR) << "Could not create mutex.";
- NOTREACHED();
- return; // There is nothing good we can do here.
- }
- }
- DWORD result = WaitForSingleObject(lock_, INFINITE);
- DCHECK_EQ(result, WAIT_OBJECT_0);
-}
-
-void SharedMemory::UnlockDeprecated() {
- DCHECK(lock_ != NULL);
- ReleaseMutex(lock_);
-}
-
SharedMemoryHandle SharedMemory::handle() const {
return mapped_file_;
}
diff --git a/base/message_loop/message_loop.cc b/base/message_loop/message_loop.cc
index 4222c77..6bd6730 100644
--- a/base/message_loop/message_loop.cc
+++ b/base/message_loop/message_loop.cc
@@ -19,6 +19,7 @@
#include "base/thread_task_runner_handle.h"
#include "base/threading/thread_local.h"
#include "base/time/time.h"
+#include "base/trace_event/trace_event.h"
#include "base/tracked_objects.h"
#if defined(OS_MACOSX)
@@ -170,7 +171,8 @@
// Tell the incoming queue that we are dying.
incoming_task_queue_->WillDestroyCurrentMessageLoop();
incoming_task_queue_ = NULL;
- message_loop_proxy_ = NULL;
+ unbound_task_runner_ = NULL;
+ task_runner_ = NULL;
// OK, now make it so that no one can find us.
lazy_tls_ptr.Pointer()->Set(NULL);
@@ -257,27 +259,27 @@
void MessageLoop::PostTask(
const tracked_objects::Location& from_here,
const Closure& task) {
- message_loop_proxy_->PostTask(from_here, task);
+ task_runner_->PostTask(from_here, task);
}
void MessageLoop::PostDelayedTask(
const tracked_objects::Location& from_here,
const Closure& task,
TimeDelta delay) {
- message_loop_proxy_->PostDelayedTask(from_here, task, delay);
+ task_runner_->PostDelayedTask(from_here, task, delay);
}
void MessageLoop::PostNonNestableTask(
const tracked_objects::Location& from_here,
const Closure& task) {
- message_loop_proxy_->PostNonNestableTask(from_here, task);
+ task_runner_->PostNonNestableTask(from_here, task);
}
void MessageLoop::PostNonNestableDelayedTask(
const tracked_objects::Location& from_here,
const Closure& task,
TimeDelta delay) {
- message_loop_proxy_->PostNonNestableDelayedTask(from_here, task, delay);
+ task_runner_->PostNonNestableDelayedTask(from_here, task, delay);
}
void MessageLoop::Run() {
@@ -367,6 +369,7 @@
//------------------------------------------------------------------------------
+// static
scoped_ptr<MessageLoop> MessageLoop::CreateUnbound(
Type type, MessagePumpFactoryCallback pump_factory) {
return make_scoped_ptr(new MessageLoop(type, pump_factory));
@@ -386,8 +389,9 @@
message_histogram_(NULL),
run_loop_(NULL),
incoming_task_queue_(new internal::IncomingTaskQueue(this)),
- message_loop_proxy_(
- new internal::MessageLoopProxyImpl(incoming_task_queue_)) {
+ unbound_task_runner_(
+ new internal::MessageLoopTaskRunner(incoming_task_queue_)),
+ task_runner_(unbound_task_runner_) {
// If type is TYPE_CUSTOM non-null pump_factory must be given.
DCHECK_EQ(type_ == TYPE_CUSTOM, !pump_factory_.is_null());
}
@@ -403,9 +407,26 @@
lazy_tls_ptr.Pointer()->Set(this);
incoming_task_queue_->StartScheduling();
- message_loop_proxy_->BindToCurrentThread();
- thread_task_runner_handle_.reset(
- new ThreadTaskRunnerHandle(message_loop_proxy_));
+ unbound_task_runner_->BindToCurrentThread();
+ unbound_task_runner_ = nullptr;
+ SetThreadTaskRunnerHandle();
+}
+
+void MessageLoop::SetTaskRunner(
+ scoped_refptr<SingleThreadTaskRunner> task_runner) {
+ DCHECK_EQ(this, current());
+ DCHECK(task_runner->BelongsToCurrentThread());
+ DCHECK(!unbound_task_runner_);
+ task_runner_ = task_runner.Pass();
+ SetThreadTaskRunnerHandle();
+}
+
+void MessageLoop::SetThreadTaskRunnerHandle() {
+ DCHECK_EQ(this, current());
+ // Clear the previous thread task runner first because only one can exist at
+ // a time.
+ thread_task_runner_handle_.reset();
+ thread_task_runner_handle_.reset(new ThreadTaskRunnerHandle(task_runner_));
}
void MessageLoop::RunHandler() {
@@ -453,10 +474,11 @@
HistogramEvent(kTaskRunEvent);
+ TRACE_TASK_EXECUTION("toplevel", pending_task);
+
FOR_EACH_OBSERVER(TaskObserver, task_observers_,
WillProcessTask(pending_task));
- task_annotator_.RunTask(
- "MessageLoop::PostTask", "MessageLoop::RunTask", pending_task);
+ task_annotator_.RunTask("MessageLoop::PostTask", pending_task);
FOR_EACH_OBSERVER(TaskObserver, task_observers_,
DidProcessTask(pending_task));
diff --git a/base/message_loop/message_loop.h b/base/message_loop/message_loop.h
index f2f89d0..fbb8309 100644
--- a/base/message_loop/message_loop.h
+++ b/base/message_loop/message_loop.h
@@ -16,8 +16,7 @@
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/incoming_task_queue.h"
-#include "base/message_loop/message_loop_proxy.h"
-#include "base/message_loop/message_loop_proxy_impl.h"
+#include "base/message_loop/message_loop_task_runner.h"
#include "base/message_loop/message_pump.h"
#include "base/message_loop/timer_slack.h"
#include "base/observer_list.h"
@@ -296,19 +295,17 @@
}
const std::string& thread_name() const { return thread_name_; }
- // Gets the message loop proxy associated with this message loop.
- //
- // NOTE: Deprecated; prefer task_runner() and the TaskRunner interfaces
- scoped_refptr<MessageLoopProxy> message_loop_proxy() {
- return message_loop_proxy_;
+ // Gets the TaskRunner associated with this message loop.
+ const scoped_refptr<SingleThreadTaskRunner>& task_runner() {
+ return task_runner_;
}
- // Gets the TaskRunner associated with this message loop.
- // TODO(skyostil): Change this to return a const reference to a refptr
- // once the internal type matches what is being returned (crbug.com/465354).
- scoped_refptr<SingleThreadTaskRunner> task_runner() {
- return message_loop_proxy_;
- }
+ // Sets a new TaskRunner for this message loop. The message loop must already
+ // have been bound to a thread prior to this call, and the task runner must
+ // belong to that thread. Note that changing the task runner will also affect
+ // the ThreadTaskRunnerHandle for the target thread. Must be called on the
+ // thread to which the message loop is bound.
+ void SetTaskRunner(scoped_refptr<SingleThreadTaskRunner> task_runner);
// Enables or disables the recursive task processing. This happens in the case
// of recursive message loops. Some unwanted message loop may occurs when
@@ -425,7 +422,7 @@
// thread the message loop runs on, before calling Run().
// Before BindToCurrentThread() is called only Post*Task() functions can
// be called on the message loop.
- scoped_ptr<MessageLoop> CreateUnbound(
+ static scoped_ptr<MessageLoop> CreateUnbound(
Type type,
MessagePumpFactoryCallback pump_factory);
@@ -436,6 +433,10 @@
// Configure various members and bind this message loop to the current thread.
void BindToCurrentThread();
+ // Sets the ThreadTaskRunnerHandle for the current thread to point to the
+ // task runner for this message loop.
+ void SetThreadTaskRunnerHandle();
+
// Invokes the actual run loop using the message pump.
void RunHandler();
@@ -531,8 +532,11 @@
scoped_refptr<internal::IncomingTaskQueue> incoming_task_queue_;
- // The message loop proxy associated with this message loop.
- scoped_refptr<internal::MessageLoopProxyImpl> message_loop_proxy_;
+ // A task runner which we haven't bound to a thread yet.
+ scoped_refptr<internal::MessageLoopTaskRunner> unbound_task_runner_;
+
+ // The task runner associated with this message loop.
+ scoped_refptr<SingleThreadTaskRunner> task_runner_;
scoped_ptr<ThreadTaskRunnerHandle> thread_task_runner_handle_;
template <class T, class R> friend class base::subtle::DeleteHelperInternal;
diff --git a/base/message_loop/message_loop_proxy.cc b/base/message_loop/message_loop_proxy.cc
deleted file mode 100644
index e5f0142..0000000
--- a/base/message_loop/message_loop_proxy.cc
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright (c) 2012 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/message_loop/message_loop_proxy.h"
-
-#include "base/bind.h"
-
-namespace base {
-
-MessageLoopProxy::MessageLoopProxy() {
-}
-
-MessageLoopProxy::~MessageLoopProxy() {
-}
-
-} // namespace base
diff --git a/base/message_loop/message_loop_proxy.h b/base/message_loop/message_loop_proxy.h
deleted file mode 100644
index d5ecc04..0000000
--- a/base/message_loop/message_loop_proxy.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (c) 2012 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_MESSAGE_LOOP_MESSAGE_LOOP_PROXY_H_
-#define BASE_MESSAGE_LOOP_MESSAGE_LOOP_PROXY_H_
-
-#include "base/base_export.h"
-#include "base/compiler_specific.h"
-#include "base/memory/ref_counted.h"
-#include "base/single_thread_task_runner.h"
-
-// MessageLoopProxy is deprecated. Code should prefer to depend on TaskRunner
-// (or the various specializations) for passing task runners around, and should
-// use ThreadTaskRunnerHandle::Get() to get the thread's associated task runner.
-//
-// See http://crbug.com/391045 for more details.
-// Example for these changes:
-//
-// base::MessageLoopProxy::current() -> base::ThreadTaskRunnerHandle::Get()
-// scoped_refptr<base::MessageLoopProxy> ->
-// scoped_refptr<base::SingleThreadTaskRunner>
-// base::MessageLoopProxy -> base::SingleThreadTaskRunner
-
-namespace base {
-
-// This class provides a thread-safe refcounted interface to the Post* methods
-// of a message loop. This class can outlive the target message loop.
-// MessageLoopProxy objects are constructed automatically for all MessageLoops.
-// So, to access them, you can use any of the following:
-// Thread::message_loop_proxy()
-// MessageLoop::current()->message_loop_proxy()
-// MessageLoopProxy::current()
-//
-// TODO(akalin): Now that we have the *TaskRunner interfaces, we can
-// merge this with MessageLoopProxyImpl.
-class BASE_EXPORT MessageLoopProxy : public SingleThreadTaskRunner {
- public:
- // Gets the MessageLoopProxy for the current message loop, creating one if
- // needed.
- static scoped_refptr<MessageLoopProxy> current();
-
- protected:
- MessageLoopProxy();
- ~MessageLoopProxy() override;
-};
-
-} // namespace base
-
-#endif // BASE_MESSAGE_LOOP_MESSAGE_LOOP_PROXY_H_
diff --git a/base/message_loop/message_loop_proxy_impl_unittest.cc b/base/message_loop/message_loop_proxy_impl_unittest.cc
deleted file mode 100644
index fa25371..0000000
--- a/base/message_loop/message_loop_proxy_impl_unittest.cc
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright (c) 2012 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/message_loop/message_loop_proxy_impl.h"
-
-#include "base/bind.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/message_loop/message_loop.h"
-#include "base/message_loop/message_loop_proxy.h"
-#include "base/threading/thread.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/platform_test.h"
-
-namespace base {
-
-class MessageLoopProxyImplTest : public testing::Test {
- public:
- void Release() const {
- AssertOnIOThread();
- Quit();
- }
-
- void Quit() const {
- loop_.PostTask(FROM_HERE, MessageLoop::QuitWhenIdleClosure());
- }
-
- void AssertOnIOThread() const {
- ASSERT_TRUE(io_thread_->message_loop_proxy()->BelongsToCurrentThread());
- ASSERT_EQ(io_thread_->message_loop_proxy(),
- MessageLoopProxy::current());
- }
-
- void AssertOnFileThread() const {
- ASSERT_TRUE(file_thread_->message_loop_proxy()->BelongsToCurrentThread());
- ASSERT_EQ(file_thread_->message_loop_proxy(),
- MessageLoopProxy::current());
- }
-
- protected:
- void SetUp() override {
- io_thread_.reset(new Thread("MessageLoopProxyImplTest_IO"));
- file_thread_.reset(new Thread("MessageLoopProxyImplTest_File"));
- io_thread_->Start();
- file_thread_->Start();
- }
-
- void TearDown() override {
- io_thread_->Stop();
- file_thread_->Stop();
- }
-
- static void BasicFunction(MessageLoopProxyImplTest* test) {
- test->AssertOnFileThread();
- test->Quit();
- }
-
- static void AssertNotRun() {
- FAIL() << "Callback Should not get executed.";
- }
-
- class DeletedOnFile {
- public:
- explicit DeletedOnFile(MessageLoopProxyImplTest* test) : test_(test) {}
-
- ~DeletedOnFile() {
- test_->AssertOnFileThread();
- test_->Quit();
- }
-
- private:
- MessageLoopProxyImplTest* test_;
- };
-
- scoped_ptr<Thread> io_thread_;
- scoped_ptr<Thread> file_thread_;
-
- private:
- mutable MessageLoop loop_;
-};
-
-TEST_F(MessageLoopProxyImplTest, Release) {
- EXPECT_TRUE(io_thread_->message_loop_proxy()->ReleaseSoon(FROM_HERE, this));
- MessageLoop::current()->Run();
-}
-
-TEST_F(MessageLoopProxyImplTest, Delete) {
- DeletedOnFile* deleted_on_file = new DeletedOnFile(this);
- EXPECT_TRUE(file_thread_->message_loop_proxy()->DeleteSoon(
- FROM_HERE, deleted_on_file));
- MessageLoop::current()->Run();
-}
-
-TEST_F(MessageLoopProxyImplTest, PostTask) {
- EXPECT_TRUE(file_thread_->message_loop_proxy()->PostTask(
- FROM_HERE, Bind(&MessageLoopProxyImplTest::BasicFunction,
- Unretained(this))));
- MessageLoop::current()->Run();
-}
-
-TEST_F(MessageLoopProxyImplTest, PostTaskAfterThreadExits) {
- scoped_ptr<Thread> test_thread(
- new Thread("MessageLoopProxyImplTest_Dummy"));
- test_thread->Start();
- scoped_refptr<MessageLoopProxy> message_loop_proxy =
- test_thread->message_loop_proxy();
- test_thread->Stop();
-
- bool ret = message_loop_proxy->PostTask(
- FROM_HERE,
- Bind(&MessageLoopProxyImplTest::AssertNotRun));
- EXPECT_FALSE(ret);
-}
-
-TEST_F(MessageLoopProxyImplTest, PostTaskAfterThreadIsDeleted) {
- scoped_refptr<MessageLoopProxy> message_loop_proxy;
- {
- scoped_ptr<Thread> test_thread(
- new Thread("MessageLoopProxyImplTest_Dummy"));
- test_thread->Start();
- message_loop_proxy = test_thread->message_loop_proxy();
- }
- bool ret = message_loop_proxy->PostTask(
- FROM_HERE,
- Bind(&MessageLoopProxyImplTest::AssertNotRun));
- EXPECT_FALSE(ret);
-}
-
-} // namespace base
diff --git a/base/message_loop/message_loop_proxy_unittest.cc b/base/message_loop/message_loop_proxy_unittest.cc
deleted file mode 100644
index 0b0d9f8..0000000
--- a/base/message_loop/message_loop_proxy_unittest.cc
+++ /dev/null
@@ -1,266 +0,0 @@
-// Copyright (c) 2012 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/message_loop/message_loop_proxy.h"
-
-#include "base/atomic_sequence_num.h"
-#include "base/bind.h"
-#include "base/debug/leak_annotations.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/message_loop/message_loop.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/threading/thread.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace base {
-
-namespace {
-
-class MessageLoopProxyTest : public testing::Test {
- public:
- MessageLoopProxyTest()
- : current_loop_(new MessageLoop()),
- task_thread_("task_thread"),
- thread_sync_(true, false) {
- }
-
- void DeleteCurrentMessageLoop() {
- current_loop_.reset();
- }
-
- protected:
- void SetUp() override {
- // Use SetUp() instead of the constructor to avoid posting a task to a
- // partialy constructed object.
- task_thread_.Start();
-
- // Allow us to pause the |task_thread_|'s MessageLoop.
- task_thread_.message_loop()->PostTask(
- FROM_HERE,
- Bind(&MessageLoopProxyTest::BlockTaskThreadHelper, Unretained(this)));
- }
-
- void TearDown() override {
- // Make sure the |task_thread_| is not blocked, and stop the thread
- // fully before destuction because its tasks may still depend on the
- // |thread_sync_| event.
- thread_sync_.Signal();
- task_thread_.Stop();
- DeleteCurrentMessageLoop();
- }
-
- // Make LoopRecorder threadsafe so that there is defined behavior even if a
- // threading mistake sneaks into the PostTaskAndReplyRelay implementation.
- class LoopRecorder : public RefCountedThreadSafe<LoopRecorder> {
- public:
- LoopRecorder(MessageLoop** run_on, MessageLoop** deleted_on,
- int* destruct_order)
- : run_on_(run_on),
- deleted_on_(deleted_on),
- destruct_order_(destruct_order) {
- }
-
- void RecordRun() {
- *run_on_ = MessageLoop::current();
- }
-
- private:
- friend class RefCountedThreadSafe<LoopRecorder>;
- ~LoopRecorder() {
- *deleted_on_ = MessageLoop::current();
- *destruct_order_ = g_order.GetNext();
- }
-
- MessageLoop** run_on_;
- MessageLoop** deleted_on_;
- int* destruct_order_;
- };
-
- static void RecordLoop(scoped_refptr<LoopRecorder> recorder) {
- recorder->RecordRun();
- }
-
- static void RecordLoopAndQuit(scoped_refptr<LoopRecorder> recorder) {
- recorder->RecordRun();
- MessageLoop::current()->QuitWhenIdle();
- }
-
- void UnblockTaskThread() {
- thread_sync_.Signal();
- }
-
- void BlockTaskThreadHelper() {
- thread_sync_.Wait();
- }
-
- static StaticAtomicSequenceNumber g_order;
-
- scoped_ptr<MessageLoop> current_loop_;
- Thread task_thread_;
-
- private:
- base::WaitableEvent thread_sync_;
-};
-
-StaticAtomicSequenceNumber MessageLoopProxyTest::g_order;
-
-TEST_F(MessageLoopProxyTest, PostTaskAndReply_Basic) {
- MessageLoop* task_run_on = NULL;
- MessageLoop* task_deleted_on = NULL;
- int task_delete_order = -1;
- MessageLoop* reply_run_on = NULL;
- MessageLoop* reply_deleted_on = NULL;
- int reply_delete_order = -1;
-
- scoped_refptr<LoopRecorder> task_recoder =
- new LoopRecorder(&task_run_on, &task_deleted_on, &task_delete_order);
- scoped_refptr<LoopRecorder> reply_recoder =
- new LoopRecorder(&reply_run_on, &reply_deleted_on, &reply_delete_order);
-
- ASSERT_TRUE(task_thread_.message_loop_proxy()->PostTaskAndReply(
- FROM_HERE,
- Bind(&RecordLoop, task_recoder),
- Bind(&RecordLoopAndQuit, reply_recoder)));
-
- // Die if base::Bind doesn't retain a reference to the recorders.
- task_recoder = NULL;
- reply_recoder = NULL;
- ASSERT_FALSE(task_deleted_on);
- ASSERT_FALSE(reply_deleted_on);
-
- UnblockTaskThread();
- current_loop_->Run();
-
- EXPECT_EQ(task_thread_.message_loop(), task_run_on);
- EXPECT_EQ(current_loop_.get(), task_deleted_on);
- EXPECT_EQ(current_loop_.get(), reply_run_on);
- EXPECT_EQ(current_loop_.get(), reply_deleted_on);
- EXPECT_LT(task_delete_order, reply_delete_order);
-}
-
-TEST_F(MessageLoopProxyTest, PostTaskAndReplyOnDeletedThreadDoesNotLeak) {
- MessageLoop* task_run_on = NULL;
- MessageLoop* task_deleted_on = NULL;
- int task_delete_order = -1;
- MessageLoop* reply_run_on = NULL;
- MessageLoop* reply_deleted_on = NULL;
- int reply_delete_order = -1;
-
- scoped_refptr<LoopRecorder> task_recoder =
- new LoopRecorder(&task_run_on, &task_deleted_on, &task_delete_order);
- scoped_refptr<LoopRecorder> reply_recoder =
- new LoopRecorder(&reply_run_on, &reply_deleted_on, &reply_delete_order);
-
- // Grab a MessageLoopProxy to a dead MessageLoop.
- scoped_refptr<MessageLoopProxy> task_loop_proxy =
- task_thread_.message_loop_proxy();
- UnblockTaskThread();
- task_thread_.Stop();
-
- ASSERT_FALSE(task_loop_proxy->PostTaskAndReply(
- FROM_HERE,
- Bind(&RecordLoop, task_recoder),
- Bind(&RecordLoopAndQuit, reply_recoder)));
-
- // The relay should have properly deleted its resources leaving us as the only
- // reference.
- EXPECT_EQ(task_delete_order, reply_delete_order);
- ASSERT_TRUE(task_recoder->HasOneRef());
- ASSERT_TRUE(reply_recoder->HasOneRef());
-
- // Nothing should have run though.
- EXPECT_FALSE(task_run_on);
- EXPECT_FALSE(reply_run_on);
-}
-
-TEST_F(MessageLoopProxyTest, PostTaskAndReply_SameLoop) {
- MessageLoop* task_run_on = NULL;
- MessageLoop* task_deleted_on = NULL;
- int task_delete_order = -1;
- MessageLoop* reply_run_on = NULL;
- MessageLoop* reply_deleted_on = NULL;
- int reply_delete_order = -1;
-
- scoped_refptr<LoopRecorder> task_recoder =
- new LoopRecorder(&task_run_on, &task_deleted_on, &task_delete_order);
- scoped_refptr<LoopRecorder> reply_recoder =
- new LoopRecorder(&reply_run_on, &reply_deleted_on, &reply_delete_order);
-
- // Enqueue the relay.
- ASSERT_TRUE(current_loop_->message_loop_proxy()->PostTaskAndReply(
- FROM_HERE,
- Bind(&RecordLoop, task_recoder),
- Bind(&RecordLoopAndQuit, reply_recoder)));
-
- // Die if base::Bind doesn't retain a reference to the recorders.
- task_recoder = NULL;
- reply_recoder = NULL;
- ASSERT_FALSE(task_deleted_on);
- ASSERT_FALSE(reply_deleted_on);
-
- current_loop_->Run();
-
- EXPECT_EQ(current_loop_.get(), task_run_on);
- EXPECT_EQ(current_loop_.get(), task_deleted_on);
- EXPECT_EQ(current_loop_.get(), reply_run_on);
- EXPECT_EQ(current_loop_.get(), reply_deleted_on);
- EXPECT_LT(task_delete_order, reply_delete_order);
-}
-
-TEST_F(MessageLoopProxyTest, PostTaskAndReply_DeadReplyLoopDoesNotDelete) {
- // Annotate the scope as having memory leaks to suppress heapchecker reports.
- ANNOTATE_SCOPED_MEMORY_LEAK;
- MessageLoop* task_run_on = NULL;
- MessageLoop* task_deleted_on = NULL;
- int task_delete_order = -1;
- MessageLoop* reply_run_on = NULL;
- MessageLoop* reply_deleted_on = NULL;
- int reply_delete_order = -1;
-
- scoped_refptr<LoopRecorder> task_recoder =
- new LoopRecorder(&task_run_on, &task_deleted_on, &task_delete_order);
- scoped_refptr<LoopRecorder> reply_recoder =
- new LoopRecorder(&reply_run_on, &reply_deleted_on, &reply_delete_order);
-
- // Enqueue the relay.
- task_thread_.message_loop_proxy()->PostTaskAndReply(
- FROM_HERE,
- Bind(&RecordLoop, task_recoder),
- Bind(&RecordLoopAndQuit, reply_recoder));
-
- // Die if base::Bind doesn't retain a reference to the recorders.
- task_recoder = NULL;
- reply_recoder = NULL;
- ASSERT_FALSE(task_deleted_on);
- ASSERT_FALSE(reply_deleted_on);
-
- UnblockTaskThread();
-
- // Mercilessly whack the current loop before |reply| gets to run.
- current_loop_.reset();
-
- // This should ensure the relay has been run. We need to record the
- // MessageLoop pointer before stopping the thread because Thread::Stop() will
- // NULL out its own pointer.
- MessageLoop* task_loop = task_thread_.message_loop();
- task_thread_.Stop();
-
- EXPECT_EQ(task_loop, task_run_on);
- ASSERT_FALSE(task_deleted_on);
- EXPECT_FALSE(reply_run_on);
- ASSERT_FALSE(reply_deleted_on);
- EXPECT_EQ(task_delete_order, reply_delete_order);
-
- // The PostTaskAndReplyRelay is leaked here. Even if we had a reference to
- // it, we cannot just delete it because PostTaskAndReplyRelay's destructor
- // checks that MessageLoop::current() is the the same as when the
- // PostTaskAndReplyRelay object was constructed. However, this loop must have
- // aleady been deleted in order to perform this test. See
- // http://crbug.com/86301.
-}
-
-} // namespace
-
-} // namespace base
diff --git a/base/message_loop/message_loop_proxy_impl.cc b/base/message_loop/message_loop_task_runner.cc
similarity index 62%
rename from base/message_loop/message_loop_proxy_impl.cc
rename to base/message_loop/message_loop_task_runner.cc
index 580620d..d553cfe 100644
--- a/base/message_loop/message_loop_proxy_impl.cc
+++ b/base/message_loop/message_loop_task_runner.cc
@@ -2,29 +2,26 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "base/message_loop/message_loop_proxy_impl.h"
+#include "base/message_loop/message_loop_task_runner.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/message_loop/incoming_task_queue.h"
-#include "base/message_loop/message_loop.h"
namespace base {
namespace internal {
-MessageLoopProxyImpl::MessageLoopProxyImpl(
+MessageLoopTaskRunner::MessageLoopTaskRunner(
scoped_refptr<IncomingTaskQueue> incoming_queue)
- : incoming_queue_(incoming_queue),
- valid_thread_id_(kInvalidThreadId) {
-}
+ : incoming_queue_(incoming_queue), valid_thread_id_(kInvalidThreadId) {}
-void MessageLoopProxyImpl::BindToCurrentThread() {
+void MessageLoopTaskRunner::BindToCurrentThread() {
AutoLock lock(valid_thread_id_lock_);
DCHECK_EQ(kInvalidThreadId, valid_thread_id_);
valid_thread_id_ = PlatformThread::CurrentId();
}
-bool MessageLoopProxyImpl::PostDelayedTask(
+bool MessageLoopTaskRunner::PostDelayedTask(
const tracked_objects::Location& from_here,
const base::Closure& task,
base::TimeDelta delay) {
@@ -32,7 +29,7 @@
return incoming_queue_->AddToIncomingQueue(from_here, task, delay, true);
}
-bool MessageLoopProxyImpl::PostNonNestableDelayedTask(
+bool MessageLoopTaskRunner::PostNonNestableDelayedTask(
const tracked_objects::Location& from_here,
const base::Closure& task,
base::TimeDelta delay) {
@@ -40,22 +37,13 @@
return incoming_queue_->AddToIncomingQueue(from_here, task, delay, false);
}
-bool MessageLoopProxyImpl::RunsTasksOnCurrentThread() const {
+bool MessageLoopTaskRunner::RunsTasksOnCurrentThread() const {
AutoLock lock(valid_thread_id_lock_);
return valid_thread_id_ == PlatformThread::CurrentId();
}
-MessageLoopProxyImpl::~MessageLoopProxyImpl() {
-}
+MessageLoopTaskRunner::~MessageLoopTaskRunner() {}
} // namespace internal
-scoped_refptr<MessageLoopProxy>
-MessageLoopProxy::current() {
- MessageLoop* cur_loop = MessageLoop::current();
- if (!cur_loop)
- return NULL;
- return cur_loop->message_loop_proxy();
-}
-
} // namespace base
diff --git a/base/message_loop/message_loop_proxy_impl.h b/base/message_loop/message_loop_task_runner.h
similarity index 60%
rename from base/message_loop/message_loop_proxy_impl.h
rename to base/message_loop/message_loop_task_runner.h
index fa611c2..dc2947d 100644
--- a/base/message_loop/message_loop_proxy_impl.h
+++ b/base/message_loop/message_loop_task_runner.h
@@ -2,13 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef BASE_MESSAGE_LOOP_MESSAGE_LOOP_PROXY_IMPL_H_
-#define BASE_MESSAGE_LOOP_MESSAGE_LOOP_PROXY_IMPL_H_
+#ifndef BASE_MESSAGE_LOOP_MESSAGE_LOOP_TASK_RUNNER_H_
+#define BASE_MESSAGE_LOOP_MESSAGE_LOOP_TASK_RUNNER_H_
#include "base/base_export.h"
#include "base/memory/ref_counted.h"
-#include "base/message_loop/message_loop_proxy.h"
#include "base/pending_task.h"
+#include "base/single_thread_task_runner.h"
#include "base/synchronization/lock.h"
#include "base/threading/platform_thread.h"
@@ -17,18 +17,18 @@
class IncomingTaskQueue;
-// A stock implementation of MessageLoopProxy that is created and managed by a
-// MessageLoop. For now a MessageLoopProxyImpl can only be created as part of a
-// MessageLoop.
-class BASE_EXPORT MessageLoopProxyImpl : public MessageLoopProxy {
+// A stock implementation of SingleThreadTaskRunner that is created and managed
+// by a MessageLoop. For now a MessageLoopTaskRunner can only be created as
+// part of a MessageLoop.
+class BASE_EXPORT MessageLoopTaskRunner : public SingleThreadTaskRunner {
public:
- explicit MessageLoopProxyImpl(
+ explicit MessageLoopTaskRunner(
scoped_refptr<IncomingTaskQueue> incoming_queue);
- // Initialize this message loop proxy on the current thread.
+ // Initialize this message loop task runner on the current thread.
void BindToCurrentThread();
- // MessageLoopProxy implementation
+ // SingleThreadTaskRunner implementation
bool PostDelayedTask(const tracked_objects::Location& from_here,
const base::Closure& task,
base::TimeDelta delay) override;
@@ -38,10 +38,10 @@
bool RunsTasksOnCurrentThread() const override;
private:
- friend class RefCountedThreadSafe<MessageLoopProxyImpl>;
- ~MessageLoopProxyImpl() override;
+ friend class RefCountedThreadSafe<MessageLoopTaskRunner>;
+ ~MessageLoopTaskRunner() override;
- // THe incoming queue receiving all posted tasks.
+ // The incoming queue receiving all posted tasks.
scoped_refptr<IncomingTaskQueue> incoming_queue_;
// ID of the thread |this| was created on. Could be accessed on multiple
@@ -49,10 +49,10 @@
PlatformThreadId valid_thread_id_;
mutable Lock valid_thread_id_lock_;
- DISALLOW_COPY_AND_ASSIGN(MessageLoopProxyImpl);
+ DISALLOW_COPY_AND_ASSIGN(MessageLoopTaskRunner);
};
} // namespace internal
} // namespace base
-#endif // BASE_MESSAGE_LOOP_MESSAGE_LOOP_PROXY_IMPL_H_
+#endif // BASE_MESSAGE_LOOP_MESSAGE_LOOP_TASK_RUNNER_H_
diff --git a/base/message_loop/message_loop_task_runner_unittest.cc b/base/message_loop/message_loop_task_runner_unittest.cc
new file mode 100644
index 0000000..caf88af
--- /dev/null
+++ b/base/message_loop/message_loop_task_runner_unittest.cc
@@ -0,0 +1,358 @@
+// Copyright (c) 2012 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/message_loop/message_loop_task_runner.h"
+
+#include "base/atomic_sequence_num.h"
+#include "base/bind.h"
+#include "base/debug/leak_annotations.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_task_runner.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/thread_task_runner_handle.h"
+#include "base/threading/thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+namespace base {
+
+class MessageLoopTaskRunnerTest : public testing::Test {
+ public:
+ MessageLoopTaskRunnerTest()
+ : current_loop_(new MessageLoop()),
+ task_thread_("task_thread"),
+ thread_sync_(true, false) {}
+
+ void DeleteCurrentMessageLoop() { current_loop_.reset(); }
+
+ protected:
+ void SetUp() override {
+ // Use SetUp() instead of the constructor to avoid posting a task to a
+ // partialy constructed object.
+ task_thread_.Start();
+
+ // Allow us to pause the |task_thread_|'s MessageLoop.
+ task_thread_.message_loop()->PostTask(
+ FROM_HERE, Bind(&MessageLoopTaskRunnerTest::BlockTaskThreadHelper,
+ Unretained(this)));
+ }
+
+ void TearDown() override {
+ // Make sure the |task_thread_| is not blocked, and stop the thread
+ // fully before destuction because its tasks may still depend on the
+ // |thread_sync_| event.
+ thread_sync_.Signal();
+ task_thread_.Stop();
+ DeleteCurrentMessageLoop();
+ }
+
+ // Make LoopRecorder threadsafe so that there is defined behavior even if a
+ // threading mistake sneaks into the PostTaskAndReplyRelay implementation.
+ class LoopRecorder : public RefCountedThreadSafe<LoopRecorder> {
+ public:
+ LoopRecorder(MessageLoop** run_on,
+ MessageLoop** deleted_on,
+ int* destruct_order)
+ : run_on_(run_on),
+ deleted_on_(deleted_on),
+ destruct_order_(destruct_order) {}
+
+ void RecordRun() { *run_on_ = MessageLoop::current(); }
+
+ private:
+ friend class RefCountedThreadSafe<LoopRecorder>;
+ ~LoopRecorder() {
+ *deleted_on_ = MessageLoop::current();
+ *destruct_order_ = g_order.GetNext();
+ }
+
+ MessageLoop** run_on_;
+ MessageLoop** deleted_on_;
+ int* destruct_order_;
+ };
+
+ static void RecordLoop(scoped_refptr<LoopRecorder> recorder) {
+ recorder->RecordRun();
+ }
+
+ static void RecordLoopAndQuit(scoped_refptr<LoopRecorder> recorder) {
+ recorder->RecordRun();
+ MessageLoop::current()->QuitWhenIdle();
+ }
+
+ void UnblockTaskThread() { thread_sync_.Signal(); }
+
+ void BlockTaskThreadHelper() { thread_sync_.Wait(); }
+
+ static StaticAtomicSequenceNumber g_order;
+
+ scoped_ptr<MessageLoop> current_loop_;
+ Thread task_thread_;
+
+ private:
+ base::WaitableEvent thread_sync_;
+};
+
+StaticAtomicSequenceNumber MessageLoopTaskRunnerTest::g_order;
+
+TEST_F(MessageLoopTaskRunnerTest, PostTaskAndReply_Basic) {
+ MessageLoop* task_run_on = NULL;
+ MessageLoop* task_deleted_on = NULL;
+ int task_delete_order = -1;
+ MessageLoop* reply_run_on = NULL;
+ MessageLoop* reply_deleted_on = NULL;
+ int reply_delete_order = -1;
+
+ scoped_refptr<LoopRecorder> task_recoder =
+ new LoopRecorder(&task_run_on, &task_deleted_on, &task_delete_order);
+ scoped_refptr<LoopRecorder> reply_recoder =
+ new LoopRecorder(&reply_run_on, &reply_deleted_on, &reply_delete_order);
+
+ ASSERT_TRUE(task_thread_.task_runner()->PostTaskAndReply(
+ FROM_HERE, Bind(&RecordLoop, task_recoder),
+ Bind(&RecordLoopAndQuit, reply_recoder)));
+
+ // Die if base::Bind doesn't retain a reference to the recorders.
+ task_recoder = NULL;
+ reply_recoder = NULL;
+ ASSERT_FALSE(task_deleted_on);
+ ASSERT_FALSE(reply_deleted_on);
+
+ UnblockTaskThread();
+ current_loop_->Run();
+
+ EXPECT_EQ(task_thread_.message_loop(), task_run_on);
+ EXPECT_EQ(current_loop_.get(), task_deleted_on);
+ EXPECT_EQ(current_loop_.get(), reply_run_on);
+ EXPECT_EQ(current_loop_.get(), reply_deleted_on);
+ EXPECT_LT(task_delete_order, reply_delete_order);
+}
+
+TEST_F(MessageLoopTaskRunnerTest, PostTaskAndReplyOnDeletedThreadDoesNotLeak) {
+ MessageLoop* task_run_on = NULL;
+ MessageLoop* task_deleted_on = NULL;
+ int task_delete_order = -1;
+ MessageLoop* reply_run_on = NULL;
+ MessageLoop* reply_deleted_on = NULL;
+ int reply_delete_order = -1;
+
+ scoped_refptr<LoopRecorder> task_recoder =
+ new LoopRecorder(&task_run_on, &task_deleted_on, &task_delete_order);
+ scoped_refptr<LoopRecorder> reply_recoder =
+ new LoopRecorder(&reply_run_on, &reply_deleted_on, &reply_delete_order);
+
+ // Grab a task runner to a dead MessageLoop.
+ scoped_refptr<SingleThreadTaskRunner> task_runner =
+ task_thread_.task_runner();
+ UnblockTaskThread();
+ task_thread_.Stop();
+
+ ASSERT_FALSE(
+ task_runner->PostTaskAndReply(FROM_HERE, Bind(&RecordLoop, task_recoder),
+ Bind(&RecordLoopAndQuit, reply_recoder)));
+
+ // The relay should have properly deleted its resources leaving us as the only
+ // reference.
+ EXPECT_EQ(task_delete_order, reply_delete_order);
+ ASSERT_TRUE(task_recoder->HasOneRef());
+ ASSERT_TRUE(reply_recoder->HasOneRef());
+
+ // Nothing should have run though.
+ EXPECT_FALSE(task_run_on);
+ EXPECT_FALSE(reply_run_on);
+}
+
+TEST_F(MessageLoopTaskRunnerTest, PostTaskAndReply_SameLoop) {
+ MessageLoop* task_run_on = NULL;
+ MessageLoop* task_deleted_on = NULL;
+ int task_delete_order = -1;
+ MessageLoop* reply_run_on = NULL;
+ MessageLoop* reply_deleted_on = NULL;
+ int reply_delete_order = -1;
+
+ scoped_refptr<LoopRecorder> task_recoder =
+ new LoopRecorder(&task_run_on, &task_deleted_on, &task_delete_order);
+ scoped_refptr<LoopRecorder> reply_recoder =
+ new LoopRecorder(&reply_run_on, &reply_deleted_on, &reply_delete_order);
+
+ // Enqueue the relay.
+ ASSERT_TRUE(current_loop_->task_runner()->PostTaskAndReply(
+ FROM_HERE, Bind(&RecordLoop, task_recoder),
+ Bind(&RecordLoopAndQuit, reply_recoder)));
+
+ // Die if base::Bind doesn't retain a reference to the recorders.
+ task_recoder = NULL;
+ reply_recoder = NULL;
+ ASSERT_FALSE(task_deleted_on);
+ ASSERT_FALSE(reply_deleted_on);
+
+ current_loop_->Run();
+
+ EXPECT_EQ(current_loop_.get(), task_run_on);
+ EXPECT_EQ(current_loop_.get(), task_deleted_on);
+ EXPECT_EQ(current_loop_.get(), reply_run_on);
+ EXPECT_EQ(current_loop_.get(), reply_deleted_on);
+ EXPECT_LT(task_delete_order, reply_delete_order);
+}
+
+TEST_F(MessageLoopTaskRunnerTest, PostTaskAndReply_DeadReplyLoopDoesNotDelete) {
+ // Annotate the scope as having memory leaks to suppress heapchecker reports.
+ ANNOTATE_SCOPED_MEMORY_LEAK;
+ MessageLoop* task_run_on = NULL;
+ MessageLoop* task_deleted_on = NULL;
+ int task_delete_order = -1;
+ MessageLoop* reply_run_on = NULL;
+ MessageLoop* reply_deleted_on = NULL;
+ int reply_delete_order = -1;
+
+ scoped_refptr<LoopRecorder> task_recoder =
+ new LoopRecorder(&task_run_on, &task_deleted_on, &task_delete_order);
+ scoped_refptr<LoopRecorder> reply_recoder =
+ new LoopRecorder(&reply_run_on, &reply_deleted_on, &reply_delete_order);
+
+ // Enqueue the relay.
+ task_thread_.task_runner()->PostTaskAndReply(
+ FROM_HERE, Bind(&RecordLoop, task_recoder),
+ Bind(&RecordLoopAndQuit, reply_recoder));
+
+ // Die if base::Bind doesn't retain a reference to the recorders.
+ task_recoder = NULL;
+ reply_recoder = NULL;
+ ASSERT_FALSE(task_deleted_on);
+ ASSERT_FALSE(reply_deleted_on);
+
+ UnblockTaskThread();
+
+ // Mercilessly whack the current loop before |reply| gets to run.
+ current_loop_.reset();
+
+ // This should ensure the relay has been run. We need to record the
+ // MessageLoop pointer before stopping the thread because Thread::Stop() will
+ // NULL out its own pointer.
+ MessageLoop* task_loop = task_thread_.message_loop();
+ task_thread_.Stop();
+
+ EXPECT_EQ(task_loop, task_run_on);
+ ASSERT_FALSE(task_deleted_on);
+ EXPECT_FALSE(reply_run_on);
+ ASSERT_FALSE(reply_deleted_on);
+ EXPECT_EQ(task_delete_order, reply_delete_order);
+
+ // The PostTaskAndReplyRelay is leaked here. Even if we had a reference to
+ // it, we cannot just delete it because PostTaskAndReplyRelay's destructor
+ // checks that MessageLoop::current() is the the same as when the
+ // PostTaskAndReplyRelay object was constructed. However, this loop must have
+ // aleady been deleted in order to perform this test. See
+ // http://crbug.com/86301.
+}
+
+class MessageLoopTaskRunnerThreadingTest : public testing::Test {
+ public:
+ void Release() const {
+ AssertOnIOThread();
+ Quit();
+ }
+
+ void Quit() const {
+ loop_.PostTask(FROM_HERE, MessageLoop::QuitWhenIdleClosure());
+ }
+
+ void AssertOnIOThread() const {
+ ASSERT_TRUE(io_thread_->task_runner()->BelongsToCurrentThread());
+ ASSERT_EQ(io_thread_->task_runner(), ThreadTaskRunnerHandle::Get());
+ }
+
+ void AssertOnFileThread() const {
+ ASSERT_TRUE(file_thread_->task_runner()->BelongsToCurrentThread());
+ ASSERT_EQ(file_thread_->task_runner(), ThreadTaskRunnerHandle::Get());
+ }
+
+ protected:
+ void SetUp() override {
+ io_thread_.reset(new Thread("MessageLoopTaskRunnerThreadingTest_IO"));
+ file_thread_.reset(new Thread("MessageLoopTaskRunnerThreadingTest_File"));
+ io_thread_->Start();
+ file_thread_->Start();
+ }
+
+ void TearDown() override {
+ io_thread_->Stop();
+ file_thread_->Stop();
+ }
+
+ static void BasicFunction(MessageLoopTaskRunnerThreadingTest* test) {
+ test->AssertOnFileThread();
+ test->Quit();
+ }
+
+ static void AssertNotRun() { FAIL() << "Callback Should not get executed."; }
+
+ class DeletedOnFile {
+ public:
+ explicit DeletedOnFile(MessageLoopTaskRunnerThreadingTest* test)
+ : test_(test) {}
+
+ ~DeletedOnFile() {
+ test_->AssertOnFileThread();
+ test_->Quit();
+ }
+
+ private:
+ MessageLoopTaskRunnerThreadingTest* test_;
+ };
+
+ scoped_ptr<Thread> io_thread_;
+ scoped_ptr<Thread> file_thread_;
+
+ private:
+ mutable MessageLoop loop_;
+};
+
+TEST_F(MessageLoopTaskRunnerThreadingTest, Release) {
+ EXPECT_TRUE(io_thread_->task_runner()->ReleaseSoon(FROM_HERE, this));
+ MessageLoop::current()->Run();
+}
+
+TEST_F(MessageLoopTaskRunnerThreadingTest, Delete) {
+ DeletedOnFile* deleted_on_file = new DeletedOnFile(this);
+ EXPECT_TRUE(
+ file_thread_->task_runner()->DeleteSoon(FROM_HERE, deleted_on_file));
+ MessageLoop::current()->Run();
+}
+
+TEST_F(MessageLoopTaskRunnerThreadingTest, PostTask) {
+ EXPECT_TRUE(file_thread_->task_runner()->PostTask(
+ FROM_HERE, Bind(&MessageLoopTaskRunnerThreadingTest::BasicFunction,
+ Unretained(this))));
+ MessageLoop::current()->Run();
+}
+
+TEST_F(MessageLoopTaskRunnerThreadingTest, PostTaskAfterThreadExits) {
+ scoped_ptr<Thread> test_thread(
+ new Thread("MessageLoopTaskRunnerThreadingTest_Dummy"));
+ test_thread->Start();
+ scoped_refptr<SingleThreadTaskRunner> task_runner =
+ test_thread->task_runner();
+ test_thread->Stop();
+
+ bool ret = task_runner->PostTask(
+ FROM_HERE, Bind(&MessageLoopTaskRunnerThreadingTest::AssertNotRun));
+ EXPECT_FALSE(ret);
+}
+
+TEST_F(MessageLoopTaskRunnerThreadingTest, PostTaskAfterThreadIsDeleted) {
+ scoped_refptr<SingleThreadTaskRunner> task_runner;
+ {
+ scoped_ptr<Thread> test_thread(
+ new Thread("MessageLoopTaskRunnerThreadingTest_Dummy"));
+ test_thread->Start();
+ task_runner = test_thread->task_runner();
+ }
+ bool ret = task_runner->PostTask(
+ FROM_HERE, Bind(&MessageLoopTaskRunnerThreadingTest::AssertNotRun));
+ EXPECT_FALSE(ret);
+}
+
+} // namespace base
diff --git a/base/message_loop/message_loop_unittest.cc b/base/message_loop/message_loop_unittest.cc
index ddde6bb..9c17017 100644
--- a/base/message_loop/message_loop_unittest.cc
+++ b/base/message_loop/message_loop_unittest.cc
@@ -10,12 +10,12 @@
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop.h"
-#include "base/message_loop/message_loop_proxy_impl.h"
#include "base/message_loop/message_loop_test.h"
#include "base/pending_task.h"
#include "base/posix/eintr_wrapper.h"
#include "base/run_loop.h"
#include "base/synchronization/waitable_event.h"
+#include "base/test/test_simple_task_runner.h"
#include "base/thread_task_runner_handle.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread.h"
@@ -1012,4 +1012,26 @@
}
#endif // defined(OS_WIN)
+TEST(MessageLoopTest, SetTaskRunner) {
+ MessageLoop loop;
+ scoped_refptr<SingleThreadTaskRunner> new_runner(new TestSimpleTaskRunner());
+
+ loop.SetTaskRunner(new_runner);
+ EXPECT_EQ(new_runner, loop.task_runner());
+ EXPECT_EQ(new_runner, ThreadTaskRunnerHandle::Get());
+}
+
+TEST(MessageLoopTest, OriginalRunnerWorks) {
+ MessageLoop loop;
+ scoped_refptr<SingleThreadTaskRunner> new_runner(new TestSimpleTaskRunner());
+ scoped_refptr<SingleThreadTaskRunner> original_runner(loop.task_runner());
+ loop.SetTaskRunner(new_runner);
+
+ scoped_refptr<Foo> foo(new Foo());
+ original_runner->PostTask(FROM_HERE,
+ Bind(&Foo::Test1ConstRef, foo.get(), "a"));
+ loop.RunUntilIdle();
+ EXPECT_EQ(1, foo->test_count());
+}
+
} // namespace base
diff --git a/base/message_loop/message_pump_mac.mm b/base/message_loop/message_pump_mac.mm
index 914977b..53e3363 100644
--- a/base/message_loop/message_pump_mac.mm
+++ b/base/message_loop/message_pump_mac.mm
@@ -10,6 +10,7 @@
#include <limits>
#include "base/logging.h"
+#include "base/mac/call_with_eh_frame.h"
#include "base/mac/scoped_cftyperef.h"
#include "base/message_loop/timer_slack.h"
#include "base/run_loop.h"
@@ -300,7 +301,9 @@
// static
void MessagePumpCFRunLoopBase::RunWorkSource(void* info) {
MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
- self->RunWork();
+ base::mac::CallWithEHFrame(^{
+ self->RunWork();
+ });
}
// Called by MessagePumpCFRunLoopBase::RunWorkSource.
@@ -359,7 +362,9 @@
// static
void MessagePumpCFRunLoopBase::RunIdleWorkSource(void* info) {
MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
- self->RunIdleWork();
+ base::mac::CallWithEHFrame(^{
+ self->RunIdleWork();
+ });
}
// Called by MessagePumpCFRunLoopBase::RunIdleWorkSource.
@@ -393,7 +398,9 @@
// static
void MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource(void* info) {
MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
- self->RunNestingDeferredWork();
+ base::mac::CallWithEHFrame(^{
+ self->RunNestingDeferredWork();
+ });
}
// Called by MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource.
@@ -440,15 +447,16 @@
CFRunLoopActivity activity,
void* info) {
MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
+ base::mac::CallWithEHFrame(^{
+ // Attempt to do some idle work before going to sleep.
+ self->RunIdleWork();
- // Attempt to do some idle work before going to sleep.
- self->RunIdleWork();
-
- // The run loop is about to go to sleep. If any of the work done since it
- // started or woke up resulted in a nested run loop running,
- // nesting-deferred work may have accumulated. Schedule it for processing
- // if appropriate.
- self->MaybeScheduleNestingDeferredWork();
+ // The run loop is about to go to sleep. If any of the work done since it
+ // started or woke up resulted in a nested run loop running,
+ // nesting-deferred work may have accumulated. Schedule it for processing
+ // if appropriate.
+ self->MaybeScheduleNestingDeferredWork();
+ });
}
// Called from the run loop.
@@ -463,7 +471,9 @@
// level did not sleep or exit, nesting-deferred work may have accumulated
// if a nested loop ran. Schedule nesting-deferred work for processing if
// appropriate.
- self->MaybeScheduleNestingDeferredWork();
+ base::mac::CallWithEHFrame(^{
+ self->MaybeScheduleNestingDeferredWork();
+ });
}
// Called from the run loop.
@@ -498,7 +508,9 @@
// to sleep or exiting. It must be called before decrementing the
// value so that the value still corresponds to the level of the exiting
// loop.
- self->MaybeScheduleNestingDeferredWork();
+ base::mac::CallWithEHFrame(^{
+ self->MaybeScheduleNestingDeferredWork();
+ });
--self->nesting_level_;
break;
@@ -506,7 +518,9 @@
break;
}
- self->EnterExitRunLoop(activity);
+ base::mac::CallWithEHFrame(^{
+ self->EnterExitRunLoop(activity);
+ });
}
// Called by MessagePumpCFRunLoopBase::EnterExitRunLoop. The default
diff --git a/base/metrics/field_trial_unittest.cc b/base/metrics/field_trial_unittest.cc
index f1a1042..ed84d86 100644
--- a/base/metrics/field_trial_unittest.cc
+++ b/base/metrics/field_trial_unittest.cc
@@ -1021,9 +1021,9 @@
scoped_refptr<base::FieldTrial> trial(
new base::FieldTrial("test", kBucketCount, "default", entropy));
for (int j = 0; j < kBucketCount; ++j)
- trial->AppendGroup(base::StringPrintf("%d", j), 1);
+ trial->AppendGroup(base::IntToString(j), 1);
- EXPECT_EQ(base::StringPrintf("%d", i), trial->group_name());
+ EXPECT_EQ(base::IntToString(i), trial->group_name());
}
}
diff --git a/base/metrics/histogram.cc b/base/metrics/histogram.cc
index 1550420..0c9fcb7 100644
--- a/base/metrics/histogram.cc
+++ b/base/metrics/histogram.cc
@@ -17,6 +17,7 @@
#include "base/compiler_specific.h"
#include "base/debug/alias.h"
#include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
#include "base/metrics/sample_vector.h"
#include "base/metrics/statistics_recorder.h"
#include "base/pickle.h"
@@ -130,6 +131,23 @@
flags);
}
+HistogramBase* Histogram::FactoryGet(const char* name,
+ Sample minimum,
+ Sample maximum,
+ size_t bucket_count,
+ int32 flags) {
+ return FactoryGet(std::string(name), minimum, maximum, bucket_count, flags);
+}
+
+HistogramBase* Histogram::FactoryTimeGet(const char* name,
+ TimeDelta minimum,
+ TimeDelta maximum,
+ size_t bucket_count,
+ int32 flags) {
+ return FactoryTimeGet(std::string(name), minimum, maximum, bucket_count,
+ flags);
+}
+
// Calculate what range of values are held in each bucket.
// We have to be careful that we don't pick a ratio between starting points in
// consecutive buckets that is sooo small, that the integer bounds are the same
@@ -262,6 +280,8 @@
if (value < 0)
value = 0;
samples_->Accumulate(value, 1);
+
+ FindAndRunCallback(value);
}
scoped_ptr<HistogramSamples> Histogram::SnapshotSamples() const {
@@ -531,6 +551,23 @@
flags);
}
+HistogramBase* LinearHistogram::FactoryGet(const char* name,
+ Sample minimum,
+ Sample maximum,
+ size_t bucket_count,
+ int32 flags) {
+ return FactoryGet(std::string(name), minimum, maximum, bucket_count, flags);
+}
+
+HistogramBase* LinearHistogram::FactoryTimeGet(const char* name,
+ TimeDelta minimum,
+ TimeDelta maximum,
+ size_t bucket_count,
+ int32 flags) {
+ return FactoryTimeGet(std::string(name), minimum, maximum, bucket_count,
+ flags);
+}
+
HistogramBase* LinearHistogram::FactoryGetWithRangeDescription(
const std::string& name,
Sample minimum,
@@ -676,6 +713,10 @@
return histogram;
}
+HistogramBase* BooleanHistogram::FactoryGet(const char* name, int32 flags) {
+ return FactoryGet(std::string(name), flags);
+}
+
HistogramType BooleanHistogram::GetHistogramType() const {
return BOOLEAN_HISTOGRAM;
}
@@ -736,6 +777,13 @@
return histogram;
}
+HistogramBase* CustomHistogram::FactoryGet(
+ const char* name,
+ const std::vector<Sample>& custom_ranges,
+ int32 flags) {
+ return FactoryGet(std::string(name), custom_ranges, flags);
+}
+
HistogramType CustomHistogram::GetHistogramType() const {
return CUSTOM_HISTOGRAM;
}
diff --git a/base/metrics/histogram.h b/base/metrics/histogram.h
index c13f05e..58bc029 100644
--- a/base/metrics/histogram.h
+++ b/base/metrics/histogram.h
@@ -121,6 +121,20 @@
size_t bucket_count,
int32 flags);
+ // Overloads of the above two functions that take a const char* |name| param,
+ // to avoid code bloat from the std::string constructor being inlined into
+ // call sites.
+ static HistogramBase* FactoryGet(const char* name,
+ Sample minimum,
+ Sample maximum,
+ size_t bucket_count,
+ int32 flags);
+ static HistogramBase* FactoryTimeGet(const char* name,
+ base::TimeDelta minimum,
+ base::TimeDelta maximum,
+ size_t bucket_count,
+ int32 flags);
+
static void InitializeBucketRanges(Sample minimum,
Sample maximum,
BucketRanges* ranges);
@@ -277,6 +291,20 @@
size_t bucket_count,
int32 flags);
+ // Overloads of the above two functions that take a const char* |name| param,
+ // to avoid code bloat from the std::string constructor being inlined into
+ // call sites.
+ static HistogramBase* FactoryGet(const char* name,
+ Sample minimum,
+ Sample maximum,
+ size_t bucket_count,
+ int32 flags);
+ static HistogramBase* FactoryTimeGet(const char* name,
+ TimeDelta minimum,
+ TimeDelta maximum,
+ size_t bucket_count,
+ int32 flags);
+
struct DescriptionPair {
Sample sample;
const char* description; // Null means end of a list of pairs.
@@ -339,6 +367,11 @@
public:
static HistogramBase* FactoryGet(const std::string& name, int32 flags);
+ // Overload of the above function that takes a const char* |name| param,
+ // to avoid code bloat from the std::string constructor being inlined into
+ // call sites.
+ static HistogramBase* FactoryGet(const char* name, int32 flags);
+
HistogramType GetHistogramType() const override;
private:
@@ -364,6 +397,13 @@
const std::vector<Sample>& custom_ranges,
int32 flags);
+ // Overload of the above function that takes a const char* |name| param,
+ // to avoid code bloat from the std::string constructor being inlined into
+ // call sites.
+ static HistogramBase* FactoryGet(const char* name,
+ const std::vector<Sample>& custom_ranges,
+ int32 flags);
+
// Overridden from Histogram:
HistogramType GetHistogramType() const override;
diff --git a/base/metrics/histogram_base.cc b/base/metrics/histogram_base.cc
index de34c79..6b3f69c 100644
--- a/base/metrics/histogram_base.cc
+++ b/base/metrics/histogram_base.cc
@@ -12,6 +12,7 @@
#include "base/metrics/histogram.h"
#include "base/metrics/histogram_samples.h"
#include "base/metrics/sparse_histogram.h"
+#include "base/metrics/statistics_recorder.h"
#include "base/pickle.h"
#include "base/process/process_handle.h"
#include "base/strings/stringprintf.h"
@@ -117,6 +118,16 @@
serializer.Serialize(root);
}
+void HistogramBase::FindAndRunCallback(HistogramBase::Sample sample) const {
+ if ((flags_ & kCallbackExists) == 0)
+ return;
+
+ StatisticsRecorder::OnSampleCallback cb =
+ StatisticsRecorder::FindCallback(histogram_name());
+ if (!cb.is_null())
+ cb.Run(sample);
+}
+
void HistogramBase::WriteAsciiBucketGraph(double current_size,
double max_size,
std::string* output) const {
diff --git a/base/metrics/histogram_base.h b/base/metrics/histogram_base.h
index 006395b..1bc1f6e 100644
--- a/base/metrics/histogram_base.h
+++ b/base/metrics/histogram_base.h
@@ -31,7 +31,7 @@
// processes into the browser. If you create another class that inherits from
// HistogramBase, add new histogram types and names below.
-enum BASE_EXPORT HistogramType {
+enum HistogramType {
HISTOGRAM,
LINEAR_HISTOGRAM,
BOOLEAN_HISTOGRAM,
@@ -74,6 +74,12 @@
// the source histogram!).
kIPCSerializationSourceFlag = 0x10,
+ // Indicates that a callback exists for when a new sample is recorded on
+ // this histogram. We store this as a flag with the histogram since
+ // histograms can be in performance critical code, and this allows us
+ // to shortcut looking up the callback if it doesn't exist.
+ kCallbackExists = 0x20,
+
// Only for Histogram and its sub classes: fancy bucket-naming support.
kHexRangePrintingFlag = 0x8000,
};
@@ -92,7 +98,7 @@
explicit HistogramBase(const std::string& name);
virtual ~HistogramBase();
- std::string histogram_name() const { return histogram_name_; }
+ const std::string& histogram_name() const { return histogram_name_; }
// Comapres |name| to the histogram name and triggers a DCHECK if they do not
// match. This is a helper function used by histogram macros, which results in
@@ -172,6 +178,10 @@
double scaled_sum,
std::string* output) const;
+ // Retrieves the callback for this histogram, if one exists, and runs it
+ // passing |sample| as the parameter.
+ void FindAndRunCallback(Sample sample) const;
+
private:
const std::string histogram_name_;
int32_t flags_;
diff --git a/base/metrics/histogram_macros.h b/base/metrics/histogram_macros.h
index 2aee1a5..9867df8 100644
--- a/base/metrics/histogram_macros.h
+++ b/base/metrics/histogram_macros.h
@@ -189,6 +189,9 @@
#define UMA_HISTOGRAM_COUNTS_100(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
name, sample, 1, 100, 50)
+#define UMA_HISTOGRAM_COUNTS_1000(name, sample) \
+ UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 1000, 50)
+
#define UMA_HISTOGRAM_COUNTS_10000(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
name, sample, 1, 10000, 50)
diff --git a/base/metrics/histogram_snapshot_manager_unittest.cc b/base/metrics/histogram_snapshot_manager_unittest.cc
index 3a1fd40..e9e7398 100644
--- a/base/metrics/histogram_snapshot_manager_unittest.cc
+++ b/base/metrics/histogram_snapshot_manager_unittest.cc
@@ -7,8 +7,8 @@
#include <string>
#include <vector>
-#include "base/metrics/histogram.h"
#include "base/metrics/histogram_delta_serialization.h"
+#include "base/metrics/histogram_macros.h"
#include "base/metrics/statistics_recorder.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/base/metrics/histogram_unittest.cc b/base/metrics/histogram_unittest.cc
index 2e3a1ee..f189267 100644
--- a/base/metrics/histogram_unittest.cc
+++ b/base/metrics/histogram_unittest.cc
@@ -11,6 +11,7 @@
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/metrics/bucket_ranges.h"
+#include "base/metrics/histogram_macros.h"
#include "base/metrics/sample_vector.h"
#include "base/metrics/statistics_recorder.h"
#include "base/pickle.h"
diff --git a/base/metrics/sparse_histogram.cc b/base/metrics/sparse_histogram.cc
index e5cdb43..a853dce 100644
--- a/base/metrics/sparse_histogram.cc
+++ b/base/metrics/sparse_histogram.cc
@@ -46,8 +46,12 @@
}
void SparseHistogram::Add(Sample value) {
- base::AutoLock auto_lock(lock_);
- samples_.Accumulate(value, 1);
+ {
+ base::AutoLock auto_lock(lock_);
+ samples_.Accumulate(value, 1);
+ }
+
+ FindAndRunCallback(value);
}
scoped_ptr<HistogramSamples> SparseHistogram::SnapshotSamples() const {
diff --git a/base/metrics/statistics_recorder.cc b/base/metrics/statistics_recorder.cc
index 85408e1..87ffa3d 100644
--- a/base/metrics/statistics_recorder.cc
+++ b/base/metrics/statistics_recorder.cc
@@ -10,6 +10,7 @@
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/metrics/histogram.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "base/synchronization/lock.h"
#include "base/values.h"
@@ -57,10 +58,19 @@
histogram_to_return = histogram;
} else {
const std::string& name = histogram->histogram_name();
- HistogramMap::iterator it = histograms_->find(name);
+ HistogramMap::iterator it = histograms_->find(HistogramNameRef(name));
if (histograms_->end() == it) {
- (*histograms_)[name] = histogram;
+ (*histograms_)[HistogramNameRef(name)] = histogram;
ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322
+ // If there are callbacks for this histogram, we set the kCallbackExists
+ // flag.
+ auto callback_iterator = callbacks_->find(name);
+ if (callback_iterator != callbacks_->end()) {
+ if (!callback_iterator->second.is_null())
+ histogram->SetFlags(HistogramBase::kCallbackExists);
+ else
+ histogram->ClearFlags(HistogramBase::kCallbackExists);
+ }
histogram_to_return = histogram;
} else if (histogram == it->second) {
// The histogram was registered before.
@@ -190,7 +200,7 @@
return;
for (const auto& entry : *histograms_) {
- DCHECK_EQ(entry.first, entry.second->histogram_name());
+ DCHECK_EQ(entry.first.name_, entry.second->histogram_name());
output->push_back(entry.second);
}
}
@@ -219,12 +229,64 @@
if (histograms_ == NULL)
return NULL;
- HistogramMap::iterator it = histograms_->find(name);
+ HistogramMap::iterator it = histograms_->find(HistogramNameRef(name));
if (histograms_->end() == it)
return NULL;
return it->second;
}
+// static
+bool StatisticsRecorder::SetCallback(
+ const std::string& name,
+ const StatisticsRecorder::OnSampleCallback& cb) {
+ DCHECK(!cb.is_null());
+ if (lock_ == NULL)
+ return false;
+ base::AutoLock auto_lock(*lock_);
+ if (histograms_ == NULL)
+ return false;
+
+ if (ContainsKey(*callbacks_, name))
+ return false;
+ callbacks_->insert(std::make_pair(name, cb));
+
+ auto histogram_iterator = histograms_->find(HistogramNameRef(name));
+ if (histogram_iterator != histograms_->end())
+ histogram_iterator->second->SetFlags(HistogramBase::kCallbackExists);
+
+ return true;
+}
+
+// static
+void StatisticsRecorder::ClearCallback(const std::string& name) {
+ if (lock_ == NULL)
+ return;
+ base::AutoLock auto_lock(*lock_);
+ if (histograms_ == NULL)
+ return;
+
+ callbacks_->erase(name);
+
+ // We also clear the flag from the histogram (if it exists).
+ auto histogram_iterator = histograms_->find(HistogramNameRef(name));
+ if (histogram_iterator != histograms_->end())
+ histogram_iterator->second->ClearFlags(HistogramBase::kCallbackExists);
+}
+
+// static
+StatisticsRecorder::OnSampleCallback StatisticsRecorder::FindCallback(
+ const std::string& name) {
+ if (lock_ == NULL)
+ return OnSampleCallback();
+ base::AutoLock auto_lock(*lock_);
+ if (histograms_ == NULL)
+ return OnSampleCallback();
+
+ auto callback_iterator = callbacks_->find(name);
+ return callback_iterator != callbacks_->end() ? callback_iterator->second
+ : OnSampleCallback();
+}
+
// private static
void StatisticsRecorder::GetSnapshot(const std::string& query,
Histograms* snapshot) {
@@ -235,7 +297,7 @@
return;
for (const auto& entry : *histograms_) {
- if (entry.first.find(query) != std::string::npos)
+ if (entry.first.name_.find(query) != std::string::npos)
snapshot->push_back(entry.second);
}
}
@@ -256,6 +318,7 @@
}
base::AutoLock auto_lock(*lock_);
histograms_ = new HistogramMap;
+ callbacks_ = new CallbackMap;
ranges_ = new RangesMap;
if (VLOG_IS_ON(1))
@@ -274,14 +337,17 @@
// Clean up.
scoped_ptr<HistogramMap> histograms_deleter;
+ scoped_ptr<CallbackMap> callbacks_deleter;
scoped_ptr<RangesMap> ranges_deleter;
// We don't delete lock_ on purpose to avoid having to properly protect
// against it going away after we checked for NULL in the static methods.
{
base::AutoLock auto_lock(*lock_);
histograms_deleter.reset(histograms_);
+ callbacks_deleter.reset(callbacks_);
ranges_deleter.reset(ranges_);
histograms_ = NULL;
+ callbacks_ = NULL;
ranges_ = NULL;
}
// We are going to leak the histograms and the ranges.
@@ -291,6 +357,8 @@
// static
StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = NULL;
// static
+StatisticsRecorder::CallbackMap* StatisticsRecorder::callbacks_ = NULL;
+// static
StatisticsRecorder::RangesMap* StatisticsRecorder::ranges_ = NULL;
// static
base::Lock* StatisticsRecorder::lock_ = NULL;
diff --git a/base/metrics/statistics_recorder.h b/base/metrics/statistics_recorder.h
index b523057..729f230 100644
--- a/base/metrics/statistics_recorder.h
+++ b/base/metrics/statistics_recorder.h
@@ -17,13 +17,14 @@
#include "base/base_export.h"
#include "base/basictypes.h"
+#include "base/callback.h"
#include "base/gtest_prod_util.h"
#include "base/lazy_instance.h"
+#include "base/metrics/histogram_base.h"
namespace base {
class BucketRanges;
-class HistogramBase;
class Lock;
class BASE_EXPORT StatisticsRecorder {
@@ -75,9 +76,47 @@
// histograms).
static void GetSnapshot(const std::string& query, Histograms* snapshot);
+ typedef base::Callback<void(HistogramBase::Sample)> OnSampleCallback;
+
+ // SetCallback sets the callback to notify when a new sample is recorded on
+ // the histogram referred to by |histogram_name|. The call to this method can
+ // be be done before or after the histogram is created. This method is thread
+ // safe. The return value is whether or not the callback was successfully set.
+ static bool SetCallback(const std::string& histogram_name,
+ const OnSampleCallback& callback);
+
+ // ClearCallback clears any callback set on the histogram referred to by
+ // |histogram_name|. This method is thread safe.
+ static void ClearCallback(const std::string& histogram_name);
+
+ // FindCallback retrieves the callback for the histogram referred to by
+ // |histogram_name|, or a null callback if no callback exists for this
+ // histogram. This method is thread safe.
+ static OnSampleCallback FindCallback(const std::string& histogram_name);
+
private:
+ // HistogramNameRef holds a weak const ref to the name field of the associated
+ // Histogram object, allowing re-use of the underlying string storage for the
+ // map keys. The wrapper is required as using "const std::string&" as the key
+ // results in compile errors.
+ struct HistogramNameRef {
+ explicit HistogramNameRef(const std::string& name) : name_(name){};
+
+ // Operator < is necessary to use this type as a std::map key.
+ bool operator<(const HistogramNameRef& other) const {
+ return name_ < other.name_;
+ }
+
+ // Weak, owned by the associated Histogram object.
+ const std::string& name_;
+ };
+
// We keep all registered histograms in a map, from name to histogram.
- typedef std::map<std::string, HistogramBase*> HistogramMap;
+ typedef std::map<HistogramNameRef, HistogramBase*> HistogramMap;
+
+ // We keep a map of callbacks to histograms, so that as histograms are
+ // created, we can set the callback properly.
+ typedef std::map<std::string, OnSampleCallback> CallbackMap;
// We keep all |bucket_ranges_| in a map, from checksum to a list of
// |bucket_ranges_|. Checksum is calculated from the |ranges_| in
@@ -103,6 +142,7 @@
static void DumpHistogramsToVlog(void* instance);
static HistogramMap* histograms_;
+ static CallbackMap* callbacks_;
static RangesMap* ranges_;
// Lock protects access to above maps.
diff --git a/base/metrics/statistics_recorder_unittest.cc b/base/metrics/statistics_recorder_unittest.cc
index d26df69..b18c580 100644
--- a/base/metrics/statistics_recorder_unittest.cc
+++ b/base/metrics/statistics_recorder_unittest.cc
@@ -4,9 +4,11 @@
#include <vector>
+#include "base/bind.h"
#include "base/json/json_reader.h"
#include "base/memory/scoped_ptr.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/sparse_histogram.h"
#include "base/metrics/statistics_recorder.h"
#include "base/values.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -312,4 +314,178 @@
EXPECT_TRUE(json.empty());
}
+namespace {
+
+// CallbackCheckWrapper is simply a convenient way to check and store that
+// a callback was actually run.
+struct CallbackCheckWrapper {
+ CallbackCheckWrapper() : called(false), last_histogram_value(0) {}
+
+ void OnHistogramChanged(base::HistogramBase::Sample histogram_value) {
+ called = true;
+ last_histogram_value = histogram_value;
+ }
+
+ bool called;
+ base::HistogramBase::Sample last_histogram_value;
+};
+
+} // namespace
+
+// Check that you can't overwrite the callback with another.
+TEST_F(StatisticsRecorderTest, SetCallbackFailsWithoutHistogramTest) {
+ CallbackCheckWrapper callback_wrapper;
+
+ bool result = base::StatisticsRecorder::SetCallback(
+ "TestHistogram", base::Bind(&CallbackCheckWrapper::OnHistogramChanged,
+ base::Unretained(&callback_wrapper)));
+ EXPECT_TRUE(result);
+
+ result = base::StatisticsRecorder::SetCallback(
+ "TestHistogram", base::Bind(&CallbackCheckWrapper::OnHistogramChanged,
+ base::Unretained(&callback_wrapper)));
+ EXPECT_FALSE(result);
+}
+
+// Check that you can't overwrite the callback with another.
+TEST_F(StatisticsRecorderTest, SetCallbackFailsWithHistogramTest) {
+ HistogramBase* histogram = Histogram::FactoryGet("TestHistogram", 1, 1000, 10,
+ HistogramBase::kNoFlags);
+ EXPECT_TRUE(histogram);
+
+ CallbackCheckWrapper callback_wrapper;
+
+ bool result = base::StatisticsRecorder::SetCallback(
+ "TestHistogram", base::Bind(&CallbackCheckWrapper::OnHistogramChanged,
+ base::Unretained(&callback_wrapper)));
+ EXPECT_TRUE(result);
+ EXPECT_EQ(histogram->flags() & base::HistogramBase::kCallbackExists,
+ base::HistogramBase::kCallbackExists);
+
+ result = base::StatisticsRecorder::SetCallback(
+ "TestHistogram", base::Bind(&CallbackCheckWrapper::OnHistogramChanged,
+ base::Unretained(&callback_wrapper)));
+ EXPECT_FALSE(result);
+ EXPECT_EQ(histogram->flags() & base::HistogramBase::kCallbackExists,
+ base::HistogramBase::kCallbackExists);
+
+ histogram->Add(1);
+
+ EXPECT_TRUE(callback_wrapper.called);
+}
+
+// Check that you can't overwrite the callback with another.
+TEST_F(StatisticsRecorderTest, ClearCallbackSuceedsWithHistogramTest) {
+ HistogramBase* histogram = Histogram::FactoryGet("TestHistogram", 1, 1000, 10,
+ HistogramBase::kNoFlags);
+ EXPECT_TRUE(histogram);
+
+ CallbackCheckWrapper callback_wrapper;
+
+ bool result = base::StatisticsRecorder::SetCallback(
+ "TestHistogram", base::Bind(&CallbackCheckWrapper::OnHistogramChanged,
+ base::Unretained(&callback_wrapper)));
+ EXPECT_TRUE(result);
+ EXPECT_EQ(histogram->flags() & base::HistogramBase::kCallbackExists,
+ base::HistogramBase::kCallbackExists);
+
+ base::StatisticsRecorder::ClearCallback("TestHistogram");
+ EXPECT_EQ(histogram->flags() & base::HistogramBase::kCallbackExists, 0);
+
+ histogram->Add(1);
+
+ EXPECT_FALSE(callback_wrapper.called);
+}
+
+// Check that callback is used.
+TEST_F(StatisticsRecorderTest, CallbackUsedTest) {
+ {
+ HistogramBase* histogram = Histogram::FactoryGet(
+ "TestHistogram", 1, 1000, 10, HistogramBase::kNoFlags);
+ EXPECT_TRUE(histogram);
+
+ CallbackCheckWrapper callback_wrapper;
+
+ base::StatisticsRecorder::SetCallback(
+ "TestHistogram", base::Bind(&CallbackCheckWrapper::OnHistogramChanged,
+ base::Unretained(&callback_wrapper)));
+
+ histogram->Add(1);
+
+ EXPECT_TRUE(callback_wrapper.called);
+ EXPECT_EQ(callback_wrapper.last_histogram_value, 1);
+ }
+
+ {
+ HistogramBase* linear_histogram = LinearHistogram::FactoryGet(
+ "TestLinearHistogram", 1, 1000, 10, HistogramBase::kNoFlags);
+
+ CallbackCheckWrapper callback_wrapper;
+
+ base::StatisticsRecorder::SetCallback(
+ "TestLinearHistogram",
+ base::Bind(&CallbackCheckWrapper::OnHistogramChanged,
+ base::Unretained(&callback_wrapper)));
+
+ linear_histogram->Add(1);
+
+ EXPECT_TRUE(callback_wrapper.called);
+ EXPECT_EQ(callback_wrapper.last_histogram_value, 1);
+ }
+
+ {
+ std::vector<int> custom_ranges;
+ custom_ranges.push_back(1);
+ custom_ranges.push_back(5);
+ HistogramBase* custom_histogram = CustomHistogram::FactoryGet(
+ "TestCustomHistogram", custom_ranges, HistogramBase::kNoFlags);
+
+ CallbackCheckWrapper callback_wrapper;
+
+ base::StatisticsRecorder::SetCallback(
+ "TestCustomHistogram",
+ base::Bind(&CallbackCheckWrapper::OnHistogramChanged,
+ base::Unretained(&callback_wrapper)));
+
+ custom_histogram->Add(1);
+
+ EXPECT_TRUE(callback_wrapper.called);
+ EXPECT_EQ(callback_wrapper.last_histogram_value, 1);
+ }
+
+ {
+ HistogramBase* custom_histogram = SparseHistogram::FactoryGet(
+ "TestSparseHistogram", HistogramBase::kNoFlags);
+
+ CallbackCheckWrapper callback_wrapper;
+
+ base::StatisticsRecorder::SetCallback(
+ "TestSparseHistogram",
+ base::Bind(&CallbackCheckWrapper::OnHistogramChanged,
+ base::Unretained(&callback_wrapper)));
+
+ custom_histogram->Add(1);
+
+ EXPECT_TRUE(callback_wrapper.called);
+ EXPECT_EQ(callback_wrapper.last_histogram_value, 1);
+ }
+}
+
+// Check that setting a callback before the histogram exists works.
+TEST_F(StatisticsRecorderTest, CallbackUsedBeforeHistogramCreatedTest) {
+ CallbackCheckWrapper callback_wrapper;
+
+ base::StatisticsRecorder::SetCallback(
+ "TestHistogram", base::Bind(&CallbackCheckWrapper::OnHistogramChanged,
+ base::Unretained(&callback_wrapper)));
+
+ HistogramBase* histogram = Histogram::FactoryGet("TestHistogram", 1, 1000, 10,
+ HistogramBase::kNoFlags);
+ EXPECT_TRUE(histogram);
+ histogram->Add(1);
+
+ EXPECT_TRUE(callback_wrapper.called);
+ EXPECT_EQ(callback_wrapper.last_histogram_value, 1);
+}
+
} // namespace base
diff --git a/base/pickle.cc b/base/pickle.cc
index 09d42c9..cf4a865 100644
--- a/base/pickle.cc
+++ b/base/pickle.cc
@@ -317,13 +317,17 @@
}
void Pickle::Resize(size_t new_capacity) {
- new_capacity = AlignInt(new_capacity, kPayloadUnit);
-
CHECK_NE(capacity_after_header_, kCapacityReadOnly);
- void* p = realloc(header_, header_size_ + new_capacity);
+ capacity_after_header_ = AlignInt(new_capacity, kPayloadUnit);
+ void* p = realloc(header_, GetTotalAllocatedSize());
CHECK(p);
header_ = reinterpret_cast<Header*>(p);
- capacity_after_header_ = new_capacity;
+}
+
+size_t Pickle::GetTotalAllocatedSize() const {
+ if (capacity_after_header_ == kCapacityReadOnly)
+ return 0;
+ return header_size_ + capacity_after_header_;
}
// static
diff --git a/base/pickle.h b/base/pickle.h
index 7a6b0c8..c9fef71 100644
--- a/base/pickle.h
+++ b/base/pickle.h
@@ -153,12 +153,18 @@
// Performs a deep copy.
Pickle& operator=(const Pickle& other);
- // Returns the size of the Pickle's data.
+ // Returns the number of bytes written in the Pickle, including the header.
size_t size() const { return header_size_ + header_->payload_size; }
// Returns the data for this Pickle.
const void* data() const { return header_; }
+ // Returns the effective memory capacity of this Pickle, that is, the total
+ // number of bytes currently dynamically allocated or 0 in the case of a
+ // read-only Pickle. This should be used only for diagnostic / profiling
+ // purposes.
+ size_t GetTotalAllocatedSize() const;
+
// Methods for adding to the payload of the Pickle. These values are
// appended to the end of the Pickle's payload. When reading values from a
// Pickle, it is important to read them in the order in which they were added
diff --git a/base/port.h b/base/port.h
deleted file mode 100644
index 56c4d4e..0000000
--- a/base/port.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (c) 2006-2008 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_PORT_H_
-#define BASE_PORT_H_
-
-#include <stdarg.h>
-#include "build/build_config.h"
-
-// DEPRECATED: Use ...LL and ...ULL suffixes.
-// TODO(viettrungluu): Delete these. These are only here until |GG_(U)INT64_C|
-// are deleted (some other header files (re)define |GG_(U)INT64_C|, so our
-// definitions of them must exactly match theirs).
-#ifdef COMPILER_MSVC
-#define GG_LONGLONG(x) x##I64
-#define GG_ULONGLONG(x) x##UI64
-#else
-#define GG_LONGLONG(x) x##LL
-#define GG_ULONGLONG(x) x##ULL
-#endif
-
-// DEPRECATED: In Chromium, we force-define __STDC_CONSTANT_MACROS, so you can
-// just use the regular (U)INTn_C macros from <stdint.h>.
-// TODO(viettrungluu): Remove the remaining GG_(U)INTn_C macros.
-#define GG_INT64_C(x) GG_LONGLONG(x)
-#define GG_UINT64_C(x) GG_ULONGLONG(x)
-
-// Define an OS-neutral wrapper for shared library entry points
-#if defined(OS_WIN)
-#define API_CALL __stdcall
-#else
-#define API_CALL
-#endif
-
-#endif // BASE_PORT_H_
diff --git a/base/prefs/default_pref_store.cc b/base/prefs/default_pref_store.cc
index 92abba1..efb4a75 100644
--- a/base/prefs/default_pref_store.cc
+++ b/base/prefs/default_pref_store.cc
@@ -29,7 +29,7 @@
void DefaultPrefStore::SetDefaultValue(const std::string& key,
scoped_ptr<Value> value) {
DCHECK(!GetValue(key, NULL));
- prefs_.SetValue(key, value.release());
+ prefs_.SetValue(key, value.Pass());
}
void DefaultPrefStore::ReplaceDefaultValue(const std::string& key,
@@ -37,7 +37,7 @@
const Value* old_value = NULL;
GetValue(key, &old_value);
bool notify = !old_value->Equals(value.get());
- prefs_.SetValue(key, value.release());
+ prefs_.SetValue(key, value.Pass());
if (notify)
FOR_EACH_OBSERVER(Observer, observers_, OnPrefValueChanged(key));
}
diff --git a/base/prefs/json_pref_store.cc b/base/prefs/json_pref_store.cc
index c2ff425..354fd94 100644
--- a/base/prefs/json_pref_store.cc
+++ b/base/prefs/json_pref_store.cc
@@ -221,31 +221,29 @@
}
void JsonPrefStore::SetValue(const std::string& key,
- base::Value* value,
+ scoped_ptr<base::Value> value,
uint32 flags) {
DCHECK(CalledOnValidThread());
DCHECK(value);
- scoped_ptr<base::Value> new_value(value);
base::Value* old_value = NULL;
prefs_->Get(key, &old_value);
if (!old_value || !value->Equals(old_value)) {
- prefs_->Set(key, new_value.Pass());
+ prefs_->Set(key, value.Pass());
ReportValueChanged(key, flags);
}
}
void JsonPrefStore::SetValueSilently(const std::string& key,
- base::Value* value,
+ scoped_ptr<base::Value> value,
uint32 flags) {
DCHECK(CalledOnValidThread());
DCHECK(value);
- scoped_ptr<base::Value> new_value(value);
base::Value* old_value = NULL;
prefs_->Get(key, &old_value);
if (!old_value || !value->Equals(old_value)) {
- prefs_->Set(key, new_value.Pass());
+ prefs_->Set(key, value.Pass());
ScheduleWrite(flags);
}
}
diff --git a/base/prefs/json_pref_store.h b/base/prefs/json_pref_store.h
index d6943e0..0be7702 100644
--- a/base/prefs/json_pref_store.h
+++ b/base/prefs/json_pref_store.h
@@ -84,10 +84,10 @@
// PersistentPrefStore overrides:
bool GetMutableValue(const std::string& key, base::Value** result) override;
void SetValue(const std::string& key,
- base::Value* value,
+ scoped_ptr<base::Value> value,
uint32 flags) override;
void SetValueSilently(const std::string& key,
- base::Value* value,
+ scoped_ptr<base::Value> value,
uint32 flags) override;
void RemoveValue(const std::string& key, uint32 flags) override;
bool ReadOnly() const override;
diff --git a/base/prefs/json_pref_store_unittest.cc b/base/prefs/json_pref_store_unittest.cc
index 4c7bc1f..5195a18 100644
--- a/base/prefs/json_pref_store_unittest.cc
+++ b/base/prefs/json_pref_store_unittest.cc
@@ -192,7 +192,8 @@
EXPECT_EQ(base::FilePath::StringType(FILE_PATH_LITERAL("/usr/local/")), path);
base::FilePath some_path(FILE_PATH_LITERAL("/usr/sbin/"));
- pref_store->SetValue(kSomeDirectory, new StringValue(some_path.value()),
+ pref_store->SetValue(kSomeDirectory,
+ make_scoped_ptr(new StringValue(some_path.value())),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
EXPECT_TRUE(pref_store->GetValue(kSomeDirectory, &actual));
EXPECT_TRUE(actual->GetAsString(&path));
@@ -204,7 +205,8 @@
EXPECT_TRUE(actual->GetAsBoolean(&boolean));
EXPECT_TRUE(boolean);
- pref_store->SetValue(kNewWindowsInTabs, new FundamentalValue(false),
+ pref_store->SetValue(kNewWindowsInTabs,
+ make_scoped_ptr(new FundamentalValue(false)),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
EXPECT_TRUE(pref_store->GetValue(kNewWindowsInTabs, &actual));
EXPECT_TRUE(actual->GetAsBoolean(&boolean));
@@ -214,15 +216,16 @@
int integer = 0;
EXPECT_TRUE(actual->GetAsInteger(&integer));
EXPECT_EQ(20, integer);
- pref_store->SetValue(kMaxTabs, new FundamentalValue(10),
+ pref_store->SetValue(kMaxTabs, make_scoped_ptr(new FundamentalValue(10)),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
EXPECT_TRUE(pref_store->GetValue(kMaxTabs, &actual));
EXPECT_TRUE(actual->GetAsInteger(&integer));
EXPECT_EQ(10, integer);
- pref_store->SetValue(kLongIntPref,
- new StringValue(base::Int64ToString(214748364842LL)),
- WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ pref_store->SetValue(
+ kLongIntPref,
+ make_scoped_ptr(new StringValue(base::Int64ToString(214748364842LL))),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
EXPECT_TRUE(pref_store->GetValue(kLongIntPref, &actual));
EXPECT_TRUE(actual->GetAsString(&string_value));
int64 value;
@@ -312,9 +315,9 @@
pref_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>());
// Set some keys with empty values.
- pref_store->SetValue("list", new base::ListValue,
+ pref_store->SetValue("list", make_scoped_ptr(new base::ListValue),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
- pref_store->SetValue("dict", new base::DictionaryValue,
+ pref_store->SetValue("dict", make_scoped_ptr(new base::DictionaryValue),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
// Write to file.
@@ -343,9 +346,9 @@
scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
pref_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>());
- base::DictionaryValue* dict = new base::DictionaryValue;
+ scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
dict->SetString("key", "value");
- pref_store->SetValue("dict", dict,
+ pref_store->SetValue("dict", dict.Pass(),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
pref_store->RemoveValue("dict.key",
@@ -845,7 +848,8 @@
// Set a normal pref and check that it gets scheduled to be written.
ASSERT_FALSE(file_writer->HasPendingWrite());
- pref_store->SetValue("normal", new base::StringValue("normal"),
+ pref_store->SetValue("normal",
+ make_scoped_ptr(new base::StringValue("normal")),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
ASSERT_TRUE(file_writer->HasPendingWrite());
file_writer->DoScheduledWrite();
@@ -854,14 +858,15 @@
// Set a lossy pref and check that it is not scheduled to be written.
// SetValue/RemoveValue.
- pref_store->SetValue("lossy", new base::StringValue("lossy"),
+ pref_store->SetValue("lossy", make_scoped_ptr(new base::StringValue("lossy")),
WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
ASSERT_FALSE(file_writer->HasPendingWrite());
pref_store->RemoveValue("lossy", WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
ASSERT_FALSE(file_writer->HasPendingWrite());
// SetValueSilently/RemoveValueSilently.
- pref_store->SetValueSilently("lossy", new base::StringValue("lossy"),
+ pref_store->SetValueSilently("lossy",
+ make_scoped_ptr(new base::StringValue("lossy")),
WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
ASSERT_FALSE(file_writer->HasPendingWrite());
pref_store->RemoveValueSilently("lossy",
@@ -869,7 +874,7 @@
ASSERT_FALSE(file_writer->HasPendingWrite());
// ReportValueChanged.
- pref_store->SetValue("lossy", new base::StringValue("lossy"),
+ pref_store->SetValue("lossy", make_scoped_ptr(new base::StringValue("lossy")),
WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
ASSERT_FALSE(file_writer->HasPendingWrite());
pref_store->ReportValueChanged("lossy",
@@ -890,12 +895,13 @@
// Set a lossy pref and check that it is not scheduled to be written.
ASSERT_FALSE(file_writer->HasPendingWrite());
- pref_store->SetValue("lossy", new base::StringValue("lossy"),
+ pref_store->SetValue("lossy", make_scoped_ptr(new base::StringValue("lossy")),
WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
ASSERT_FALSE(file_writer->HasPendingWrite());
// Set a normal pref and check that it is scheduled to be written.
- pref_store->SetValue("normal", new base::StringValue("normal"),
+ pref_store->SetValue("normal",
+ make_scoped_ptr(new base::StringValue("normal")),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
ASSERT_TRUE(file_writer->HasPendingWrite());
@@ -912,12 +918,13 @@
// Set a normal pref and check that it is scheduled to be written.
ASSERT_FALSE(file_writer->HasPendingWrite());
- pref_store->SetValue("normal", new base::StringValue("normal"),
+ pref_store->SetValue("normal",
+ make_scoped_ptr(new base::StringValue("normal")),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
ASSERT_TRUE(file_writer->HasPendingWrite());
// Set a lossy pref and check that the write is still scheduled.
- pref_store->SetValue("lossy", new base::StringValue("lossy"),
+ pref_store->SetValue("lossy", make_scoped_ptr(new base::StringValue("lossy")),
WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
ASSERT_TRUE(file_writer->HasPendingWrite());
@@ -933,7 +940,7 @@
ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store);
// Set a lossy pref and check that it is not scheduled to be written.
- pref_store->SetValue("lossy", new base::StringValue("lossy"),
+ pref_store->SetValue("lossy", make_scoped_ptr(new base::StringValue("lossy")),
WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
ASSERT_FALSE(file_writer->HasPendingWrite());
diff --git a/base/prefs/overlay_user_pref_store.cc b/base/prefs/overlay_user_pref_store.cc
index 4c236f1..d76b537 100644
--- a/base/prefs/overlay_user_pref_store.cc
+++ b/base/prefs/overlay_user_pref_store.cc
@@ -58,31 +58,31 @@
return false;
*result = underlay_value->DeepCopy();
- overlay_.SetValue(key, *result);
+ overlay_.SetValue(key, make_scoped_ptr(*result));
return true;
}
void OverlayUserPrefStore::SetValue(const std::string& key,
- base::Value* value,
+ scoped_ptr<base::Value> value,
uint32 flags) {
if (!ShallBeStoredInOverlay(key)) {
- underlay_->SetValue(GetUnderlayKey(key), value, flags);
+ underlay_->SetValue(GetUnderlayKey(key), value.Pass(), flags);
return;
}
- if (overlay_.SetValue(key, value))
+ if (overlay_.SetValue(key, value.Pass()))
ReportValueChanged(key, flags);
}
void OverlayUserPrefStore::SetValueSilently(const std::string& key,
- base::Value* value,
+ scoped_ptr<base::Value> value,
uint32 flags) {
if (!ShallBeStoredInOverlay(key)) {
- underlay_->SetValueSilently(GetUnderlayKey(key), value, flags);
+ underlay_->SetValueSilently(GetUnderlayKey(key), value.Pass(), flags);
return;
}
- overlay_.SetValue(key, value);
+ overlay_.SetValue(key, value.Pass());
}
void OverlayUserPrefStore::RemoveValue(const std::string& key, uint32 flags) {
diff --git a/base/prefs/overlay_user_pref_store.h b/base/prefs/overlay_user_pref_store.h
index 885da08..737b426 100644
--- a/base/prefs/overlay_user_pref_store.h
+++ b/base/prefs/overlay_user_pref_store.h
@@ -40,10 +40,10 @@
// Methods of PersistentPrefStore.
bool GetMutableValue(const std::string& key, base::Value** result) override;
void SetValue(const std::string& key,
- base::Value* value,
+ scoped_ptr<base::Value> value,
uint32 flags) override;
void SetValueSilently(const std::string& key,
- base::Value* value,
+ scoped_ptr<base::Value> value,
uint32 flags) override;
void RemoveValue(const std::string& key, uint32 flags) override;
bool ReadOnly() const override;
diff --git a/base/prefs/overlay_user_pref_store_unittest.cc b/base/prefs/overlay_user_pref_store_unittest.cc
index 06b4ec9..bf5e6a5 100644
--- a/base/prefs/overlay_user_pref_store_unittest.cc
+++ b/base/prefs/overlay_user_pref_store_unittest.cc
@@ -48,22 +48,22 @@
overlay_->AddObserver(&obs);
// Check that underlay first value is reported.
- underlay_->SetValue(overlay_key, new FundamentalValue(42),
+ underlay_->SetValue(overlay_key, make_scoped_ptr(new FundamentalValue(42)),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
obs.VerifyAndResetChangedKey(overlay_key);
// Check that underlay overwriting is reported.
- underlay_->SetValue(overlay_key, new FundamentalValue(43),
+ underlay_->SetValue(overlay_key, make_scoped_ptr(new FundamentalValue(43)),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
obs.VerifyAndResetChangedKey(overlay_key);
// Check that overwriting change in overlay is reported.
- overlay_->SetValue(overlay_key, new FundamentalValue(44),
+ overlay_->SetValue(overlay_key, make_scoped_ptr(new FundamentalValue(44)),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
obs.VerifyAndResetChangedKey(overlay_key);
// Check that hidden underlay change is not reported.
- underlay_->SetValue(overlay_key, new FundamentalValue(45),
+ underlay_->SetValue(overlay_key, make_scoped_ptr(new FundamentalValue(45)),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
EXPECT_TRUE(obs.changed_keys.empty());
@@ -78,16 +78,17 @@
obs.VerifyAndResetChangedKey(overlay_key);
// Check respecting of silence.
- overlay_->SetValueSilently(overlay_key, new FundamentalValue(46),
+ overlay_->SetValueSilently(overlay_key,
+ make_scoped_ptr(new FundamentalValue(46)),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
EXPECT_TRUE(obs.changed_keys.empty());
overlay_->RemoveObserver(&obs);
// Check successful unsubscription.
- underlay_->SetValue(overlay_key, new FundamentalValue(47),
+ underlay_->SetValue(overlay_key, make_scoped_ptr(new FundamentalValue(47)),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
- overlay_->SetValue(overlay_key, new FundamentalValue(48),
+ overlay_->SetValue(overlay_key, make_scoped_ptr(new FundamentalValue(48)),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
EXPECT_TRUE(obs.changed_keys.empty());
}
@@ -97,7 +98,7 @@
EXPECT_FALSE(overlay_->GetValue(overlay_key, &value));
EXPECT_FALSE(underlay_->GetValue(overlay_key, &value));
- underlay_->SetValue(overlay_key, new FundamentalValue(42),
+ underlay_->SetValue(overlay_key, make_scoped_ptr(new FundamentalValue(42)),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
// Value shines through:
@@ -107,7 +108,7 @@
EXPECT_TRUE(underlay_->GetValue(overlay_key, &value));
EXPECT_TRUE(base::FundamentalValue(42).Equals(value));
- overlay_->SetValue(overlay_key, new FundamentalValue(43),
+ overlay_->SetValue(overlay_key, make_scoped_ptr(new FundamentalValue(43)),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
EXPECT_TRUE(overlay_->GetValue(overlay_key, &value));
@@ -129,7 +130,7 @@
// Check that GetMutableValue does not return the dictionary of the underlay.
TEST_F(OverlayUserPrefStoreTest, ModifyDictionaries) {
- underlay_->SetValue(overlay_key, new DictionaryValue,
+ underlay_->SetValue(overlay_key, make_scoped_ptr(new DictionaryValue),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
Value* modify = NULL;
@@ -159,12 +160,12 @@
const Value* value = NULL;
// Check that underlay first value is reported.
- underlay_->SetValue(regular_key, new FundamentalValue(42),
+ underlay_->SetValue(regular_key, make_scoped_ptr(new FundamentalValue(42)),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
obs.VerifyAndResetChangedKey(regular_key);
// Check that underlay overwriting is reported.
- underlay_->SetValue(regular_key, new FundamentalValue(43),
+ underlay_->SetValue(regular_key, make_scoped_ptr(new FundamentalValue(43)),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
obs.VerifyAndResetChangedKey(regular_key);
@@ -173,7 +174,7 @@
EXPECT_TRUE(base::FundamentalValue(43).Equals(value));
// Check that overwriting change in overlay is reported.
- overlay_->SetValue(regular_key, new FundamentalValue(44),
+ overlay_->SetValue(regular_key, make_scoped_ptr(new FundamentalValue(44)),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
obs.VerifyAndResetChangedKey(regular_key);
@@ -193,16 +194,17 @@
EXPECT_FALSE(underlay_->GetValue(regular_key, &value));
// Check respecting of silence.
- overlay_->SetValueSilently(regular_key, new FundamentalValue(46),
+ overlay_->SetValueSilently(regular_key,
+ make_scoped_ptr(new FundamentalValue(46)),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
EXPECT_TRUE(obs.changed_keys.empty());
overlay_->RemoveObserver(&obs);
// Check successful unsubscription.
- underlay_->SetValue(regular_key, new FundamentalValue(47),
+ underlay_->SetValue(regular_key, make_scoped_ptr(new FundamentalValue(47)),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
- overlay_->SetValue(regular_key, new FundamentalValue(48),
+ overlay_->SetValue(regular_key, make_scoped_ptr(new FundamentalValue(48)),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
EXPECT_TRUE(obs.changed_keys.empty());
}
@@ -216,12 +218,14 @@
// Check that if there is no override in the overlay, changing underlay value
// is reported as changing an overlay value.
- underlay_->SetValue(mapped_underlay_key, new FundamentalValue(42),
+ underlay_->SetValue(mapped_underlay_key,
+ make_scoped_ptr(new FundamentalValue(42)),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
obs.VerifyAndResetChangedKey(mapped_overlay_key);
// Check that underlay overwriting is reported.
- underlay_->SetValue(mapped_underlay_key, new FundamentalValue(43),
+ underlay_->SetValue(mapped_underlay_key,
+ make_scoped_ptr(new FundamentalValue(43)),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
obs.VerifyAndResetChangedKey(mapped_overlay_key);
@@ -233,7 +237,8 @@
EXPECT_TRUE(base::FundamentalValue(43).Equals(value));
// Check that overwriting change in overlay is reported.
- overlay_->SetValue(mapped_overlay_key, new FundamentalValue(44),
+ overlay_->SetValue(mapped_overlay_key,
+ make_scoped_ptr(new FundamentalValue(44)),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
obs.VerifyAndResetChangedKey(mapped_overlay_key);
@@ -247,7 +252,8 @@
EXPECT_TRUE(base::FundamentalValue(43).Equals(value));
// Check that hidden underlay change is not reported.
- underlay_->SetValue(mapped_underlay_key, new FundamentalValue(45),
+ underlay_->SetValue(mapped_underlay_key,
+ make_scoped_ptr(new FundamentalValue(45)),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
EXPECT_TRUE(obs.changed_keys.empty());
@@ -266,16 +272,19 @@
EXPECT_FALSE(overlay_->GetValue(mapped_underlay_key, &value));
// Check respecting of silence.
- overlay_->SetValueSilently(mapped_overlay_key, new FundamentalValue(46),
+ overlay_->SetValueSilently(mapped_overlay_key,
+ make_scoped_ptr(new FundamentalValue(46)),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
EXPECT_TRUE(obs.changed_keys.empty());
overlay_->RemoveObserver(&obs);
// Check successful unsubscription.
- underlay_->SetValue(mapped_underlay_key, new FundamentalValue(47),
+ underlay_->SetValue(mapped_underlay_key,
+ make_scoped_ptr(new FundamentalValue(47)),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
- overlay_->SetValue(mapped_overlay_key, new FundamentalValue(48),
+ overlay_->SetValue(mapped_overlay_key,
+ make_scoped_ptr(new FundamentalValue(48)),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
EXPECT_TRUE(obs.changed_keys.empty());
}
diff --git a/base/prefs/pref_service.cc b/base/prefs/pref_service.cc
index a9749b2..c53b896 100644
--- a/base/prefs/pref_service.cc
+++ b/base/prefs/pref_service.cc
@@ -471,7 +471,8 @@
} else {
NOTREACHED();
}
- user_pref_store_->SetValueSilently(path, value, GetWriteFlags(pref));
+ user_pref_store_->SetValueSilently(path, make_scoped_ptr(value),
+ GetWriteFlags(pref));
}
return value;
}
@@ -498,7 +499,7 @@
return;
}
- user_pref_store_->SetValue(path, owned_value.release(), GetWriteFlags(pref));
+ user_pref_store_->SetValue(path, owned_value.Pass(), GetWriteFlags(pref));
}
void PrefService::UpdateCommandLinePrefStore(PrefStore* command_line_store) {
diff --git a/base/prefs/pref_service_unittest.cc b/base/prefs/pref_service_unittest.cc
index 262d7e9..2506b1d 100644
--- a/base/prefs/pref_service_unittest.cc
+++ b/base/prefs/pref_service_unittest.cc
@@ -239,17 +239,15 @@
}
void SetValue(const std::string& key,
- base::Value* value,
+ scoped_ptr<base::Value> value,
uint32 flags) override {
SetLastWriteFlags(flags);
- delete value;
}
void SetValueSilently(const std::string& key,
- base::Value* value,
+ scoped_ptr<base::Value> value,
uint32 flags) override {
SetLastWriteFlags(flags);
- delete value;
}
void RemoveValue(const std::string& key, uint32 flags) override {
diff --git a/base/prefs/pref_value_map.cc b/base/prefs/pref_value_map.cc
index 5f2dc50..93eadb7 100644
--- a/base/prefs/pref_value_map.cc
+++ b/base/prefs/pref_value_map.cc
@@ -13,60 +13,43 @@
PrefValueMap::PrefValueMap() {}
-PrefValueMap::~PrefValueMap() {
- Clear();
-}
+PrefValueMap::~PrefValueMap() {}
bool PrefValueMap::GetValue(const std::string& key,
const base::Value** value) const {
- const Map::const_iterator entry = prefs_.find(key);
- if (entry == prefs_.end())
- return false;
+ const base::Value* got_value = prefs_.get(key);
+ if (value && got_value)
+ *value = got_value;
- if (value)
- *value = entry->second;
- return true;
+ return !!got_value;
}
bool PrefValueMap::GetValue(const std::string& key, base::Value** value) {
- const Map::const_iterator entry = prefs_.find(key);
- if (entry == prefs_.end())
- return false;
+ base::Value* got_value = prefs_.get(key);
+ if (value && got_value)
+ *value = got_value;
- if (value)
- *value = entry->second;
- return true;
+ return !!got_value;
}
-bool PrefValueMap::SetValue(const std::string& key, base::Value* value) {
+bool PrefValueMap::SetValue(const std::string& key,
+ scoped_ptr<base::Value> value) {
DCHECK(value);
- auto result = prefs_.insert(std::make_pair(key, value));
- if (result.second)
- return true;
- scoped_ptr<base::Value> value_ptr(value);
- const Map::iterator& entry = result.first;
- if (base::Value::Equals(entry->second, value))
+ base::Value* old_value = prefs_.get(key);
+ if (old_value && value->Equals(old_value))
return false;
- delete entry->second;
- entry->second = value_ptr.release();
-
+ prefs_.set(key, value.Pass());
return true;
}
bool PrefValueMap::RemoveValue(const std::string& key) {
- const Map::iterator entry = prefs_.find(key);
- if (entry == prefs_.end())
- return false;
-
- delete entry->second;
- prefs_.erase(entry);
- return true;
+ return prefs_.erase(key) != 0;
}
void PrefValueMap::Clear() {
- STLDeleteValues(&prefs_);
+ prefs_.clear();
}
void PrefValueMap::Swap(PrefValueMap* other) {
@@ -96,7 +79,7 @@
}
void PrefValueMap::SetBoolean(const std::string& key, bool value) {
- SetValue(key, new base::FundamentalValue(value));
+ SetValue(key, make_scoped_ptr(new base::FundamentalValue(value)));
}
bool PrefValueMap::GetString(const std::string& key,
@@ -107,7 +90,7 @@
void PrefValueMap::SetString(const std::string& key,
const std::string& value) {
- SetValue(key, new base::StringValue(value));
+ SetValue(key, make_scoped_ptr(new base::StringValue(value)));
}
bool PrefValueMap::GetInteger(const std::string& key, int* value) const {
@@ -116,11 +99,11 @@
}
void PrefValueMap::SetInteger(const std::string& key, const int value) {
- SetValue(key, new base::FundamentalValue(value));
+ SetValue(key, make_scoped_ptr(new base::FundamentalValue(value)));
}
void PrefValueMap::SetDouble(const std::string& key, const double value) {
- SetValue(key, new base::FundamentalValue(value));
+ SetValue(key, make_scoped_ptr(new base::FundamentalValue(value)));
}
void PrefValueMap::GetDifferingKeys(
diff --git a/base/prefs/pref_value_map.h b/base/prefs/pref_value_map.h
index 12b30c6..7d43f2b 100644
--- a/base/prefs/pref_value_map.h
+++ b/base/prefs/pref_value_map.h
@@ -9,7 +9,8 @@
#include <vector>
#include "base/basictypes.h"
-#include "base/containers/hash_tables.h"
+#include "base/containers/scoped_ptr_hash_map.h"
+#include "base/memory/scoped_ptr.h"
#include "base/prefs/base_prefs_export.h"
namespace base {
@@ -19,7 +20,7 @@
// A generic string to value map used by the PrefStore implementations.
class BASE_PREFS_EXPORT PrefValueMap {
public:
- using Map = base::hash_map<std::string, base::Value*>;
+ using Map = base::ScopedPtrHashMap<std::string, scoped_ptr<base::Value>>;
using iterator = Map::iterator;
using const_iterator = Map::const_iterator;
@@ -32,9 +33,9 @@
bool GetValue(const std::string& key, const base::Value** value) const;
bool GetValue(const std::string& key, base::Value** value);
- // Sets a new |value| for |key|. Takes ownership of |value|, which must be
- // non-NULL. Returns true if the value changed.
- bool SetValue(const std::string& key, base::Value* value);
+ // Sets a new |value| for |key|. |value| must be non-null. Returns true if the
+ // value changed.
+ bool SetValue(const std::string& key, scoped_ptr<base::Value> value);
// Removes the value for |key| from the map. Returns true if a value was
// removed.
diff --git a/base/prefs/pref_value_map_unittest.cc b/base/prefs/pref_value_map_unittest.cc
index 82499da..f78c999 100644
--- a/base/prefs/pref_value_map_unittest.cc
+++ b/base/prefs/pref_value_map_unittest.cc
@@ -16,9 +16,9 @@
EXPECT_FALSE(map.GetValue("key", &result));
EXPECT_FALSE(result);
- EXPECT_TRUE(map.SetValue("key", new StringValue("test")));
- EXPECT_FALSE(map.SetValue("key", new StringValue("test")));
- EXPECT_TRUE(map.SetValue("key", new StringValue("hi mom!")));
+ EXPECT_TRUE(map.SetValue("key", make_scoped_ptr(new StringValue("test"))));
+ EXPECT_FALSE(map.SetValue("key", make_scoped_ptr(new StringValue("test"))));
+ EXPECT_TRUE(map.SetValue("key", make_scoped_ptr(new StringValue("hi mom!"))));
EXPECT_TRUE(map.GetValue("key", &result));
EXPECT_TRUE(StringValue("hi mom!").Equals(result));
@@ -26,7 +26,7 @@
TEST(PrefValueMapTest, GetAndSetIntegerValue) {
PrefValueMap map;
- ASSERT_TRUE(map.SetValue("key", new FundamentalValue(5)));
+ ASSERT_TRUE(map.SetValue("key", make_scoped_ptr(new FundamentalValue(5))));
int int_value = 0;
EXPECT_TRUE(map.GetInteger("key", &int_value));
@@ -39,7 +39,7 @@
TEST(PrefValueMapTest, SetDoubleValue) {
PrefValueMap map;
- ASSERT_TRUE(map.SetValue("key", new FundamentalValue(5.5)));
+ ASSERT_TRUE(map.SetValue("key", make_scoped_ptr(new FundamentalValue(5.5))));
const Value* result = NULL;
ASSERT_TRUE(map.GetValue("key", &result));
@@ -52,7 +52,7 @@
PrefValueMap map;
EXPECT_FALSE(map.RemoveValue("key"));
- EXPECT_TRUE(map.SetValue("key", new StringValue("test")));
+ EXPECT_TRUE(map.SetValue("key", make_scoped_ptr(new StringValue("test"))));
EXPECT_TRUE(map.GetValue("key", NULL));
EXPECT_TRUE(map.RemoveValue("key"));
@@ -63,7 +63,7 @@
TEST(PrefValueMapTest, Clear) {
PrefValueMap map;
- EXPECT_TRUE(map.SetValue("key", new StringValue("test")));
+ EXPECT_TRUE(map.SetValue("key", make_scoped_ptr(new StringValue("test"))));
EXPECT_TRUE(map.GetValue("key", NULL));
map.Clear();
@@ -73,9 +73,12 @@
TEST(PrefValueMapTest, GetDifferingKeys) {
PrefValueMap reference;
- EXPECT_TRUE(reference.SetValue("b", new StringValue("test")));
- EXPECT_TRUE(reference.SetValue("c", new StringValue("test")));
- EXPECT_TRUE(reference.SetValue("e", new StringValue("test")));
+ EXPECT_TRUE(
+ reference.SetValue("b", make_scoped_ptr(new StringValue("test"))));
+ EXPECT_TRUE(
+ reference.SetValue("c", make_scoped_ptr(new StringValue("test"))));
+ EXPECT_TRUE(
+ reference.SetValue("e", make_scoped_ptr(new StringValue("test"))));
PrefValueMap check;
std::vector<std::string> differing_paths;
@@ -87,9 +90,9 @@
expected_differing_paths.push_back("e");
EXPECT_EQ(expected_differing_paths, differing_paths);
- EXPECT_TRUE(check.SetValue("a", new StringValue("test")));
- EXPECT_TRUE(check.SetValue("c", new StringValue("test")));
- EXPECT_TRUE(check.SetValue("d", new StringValue("test")));
+ EXPECT_TRUE(check.SetValue("a", make_scoped_ptr(new StringValue("test"))));
+ EXPECT_TRUE(check.SetValue("c", make_scoped_ptr(new StringValue("test"))));
+ EXPECT_TRUE(check.SetValue("d", make_scoped_ptr(new StringValue("test"))));
reference.GetDifferingKeys(&check, &differing_paths);
expected_differing_paths.clear();
@@ -102,14 +105,20 @@
TEST(PrefValueMapTest, SwapTwoMaps) {
PrefValueMap first_map;
- EXPECT_TRUE(first_map.SetValue("a", new StringValue("test")));
- EXPECT_TRUE(first_map.SetValue("b", new StringValue("test")));
- EXPECT_TRUE(first_map.SetValue("c", new StringValue("test")));
+ EXPECT_TRUE(
+ first_map.SetValue("a", make_scoped_ptr(new StringValue("test"))));
+ EXPECT_TRUE(
+ first_map.SetValue("b", make_scoped_ptr(new StringValue("test"))));
+ EXPECT_TRUE(
+ first_map.SetValue("c", make_scoped_ptr(new StringValue("test"))));
PrefValueMap second_map;
- EXPECT_TRUE(second_map.SetValue("d", new StringValue("test")));
- EXPECT_TRUE(second_map.SetValue("e", new StringValue("test")));
- EXPECT_TRUE(second_map.SetValue("f", new StringValue("test")));
+ EXPECT_TRUE(
+ second_map.SetValue("d", make_scoped_ptr(new StringValue("test"))));
+ EXPECT_TRUE(
+ second_map.SetValue("e", make_scoped_ptr(new StringValue("test"))));
+ EXPECT_TRUE(
+ second_map.SetValue("f", make_scoped_ptr(new StringValue("test"))));
first_map.Swap(&second_map);
diff --git a/base/prefs/testing_pref_service.h b/base/prefs/testing_pref_service.h
index 7587383..cbc978d 100644
--- a/base/prefs/testing_pref_service.h
+++ b/base/prefs/testing_pref_service.h
@@ -182,7 +182,7 @@
SetPref(TestingPrefStore* pref_store,
const std::string& path,
base::Value* value) {
- pref_store->SetValue(path, value,
+ pref_store->SetValue(path, make_scoped_ptr(value),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
}
diff --git a/base/prefs/testing_pref_store.cc b/base/prefs/testing_pref_store.cc
index 1a1e6bc..2322f4e 100644
--- a/base/prefs/testing_pref_store.cc
+++ b/base/prefs/testing_pref_store.cc
@@ -43,18 +43,18 @@
}
void TestingPrefStore::SetValue(const std::string& key,
- base::Value* value,
+ scoped_ptr<base::Value> value,
uint32 flags) {
- if (prefs_.SetValue(key, value)) {
+ if (prefs_.SetValue(key, value.Pass())) {
committed_ = false;
NotifyPrefValueChanged(key);
}
}
void TestingPrefStore::SetValueSilently(const std::string& key,
- base::Value* value,
+ scoped_ptr<base::Value> value,
uint32 flags) {
- if (prefs_.SetValue(key, value))
+ if (prefs_.SetValue(key, value.Pass()))
committed_ = false;
}
@@ -115,15 +115,18 @@
void TestingPrefStore::SetString(const std::string& key,
const std::string& value) {
- SetValue(key, new base::StringValue(value), DEFAULT_PREF_WRITE_FLAGS);
+ SetValue(key, make_scoped_ptr(new base::StringValue(value)),
+ DEFAULT_PREF_WRITE_FLAGS);
}
void TestingPrefStore::SetInteger(const std::string& key, int value) {
- SetValue(key, new base::FundamentalValue(value), DEFAULT_PREF_WRITE_FLAGS);
+ SetValue(key, make_scoped_ptr(new base::FundamentalValue(value)),
+ DEFAULT_PREF_WRITE_FLAGS);
}
void TestingPrefStore::SetBoolean(const std::string& key, bool value) {
- SetValue(key, new base::FundamentalValue(value), DEFAULT_PREF_WRITE_FLAGS);
+ SetValue(key, make_scoped_ptr(new base::FundamentalValue(value)),
+ DEFAULT_PREF_WRITE_FLAGS);
}
bool TestingPrefStore::GetString(const std::string& key,
diff --git a/base/prefs/testing_pref_store.h b/base/prefs/testing_pref_store.h
index f43a030..72e61b3 100644
--- a/base/prefs/testing_pref_store.h
+++ b/base/prefs/testing_pref_store.h
@@ -32,10 +32,10 @@
bool GetMutableValue(const std::string& key, base::Value** result) override;
void ReportValueChanged(const std::string& key, uint32 flags) override;
void SetValue(const std::string& key,
- base::Value* value,
+ scoped_ptr<base::Value> value,
uint32 flags) override;
void SetValueSilently(const std::string& key,
- base::Value* value,
+ scoped_ptr<base::Value> value,
uint32 flags) override;
void RemoveValue(const std::string& key, uint32 flags) override;
bool ReadOnly() const override;
diff --git a/base/prefs/value_map_pref_store.cc b/base/prefs/value_map_pref_store.cc
index d850150..f22f93a 100644
--- a/base/prefs/value_map_pref_store.cc
+++ b/base/prefs/value_map_pref_store.cc
@@ -29,9 +29,9 @@
}
void ValueMapPrefStore::SetValue(const std::string& key,
- base::Value* value,
+ scoped_ptr<base::Value> value,
uint32 flags) {
- if (prefs_.SetValue(key, value))
+ if (prefs_.SetValue(key, value.Pass()))
FOR_EACH_OBSERVER(Observer, observers_, OnPrefValueChanged(key));
}
@@ -51,9 +51,9 @@
}
void ValueMapPrefStore::SetValueSilently(const std::string& key,
- base::Value* value,
+ scoped_ptr<base::Value> value,
uint32 flags) {
- prefs_.SetValue(key, value);
+ prefs_.SetValue(key, value.Pass());
}
ValueMapPrefStore::~ValueMapPrefStore() {}
diff --git a/base/prefs/value_map_pref_store.h b/base/prefs/value_map_pref_store.h
index 9d8fa3e..badfef7 100644
--- a/base/prefs/value_map_pref_store.h
+++ b/base/prefs/value_map_pref_store.h
@@ -29,13 +29,13 @@
// WriteablePrefStore overrides:
void SetValue(const std::string& key,
- base::Value* value,
+ scoped_ptr<base::Value> value,
uint32 flags) override;
void RemoveValue(const std::string& key, uint32 flags) override;
bool GetMutableValue(const std::string& key, base::Value** value) override;
void ReportValueChanged(const std::string& key, uint32 flags) override;
void SetValueSilently(const std::string& key,
- base::Value* value,
+ scoped_ptr<base::Value> value,
uint32 flags) override;
protected:
diff --git a/base/prefs/writeable_pref_store.h b/base/prefs/writeable_pref_store.h
index d85b4c8..cde3c84 100644
--- a/base/prefs/writeable_pref_store.h
+++ b/base/prefs/writeable_pref_store.h
@@ -8,6 +8,7 @@
#include <string>
#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
#include "base/prefs/pref_store.h"
namespace base {
@@ -30,10 +31,10 @@
WriteablePrefStore() {}
- // Sets a |value| for |key| in the store. Assumes ownership of |value|, which
- // must be non-NULL. |flags| is a bitmask of PrefWriteFlags.
+ // Sets a |value| for |key| in the store. |value| must be non-NULL. |flags| is
+ // a bitmask of PrefWriteFlags.
virtual void SetValue(const std::string& key,
- base::Value* value,
+ scoped_ptr<base::Value> value,
uint32 flags) = 0;
// Removes the value for |key|.
@@ -56,7 +57,7 @@
// tests rely on the number of notifications generated. |flags| is a bitmask
// of PrefWriteFlags.
virtual void SetValueSilently(const std::string& key,
- base::Value* value,
+ scoped_ptr<base::Value> value,
uint32 flags) = 0;
protected:
diff --git a/base/process/BUILD.gn b/base/process/BUILD.gn
index a362cbb..91f2725 100644
--- a/base/process/BUILD.gn
+++ b/base/process/BUILD.gn
@@ -77,7 +77,7 @@
set_sources_assignment_filter(sources_assignment_filter)
}
- if (is_nacl) {
+ if (is_nacl || is_ios) {
sources -= [
"kill.cc",
"kill.h",
@@ -93,6 +93,10 @@
]
}
+ if (is_ios) {
+ sources += [ "process_metrics.cc" ]
+ }
+
configs += [ "//base:base_implementation" ]
deps = [
diff --git a/base/process/internal_linux.cc b/base/process/internal_linux.cc
index d2e9ec5..4f3fcac 100644
--- a/base/process/internal_linux.cc
+++ b/base/process/internal_linux.cc
@@ -25,8 +25,8 @@
const char kStatFile[] = "stat";
-base::FilePath GetProcPidDir(pid_t pid) {
- return base::FilePath(kProcDir).Append(IntToString(pid));
+FilePath GetProcPidDir(pid_t pid) {
+ return FilePath(kProcDir).Append(IntToString(pid));
}
pid_t ProcDirSlotToPid(const char* d_name) {
@@ -106,7 +106,7 @@
typedef std::map<std::string, std::string> ProcStatMap;
void ParseProcStat(const std::string& contents, ProcStatMap* output) {
- base::StringPairs key_value_pairs;
+ StringPairs key_value_pairs;
SplitStringIntoKeyValuePairs(contents, ' ', '\n', &key_value_pairs);
for (size_t i = 0; i < key_value_pairs.size(); ++i) {
output->insert(key_value_pairs[i]);
diff --git a/base/process/launch.h b/base/process/launch.h
index 56f27a8..0e42cd0 100644
--- a/base/process/launch.h
+++ b/base/process/launch.h
@@ -297,7 +297,7 @@
// binary. This should not be called in production/released code.
BASE_EXPORT LaunchOptions LaunchOptionsForTest();
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_NACL_NONSFI)
// A wrapper for clone with fork-like behavior, meaning that it returns the
// child's pid in the parent and 0 in the child. |flags|, |ptid|, and |ctid| are
// as in the clone system call (the CLONE_VM flag is not supported).
diff --git a/base/process/launch_posix.cc b/base/process/launch_posix.cc
index 77edc12..ed9faeb 100644
--- a/base/process/launch_posix.cc
+++ b/base/process/launch_posix.cc
@@ -65,6 +65,8 @@
namespace base {
+#if !defined(OS_NACL_NONSFI)
+
namespace {
// Get the process's "environment" (i.e. the thing that setenv/getenv
@@ -188,55 +190,6 @@
}
#endif // !defined(OS_LINUX) ||
// (!defined(__i386__) && !defined(__x86_64__) && !defined(__arm__))
-
-#if defined(OS_LINUX)
-bool IsRunningOnValgrind() {
- return RUNNING_ON_VALGRIND;
-}
-
-// This function runs on the stack specified on the clone call. It uses longjmp
-// to switch back to the original stack so the child can return from sys_clone.
-int CloneHelper(void* arg) {
- jmp_buf* env_ptr = reinterpret_cast<jmp_buf*>(arg);
- longjmp(*env_ptr, 1);
-
- // Should not be reached.
- RAW_CHECK(false);
- return 1;
-}
-
-// This function is noinline to ensure that stack_buf is below the stack pointer
-// that is saved when setjmp is called below. This is needed because when
-// compiled with FORTIFY_SOURCE, glibc's longjmp checks that the stack is moved
-// upwards. See crbug.com/442912 for more details.
-#if defined(ADDRESS_SANITIZER)
-// Disable AddressSanitizer instrumentation for this function to make sure
-// |stack_buf| is allocated on thread stack instead of ASan's fake stack.
-// Under ASan longjmp() will attempt to clean up the area between the old and
-// new stack pointers and print a warning that may confuse the user.
-__attribute__((no_sanitize_address))
-#endif
-NOINLINE pid_t CloneAndLongjmpInChild(unsigned long flags,
- pid_t* ptid,
- pid_t* ctid,
- jmp_buf* env) {
- // We use the libc clone wrapper instead of making the syscall
- // directly because making the syscall may fail to update the libc's
- // internal pid cache. The libc interface unfortunately requires
- // specifying a new stack, so we use setjmp/longjmp to emulate
- // fork-like behavior.
- char stack_buf[PTHREAD_STACK_MIN];
-#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM_FAMILY) || \
- defined(ARCH_CPU_MIPS64_FAMILY) || defined(ARCH_CPU_MIPS_FAMILY)
- // The stack grows downward.
- void* stack = stack_buf + sizeof(stack_buf);
-#else
-#error "Unsupported architecture"
-#endif
- return clone(&CloneHelper, stack, flags, env, ptid, nullptr, ctid);
-}
-#endif // defined(OS_LINUX)
-
} // anonymous namespace
// Functor for |ScopedDIR| (below).
@@ -741,7 +694,60 @@
return result == EXECUTE_SUCCESS;
}
-#if defined(OS_LINUX)
+#endif // !defined(OS_NACL_NONSFI)
+
+#if defined(OS_LINUX) || defined(OS_NACL_NONSFI)
+namespace {
+
+bool IsRunningOnValgrind() {
+ return RUNNING_ON_VALGRIND;
+}
+
+// This function runs on the stack specified on the clone call. It uses longjmp
+// to switch back to the original stack so the child can return from sys_clone.
+int CloneHelper(void* arg) {
+ jmp_buf* env_ptr = reinterpret_cast<jmp_buf*>(arg);
+ longjmp(*env_ptr, 1);
+
+ // Should not be reached.
+ RAW_CHECK(false);
+ return 1;
+}
+
+// This function is noinline to ensure that stack_buf is below the stack pointer
+// that is saved when setjmp is called below. This is needed because when
+// compiled with FORTIFY_SOURCE, glibc's longjmp checks that the stack is moved
+// upwards. See crbug.com/442912 for more details.
+#if defined(ADDRESS_SANITIZER)
+// Disable AddressSanitizer instrumentation for this function to make sure
+// |stack_buf| is allocated on thread stack instead of ASan's fake stack.
+// Under ASan longjmp() will attempt to clean up the area between the old and
+// new stack pointers and print a warning that may confuse the user.
+__attribute__((no_sanitize_address))
+#endif
+NOINLINE pid_t
+CloneAndLongjmpInChild(unsigned long flags,
+ pid_t* ptid,
+ pid_t* ctid,
+ jmp_buf* env) {
+ // We use the libc clone wrapper instead of making the syscall
+ // directly because making the syscall may fail to update the libc's
+ // internal pid cache. The libc interface unfortunately requires
+ // specifying a new stack, so we use setjmp/longjmp to emulate
+ // fork-like behavior.
+ char stack_buf[PTHREAD_STACK_MIN];
+#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM_FAMILY) || \
+ defined(ARCH_CPU_MIPS64_FAMILY) || defined(ARCH_CPU_MIPS_FAMILY)
+ // The stack grows downward.
+ void* stack = stack_buf + sizeof(stack_buf);
+#else
+#error "Unsupported architecture"
+#endif
+ return clone(&CloneHelper, stack, flags, env, ptid, nullptr, ctid);
+}
+
+} // anonymous namespace
+
pid_t ForkWithFlags(unsigned long flags, pid_t* ptid, pid_t* ctid) {
const bool clone_tls_used = flags & CLONE_SETTLS;
const bool invalid_ctid =
@@ -780,6 +786,6 @@
return 0;
}
-#endif // defined(OS_LINUX)
+#endif // defined(OS_LINUX) || defined(OS_NACL_NONSFI)
} // namespace base
diff --git a/base/process/launch_win.cc b/base/process/launch_win.cc
index ebc19b8..fa59f1a 100644
--- a/base/process/launch_win.cc
+++ b/base/process/launch_win.cc
@@ -238,7 +238,7 @@
const string16 file = cmdline.GetProgram().value();
const string16 arguments = cmdline.GetArgumentsString();
- SHELLEXECUTEINFO shex_info = {0};
+ SHELLEXECUTEINFO shex_info = {};
shex_info.cbSize = sizeof(shex_info);
shex_info.fMask = SEE_MASK_NOCLOSEPROCESS;
shex_info.hwnd = GetActiveWindow();
@@ -261,7 +261,7 @@
}
bool SetJobObjectLimitFlags(HANDLE job_object, DWORD limit_flags) {
- JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info = {0};
+ JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info = {};
limit_info.BasicLimitInformation.LimitFlags = limit_flags;
return 0 != SetInformationJobObject(
job_object,
diff --git a/base/process/process_iterator_freebsd.cc b/base/process/process_iterator_freebsd.cc
index 5b1e2ab..51427b3 100644
--- a/base/process/process_iterator_freebsd.cc
+++ b/base/process/process_iterator_freebsd.cc
@@ -9,6 +9,7 @@
#include <unistd.h>
#include "base/logging.h"
+#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
namespace base {
@@ -88,7 +89,8 @@
std::string delimiters;
delimiters.push_back('\0');
- Tokenize(data, delimiters, &entry_.cmd_line_args_);
+ entry_.cmd_line_args_ =
+ SplitString(data, delimiters, KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY);
size_t exec_name_end = data.find('\0');
if (exec_name_end == std::string::npos) {
diff --git a/base/process/process_iterator_linux.cc b/base/process/process_iterator_linux.cc
index 3319552..5c8ca08 100644
--- a/base/process/process_iterator_linux.cc
+++ b/base/process/process_iterator_linux.cc
@@ -7,6 +7,7 @@
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/process/internal_linux.h"
+#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/threading/thread_restrictions.h"
@@ -48,7 +49,8 @@
return false;
std::string delimiters;
delimiters.push_back('\0');
- Tokenize(cmd_line, delimiters, proc_cmd_line_args);
+ *proc_cmd_line_args =
+ SplitString(cmd_line, delimiters, KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY);
return true;
}
diff --git a/base/process/process_iterator_mac.cc b/base/process/process_iterator_mac.cc
index e35c2ae..add62d5 100644
--- a/base/process/process_iterator_mac.cc
+++ b/base/process/process_iterator_mac.cc
@@ -10,6 +10,7 @@
#include <unistd.h>
#include "base/logging.h"
+#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
namespace base {
@@ -99,7 +100,8 @@
// |entry_.cmd_line_args_|.
std::string delimiters;
delimiters.push_back('\0');
- Tokenize(data, delimiters, &entry_.cmd_line_args_);
+ entry_.cmd_line_args_ =
+ SplitString(data, delimiters, KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY);
// |data| starts with the full executable path followed by a null character.
// We search for the first instance of '\0' and extract everything before it
diff --git a/base/process/process_iterator_openbsd.cc b/base/process/process_iterator_openbsd.cc
index 7c44eb1..b0d52b8 100644
--- a/base/process/process_iterator_openbsd.cc
+++ b/base/process/process_iterator_openbsd.cc
@@ -8,6 +8,7 @@
#include <sys/sysctl.h>
#include "base/logging.h"
+#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
namespace base {
@@ -92,7 +93,8 @@
// |entry_.cmd_line_args_|.
std::string delimiters;
delimiters.push_back('\0');
- Tokenize(data, delimiters, &entry_.cmd_line_args_);
+ entry_.cmd_line_args_ =
+ SplitString(data, delimiters, KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY);
// |data| starts with the full executable path followed by a null character.
// We search for the first instance of '\0' and extract everything before it
diff --git a/base/process/process_linux.cc b/base/process/process_linux.cc
index 88a310e..6e10dd2 100644
--- a/base/process/process_linux.cc
+++ b/base/process/process_linux.cc
@@ -10,6 +10,7 @@
#include "base/files/file_util.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/stringprintf.h"
#include "base/synchronization/lock.h"
@@ -119,7 +120,7 @@
#if defined(OS_CHROMEOS)
if (cgroups.Get().enabled) {
- std::string pid = StringPrintf("%d", process_);
+ std::string pid = IntToString(process_);
const base::FilePath file =
background ?
cgroups.Get().background_file : cgroups.Get().foreground_file;
diff --git a/base/process/process_metrics.h b/base/process/process_metrics.h
index 5916b94..8b4ec86 100644
--- a/base/process/process_metrics.h
+++ b/base/process/process_metrics.h
@@ -140,7 +140,8 @@
// memory usage as per definition of CommittedBytes.
void GetCommittedKBytes(CommittedKBytes* usage) const;
// Fills a WorkingSetKBytes containing resident private and shared memory
- // usage in bytes, as per definition of WorkingSetBytes.
+ // usage in bytes, as per definition of WorkingSetBytes. Note that this
+ // function is somewhat expensive on Windows (a few ms per process).
bool GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const;
#if defined(OS_MACOSX)
diff --git a/base/process/process_metrics_linux.cc b/base/process/process_metrics_linux.cc
index c564a67..0da1ec3 100644
--- a/base/process/process_metrics_linux.cc
+++ b/base/process/process_metrics_linux.cc
@@ -563,17 +563,15 @@
// MemTotal value
meminfo->total = 0;
- std::vector<std::string> meminfo_lines;
- Tokenize(meminfo_data, "\n", &meminfo_lines);
- for (std::vector<std::string>::iterator it = meminfo_lines.begin();
- it != meminfo_lines.end(); ++it) {
- std::vector<std::string> tokens;
- SplitStringAlongWhitespace(*it, &tokens);
+ for (const StringPiece& line : SplitStringPiece(
+ meminfo_data, "\n", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY)) {
+ std::vector<StringPiece> tokens = SplitStringPiece(
+ line, kWhitespaceASCII, TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
// HugePages_* only has a number and no suffix so we can't rely on
// there being exactly 3 tokens.
if (tokens.size() <= 1) {
DLOG(WARNING) << "meminfo: tokens: " << tokens.size()
- << " malformed line: " << *it;
+ << " malformed line: " << line.as_string();
continue;
}
@@ -630,12 +628,10 @@
// We iterate through the whole file because the position of the
// fields are dependent on the kernel version and configuration.
- std::vector<std::string> vmstat_lines;
- Tokenize(vmstat_data, "\n", &vmstat_lines);
- for (std::vector<std::string>::iterator it = vmstat_lines.begin();
- it != vmstat_lines.end(); ++it) {
- std::vector<std::string> tokens;
- SplitString(*it, ' ', &tokens);
+ for (const StringPiece& line : SplitStringPiece(
+ vmstat_data, "\n", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY)) {
+ std::vector<StringPiece> tokens =
+ SplitStringPiece(line, " ", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY);
if (tokens.size() != 2)
continue;
@@ -792,9 +788,9 @@
return false;
}
- std::vector<std::string> diskinfo_lines;
- size_t line_count = Tokenize(diskinfo_data, "\n", &diskinfo_lines);
- if (line_count == 0) {
+ std::vector<StringPiece> diskinfo_lines = SplitStringPiece(
+ diskinfo_data, "\n", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY);
+ if (diskinfo_lines.size() == 0) {
DLOG(WARNING) << "No lines found";
return false;
}
@@ -823,12 +819,12 @@
uint64 io_time = 0;
uint64 weighted_io_time = 0;
- for (size_t i = 0; i < line_count; i++) {
- std::vector<std::string> disk_fields;
- SplitStringAlongWhitespace(diskinfo_lines[i], &disk_fields);
+ for (const StringPiece& line : diskinfo_lines) {
+ std::vector<StringPiece> disk_fields = SplitStringPiece(
+ line, kWhitespaceASCII, TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
// Fields may have overflowed and reset to zero.
- if (IsValidDiskName(disk_fields[kDiskDriveName])) {
+ if (IsValidDiskName(disk_fields[kDiskDriveName].as_string())) {
StringToUint64(disk_fields[kDiskReads], &reads);
StringToUint64(disk_fields[kDiskReadsMerged], &reads_merged);
StringToUint64(disk_fields[kDiskSectorsRead], §ors_read);
diff --git a/base/process/process_metrics_win.cc b/base/process/process_metrics_win.cc
index 6c424e3..170f6dc 100644
--- a/base/process/process_metrics_win.cc
+++ b/base/process/process_metrics_win.cc
@@ -260,7 +260,7 @@
GetProcAddress(psapi_dll, "GetPerformanceInfo"));
if (!GetPerformanceInfo_func) {
- // The function could be loaded!
+ // The function could not be loaded!
memset(pPerformanceInformation, 0, cb);
return FALSE;
}
diff --git a/base/process/process_posix.cc b/base/process/process_posix.cc
index 47b0d5b..b6f22c1 100644
--- a/base/process/process_posix.cc
+++ b/base/process/process_posix.cc
@@ -295,9 +295,10 @@
#if !defined(OS_NACL_NONSFI)
bool Process::Terminate(int exit_code, bool wait) const {
- // result_code isn't supportable.
+ // exit_code isn't supportable.
DCHECK(IsValid());
- DCHECK_GT(process_, 1);
+ CHECK_GT(process_, 0);
+
bool result = kill(process_, SIGTERM) == 0;
if (result && wait) {
int tries = 60;
diff --git a/base/process/process_util_unittest.cc b/base/process/process_util_unittest.cc
index 1f7f1b2..6c1a3f1 100644
--- a/base/process/process_util_unittest.cc
+++ b/base/process/process_util_unittest.cc
@@ -64,6 +64,12 @@
namespace {
+const char kSignalFileSlow[] = "SlowChildProcess.die";
+const char kSignalFileKill[] = "KilledChildProcess.die";
+
+#if defined(OS_POSIX)
+const char kSignalFileTerm[] = "TerminatedChildProcess.die";
+
#if defined(OS_ANDROID)
const char kShellPath[] = "/system/bin/sh";
const char kPosixShell[] = "sh";
@@ -71,13 +77,7 @@
const char kShellPath[] = "/bin/sh";
const char kPosixShell[] = "bash";
#endif
-
-const char kSignalFileSlow[] = "SlowChildProcess.die";
-const char kSignalFileKill[] = "KilledChildProcess.die";
-
-#if defined(OS_POSIX)
-const char kSignalFileTerm[] = "TerminatedChildProcess.die";
-#endif
+#endif // defined(OS_POSIX)
#if defined(OS_WIN)
const int kExpectedStillRunningExitCode = 0x102;
@@ -1025,6 +1025,7 @@
return kSuccess;
}
+#if defined(CLONE_NEWUSER) && defined(CLONE_NEWPID)
TEST_F(ProcessUtilTest, CloneFlags) {
if (RunningOnValgrind() ||
!base::PathExists(FilePath("/proc/self/ns/user")) ||
@@ -1043,6 +1044,7 @@
EXPECT_TRUE(process.WaitForExit(&exit_code));
EXPECT_EQ(kSuccess, exit_code);
}
+#endif
TEST(ForkWithFlagsTest, UpdatesPidCache) {
// The libc clone function, which allows ForkWithFlags to keep the pid cache
diff --git a/base/process/process_win.cc b/base/process/process_win.cc
index 2ad72c7..30cd9dc 100644
--- a/base/process/process_win.cc
+++ b/base/process/process_win.cc
@@ -9,6 +9,7 @@
#include "base/metrics/field_trial.h"
#include "base/numerics/safe_conversions.h"
#include "base/process/kill.h"
+#include "base/strings/string_util.h"
#include "base/win/windows_version.h"
namespace {
@@ -189,8 +190,10 @@
DWORD background_priority = IDLE_PRIORITY_CLASS;
base::FieldTrial* trial =
base::FieldTrialList::Find("BackgroundRendererProcesses");
- if (trial && trial->group_name() == "AllowBelowNormalFromBrowser")
+ if (trial && StartsWith(trial->group_name(), "AllowBelowNormalFromBrowser",
+ CompareCase::SENSITIVE)) {
background_priority = BELOW_NORMAL_PRIORITY_CLASS;
+ }
priority = value ? background_priority : NORMAL_PRIORITY_CLASS;
}
diff --git a/base/profiler/stack_sampling_profiler_win.cc b/base/profiler/stack_sampling_profiler_win.cc
index 1ccd134..73b8e11 100644
--- a/base/profiler/stack_sampling_profiler_win.cc
+++ b/base/profiler/stack_sampling_profiler_win.cc
@@ -40,7 +40,7 @@
instruction_pointers[i] = reinterpret_cast<const void*>(context->Rip);
if (runtime_function) {
- KNONVOLATILE_CONTEXT_POINTERS nvcontext = {0};
+ KNONVOLATILE_CONTEXT_POINTERS nvcontext = {};
void* handler_data;
ULONG64 establisher_frame;
RtlVirtualUnwind(0, image_base, context->Rip, runtime_function, context,
diff --git a/base/strings/pattern.cc b/base/strings/pattern.cc
new file mode 100644
index 0000000..56915fe
--- /dev/null
+++ b/base/strings/pattern.cc
@@ -0,0 +1,171 @@
+// 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/strings/pattern.h"
+
+#include "base/third_party/icu/icu_utf.h"
+
+namespace base {
+
+namespace {
+
+static bool IsWildcard(base_icu::UChar32 character) {
+ return character == '*' || character == '?';
+}
+
+// Move the strings pointers to the point where they start to differ.
+template <typename CHAR, typename NEXT>
+static void EatSameChars(const CHAR** pattern,
+ const CHAR* pattern_end,
+ const CHAR** string,
+ const CHAR* string_end,
+ NEXT next) {
+ const CHAR* escape = NULL;
+ while (*pattern != pattern_end && *string != string_end) {
+ if (!escape && IsWildcard(**pattern)) {
+ // We don't want to match wildcard here, except if it's escaped.
+ return;
+ }
+
+ // Check if the escapement char is found. If so, skip it and move to the
+ // next character.
+ if (!escape && **pattern == '\\') {
+ escape = *pattern;
+ next(pattern, pattern_end);
+ continue;
+ }
+
+ // Check if the chars match, if so, increment the ptrs.
+ const CHAR* pattern_next = *pattern;
+ const CHAR* string_next = *string;
+ base_icu::UChar32 pattern_char = next(&pattern_next, pattern_end);
+ if (pattern_char == next(&string_next, string_end) &&
+ pattern_char != CBU_SENTINEL) {
+ *pattern = pattern_next;
+ *string = string_next;
+ } else {
+ // Uh oh, it did not match, we are done. If the last char was an
+ // escapement, that means that it was an error to advance the ptr here,
+ // let's put it back where it was. This also mean that the MatchPattern
+ // function will return false because if we can't match an escape char
+ // here, then no one will.
+ if (escape) {
+ *pattern = escape;
+ }
+ return;
+ }
+
+ escape = NULL;
+ }
+}
+
+template <typename CHAR, typename NEXT>
+static void EatWildcard(const CHAR** pattern, const CHAR* end, NEXT next) {
+ while (*pattern != end) {
+ if (!IsWildcard(**pattern))
+ return;
+ next(pattern, end);
+ }
+}
+
+template <typename CHAR, typename NEXT>
+static bool MatchPatternT(const CHAR* eval,
+ const CHAR* eval_end,
+ const CHAR* pattern,
+ const CHAR* pattern_end,
+ int depth,
+ NEXT next) {
+ const int kMaxDepth = 16;
+ if (depth > kMaxDepth)
+ return false;
+
+ // Eat all the matching chars.
+ EatSameChars(&pattern, pattern_end, &eval, eval_end, next);
+
+ // If the string is empty, then the pattern must be empty too, or contains
+ // only wildcards.
+ if (eval == eval_end) {
+ EatWildcard(&pattern, pattern_end, next);
+ return pattern == pattern_end;
+ }
+
+ // Pattern is empty but not string, this is not a match.
+ if (pattern == pattern_end)
+ return false;
+
+ // If this is a question mark, then we need to compare the rest with
+ // the current string or the string with one character eaten.
+ const CHAR* next_pattern = pattern;
+ next(&next_pattern, pattern_end);
+ if (pattern[0] == '?') {
+ if (MatchPatternT(eval, eval_end, next_pattern, pattern_end, depth + 1,
+ next))
+ return true;
+ const CHAR* next_eval = eval;
+ next(&next_eval, eval_end);
+ if (MatchPatternT(next_eval, eval_end, next_pattern, pattern_end, depth + 1,
+ next))
+ return true;
+ }
+
+ // This is a *, try to match all the possible substrings with the remainder
+ // of the pattern.
+ if (pattern[0] == '*') {
+ // Collapse duplicate wild cards (********** into *) so that the
+ // method does not recurse unnecessarily. http://crbug.com/52839
+ EatWildcard(&next_pattern, pattern_end, next);
+
+ while (eval != eval_end) {
+ if (MatchPatternT(eval, eval_end, next_pattern, pattern_end, depth + 1,
+ next))
+ return true;
+ eval++;
+ }
+
+ // We reached the end of the string, let see if the pattern contains only
+ // wildcards.
+ if (eval == eval_end) {
+ EatWildcard(&pattern, pattern_end, next);
+ if (pattern != pattern_end)
+ return false;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+struct NextCharUTF8 {
+ base_icu::UChar32 operator()(const char** p, const char* end) {
+ base_icu::UChar32 c;
+ int offset = 0;
+ CBU8_NEXT(*p, offset, end - *p, c);
+ *p += offset;
+ return c;
+ }
+};
+
+struct NextCharUTF16 {
+ base_icu::UChar32 operator()(const char16** p, const char16* end) {
+ base_icu::UChar32 c;
+ int offset = 0;
+ CBU16_NEXT(*p, offset, end - *p, c);
+ *p += offset;
+ return c;
+ }
+};
+
+} // namespace
+
+bool MatchPattern(const StringPiece& eval, const StringPiece& pattern) {
+ return MatchPatternT(eval.data(), eval.data() + eval.size(), pattern.data(),
+ pattern.data() + pattern.size(), 0, NextCharUTF8());
+}
+
+bool MatchPattern(const StringPiece16& eval, const StringPiece16& pattern) {
+ return MatchPatternT(eval.data(), eval.data() + eval.size(), pattern.data(),
+ pattern.data() + pattern.size(), 0, NextCharUTF16());
+}
+
+} // namespace base
diff --git a/base/strings/pattern.h b/base/strings/pattern.h
new file mode 100644
index 0000000..b698207
--- /dev/null
+++ b/base/strings/pattern.h
@@ -0,0 +1,26 @@
+// 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_STRINGS_PATTERN_H_
+#define BASE_STRINGS_PATTERN_H_
+
+#include "base/base_export.h"
+#include "base/strings/string_piece.h"
+
+namespace base {
+
+// Returns true if the string passed in matches the pattern. The pattern
+// string can contain wildcards like * and ?
+//
+// The backslash character (\) is an escape character for * and ?
+// We limit the patterns to having a max of 16 * or ? characters.
+// ? matches 0 or 1 character, while * matches 0 or more characters.
+BASE_EXPORT bool MatchPattern(const StringPiece& string,
+ const StringPiece& pattern);
+BASE_EXPORT bool MatchPattern(const StringPiece16& string,
+ const StringPiece16& pattern);
+
+} // namespace base
+
+#endif // BASE_STRINGS_PATTERN_H_
diff --git a/base/strings/pattern_unittest.cc b/base/strings/pattern_unittest.cc
new file mode 100644
index 0000000..affd1d2
--- /dev/null
+++ b/base/strings/pattern_unittest.cc
@@ -0,0 +1,50 @@
+// 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/strings/pattern.h"
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+TEST(StringUtilTest, MatchPatternTest) {
+ EXPECT_TRUE(MatchPattern("www.google.com", "*.com"));
+ EXPECT_TRUE(MatchPattern("www.google.com", "*"));
+ EXPECT_FALSE(MatchPattern("www.google.com", "www*.g*.org"));
+ EXPECT_TRUE(MatchPattern("Hello", "H?l?o"));
+ EXPECT_FALSE(MatchPattern("www.google.com", "http://*)"));
+ EXPECT_FALSE(MatchPattern("www.msn.com", "*.COM"));
+ EXPECT_TRUE(MatchPattern("Hello*1234", "He??o\\*1*"));
+ EXPECT_FALSE(MatchPattern("", "*.*"));
+ EXPECT_TRUE(MatchPattern("", "*"));
+ EXPECT_TRUE(MatchPattern("", "?"));
+ EXPECT_TRUE(MatchPattern("", ""));
+ EXPECT_FALSE(MatchPattern("Hello", ""));
+ EXPECT_TRUE(MatchPattern("Hello*", "Hello*"));
+ // Stop after a certain recursion depth.
+ EXPECT_FALSE(MatchPattern("123456789012345678", "?????????????????*"));
+
+ // Test UTF8 matching.
+ EXPECT_TRUE(MatchPattern("heart: \xe2\x99\xa0", "*\xe2\x99\xa0"));
+ EXPECT_TRUE(MatchPattern("heart: \xe2\x99\xa0.", "heart: ?."));
+ EXPECT_TRUE(MatchPattern("hearts: \xe2\x99\xa0\xe2\x99\xa0", "*"));
+ // Invalid sequences should be handled as a single invalid character.
+ EXPECT_TRUE(MatchPattern("invalid: \xef\xbf\xbe", "invalid: ?"));
+ // If the pattern has invalid characters, it shouldn't match anything.
+ EXPECT_FALSE(MatchPattern("\xf4\x90\x80\x80", "\xf4\x90\x80\x80"));
+
+ // Test UTF16 character matching.
+ EXPECT_TRUE(
+ MatchPattern(UTF8ToUTF16("www.google.com"), UTF8ToUTF16("*.com")));
+ EXPECT_TRUE(
+ MatchPattern(UTF8ToUTF16("Hello*1234"), UTF8ToUTF16("He??o\\*1*")));
+
+ // This test verifies that consecutive wild cards are collapsed into 1
+ // wildcard (when this doesn't occur, MatchPattern reaches it's maximum
+ // recursion depth).
+ EXPECT_TRUE(MatchPattern(UTF8ToUTF16("Hello"),
+ UTF8ToUTF16("He********************************o")));
+}
+
+} // namespace base
diff --git a/base/strings/string_number_conversions.cc b/base/strings/string_number_conversions.cc
index 642d24e..b6b65d2 100644
--- a/base/strings/string_number_conversions.cc
+++ b/base/strings/string_number_conversions.cc
@@ -42,7 +42,12 @@
template <typename INT2, typename UINT2>
struct ToUnsignedT<INT2, UINT2, true> {
static UINT2 ToUnsigned(INT2 value) {
- return static_cast<UINT2>(value < 0 ? -value : value);
+ if (value >= 0) {
+ return value;
+ } else {
+ // Avoid integer overflow when negating INT_MIN.
+ return static_cast<UINT2>(-(value + 1)) + 1;
+ }
}
};
@@ -131,10 +136,10 @@
return BaseCharToDigit<CHAR, BASE, BASE <= 10>::Convert(c, digit);
}
-// There is an IsWhitespace for wchars defined in string_util.h, but it is
-// locale independent, whereas the functions we are replacing were
-// locale-dependent. TBD what is desired, but for the moment let's not introduce
-// a change in behaviour.
+// There is an IsUnicodeWhitespace for wchars defined in string_util.h, but it
+// is locale independent, whereas the functions we are replacing were
+// locale-dependent. TBD what is desired, but for the moment let's not
+// introduce a change in behaviour.
template<typename CHAR> class WhitespaceHelper {
};
diff --git a/base/strings/string_piece.h b/base/strings/string_piece.h
index 349018b..a83b7d8 100644
--- a/base/strings/string_piece.h
+++ b/base/strings/string_piece.h
@@ -18,12 +18,6 @@
// Both of these have the same lifetime semantics. Passing by value
// generates slightly smaller code. For more discussion, Googlers can see
// the thread go/stringpiecebyvalue on c-users.
-//
-// StringPiece16 is similar to StringPiece but for base::string16 instead of
-// std::string. We do not define as large of a subset of the STL functions
-// from basic_string as in StringPiece, but this can be changed if these
-// functions (find, find_first_of, etc.) are found to be useful in this context.
-//
#ifndef BASE_STRINGS_STRING_PIECE_H_
#define BASE_STRINGS_STRING_PIECE_H_
diff --git a/base/strings/string_split.cc b/base/strings/string_split.cc
index 88a6236..8998c81 100644
--- a/base/strings/string_split.cc
+++ b/base/strings/string_split.cc
@@ -12,26 +12,93 @@
namespace {
-template <typename STR>
-void SplitStringT(const STR& str,
- const typename STR::value_type s,
- bool trim_whitespace,
- std::vector<STR>* r) {
- r->clear();
- size_t last = 0;
- size_t c = str.size();
- for (size_t i = 0; i <= c; ++i) {
- if (i == c || str[i] == s) {
- STR tmp(str, last, i - last);
- if (trim_whitespace)
- TrimWhitespace(tmp, TRIM_ALL, &tmp);
- // Avoid converting an empty or all-whitespace source string into a vector
- // of one empty string.
- if (i != c || !r->empty() || !tmp.empty())
- r->push_back(tmp);
- last = i + 1;
+// PieceToOutputType converts a StringPiece as needed to a given output type,
+// which is either the same type of StringPiece (a NOP) or the corresponding
+// non-piece string type.
+//
+// The default converter is a NOP, it works when the OutputType is the
+// correct StringPiece.
+template <typename Str, typename OutputType>
+OutputType PieceToOutputType(BasicStringPiece<Str> piece) {
+ return piece;
+}
+template <> // Convert StringPiece to std::string
+std::string PieceToOutputType<std::string, std::string>(StringPiece piece) {
+ return piece.as_string();
+}
+template <> // Convert StringPiece16 to string16.
+string16 PieceToOutputType<string16, string16>(StringPiece16 piece) {
+ return piece.as_string();
+}
+
+// Returns either the ASCII or UTF-16 whitespace.
+template <typename Str>
+BasicStringPiece<Str> WhitespaceForType();
+template <>
+StringPiece16 WhitespaceForType<string16>() {
+ return kWhitespaceUTF16;
+}
+template <>
+StringPiece WhitespaceForType<std::string>() {
+ return kWhitespaceASCII;
+}
+
+// Optimize the single-character case to call find() on the string instead,
+// since this is the common case and can be made faster. This could have been
+// done with template specialization too, but would have been less clear.
+//
+// There is no corresponding FindFirstNotOf because StringPiece already
+// implements these different versions that do the optimized searching.
+size_t FindFirstOf(StringPiece piece, char c, size_t pos) {
+ return piece.find(c, pos);
+}
+size_t FindFirstOf(StringPiece16 piece, char16 c, size_t pos) {
+ return piece.find(c, pos);
+}
+size_t FindFirstOf(StringPiece piece, StringPiece one_of, size_t pos) {
+ return piece.find_first_of(one_of, pos);
+}
+size_t FindFirstOf(StringPiece16 piece, StringPiece16 one_of, size_t pos) {
+ return piece.find_first_of(one_of, pos);
+}
+
+// General string splitter template. Can take 8- or 16-bit input, can produce
+// the corresponding string or StringPiece output, and can take single- or
+// multiple-character delimiters.
+//
+// DelimiterType is either a character (Str::value_type) or a string piece of
+// multiple characters (BasicStringPiece<Str>). StringPiece has a version of
+// find for both of these cases, and the single-character version is the most
+// common and can be implemented faster, which is why this is a template.
+template <typename Str, typename OutputStringType, typename DelimiterType>
+static std::vector<OutputStringType> SplitStringT(BasicStringPiece<Str> str,
+ DelimiterType delimiter,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) {
+ std::vector<OutputStringType> result;
+ if (str.empty())
+ return result;
+
+ size_t start = 0;
+ while (start != Str::npos) {
+ size_t end = FindFirstOf(str, delimiter, start);
+
+ BasicStringPiece<Str> piece;
+ if (end == Str::npos) {
+ piece = str.substr(start);
+ start = Str::npos;
+ } else {
+ piece = str.substr(start, end - start);
+ start = end + 1;
}
+
+ if (whitespace == TRIM_WHITESPACE)
+ piece = TrimString(piece, WhitespaceForType<Str>(), TRIM_ALL);
+
+ if (result_type == SPLIT_WANT_ALL || !piece.empty())
+ result.push_back(PieceToOutputType<Str, OutputStringType>(piece));
}
+ return result;
}
bool SplitStringIntoKeyValue(const std::string& line,
@@ -62,8 +129,8 @@
template <typename STR>
void SplitStringUsingSubstrT(const STR& str,
- const STR& s,
- std::vector<STR>* r) {
+ const STR& s,
+ std::vector<STR>* r) {
r->clear();
typename STR::size_type begin_index = 0;
while (true) {
@@ -83,64 +150,86 @@
}
}
-template<typename STR>
-void SplitStringAlongWhitespaceT(const STR& str, std::vector<STR>* result) {
- result->clear();
- const size_t length = str.length();
- if (!length)
- return;
-
- bool last_was_ws = false;
- size_t last_non_ws_start = 0;
- for (size_t i = 0; i < length; ++i) {
- switch (str[i]) {
- // HTML 5 defines whitespace as: space, tab, LF, line tab, FF, or CR.
- case L' ':
- case L'\t':
- case L'\xA':
- case L'\xB':
- case L'\xC':
- case L'\xD':
- if (!last_was_ws) {
- if (i > 0) {
- result->push_back(
- str.substr(last_non_ws_start, i - last_non_ws_start));
- }
- last_was_ws = true;
- }
- break;
-
- default: // Not a space character.
- if (last_was_ws) {
- last_was_ws = false;
- last_non_ws_start = i;
- }
- break;
- }
- }
- if (!last_was_ws) {
- result->push_back(
- str.substr(last_non_ws_start, length - last_non_ws_start));
- }
-}
-
} // namespace
-void SplitString(const string16& str,
- char16 c,
- std::vector<string16>* r) {
+std::vector<std::string> SplitString(StringPiece input,
+ StringPiece separators,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) {
+ if (separators.size() == 1) {
+ return SplitStringT<std::string, std::string, char>(
+ input, separators[0], whitespace, result_type);
+ }
+ return SplitStringT<std::string, std::string, StringPiece>(
+ input, separators, whitespace, result_type);
+}
+
+std::vector<string16> SplitString(StringPiece16 input,
+ StringPiece16 separators,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) {
+ if (separators.size() == 1) {
+ return SplitStringT<string16, string16, char16>(input, separators[0],
+ whitespace, result_type);
+ }
+ return SplitStringT<string16, string16, StringPiece16>(
+ input, separators, whitespace, result_type);
+}
+
+std::vector<StringPiece> SplitStringPiece(StringPiece input,
+ StringPiece separators,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) {
+ if (separators.size() == 1) {
+ return SplitStringT<std::string, StringPiece, char>(
+ input, separators[0], whitespace, result_type);
+ }
+ return SplitStringT<std::string, StringPiece, StringPiece>(
+ input, separators, whitespace, result_type);
+}
+
+std::vector<StringPiece16> SplitStringPiece(StringPiece16 input,
+ StringPiece16 separators,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) {
+ if (separators.size() == 1) {
+ return SplitStringT<string16, StringPiece16, char16>(
+ input, separators[0], whitespace, result_type);
+ }
+ return SplitStringT<string16, StringPiece16, StringPiece16>(
+ input, separators, whitespace, result_type);
+}
+
+void SplitString(const string16& str, char16 c, std::vector<string16>* result) {
DCHECK(CBU16_IS_SINGLE(c));
- SplitStringT(str, c, true, r);
+ *result = SplitStringT<string16, string16, char16>(str, c, TRIM_WHITESPACE,
+ SPLIT_WANT_ALL);
+
+ // Backward-compat hack: The old SplitString implementation would keep
+ // empty substrings, for example:
+ // "a,,b" -> ["a", "", "b"]
+ // "a, ,b" -> ["a", "", "b"]
+ // which the current code also does. But the old one would discard them when
+ // the only result was that empty string:
+ // " " -> []
+ // In the latter case, our new code will give [""]
+ if (result->size() == 1 && (*result)[0].empty())
+ result->clear();
}
void SplitString(const std::string& str,
char c,
- std::vector<std::string>* r) {
+ std::vector<std::string>* result) {
#if CHAR_MIN < 0
DCHECK_GE(c, 0);
#endif
DCHECK_LT(c, 0x7F);
- SplitStringT(str, c, true, r);
+ *result = SplitStringT<std::string, std::string, char>(
+ str, c, TRIM_WHITESPACE, SPLIT_WANT_ALL);
+
+ // Backward-compat hack, see above.
+ if (result->size() == 1 && (*result)[0].empty())
+ result->clear();
}
bool SplitStringIntoKeyValuePairs(const std::string& line,
@@ -182,31 +271,36 @@
SplitStringUsingSubstrT(str, s, r);
}
-void SplitStringDontTrim(const string16& str,
+void SplitStringDontTrim(StringPiece16 str,
char16 c,
- std::vector<string16>* r) {
+ std::vector<string16>* result) {
DCHECK(CBU16_IS_SINGLE(c));
- SplitStringT(str, c, false, r);
+ *result = SplitStringT<string16, string16, char16>(str, c, KEEP_WHITESPACE,
+ SPLIT_WANT_ALL);
}
-void SplitStringDontTrim(const std::string& str,
+void SplitStringDontTrim(StringPiece str,
char c,
- std::vector<std::string>* r) {
+ std::vector<std::string>* result) {
#if CHAR_MIN < 0
DCHECK_GE(c, 0);
#endif
DCHECK_LT(c, 0x7F);
- SplitStringT(str, c, false, r);
+ *result = SplitStringT<std::string, std::string, char>(
+ str, c, KEEP_WHITESPACE, SPLIT_WANT_ALL);
}
void SplitStringAlongWhitespace(const string16& str,
std::vector<string16>* result) {
- SplitStringAlongWhitespaceT(str, result);
+ *result = SplitStringT<string16, string16, StringPiece16>(
+ str, StringPiece16(kWhitespaceASCIIAs16), TRIM_WHITESPACE,
+ SPLIT_WANT_NONEMPTY);
}
void SplitStringAlongWhitespace(const std::string& str,
std::vector<std::string>* result) {
- SplitStringAlongWhitespaceT(str, result);
+ *result = SplitStringT<std::string, std::string, StringPiece>(
+ str, StringPiece(kWhitespaceASCII), TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
}
} // namespace base
diff --git a/base/strings/string_split.h b/base/strings/string_split.h
index 55d8cb3..1f20571 100644
--- a/base/strings/string_split.h
+++ b/base/strings/string_split.h
@@ -11,9 +11,100 @@
#include "base/base_export.h"
#include "base/strings/string16.h"
+#include "base/strings/string_piece.h"
namespace base {
+enum WhitespaceHandling {
+ KEEP_WHITESPACE,
+ TRIM_WHITESPACE,
+};
+
+enum SplitResult {
+ // Strictly return all results.
+ //
+ // If the input is ",," and the separator is ',' this will return a
+ // vector of three empty strings.
+ SPLIT_WANT_ALL,
+
+ // Only nonempty results will be added to the results. Multiple separators
+ // will be coalesced. Separators at the beginning and end of the input will
+ // be ignored. With TRIM_WHITESPACE, whitespace-only results will be dropped.
+ //
+ // If the input is ",," and the separator is ',', this will return an empty
+ // vector.
+ SPLIT_WANT_NONEMPTY,
+};
+
+// Split the given string on ANY of the given separators, returning copies of
+// the result.
+//
+// To split on either commas or semicolons, keeping all whitespace:
+//
+// std::vector<std::string> tokens = base::SplitString(
+// input, ",;", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
+BASE_EXPORT std::vector<std::string> SplitString(StringPiece input,
+ StringPiece separators,
+ WhitespaceHandling whitespace,
+ SplitResult result_type);
+BASE_EXPORT std::vector<string16> SplitString(StringPiece16 input,
+ StringPiece16 separators,
+ WhitespaceHandling whitespace,
+ SplitResult result_type);
+
+// Like SplitString above except it returns a vector of StringPieces which
+// reference the original buffer without copying. Although you have to be
+// careful to keep the original string unmodified, this provides an efficient
+// way to iterate through tokens in a string.
+//
+// To iterate through all whitespace-separated tokens in an input string:
+//
+// for (const auto& cur :
+// base::SplitStringPiece(input, base::kWhitespaceASCII,
+// base::KEEP_WHITESPACE,
+// base::SPLIT_WANT_NONEMPTY)) {
+// ...
+BASE_EXPORT std::vector<StringPiece> SplitStringPiece(
+ StringPiece input,
+ StringPiece separators,
+ WhitespaceHandling whitespace,
+ SplitResult result_type);
+BASE_EXPORT std::vector<StringPiece16> SplitStringPiece(
+ StringPiece16 input,
+ StringPiece16 separators,
+ WhitespaceHandling whitespace,
+ SplitResult result_type);
+
+using StringPairs = std::vector<std::pair<std::string, std::string>>;
+
+// Splits |line| into key value pairs according to the given delimiters and
+// removes whitespace leading each key and trailing each value. Returns true
+// only if each pair has a non-empty key and value. |key_value_pairs| will
+// include ("","") pairs for entries without |key_value_delimiter|.
+BASE_EXPORT bool SplitStringIntoKeyValuePairs(const std::string& line,
+ char key_value_delimiter,
+ char key_value_pair_delimiter,
+ StringPairs* key_value_pairs);
+
+// Similar to SplitString, but use a substring delimiter instead of a list of
+// characters that are all possible delimiters.
+//
+// TODO(brettw) this should probably be changed and expanded to provide a
+// mirror of the SplitString[Piece] API above, just with the different
+// delimiter handling.
+BASE_EXPORT void SplitStringUsingSubstr(const string16& str,
+ const string16& s,
+ std::vector<string16>* r);
+BASE_EXPORT void SplitStringUsingSubstr(const std::string& str,
+ const std::string& s,
+ std::vector<std::string>* r);
+
+// -----------------------------------------------------------------------------
+// Backwards-compat wrappers
+//
+// New code should use one of the more general variants above.
+// TODO(brettw) remove these and convert to the versions above.
+
// Splits |str| into a vector of strings delimited by |c|, placing the results
// in |r|. If several instances of |c| are contiguous, or if |str| begins with
// or ends with |c|, then an empty string is inserted.
@@ -32,46 +123,29 @@
char c,
std::vector<std::string>* r);
-typedef std::vector<std::pair<std::string, std::string> > StringPairs;
-
-// Splits |line| into key value pairs according to the given delimiters and
-// removes whitespace leading each key and trailing each value. Returns true
-// only if each pair has a non-empty key and value. |key_value_pairs| will
-// include ("","") pairs for entries without |key_value_delimiter|.
-BASE_EXPORT bool SplitStringIntoKeyValuePairs(const std::string& line,
- char key_value_delimiter,
- char key_value_pair_delimiter,
- StringPairs* key_value_pairs);
-
-// The same as SplitString, but use a substring delimiter instead of a char.
-BASE_EXPORT void SplitStringUsingSubstr(const string16& str,
- const string16& s,
- std::vector<string16>* r);
-BASE_EXPORT void SplitStringUsingSubstr(const std::string& str,
- const std::string& s,
- std::vector<std::string>* r);
-
// The same as SplitString, but don't trim white space.
// NOTE: |c| must be in BMP (Basic Multilingual Plane)
-BASE_EXPORT void SplitStringDontTrim(const string16& str,
+BASE_EXPORT void SplitStringDontTrim(StringPiece16 str,
char16 c,
std::vector<string16>* r);
// |str| should not be in a multi-byte encoding like Shift-JIS or GBK in which
// the trailing byte of a multi-byte character can be in the ASCII range.
// UTF-8, and other single/multi-byte ASCII-compatible encodings are OK.
// Note: |c| must be in the ASCII range.
-BASE_EXPORT void SplitStringDontTrim(const std::string& str,
+BASE_EXPORT void SplitStringDontTrim(StringPiece str,
char c,
- std::vector<std::string>* r);
+ std::vector<std::string>* result);
-// WARNING: this uses whitespace as defined by the HTML5 spec. If you need
-// a function similar to this but want to trim all types of whitespace, then
-// factor this out into a function that takes a string containing the characters
-// that are treated as whitespace.
+// WARNING: this uses whitespace as defined by the HTML5 spec (ASCII whitespace
+// only).
//
-// Splits the string along whitespace (where whitespace is the five space
-// characters defined by HTML 5). Each contiguous block of non-whitespace
-// characters is added to result.
+// The difference between this and calling SplitString with the whitespace
+// characters as separators is the treatment of the first element when the
+// string starts with whitespace.
+//
+// Input SplitString SplitStringAlongWhitespace
+// --------------------------------------------------------
+// " a " "", "a" "a"
BASE_EXPORT void SplitStringAlongWhitespace(const string16& str,
std::vector<string16>* result);
BASE_EXPORT void SplitStringAlongWhitespace(const std::string& str,
diff --git a/base/strings/string_split_unittest.cc b/base/strings/string_split_unittest.cc
index 32bbe28..c745ab5 100644
--- a/base/strings/string_split_unittest.cc
+++ b/base/strings/string_split_unittest.cc
@@ -169,7 +169,81 @@
EXPECT_THAT(results, ElementsAre(""));
}
-TEST(StringUtilTest, SplitString) {
+TEST(StringUtilTest, SplitString_Basics) {
+ std::vector<std::string> r;
+
+ r = SplitString(std::string(), ",:;", KEEP_WHITESPACE, SPLIT_WANT_ALL);
+ EXPECT_TRUE(r.empty());
+
+ // Empty separator list
+ r = SplitString("hello, world", "", KEEP_WHITESPACE, SPLIT_WANT_ALL);
+ ASSERT_EQ(1u, r.size());
+ EXPECT_EQ("hello, world", r[0]);
+
+ // Should split on any of the separators.
+ r = SplitString("::,,;;", ",:;", KEEP_WHITESPACE, SPLIT_WANT_ALL);
+ ASSERT_EQ(7u, r.size());
+ for (auto str : r)
+ ASSERT_TRUE(str.empty());
+
+ r = SplitString("red, green; blue:", ",:;", TRIM_WHITESPACE,
+ SPLIT_WANT_NONEMPTY);
+ ASSERT_EQ(3u, r.size());
+ EXPECT_EQ("red", r[0]);
+ EXPECT_EQ("green", r[1]);
+ EXPECT_EQ("blue", r[2]);
+
+ // Want to split a string along whitespace sequences.
+ r = SplitString(" red green \tblue\n", " \t\n", TRIM_WHITESPACE,
+ SPLIT_WANT_NONEMPTY);
+ ASSERT_EQ(3u, r.size());
+ EXPECT_EQ("red", r[0]);
+ EXPECT_EQ("green", r[1]);
+ EXPECT_EQ("blue", r[2]);
+
+ // Weird case of splitting on spaces but not trimming.
+ r = SplitString(" red ", " ", TRIM_WHITESPACE, SPLIT_WANT_ALL);
+ ASSERT_EQ(3u, r.size());
+ EXPECT_EQ("", r[0]); // Before the first space.
+ EXPECT_EQ("red", r[1]);
+ EXPECT_EQ("", r[2]); // After the last space.
+}
+
+TEST(StringUtilTest, SplitString_WhitespaceAndResultType) {
+ std::vector<std::string> r;
+
+ // Empty input handling.
+ r = SplitString(std::string(), ",", KEEP_WHITESPACE, SPLIT_WANT_ALL);
+ EXPECT_TRUE(r.empty());
+ r = SplitString(std::string(), ",", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY);
+ EXPECT_TRUE(r.empty());
+
+ // Input string is space and we're trimming.
+ r = SplitString(" ", ",", TRIM_WHITESPACE, SPLIT_WANT_ALL);
+ ASSERT_EQ(1u, r.size());
+ EXPECT_EQ("", r[0]);
+ r = SplitString(" ", ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
+ EXPECT_TRUE(r.empty());
+
+ // Test all 4 combinations of flags on ", ,".
+ r = SplitString(", ,", ",", KEEP_WHITESPACE, SPLIT_WANT_ALL);
+ ASSERT_EQ(3u, r.size());
+ EXPECT_EQ("", r[0]);
+ EXPECT_EQ(" ", r[1]);
+ EXPECT_EQ("", r[2]);
+ r = SplitString(", ,", ",", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY);
+ ASSERT_EQ(1u, r.size());
+ ASSERT_EQ(" ", r[0]);
+ r = SplitString(", ,", ",", TRIM_WHITESPACE, SPLIT_WANT_ALL);
+ ASSERT_EQ(3u, r.size());
+ EXPECT_EQ("", r[0]);
+ EXPECT_EQ("", r[1]);
+ EXPECT_EQ("", r[2]);
+ r = SplitString(", ,", ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
+ ASSERT_TRUE(r.empty());
+}
+
+TEST(StringUtilTest, SplitString_Legacy) {
std::vector<std::wstring> r;
SplitString(std::wstring(), L',', &r);
@@ -197,6 +271,13 @@
EXPECT_EQ(r[2], L"c");
r.clear();
+ SplitString(L"a, ,c", L',', &r);
+ ASSERT_EQ(3U, r.size());
+ EXPECT_EQ(r[0], L"a");
+ EXPECT_EQ(r[1], L"");
+ EXPECT_EQ(r[2], L"c");
+ r.clear();
+
SplitString(L" ", L'*', &r);
EXPECT_EQ(0U, r.size());
r.clear();
diff --git a/base/strings/string_util.cc b/base/strings/string_util.cc
index cc77693..3317740 100644
--- a/base/strings/string_util.cc
+++ b/base/strings/string_util.cc
@@ -21,14 +21,13 @@
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/memory/singleton.h"
+#include "base/strings/string_split.h"
#include "base/strings/utf_string_conversion_utils.h"
#include "base/strings/utf_string_conversions.h"
#include "base/third_party/icu/icu_utf.h"
#include "build/build_config.h"
-// Remove when this entire file is in the base namespace.
-using base::char16;
-using base::string16;
+namespace base {
namespace {
@@ -79,14 +78,16 @@
}
template<size_t size, typename CharacterType> struct NonASCIIMask;
-template<> struct NonASCIIMask<4, base::char16> {
- static inline uint32_t value() { return 0xFF80FF80U; }
+template <>
+struct NonASCIIMask<4, char16> {
+ static inline uint32_t value() { return 0xFF80FF80U; }
};
template<> struct NonASCIIMask<4, char> {
static inline uint32_t value() { return 0x80808080U; }
};
-template<> struct NonASCIIMask<8, base::char16> {
- static inline uint64_t value() { return 0xFF80FF80FF80FF80ULL; }
+template <>
+struct NonASCIIMask<8, char16> {
+ static inline uint64_t value() { return 0xFF80FF80FF80FF80ULL; }
};
template<> struct NonASCIIMask<8, char> {
static inline uint64_t value() { return 0x8080808080808080ULL; }
@@ -100,9 +101,17 @@
};
#endif // WCHAR_T_IS_UTF32
-} // namespace
+// DO NOT USE. http://crbug.com/24917
+//
+// tolower() will given incorrect results for non-ASCII characters. Use the
+// ASCII version, base::i18n::ToLower, or base::i18n::FoldCase. This is here
+// for backwards-compat for StartsWith until such calls can be updated.
+struct CaseInsensitiveCompareDeprecated {
+ public:
+ bool operator()(char16 x, char16 y) const { return tolower(x) == tolower(y); }
+};
-namespace base {
+} // namespace
bool IsWprintfFormatPortable(const wchar_t* format) {
for (const wchar_t* position = format; *position != '\0'; ++position) {
@@ -139,6 +148,53 @@
return true;
}
+template <class StringType>
+int CompareCaseInsensitiveASCIIT(BasicStringPiece<StringType> a,
+ BasicStringPiece<StringType> b) {
+ // Find the first characters that aren't equal and compare them. If the end
+ // of one of the strings is found before a nonequal character, the lengths
+ // of the strings are compared.
+ size_t i = 0;
+ while (i < a.length() && i < b.length()) {
+ typename StringType::value_type lower_a = ToLowerASCII(a[i]);
+ typename StringType::value_type lower_b = ToLowerASCII(b[i]);
+ if (lower_a < lower_b)
+ return -1;
+ if (lower_a > lower_b)
+ return 1;
+ i++;
+ }
+
+ // End of one string hit before finding a different character. Expect the
+ // common case to be "strings equal" at this point so check that first.
+ if (a.length() == b.length())
+ return 0;
+
+ if (a.length() < b.length())
+ return -1;
+ return 1;
+}
+
+int CompareCaseInsensitiveASCII(StringPiece a, StringPiece b) {
+ return CompareCaseInsensitiveASCIIT<std::string>(a, b);
+}
+
+int CompareCaseInsensitiveASCII(StringPiece16 a, StringPiece16 b) {
+ return CompareCaseInsensitiveASCIIT<string16>(a, b);
+}
+
+bool EqualsCaseInsensitiveASCII(StringPiece a, StringPiece b) {
+ if (a.length() != b.length())
+ return false;
+ return CompareCaseInsensitiveASCIIT<std::string>(a, b) == 0;
+}
+
+bool EqualsCaseInsensitiveASCII(StringPiece16 a, StringPiece16 b) {
+ if (a.length() != b.length())
+ return false;
+ return CompareCaseInsensitiveASCIIT<string16>(a, b) == 0;
+}
+
const std::string& EmptyString() {
return EmptyStrings::GetInstance()->s;
}
@@ -168,54 +224,60 @@
}
bool ReplaceChars(const string16& input,
- const base::StringPiece16& replace_chars,
+ const StringPiece16& replace_chars,
const string16& replace_with,
string16* output) {
return ReplaceCharsT(input, replace_chars.as_string(), replace_with, output);
}
bool ReplaceChars(const std::string& input,
- const base::StringPiece& replace_chars,
+ const StringPiece& replace_chars,
const std::string& replace_with,
std::string* output) {
return ReplaceCharsT(input, replace_chars.as_string(), replace_with, output);
}
bool RemoveChars(const string16& input,
- const base::StringPiece16& remove_chars,
+ const StringPiece16& remove_chars,
string16* output) {
return ReplaceChars(input, remove_chars.as_string(), string16(), output);
}
bool RemoveChars(const std::string& input,
- const base::StringPiece& remove_chars,
+ const StringPiece& remove_chars,
std::string* output) {
return ReplaceChars(input, remove_chars.as_string(), std::string(), output);
}
-template<typename STR>
-TrimPositions TrimStringT(const STR& input,
- const STR& trim_chars,
+template <typename Str>
+TrimPositions TrimStringT(const Str& input,
+ BasicStringPiece<Str> trim_chars,
TrimPositions positions,
- STR* output) {
- // Find the edges of leading/trailing whitespace as desired.
+ Str* output) {
+ // Find the edges of leading/trailing whitespace as desired. Need to use
+ // a StringPiece version of input to be able to call find* on it with the
+ // StringPiece version of trim_chars (normally the trim_chars will be a
+ // constant so avoid making a copy).
+ BasicStringPiece<Str> input_piece(input);
const size_t last_char = input.length() - 1;
- const size_t first_good_char = (positions & TRIM_LEADING) ?
- input.find_first_not_of(trim_chars) : 0;
- const size_t last_good_char = (positions & TRIM_TRAILING) ?
- input.find_last_not_of(trim_chars) : last_char;
+ const size_t first_good_char = (positions & TRIM_LEADING)
+ ? input_piece.find_first_not_of(trim_chars)
+ : 0;
+ const size_t last_good_char = (positions & TRIM_TRAILING)
+ ? input_piece.find_last_not_of(trim_chars)
+ : last_char;
- // When the string was all whitespace, report that we stripped off whitespace
- // from whichever position the caller was interested in. For empty input, we
- // stripped no whitespace, but we still need to clear |output|.
- if (input.empty() ||
- (first_good_char == STR::npos) || (last_good_char == STR::npos)) {
+ // When the string was all trimmed, report that we stripped off characters
+ // from whichever position the caller was interested in. For empty input, we
+ // stripped no characters, but we still need to clear |output|.
+ if (input.empty() || (first_good_char == Str::npos) ||
+ (last_good_char == Str::npos)) {
bool input_was_empty = input.empty(); // in case output == &input
output->clear();
return input_was_empty ? TRIM_NONE : positions;
}
- // Trim the whitespace.
+ // Trim.
*output =
input.substr(first_good_char, last_good_char - first_good_char + 1);
@@ -226,17 +288,39 @@
}
bool TrimString(const string16& input,
- const base::StringPiece16& trim_chars,
+ StringPiece16 trim_chars,
string16* output) {
- return TrimStringT(input, trim_chars.as_string(), TRIM_ALL, output) !=
- TRIM_NONE;
+ return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
}
bool TrimString(const std::string& input,
- const base::StringPiece& trim_chars,
+ StringPiece trim_chars,
std::string* output) {
- return TrimStringT(input, trim_chars.as_string(), TRIM_ALL, output) !=
- TRIM_NONE;
+ return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
+}
+
+template <typename Str>
+BasicStringPiece<Str> TrimStringPieceT(BasicStringPiece<Str> input,
+ BasicStringPiece<Str> trim_chars,
+ TrimPositions positions) {
+ size_t begin =
+ (positions & TRIM_LEADING) ? input.find_first_not_of(trim_chars) : 0;
+ size_t end = (positions & TRIM_TRAILING)
+ ? input.find_last_not_of(trim_chars) + 1
+ : input.size();
+ return input.substr(begin, end - begin);
+}
+
+StringPiece16 TrimString(StringPiece16 input,
+ const StringPiece16& trim_chars,
+ TrimPositions positions) {
+ return TrimStringPieceT(input, trim_chars, positions);
+}
+
+StringPiece TrimString(StringPiece input,
+ const StringPiece& trim_chars,
+ TrimPositions positions) {
+ return TrimStringPieceT(input, trim_chars, positions);
}
void TruncateUTF8ToByteSize(const std::string& input,
@@ -278,14 +362,22 @@
TrimPositions TrimWhitespace(const string16& input,
TrimPositions positions,
string16* output) {
- return TrimStringT(input, base::string16(kWhitespaceUTF16), positions,
- output);
+ return TrimStringT(input, StringPiece16(kWhitespaceUTF16), positions, output);
+}
+
+StringPiece16 TrimWhitespaceASCII(StringPiece16 input,
+ TrimPositions positions) {
+ return TrimStringPieceT(input, StringPiece16(kWhitespaceUTF16), positions);
}
TrimPositions TrimWhitespaceASCII(const std::string& input,
TrimPositions positions,
std::string* output) {
- return TrimStringT(input, std::string(kWhitespaceASCII), positions, output);
+ return TrimStringT(input, StringPiece(kWhitespaceASCII), positions, output);
+}
+
+StringPiece TrimWhitespaceASCII(StringPiece input, TrimPositions positions) {
+ return TrimStringPieceT(input, StringPiece(kWhitespaceASCII), positions);
}
// This function is only for backward-compatibility.
@@ -309,7 +401,7 @@
int chars_written = 0;
for (typename STR::const_iterator i(text.begin()); i != text.end(); ++i) {
- if (IsWhitespace(*i)) {
+ if (IsUnicodeWhitespace(*i)) {
if (!in_whitespace) {
// Reduce all whitespace sequences to a single space.
in_whitespace = true;
@@ -482,55 +574,123 @@
return std::equal(b.begin(), b.end(), a.begin());
}
-} // namespace base
+template <typename Str>
+bool StartsWithT(BasicStringPiece<Str> str,
+ BasicStringPiece<Str> search_for,
+ CompareCase case_sensitivity) {
+ if (search_for.size() > str.size())
+ return false;
-bool StartsWithASCII(const std::string& str,
- const std::string& search,
- bool case_sensitive) {
- if (case_sensitive)
- return str.compare(0, search.length(), search) == 0;
- else
- return base::strncasecmp(str.c_str(), search.c_str(), search.length()) == 0;
-}
+ BasicStringPiece<Str> source = str.substr(0, search_for.size());
-template <typename STR>
-bool StartsWithT(const STR& str, const STR& search, bool case_sensitive) {
- if (case_sensitive) {
- return str.compare(0, search.length(), search) == 0;
- } else {
- if (search.size() > str.size())
+ switch (case_sensitivity) {
+ case CompareCase::SENSITIVE:
+ return source == search_for;
+
+ case CompareCase::INSENSITIVE_ASCII:
+ return std::equal(
+ search_for.begin(), search_for.end(), source.begin(),
+ CaseInsensitiveCompareASCII<typename Str::value_type>());
+
+ default:
+ NOTREACHED();
return false;
- return std::equal(search.begin(), search.end(), str.begin(),
- base::CaseInsensitiveCompare<typename STR::value_type>());
}
}
-bool StartsWith(const string16& str, const string16& search,
+bool StartsWith(StringPiece str,
+ StringPiece search_for,
+ CompareCase case_sensitivity) {
+ return StartsWithT<std::string>(str, search_for, case_sensitivity);
+}
+
+bool StartsWith(StringPiece16 str,
+ StringPiece16 search_for,
+ CompareCase case_sensitivity) {
+ return StartsWithT<string16>(str, search_for, case_sensitivity);
+}
+
+bool StartsWith(const string16& str,
+ const string16& search,
bool case_sensitive) {
- return StartsWithT(str, search, case_sensitive);
+ if (!case_sensitive) {
+ // This function was originally written using the current locale functions
+ // for case-insensitive comparisons. Emulate this behavior until callers
+ // can be converted either to use the case-insensitive ASCII one (most
+ // callers) or ICU functions in base_i18n.
+ if (search.size() > str.size())
+ return false;
+ return std::equal(search.begin(), search.end(), str.begin(),
+ CaseInsensitiveCompareDeprecated());
+ }
+ return StartsWith(StringPiece16(str), StringPiece16(search),
+ CompareCase::SENSITIVE);
}
-template <typename STR>
-bool EndsWithT(const STR& str, const STR& search, bool case_sensitive) {
- size_t str_length = str.length();
- size_t search_length = search.length();
- if (search_length > str_length)
+template <typename Str>
+bool EndsWithT(BasicStringPiece<Str> str,
+ BasicStringPiece<Str> search_for,
+ CompareCase case_sensitivity) {
+ if (search_for.size() > str.size())
return false;
- if (case_sensitive)
- return str.compare(str_length - search_length, search_length, search) == 0;
- return std::equal(search.begin(), search.end(),
- str.begin() + (str_length - search_length),
- base::CaseInsensitiveCompare<typename STR::value_type>());
+
+ BasicStringPiece<Str> source =
+ str.substr(str.size() - search_for.size(), search_for.size());
+
+ switch (case_sensitivity) {
+ case CompareCase::SENSITIVE:
+ return source == search_for;
+
+ case CompareCase::INSENSITIVE_ASCII:
+ return std::equal(
+ source.begin(), source.end(), search_for.begin(),
+ CaseInsensitiveCompareASCII<typename Str::value_type>());
+
+ default:
+ NOTREACHED();
+ return false;
+ }
}
-bool EndsWith(const std::string& str, const std::string& search,
- bool case_sensitive) {
- return EndsWithT(str, search, case_sensitive);
+bool EndsWith(StringPiece str,
+ StringPiece search_for,
+ CompareCase case_sensitivity) {
+ return EndsWithT<std::string>(str, search_for, case_sensitivity);
}
-bool EndsWith(const string16& str, const string16& search,
+bool EndsWith(StringPiece16 str,
+ StringPiece16 search_for,
+ CompareCase case_sensitivity) {
+ return EndsWithT<string16>(str, search_for, case_sensitivity);
+}
+
+bool EndsWith(const string16& str,
+ const string16& search,
bool case_sensitive) {
- return EndsWithT(str, search, case_sensitive);
+ if (!case_sensitive) {
+ // This function was originally written using the current locale functions
+ // for case-insensitive comparisons. Emulate this behavior until callers
+ // can be converted either to use the case-insensitive ASCII one (most
+ // callers) or ICU functions in base_i18n.
+ if (search.size() > str.size())
+ return false;
+ return std::equal(search.begin(), search.end(),
+ str.begin() + (str.size() - search.size()),
+ CaseInsensitiveCompareDeprecated());
+ }
+ return EndsWith(StringPiece16(str), StringPiece16(search),
+ CompareCase::SENSITIVE);
+}
+
+char HexDigitToInt(wchar_t c) {
+ DCHECK(IsHexDigit(c));
+ if (c >= '0' && c <= '9')
+ return static_cast<char>(c - '0');
+ if (c >= 'A' && c <= 'F')
+ return static_cast<char>(c - 'A' + 10);
+ if (c >= 'a' && c <= 'f')
+ return static_cast<char>(c - 'a' + 10);
+ return 0;
}
static const char* const kByteStringsUnlocalized[] = {
@@ -561,20 +721,20 @@
kByteStringsUnlocalized[dimension]);
}
- return base::ASCIIToUTF16(buf);
+ return ASCIIToUTF16(buf);
}
// Runs in O(n) time in the length of |str|.
-template<class StringType>
+template <class StringType>
void DoReplaceSubstringsAfterOffset(StringType* str,
size_t offset,
- const StringType& find_this,
- const StringType& replace_with,
+ BasicStringPiece<StringType> find_this,
+ BasicStringPiece<StringType> replace_with,
bool replace_all) {
DCHECK(!find_this.empty());
// If the find string doesn't appear, there's nothing to do.
- offset = str->find(find_this, offset);
+ offset = str->find(find_this.data(), offset, find_this.size());
if (offset == StringType::npos)
return;
@@ -582,7 +742,7 @@
// complicated.
size_t find_length = find_this.length();
if (!replace_all) {
- str->replace(offset, find_length, replace_with);
+ str->replace(offset, find_length, replace_with.data(), replace_with.size());
return;
}
@@ -591,8 +751,10 @@
size_t replace_length = replace_with.length();
if (find_length == replace_length) {
do {
- str->replace(offset, find_length, replace_with);
- offset = str->find(find_this, offset + replace_length);
+ str->replace(offset, find_length, replace_with.data(),
+ replace_with.size());
+ offset = str->find(find_this.data(), offset + replace_length,
+ find_this.size());
} while (offset != StringType::npos);
return;
}
@@ -609,11 +771,14 @@
size_t write_offset = offset;
do {
if (replace_length) {
- str->replace(write_offset, replace_length, replace_with);
+ str->replace(write_offset, replace_length, replace_with.data(),
+ replace_with.size());
write_offset += replace_length;
}
size_t read_offset = offset + find_length;
- offset = std::min(str->find(find_this, read_offset), str_length);
+ offset =
+ std::min(str->find(find_this.data(), read_offset, find_this.size()),
+ str_length);
size_t length = offset - read_offset;
if (length) {
memmove(&(*str)[write_offset], &(*str)[read_offset],
@@ -642,13 +807,15 @@
// exit from the loop, |current_match| will point at the last instance of
// the find string, and we won't need to find() it again immediately.
current_match = offset;
- offset = str->find(find_this, offset + find_length);
+ offset =
+ str->find(find_this.data(), offset + find_length, find_this.size());
} while (offset != StringType::npos);
str->resize(final_length);
// Now do the replacement loop, working backwards through the string.
- for (size_t prev_match = str_length, write_offset = final_length; ;
- current_match = str->rfind(find_this, current_match - 1)) {
+ for (size_t prev_match = str_length, write_offset = final_length;;
+ current_match =
+ str->rfind(find_this.data(), current_match - 1, find_this.size())) {
size_t read_offset = current_match + find_length;
size_t length = prev_match - read_offset;
if (length) {
@@ -657,7 +824,8 @@
length * sizeof(typename StringType::value_type));
}
write_offset -= replace_length;
- str->replace(write_offset, replace_length, replace_with);
+ str->replace(write_offset, replace_length, replace_with.data(),
+ replace_with.size());
if (current_match == first_match)
return;
prev_match = current_match;
@@ -666,128 +834,97 @@
void ReplaceFirstSubstringAfterOffset(string16* str,
size_t start_offset,
- const string16& find_this,
- const string16& replace_with) {
- DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with,
- false); // replace first instance
+ StringPiece16 find_this,
+ StringPiece16 replace_with) {
+ DoReplaceSubstringsAfterOffset<string16>(
+ str, start_offset, find_this, replace_with, false); // Replace first.
}
void ReplaceFirstSubstringAfterOffset(std::string* str,
size_t start_offset,
- const std::string& find_this,
- const std::string& replace_with) {
- DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with,
- false); // replace first instance
+ StringPiece find_this,
+ StringPiece replace_with) {
+ DoReplaceSubstringsAfterOffset<std::string>(
+ str, start_offset, find_this, replace_with, false); // Replace first.
}
void ReplaceSubstringsAfterOffset(string16* str,
size_t start_offset,
- const string16& find_this,
- const string16& replace_with) {
- DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with,
- true); // replace all instances
+ StringPiece16 find_this,
+ StringPiece16 replace_with) {
+ DoReplaceSubstringsAfterOffset<string16>(str, start_offset, find_this,
+ replace_with, true); // Replace all.
}
void ReplaceSubstringsAfterOffset(std::string* str,
size_t start_offset,
- const std::string& find_this,
- const std::string& replace_with) {
- DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with,
- true); // replace all instances
+ StringPiece find_this,
+ StringPiece replace_with) {
+ DoReplaceSubstringsAfterOffset<std::string>(
+ str, start_offset, find_this, replace_with, true); // Replace all.
}
-
-template<typename STR>
-static size_t TokenizeT(const STR& str,
- const STR& delimiters,
- std::vector<STR>* tokens) {
- tokens->clear();
-
- size_t start = str.find_first_not_of(delimiters);
- while (start != STR::npos) {
- size_t end = str.find_first_of(delimiters, start + 1);
- if (end == STR::npos) {
- tokens->push_back(str.substr(start));
- break;
- } else {
- tokens->push_back(str.substr(start, end - start));
- start = str.find_first_not_of(delimiters, end + 1);
- }
- }
-
- return tokens->size();
+template <class string_type>
+inline typename string_type::value_type* WriteIntoT(string_type* str,
+ size_t length_with_null) {
+ DCHECK_GT(length_with_null, 1u);
+ str->reserve(length_with_null);
+ str->resize(length_with_null - 1);
+ return &((*str)[0]);
}
-size_t Tokenize(const string16& str,
- const string16& delimiters,
- std::vector<string16>* tokens) {
- return TokenizeT(str, delimiters, tokens);
+char* WriteInto(std::string* str, size_t length_with_null) {
+ return WriteIntoT(str, length_with_null);
}
-size_t Tokenize(const std::string& str,
- const std::string& delimiters,
- std::vector<std::string>* tokens) {
- return TokenizeT(str, delimiters, tokens);
+char16* WriteInto(string16* str, size_t length_with_null) {
+ return WriteIntoT(str, length_with_null);
}
-size_t Tokenize(const base::StringPiece& str,
- const base::StringPiece& delimiters,
- std::vector<base::StringPiece>* tokens) {
- return TokenizeT(str, delimiters, tokens);
-}
-
-template<typename STR>
-static STR JoinStringT(const std::vector<STR>& parts, const STR& sep) {
+template <typename STR>
+static STR JoinStringT(const std::vector<STR>& parts,
+ BasicStringPiece<STR> sep) {
if (parts.empty())
return STR();
STR result(parts[0]);
- typename std::vector<STR>::const_iterator iter = parts.begin();
+ auto iter = parts.begin();
++iter;
for (; iter != parts.end(); ++iter) {
- result += sep;
+ sep.AppendToString(&result);
result += *iter;
}
return result;
}
-std::string JoinString(const std::vector<std::string>& parts, char sep) {
- return JoinStringT(parts, std::string(1, sep));
-}
-
-string16 JoinString(const std::vector<string16>& parts, char16 sep) {
- return JoinStringT(parts, string16(1, sep));
-}
-
std::string JoinString(const std::vector<std::string>& parts,
- const std::string& separator) {
+ StringPiece separator) {
return JoinStringT(parts, separator);
}
string16 JoinString(const std::vector<string16>& parts,
- const string16& separator) {
+ StringPiece16 separator) {
return JoinStringT(parts, separator);
}
-template<class FormatStringType, class OutStringType>
-OutStringType DoReplaceStringPlaceholders(const FormatStringType& format_string,
- const std::vector<OutStringType>& subst, std::vector<size_t>* offsets) {
+template <class FormatStringType, class OutStringType>
+OutStringType DoReplaceStringPlaceholders(
+ const FormatStringType& format_string,
+ const std::vector<OutStringType>& subst,
+ std::vector<size_t>* offsets) {
size_t substitutions = subst.size();
size_t sub_length = 0;
- for (typename std::vector<OutStringType>::const_iterator iter = subst.begin();
- iter != subst.end(); ++iter) {
- sub_length += iter->length();
- }
+ for (const auto& cur : subst)
+ sub_length += cur.length();
OutStringType formatted;
formatted.reserve(format_string.length() + sub_length);
std::vector<ReplacementOffset> r_offsets;
- for (typename FormatStringType::const_iterator i = format_string.begin();
- i != format_string.end(); ++i) {
+ for (auto i = format_string.begin(); i != format_string.end(); ++i) {
if ('$' == *i) {
if (i + 1 != format_string.end()) {
++i;
@@ -825,10 +962,8 @@
}
}
if (offsets) {
- for (std::vector<ReplacementOffset>::const_iterator i = r_offsets.begin();
- i != r_offsets.end(); ++i) {
- offsets->push_back(i->offset);
- }
+ for (const auto& cur : r_offsets)
+ offsets->push_back(cur.offset);
}
return formatted;
}
@@ -839,7 +974,7 @@
return DoReplaceStringPlaceholders(format_string, subst, offsets);
}
-std::string ReplaceStringPlaceholders(const base::StringPiece& format_string,
+std::string ReplaceStringPlaceholders(const StringPiece& format_string,
const std::vector<std::string>& subst,
std::vector<size_t>* offsets) {
return DoReplaceStringPlaceholders(format_string, subst, offsets);
@@ -859,161 +994,6 @@
return result;
}
-static bool IsWildcard(base_icu::UChar32 character) {
- return character == '*' || character == '?';
-}
-
-// Move the strings pointers to the point where they start to differ.
-template <typename CHAR, typename NEXT>
-static void EatSameChars(const CHAR** pattern, const CHAR* pattern_end,
- const CHAR** string, const CHAR* string_end,
- NEXT next) {
- const CHAR* escape = NULL;
- while (*pattern != pattern_end && *string != string_end) {
- if (!escape && IsWildcard(**pattern)) {
- // We don't want to match wildcard here, except if it's escaped.
- return;
- }
-
- // Check if the escapement char is found. If so, skip it and move to the
- // next character.
- if (!escape && **pattern == '\\') {
- escape = *pattern;
- next(pattern, pattern_end);
- continue;
- }
-
- // Check if the chars match, if so, increment the ptrs.
- const CHAR* pattern_next = *pattern;
- const CHAR* string_next = *string;
- base_icu::UChar32 pattern_char = next(&pattern_next, pattern_end);
- if (pattern_char == next(&string_next, string_end) &&
- pattern_char != CBU_SENTINEL) {
- *pattern = pattern_next;
- *string = string_next;
- } else {
- // Uh oh, it did not match, we are done. If the last char was an
- // escapement, that means that it was an error to advance the ptr here,
- // let's put it back where it was. This also mean that the MatchPattern
- // function will return false because if we can't match an escape char
- // here, then no one will.
- if (escape) {
- *pattern = escape;
- }
- return;
- }
-
- escape = NULL;
- }
-}
-
-template <typename CHAR, typename NEXT>
-static void EatWildcard(const CHAR** pattern, const CHAR* end, NEXT next) {
- while (*pattern != end) {
- if (!IsWildcard(**pattern))
- return;
- next(pattern, end);
- }
-}
-
-template <typename CHAR, typename NEXT>
-static bool MatchPatternT(const CHAR* eval, const CHAR* eval_end,
- const CHAR* pattern, const CHAR* pattern_end,
- int depth,
- NEXT next) {
- const int kMaxDepth = 16;
- if (depth > kMaxDepth)
- return false;
-
- // Eat all the matching chars.
- EatSameChars(&pattern, pattern_end, &eval, eval_end, next);
-
- // If the string is empty, then the pattern must be empty too, or contains
- // only wildcards.
- if (eval == eval_end) {
- EatWildcard(&pattern, pattern_end, next);
- return pattern == pattern_end;
- }
-
- // Pattern is empty but not string, this is not a match.
- if (pattern == pattern_end)
- return false;
-
- // If this is a question mark, then we need to compare the rest with
- // the current string or the string with one character eaten.
- const CHAR* next_pattern = pattern;
- next(&next_pattern, pattern_end);
- if (pattern[0] == '?') {
- if (MatchPatternT(eval, eval_end, next_pattern, pattern_end,
- depth + 1, next))
- return true;
- const CHAR* next_eval = eval;
- next(&next_eval, eval_end);
- if (MatchPatternT(next_eval, eval_end, next_pattern, pattern_end,
- depth + 1, next))
- return true;
- }
-
- // This is a *, try to match all the possible substrings with the remainder
- // of the pattern.
- if (pattern[0] == '*') {
- // Collapse duplicate wild cards (********** into *) so that the
- // method does not recurse unnecessarily. http://crbug.com/52839
- EatWildcard(&next_pattern, pattern_end, next);
-
- while (eval != eval_end) {
- if (MatchPatternT(eval, eval_end, next_pattern, pattern_end,
- depth + 1, next))
- return true;
- eval++;
- }
-
- // We reached the end of the string, let see if the pattern contains only
- // wildcards.
- if (eval == eval_end) {
- EatWildcard(&pattern, pattern_end, next);
- if (pattern != pattern_end)
- return false;
- return true;
- }
- }
-
- return false;
-}
-
-struct NextCharUTF8 {
- base_icu::UChar32 operator()(const char** p, const char* end) {
- base_icu::UChar32 c;
- int offset = 0;
- CBU8_NEXT(*p, offset, end - *p, c);
- *p += offset;
- return c;
- }
-};
-
-struct NextCharUTF16 {
- base_icu::UChar32 operator()(const char16** p, const char16* end) {
- base_icu::UChar32 c;
- int offset = 0;
- CBU16_NEXT(*p, offset, end - *p, c);
- *p += offset;
- return c;
- }
-};
-
-bool MatchPattern(const base::StringPiece& eval,
- const base::StringPiece& pattern) {
- return MatchPatternT(eval.data(), eval.data() + eval.size(),
- pattern.data(), pattern.data() + pattern.size(),
- 0, NextCharUTF8());
-}
-
-bool MatchPattern(const string16& eval, const string16& pattern) {
- return MatchPatternT(eval.c_str(), eval.c_str() + eval.size(),
- pattern.c_str(), pattern.c_str() + pattern.size(),
- 0, NextCharUTF16());
-}
-
// The following code is compatible with the OpenBSD lcpy interface. See:
// http://www.gratisoft.us/todd/papers/strlcpy.html
// ftp://ftp.openbsd.org/pub/OpenBSD/src/lib/libc/string/{wcs,str}lcpy.c
@@ -1038,9 +1018,11 @@
} // namespace
-size_t base::strlcpy(char* dst, const char* src, size_t dst_size) {
+size_t strlcpy(char* dst, const char* src, size_t dst_size) {
return lcpyT<char>(dst, src, dst_size);
}
-size_t base::wcslcpy(wchar_t* dst, const wchar_t* src, size_t dst_size) {
+size_t wcslcpy(wchar_t* dst, const wchar_t* src, size_t dst_size) {
return lcpyT<wchar_t>(dst, src, dst_size);
}
+
+} // namespace base
diff --git a/base/strings/string_util.h b/base/strings/string_util.h
index bea44ae..9e50a33 100644
--- a/base/strings/string_util.h
+++ b/base/strings/string_util.h
@@ -21,23 +21,10 @@
namespace base {
-// C standard-library functions like "strncasecmp" and "snprintf" that aren't
-// cross-platform are provided as "base::strncasecmp", and their prototypes
-// are listed below. These functions are then implemented as inline calls
-// to the platform-specific equivalents in the platform-specific headers.
-
-// Compares the two strings s1 and s2 without regard to case using
-// the current locale; returns 0 if they are equal, 1 if s1 > s2, and -1 if
-// s2 > s1 according to a lexicographic comparison.
-int strcasecmp(const char* s1, const char* s2);
-
-// Compares up to count characters of s1 and s2 without regard to case using
-// the current locale; returns 0 if they are equal, 1 if s1 > s2, and -1 if
-// s2 > s1 according to a lexicographic comparison.
-int strncasecmp(const char* s1, const char* s2, size_t count);
-
-// Same as strncmp but for char16 strings.
-int strncmp16(const char16* s1, const char16* s2, size_t count);
+// C standard-library functions that aren't cross-platform are provided as
+// "base::...", and their prototypes are listed below. These functions are
+// then implemented as inline calls to the platform-specific equivalents in the
+// platform-specific headers.
// Wrapper for vsnprintf that always null-terminates and always returns the
// number of characters that would be in an untruncated formatted
@@ -59,6 +46,19 @@
return result;
}
+// TODO(mark) http://crbug.com/472900 crashpad shouldn't use base while
+// being DEPSed in. This backwards-compat hack is provided until crashpad is
+// updated.
+#if defined(OS_WIN)
+inline int strcasecmp(const char* s1, const char* s2) {
+ return _stricmp(s1, s2);
+}
+#else // Posix
+inline int strcasecmp(const char* string1, const char* string2) {
+ return ::strcasecmp(string1, string2);
+}
+#endif
+
// BSD-style safe and consistent string copy functions.
// Copies |src| to |dst|, where |dst_size| is the total allocated size of |dst|.
// Copies at most |dst_size|-1 characters, and always NULL terminates |dst|, as
@@ -103,17 +103,14 @@
return (c >= 'a' && c <= 'z') ? (c + ('A' - 'a')) : c;
}
-// Function objects to aid in comparing/searching strings.
-
-template<typename Char> struct CaseInsensitiveCompare {
- public:
- bool operator()(Char x, Char y) const {
- // TODO(darin): Do we really want to do locale sensitive comparisons here?
- // See http://crbug.com/24917
- return tolower(x) == tolower(y);
- }
-};
-
+// Functor for case-insensitive ASCII comparisons for STL algorithms like
+// std::search.
+//
+// Note that a full Unicode version of this functor is not possible to write
+// because case mappings might change the number of characters, depend on
+// context (combining accents), and require handling UTF-16. If you need
+// proper Unicode support, use base::i18n::ToLower/FoldCase and then just
+// use a normal operator== on the result.
template<typename Char> struct CaseInsensitiveCompareASCII {
public:
bool operator()(Char x, Char y) const {
@@ -121,6 +118,22 @@
}
};
+// Like strcasecmp for case-insensitive ASCII characters only. Returns:
+// -1 (a < b)
+// 0 (a == b)
+// 1 (a > b)
+// (unlike strcasecmp which can return values greater or less than 1/-1). For
+// full Unicode support, use base::i18n::ToLower or base::i18h::FoldCase
+// and then just call the normal string operators on the result.
+BASE_EXPORT int CompareCaseInsensitiveASCII(StringPiece a, StringPiece b);
+BASE_EXPORT int CompareCaseInsensitiveASCII(StringPiece16 a, StringPiece16 b);
+
+// Equality for ASCII case-insensitive comparisons. For full Unicode support,
+// use base::i18n::ToLower or base::i18h::FoldCase and then compare with either
+// == or !=.
+BASE_EXPORT bool EqualsCaseInsensitiveASCII(StringPiece a, StringPiece b);
+BASE_EXPORT bool EqualsCaseInsensitiveASCII(StringPiece16 a, StringPiece16 b);
+
// These threadsafe functions return references to globally unique empty
// strings.
//
@@ -138,10 +151,12 @@
BASE_EXPORT const string16& EmptyString16();
// Contains the set of characters representing whitespace in the corresponding
-// encoding. Null-terminated.
-BASE_EXPORT extern const wchar_t kWhitespaceWide[];
-BASE_EXPORT extern const char16 kWhitespaceUTF16[];
+// encoding. Null-terminated. The ASCII versions are the whitespaces as defined
+// by HTML5, and don't include control characters.
+BASE_EXPORT extern const wchar_t kWhitespaceWide[]; // Includes Unicode.
+BASE_EXPORT extern const char16 kWhitespaceUTF16[]; // Includes Unicode.
BASE_EXPORT extern const char kWhitespaceASCII[];
+BASE_EXPORT extern const char16 kWhitespaceASCIIAs16[]; // No unicode.
// Null-terminated string representing the UTF-8 byte order mark.
BASE_EXPORT extern const char kUtf8ByteOrderMark[];
@@ -150,10 +165,10 @@
// if any characters were removed. |remove_chars| must be null-terminated.
// NOTE: Safe to use the same variable for both |input| and |output|.
BASE_EXPORT bool RemoveChars(const string16& input,
- const base::StringPiece16& remove_chars,
+ const StringPiece16& remove_chars,
string16* output);
BASE_EXPORT bool RemoveChars(const std::string& input,
- const base::StringPiece& remove_chars,
+ const StringPiece& remove_chars,
std::string* output);
// Replaces characters in |replace_chars| from anywhere in |input| with
@@ -162,49 +177,65 @@
// |replace_chars| must be null-terminated.
// NOTE: Safe to use the same variable for both |input| and |output|.
BASE_EXPORT bool ReplaceChars(const string16& input,
- const base::StringPiece16& replace_chars,
+ const StringPiece16& replace_chars,
const string16& replace_with,
string16* output);
BASE_EXPORT bool ReplaceChars(const std::string& input,
- const base::StringPiece& replace_chars,
+ const StringPiece& replace_chars,
const std::string& replace_with,
std::string* output);
+enum TrimPositions {
+ TRIM_NONE = 0,
+ TRIM_LEADING = 1 << 0,
+ TRIM_TRAILING = 1 << 1,
+ TRIM_ALL = TRIM_LEADING | TRIM_TRAILING,
+};
+
// Removes characters in |trim_chars| from the beginning and end of |input|.
-// |trim_chars| must be null-terminated.
-// NOTE: Safe to use the same variable for both |input| and |output|.
+// The 8-bit version only works on 8-bit characters, not UTF-8.
+//
+// It is safe to use the same variable for both |input| and |output| (this is
+// the normal usage to trim in-place).
BASE_EXPORT bool TrimString(const string16& input,
- const base::StringPiece16& trim_chars,
+ StringPiece16 trim_chars,
string16* output);
BASE_EXPORT bool TrimString(const std::string& input,
- const base::StringPiece& trim_chars,
+ StringPiece trim_chars,
std::string* output);
+// StringPiece versions of the above. The returned pieces refer to the original
+// buffer.
+BASE_EXPORT StringPiece16 TrimString(StringPiece16 input,
+ const StringPiece16& trim_chars,
+ TrimPositions positions);
+BASE_EXPORT StringPiece TrimString(StringPiece input,
+ const StringPiece& trim_chars,
+ TrimPositions positions);
+
// Truncates a string to the nearest UTF-8 character that will leave
// the string less than or equal to the specified byte size.
BASE_EXPORT void TruncateUTF8ToByteSize(const std::string& input,
const size_t byte_size,
std::string* output);
-// Trims any whitespace from either end of the input string. Returns where
-// whitespace was found.
-// The non-wide version has two functions:
-// * TrimWhitespaceASCII()
-// This function is for ASCII strings and only looks for ASCII whitespace;
-// Please choose the best one according to your usage.
+// Trims any whitespace from either end of the input string.
+//
+// The StringPiece versions return a substring referencing the input buffer.
+// The ASCII versions look only for ASCII whitespace.
+//
+// The std::string versions return where whitespace was found.
// NOTE: Safe to use the same variable for both input and output.
-enum TrimPositions {
- TRIM_NONE = 0,
- TRIM_LEADING = 1 << 0,
- TRIM_TRAILING = 1 << 1,
- TRIM_ALL = TRIM_LEADING | TRIM_TRAILING,
-};
BASE_EXPORT TrimPositions TrimWhitespace(const string16& input,
TrimPositions positions,
- base::string16* output);
+ string16* output);
+BASE_EXPORT StringPiece16 TrimWhitespace(StringPiece16 input,
+ TrimPositions positions);
BASE_EXPORT TrimPositions TrimWhitespaceASCII(const std::string& input,
TrimPositions positions,
std::string* output);
+BASE_EXPORT StringPiece TrimWhitespaceASCII(StringPiece input,
+ TrimPositions positions);
// Deprecated. This function is only for backward compatibility and calls
// TrimWhitespaceASCII().
@@ -315,32 +346,41 @@
// strings are not ASCII.
BASE_EXPORT bool EqualsASCII(const string16& a, const StringPiece& b);
-} // namespace base
+// Indicates case sensitivity of comparisons. Only ASCII case insensitivity
+// is supported. Full Unicode case-insensitive conversions would need to go in
+// base/i18n so it can use ICU.
+//
+// If you need to do Unicode-aware case-insensitive StartsWith/EndsWith, it's
+// best to call base::i18n::ToLower() or base::i18n::FoldCase() (see
+// base/i18n/case_conversion.h for usage advice) on the arguments, and then use
+// the results to a case-sensitive comparison.
+enum class CompareCase {
+ SENSITIVE,
+ INSENSITIVE_ASCII,
+};
-#if defined(OS_WIN)
-#include "base/strings/string_util_win.h"
-#elif defined(OS_POSIX)
-#include "base/strings/string_util_posix.h"
-#else
-#error Define string operations appropriately for your platform
-#endif
+BASE_EXPORT bool StartsWith(StringPiece str,
+ StringPiece search_for,
+ CompareCase case_sensitivity);
+BASE_EXPORT bool StartsWith(StringPiece16 str,
+ StringPiece16 search_for,
+ CompareCase case_sensitivity);
+BASE_EXPORT bool EndsWith(StringPiece str,
+ StringPiece search_for,
+ CompareCase case_sensitivity);
+BASE_EXPORT bool EndsWith(StringPiece16 str,
+ StringPiece16 search_for,
+ CompareCase case_sensitivity);
-// Returns true if str starts with search, or false otherwise.
-BASE_EXPORT bool StartsWithASCII(const std::string& str,
- const std::string& search,
- bool case_sensitive);
-BASE_EXPORT bool StartsWith(const base::string16& str,
- const base::string16& search,
- bool case_sensitive);
-
-// Returns true if str ends with search, or false otherwise.
-BASE_EXPORT bool EndsWith(const std::string& str,
- const std::string& search,
- bool case_sensitive);
-BASE_EXPORT bool EndsWith(const base::string16& str,
- const base::string16& search,
- bool case_sensitive);
-
+// DEPRECATED. Returns true if str starts/ends with search, or false otherwise.
+// TODO(brettw) remove in favor of the "enum" versions above.
+inline bool StartsWithASCII(const std::string& str,
+ const std::string& search,
+ bool case_sensitive) {
+ return StartsWith(
+ StringPiece(str), StringPiece(search),
+ case_sensitive ? CompareCase::SENSITIVE : CompareCase::INSENSITIVE_ASCII);
+}
// Determines the type of ASCII character, independent of locale (the C
// library versions will change based on locale).
@@ -364,20 +404,15 @@
(c >= 'a' && c <= 'f');
}
-template <typename Char>
-inline char HexDigitToInt(Char c) {
- DCHECK(IsHexDigit(c));
- if (c >= '0' && c <= '9')
- return static_cast<char>(c - '0');
- if (c >= 'A' && c <= 'F')
- return static_cast<char>(c - 'A' + 10);
- if (c >= 'a' && c <= 'f')
- return static_cast<char>(c - 'a' + 10);
- return 0;
-}
+// Returns the integer corresponding to the given hex character. For example:
+// '4' -> 4
+// 'a' -> 10
+// 'B' -> 11
+// Assumes the input is a valid hex character. DCHECKs in debug builds if not.
+BASE_EXPORT char HexDigitToInt(wchar_t c);
-// Returns true if it's a whitespace character.
-inline bool IsWhitespace(wchar_t c) {
+// Returns true if it's a Unicode whitespace character.
+inline bool IsUnicodeWhitespace(wchar_t c) {
return wcschr(base::kWhitespaceWide, c) != NULL;
}
@@ -385,20 +420,18 @@
// appropriate for use in any UI; use of FormatBytes and friends in ui/base is
// highly recommended instead. TODO(avi): Figure out how to get callers to use
// FormatBytes instead; remove this.
-BASE_EXPORT base::string16 FormatBytesUnlocalized(int64 bytes);
+BASE_EXPORT string16 FormatBytesUnlocalized(int64 bytes);
// Starting at |start_offset| (usually 0), replace the first instance of
// |find_this| with |replace_with|.
-BASE_EXPORT void ReplaceFirstSubstringAfterOffset(
- base::string16* str,
- size_t start_offset,
- const base::string16& find_this,
- const base::string16& replace_with);
-BASE_EXPORT void ReplaceFirstSubstringAfterOffset(
- std::string* str,
- size_t start_offset,
- const std::string& find_this,
- const std::string& replace_with);
+BASE_EXPORT void ReplaceFirstSubstringAfterOffset(base::string16* str,
+ size_t start_offset,
+ StringPiece16 find_this,
+ StringPiece16 replace_with);
+BASE_EXPORT void ReplaceFirstSubstringAfterOffset(std::string* str,
+ size_t start_offset,
+ StringPiece find_this,
+ StringPiece replace_with);
// Starting at |start_offset| (usually 0), look through |str| and replace all
// instances of |find_this| with |replace_with|.
@@ -406,15 +439,14 @@
// This does entire substrings; use std::replace in <algorithm> for single
// characters, for example:
// std::replace(str.begin(), str.end(), 'a', 'b');
-BASE_EXPORT void ReplaceSubstringsAfterOffset(
- base::string16* str,
- size_t start_offset,
- const base::string16& find_this,
- const base::string16& replace_with);
+BASE_EXPORT void ReplaceSubstringsAfterOffset(string16* str,
+ size_t start_offset,
+ StringPiece16 find_this,
+ StringPiece16 replace_with);
BASE_EXPORT void ReplaceSubstringsAfterOffset(std::string* str,
size_t start_offset,
- const std::string& find_this,
- const std::string& replace_with);
+ StringPiece find_this,
+ StringPiece replace_with);
// Reserves enough memory in |str| to accommodate |length_with_null| characters,
// sets the size of |str| to |length_with_null - 1| characters, and returns a
@@ -436,72 +468,45 @@
// of the string, and not doing that will mean people who access |str| rather
// than str.c_str() will get back a string of whatever size |str| had on entry
// to this function (probably 0).
-template <class string_type>
-inline typename string_type::value_type* WriteInto(string_type* str,
- size_t length_with_null) {
- DCHECK_GT(length_with_null, 1u);
- str->reserve(length_with_null);
- str->resize(length_with_null - 1);
- return &((*str)[0]);
-}
-
-//-----------------------------------------------------------------------------
-
-// Splits a string into its fields delimited by any of the characters in
-// |delimiters|. Each field is added to the |tokens| vector. Returns the
-// number of tokens found.
-BASE_EXPORT size_t Tokenize(const base::string16& str,
- const base::string16& delimiters,
- std::vector<base::string16>* tokens);
-BASE_EXPORT size_t Tokenize(const std::string& str,
- const std::string& delimiters,
- std::vector<std::string>* tokens);
-BASE_EXPORT size_t Tokenize(const base::StringPiece& str,
- const base::StringPiece& delimiters,
- std::vector<base::StringPiece>* tokens);
+BASE_EXPORT char* WriteInto(std::string* str, size_t length_with_null);
+BASE_EXPORT char16* WriteInto(string16* str, size_t length_with_null);
+#ifndef OS_WIN
+BASE_EXPORT wchar_t* WriteInto(std::wstring* str, size_t length_with_null);
+#endif
// Does the opposite of SplitString().
-BASE_EXPORT base::string16 JoinString(const std::vector<base::string16>& parts,
- base::char16 s);
-BASE_EXPORT std::string JoinString(
- const std::vector<std::string>& parts, char s);
-
-// Join |parts| using |separator|.
-BASE_EXPORT std::string JoinString(
- const std::vector<std::string>& parts,
- const std::string& separator);
-BASE_EXPORT base::string16 JoinString(
- const std::vector<base::string16>& parts,
- const base::string16& separator);
+BASE_EXPORT std::string JoinString(const std::vector<std::string>& parts,
+ StringPiece separator);
+BASE_EXPORT string16 JoinString(const std::vector<string16>& parts,
+ StringPiece16 separator);
// Replace $1-$2-$3..$9 in the format string with |a|-|b|-|c|..|i| respectively.
// Additionally, any number of consecutive '$' characters is replaced by that
// number less one. Eg $$->$, $$$->$$, etc. The offsets parameter here can be
// NULL. This only allows you to use up to nine replacements.
-BASE_EXPORT base::string16 ReplaceStringPlaceholders(
- const base::string16& format_string,
- const std::vector<base::string16>& subst,
- std::vector<size_t>* offsets);
+BASE_EXPORT string16
+ReplaceStringPlaceholders(const string16& format_string,
+ const std::vector<string16>& subst,
+ std::vector<size_t>* offsets);
BASE_EXPORT std::string ReplaceStringPlaceholders(
- const base::StringPiece& format_string,
+ const StringPiece& format_string,
const std::vector<std::string>& subst,
std::vector<size_t>* offsets);
// Single-string shortcut for ReplaceStringHolders. |offset| may be NULL.
-BASE_EXPORT base::string16 ReplaceStringPlaceholders(
- const base::string16& format_string,
- const base::string16& a,
- size_t* offset);
+BASE_EXPORT string16 ReplaceStringPlaceholders(const string16& format_string,
+ const string16& a,
+ size_t* offset);
-// Returns true if the string passed in matches the pattern. The pattern
-// string can contain wildcards like * and ?
-// The backslash character (\) is an escape character for * and ?
-// We limit the patterns to having a max of 16 * or ? characters.
-// ? matches 0 or 1 character, while * matches 0 or more characters.
-BASE_EXPORT bool MatchPattern(const base::StringPiece& string,
- const base::StringPiece& pattern);
-BASE_EXPORT bool MatchPattern(const base::string16& string,
- const base::string16& pattern);
+} // namespace base
+
+#if defined(OS_WIN)
+#include "base/strings/string_util_win.h"
+#elif defined(OS_POSIX)
+#include "base/strings/string_util_posix.h"
+#else
+#error Define string operations appropriately for your platform
+#endif
#endif // BASE_STRINGS_STRING_UTIL_H_
diff --git a/base/strings/string_util_constants.cc b/base/strings/string_util_constants.cc
index 146e5fd..d443daa 100644
--- a/base/strings/string_util_constants.cc
+++ b/base/strings/string_util_constants.cc
@@ -52,6 +52,14 @@
0
};
+const char16 kWhitespaceASCIIAs16[] = {0x09, // CHARACTER TABULATION
+ 0x0A, // LINE FEED (LF)
+ 0x0B, // LINE TABULATION
+ 0x0C, // FORM FEED (FF)
+ 0x0D, // CARRIAGE RETURN (CR)
+ 0x20, // SPACE
+ 0};
+
const char kUtf8ByteOrderMark[] = "\xEF\xBB\xBF";
} // namespace base
diff --git a/base/strings/string_util_posix.h b/base/strings/string_util_posix.h
index f4009d4..9e96697 100644
--- a/base/strings/string_util_posix.h
+++ b/base/strings/string_util_posix.h
@@ -20,27 +20,11 @@
return ::strdup(str);
}
-inline int strcasecmp(const char* string1, const char* string2) {
- return ::strcasecmp(string1, string2);
-}
-
-inline int strncasecmp(const char* string1, const char* string2, size_t count) {
- return ::strncasecmp(string1, string2, count);
-}
-
inline int vsnprintf(char* buffer, size_t size,
const char* format, va_list arguments) {
return ::vsnprintf(buffer, size, format, arguments);
}
-inline int strncmp16(const char16* s1, const char16* s2, size_t count) {
-#if defined(WCHAR_T_IS_UTF16)
- return ::wcsncmp(s1, s2, count);
-#elif defined(WCHAR_T_IS_UTF32)
- return c16memcmp(s1, s2, count);
-#endif
-}
-
inline int vswprintf(wchar_t* buffer, size_t size,
const wchar_t* format, va_list arguments) {
DCHECK(IsWprintfFormatPortable(format));
diff --git a/base/strings/string_util_unittest.cc b/base/strings/string_util_unittest.cc
index fb0bead..75fc58a 100644
--- a/base/strings/string_util_unittest.cc
+++ b/base/strings/string_util_unittest.cc
@@ -669,128 +669,7 @@
EXPECT_EQ(15, HexDigitToInt('f'));
}
-// Test for Tokenize
-template <typename STR>
-void TokenizeTest() {
- std::vector<STR> r;
- size_t size;
-
- size = Tokenize(STR("This is a string"), STR(" "), &r);
- EXPECT_EQ(4U, size);
- ASSERT_EQ(4U, r.size());
- EXPECT_EQ(r[0], STR("This"));
- EXPECT_EQ(r[1], STR("is"));
- EXPECT_EQ(r[2], STR("a"));
- EXPECT_EQ(r[3], STR("string"));
- r.clear();
-
- size = Tokenize(STR("one,two,three"), STR(","), &r);
- EXPECT_EQ(3U, size);
- ASSERT_EQ(3U, r.size());
- EXPECT_EQ(r[0], STR("one"));
- EXPECT_EQ(r[1], STR("two"));
- EXPECT_EQ(r[2], STR("three"));
- r.clear();
-
- size = Tokenize(STR("one,two:three;four"), STR(",:"), &r);
- EXPECT_EQ(3U, size);
- ASSERT_EQ(3U, r.size());
- EXPECT_EQ(r[0], STR("one"));
- EXPECT_EQ(r[1], STR("two"));
- EXPECT_EQ(r[2], STR("three;four"));
- r.clear();
-
- size = Tokenize(STR("one,two:three;four"), STR(";,:"), &r);
- EXPECT_EQ(4U, size);
- ASSERT_EQ(4U, r.size());
- EXPECT_EQ(r[0], STR("one"));
- EXPECT_EQ(r[1], STR("two"));
- EXPECT_EQ(r[2], STR("three"));
- EXPECT_EQ(r[3], STR("four"));
- r.clear();
-
- size = Tokenize(STR("one, two, three"), STR(","), &r);
- EXPECT_EQ(3U, size);
- ASSERT_EQ(3U, r.size());
- EXPECT_EQ(r[0], STR("one"));
- EXPECT_EQ(r[1], STR(" two"));
- EXPECT_EQ(r[2], STR(" three"));
- r.clear();
-
- size = Tokenize(STR("one, two, three, "), STR(","), &r);
- EXPECT_EQ(4U, size);
- ASSERT_EQ(4U, r.size());
- EXPECT_EQ(r[0], STR("one"));
- EXPECT_EQ(r[1], STR(" two"));
- EXPECT_EQ(r[2], STR(" three"));
- EXPECT_EQ(r[3], STR(" "));
- r.clear();
-
- size = Tokenize(STR("one, two, three,"), STR(","), &r);
- EXPECT_EQ(3U, size);
- ASSERT_EQ(3U, r.size());
- EXPECT_EQ(r[0], STR("one"));
- EXPECT_EQ(r[1], STR(" two"));
- EXPECT_EQ(r[2], STR(" three"));
- r.clear();
-
- size = Tokenize(STR(), STR(","), &r);
- EXPECT_EQ(0U, size);
- ASSERT_EQ(0U, r.size());
- r.clear();
-
- size = Tokenize(STR(","), STR(","), &r);
- EXPECT_EQ(0U, size);
- ASSERT_EQ(0U, r.size());
- r.clear();
-
- size = Tokenize(STR(",;:."), STR(".:;,"), &r);
- EXPECT_EQ(0U, size);
- ASSERT_EQ(0U, r.size());
- r.clear();
-
- size = Tokenize(STR("\t\ta\t"), STR("\t"), &r);
- EXPECT_EQ(1U, size);
- ASSERT_EQ(1U, r.size());
- EXPECT_EQ(r[0], STR("a"));
- r.clear();
-
- size = Tokenize(STR("\ta\t\nb\tcc"), STR("\n"), &r);
- EXPECT_EQ(2U, size);
- ASSERT_EQ(2U, r.size());
- EXPECT_EQ(r[0], STR("\ta\t"));
- EXPECT_EQ(r[1], STR("b\tcc"));
- r.clear();
-}
-
-TEST(StringUtilTest, TokenizeStdString) {
- TokenizeTest<std::string>();
-}
-
-TEST(StringUtilTest, TokenizeStringPiece) {
- TokenizeTest<StringPiece>();
-}
-
-// Test for JoinString
TEST(StringUtilTest, JoinString) {
- std::vector<std::string> in;
- EXPECT_EQ("", JoinString(in, ','));
-
- in.push_back("a");
- EXPECT_EQ("a", JoinString(in, ','));
-
- in.push_back("b");
- in.push_back("c");
- EXPECT_EQ("a,b,c", JoinString(in, ','));
-
- in.push_back(std::string());
- EXPECT_EQ("a,b,c,", JoinString(in, ','));
- in.push_back(" ");
- EXPECT_EQ("a|b|c|| ", JoinString(in, '|'));
-}
-
-// Test for JoinString overloaded with std::string separator
-TEST(StringUtilTest, JoinStringWithString) {
std::string separator(", ");
std::vector<std::string> parts;
EXPECT_EQ(std::string(), JoinString(parts, separator));
@@ -808,8 +687,7 @@
EXPECT_EQ("a|b|c|| ", JoinString(parts, "|"));
}
-// Test for JoinString overloaded with string16 separator
-TEST(StringUtilTest, JoinStringWithString16) {
+TEST(StringUtilTest, JoinString16) {
string16 separator = ASCIIToUTF16(", ");
std::vector<string16> parts;
EXPECT_EQ(string16(), JoinString(parts, separator));
@@ -828,59 +706,83 @@
}
TEST(StringUtilTest, StartsWith) {
- EXPECT_TRUE(StartsWithASCII("javascript:url", "javascript", true));
- EXPECT_FALSE(StartsWithASCII("JavaScript:url", "javascript", true));
- EXPECT_TRUE(StartsWithASCII("javascript:url", "javascript", false));
- EXPECT_TRUE(StartsWithASCII("JavaScript:url", "javascript", false));
- EXPECT_FALSE(StartsWithASCII("java", "javascript", true));
- EXPECT_FALSE(StartsWithASCII("java", "javascript", false));
- EXPECT_FALSE(StartsWithASCII(std::string(), "javascript", false));
- EXPECT_FALSE(StartsWithASCII(std::string(), "javascript", true));
- EXPECT_TRUE(StartsWithASCII("java", std::string(), false));
- EXPECT_TRUE(StartsWithASCII("java", std::string(), true));
+ EXPECT_TRUE(
+ StartsWith("javascript:url", "javascript", base::CompareCase::SENSITIVE));
+ EXPECT_FALSE(
+ StartsWith("JavaScript:url", "javascript", base::CompareCase::SENSITIVE));
+ EXPECT_TRUE(StartsWith("javascript:url", "javascript",
+ base::CompareCase::INSENSITIVE_ASCII));
+ EXPECT_TRUE(StartsWith("JavaScript:url", "javascript",
+ base::CompareCase::INSENSITIVE_ASCII));
+ EXPECT_FALSE(StartsWith("java", "javascript", base::CompareCase::SENSITIVE));
+ EXPECT_FALSE(
+ StartsWith("java", "javascript", base::CompareCase::INSENSITIVE_ASCII));
+ EXPECT_FALSE(StartsWith(std::string(), "javascript",
+ base::CompareCase::INSENSITIVE_ASCII));
+ EXPECT_FALSE(
+ StartsWith(std::string(), "javascript", base::CompareCase::SENSITIVE));
+ EXPECT_TRUE(
+ StartsWith("java", std::string(), base::CompareCase::INSENSITIVE_ASCII));
+ EXPECT_TRUE(StartsWith("java", std::string(), base::CompareCase::SENSITIVE));
EXPECT_TRUE(StartsWith(ASCIIToUTF16("javascript:url"),
- ASCIIToUTF16("javascript"), true));
+ ASCIIToUTF16("javascript"),
+ base::CompareCase::SENSITIVE));
EXPECT_FALSE(StartsWith(ASCIIToUTF16("JavaScript:url"),
- ASCIIToUTF16("javascript"), true));
+ ASCIIToUTF16("javascript"),
+ base::CompareCase::SENSITIVE));
EXPECT_TRUE(StartsWith(ASCIIToUTF16("javascript:url"),
- ASCIIToUTF16("javascript"), false));
+ ASCIIToUTF16("javascript"),
+ base::CompareCase::INSENSITIVE_ASCII));
EXPECT_TRUE(StartsWith(ASCIIToUTF16("JavaScript:url"),
- ASCIIToUTF16("javascript"), false));
- EXPECT_FALSE(StartsWith(ASCIIToUTF16("java"),
- ASCIIToUTF16("javascript"), true));
- EXPECT_FALSE(StartsWith(ASCIIToUTF16("java"),
- ASCIIToUTF16("javascript"), false));
- EXPECT_FALSE(StartsWith(string16(), ASCIIToUTF16("javascript"), false));
- EXPECT_FALSE(StartsWith(string16(), ASCIIToUTF16("javascript"), true));
- EXPECT_TRUE(StartsWith(ASCIIToUTF16("java"), string16(), false));
- EXPECT_TRUE(StartsWith(ASCIIToUTF16("java"), string16(), true));
+ ASCIIToUTF16("javascript"),
+ base::CompareCase::INSENSITIVE_ASCII));
+ EXPECT_FALSE(StartsWith(ASCIIToUTF16("java"), ASCIIToUTF16("javascript"),
+ base::CompareCase::SENSITIVE));
+ EXPECT_FALSE(StartsWith(ASCIIToUTF16("java"), ASCIIToUTF16("javascript"),
+ base::CompareCase::INSENSITIVE_ASCII));
+ EXPECT_FALSE(StartsWith(string16(), ASCIIToUTF16("javascript"),
+ base::CompareCase::INSENSITIVE_ASCII));
+ EXPECT_FALSE(StartsWith(string16(), ASCIIToUTF16("javascript"),
+ base::CompareCase::SENSITIVE));
+ EXPECT_TRUE(StartsWith(ASCIIToUTF16("java"), string16(),
+ base::CompareCase::INSENSITIVE_ASCII));
+ EXPECT_TRUE(StartsWith(ASCIIToUTF16("java"), string16(),
+ base::CompareCase::SENSITIVE));
}
TEST(StringUtilTest, EndsWith) {
- EXPECT_TRUE(EndsWith(ASCIIToUTF16("Foo.plugin"),
- ASCIIToUTF16(".plugin"), true));
- EXPECT_FALSE(EndsWith(ASCIIToUTF16("Foo.Plugin"),
- ASCIIToUTF16(".plugin"), true));
- EXPECT_TRUE(EndsWith(ASCIIToUTF16("Foo.plugin"),
- ASCIIToUTF16(".plugin"), false));
- EXPECT_TRUE(EndsWith(ASCIIToUTF16("Foo.Plugin"),
- ASCIIToUTF16(".plugin"), false));
- EXPECT_FALSE(EndsWith(ASCIIToUTF16(".plug"), ASCIIToUTF16(".plugin"), true));
- EXPECT_FALSE(EndsWith(ASCIIToUTF16(".plug"), ASCIIToUTF16(".plugin"), false));
- EXPECT_FALSE(EndsWith(ASCIIToUTF16("Foo.plugin Bar"),
- ASCIIToUTF16(".plugin"), true));
- EXPECT_FALSE(EndsWith(ASCIIToUTF16("Foo.plugin Bar"),
- ASCIIToUTF16(".plugin"), false));
- EXPECT_FALSE(EndsWith(string16(), ASCIIToUTF16(".plugin"), false));
- EXPECT_FALSE(EndsWith(string16(), ASCIIToUTF16(".plugin"), true));
- EXPECT_TRUE(EndsWith(ASCIIToUTF16("Foo.plugin"), string16(), false));
- EXPECT_TRUE(EndsWith(ASCIIToUTF16("Foo.plugin"), string16(), true));
- EXPECT_TRUE(EndsWith(ASCIIToUTF16(".plugin"),
- ASCIIToUTF16(".plugin"), false));
- EXPECT_TRUE(EndsWith(ASCIIToUTF16(".plugin"), ASCIIToUTF16(".plugin"), true));
- EXPECT_TRUE(EndsWith(string16(), string16(), false));
- EXPECT_TRUE(EndsWith(string16(), string16(), true));
+ EXPECT_TRUE(EndsWith(ASCIIToUTF16("Foo.plugin"), ASCIIToUTF16(".plugin"),
+ base::CompareCase::SENSITIVE));
+ EXPECT_FALSE(EndsWith(ASCIIToUTF16("Foo.Plugin"), ASCIIToUTF16(".plugin"),
+ base::CompareCase::SENSITIVE));
+ EXPECT_TRUE(EndsWith(ASCIIToUTF16("Foo.plugin"), ASCIIToUTF16(".plugin"),
+ base::CompareCase::INSENSITIVE_ASCII));
+ EXPECT_TRUE(EndsWith(ASCIIToUTF16("Foo.Plugin"), ASCIIToUTF16(".plugin"),
+ base::CompareCase::INSENSITIVE_ASCII));
+ EXPECT_FALSE(EndsWith(ASCIIToUTF16(".plug"), ASCIIToUTF16(".plugin"),
+ base::CompareCase::SENSITIVE));
+ EXPECT_FALSE(EndsWith(ASCIIToUTF16(".plug"), ASCIIToUTF16(".plugin"),
+ base::CompareCase::INSENSITIVE_ASCII));
+ EXPECT_FALSE(EndsWith(ASCIIToUTF16("Foo.plugin Bar"), ASCIIToUTF16(".plugin"),
+ base::CompareCase::SENSITIVE));
+ EXPECT_FALSE(EndsWith(ASCIIToUTF16("Foo.plugin Bar"), ASCIIToUTF16(".plugin"),
+ base::CompareCase::INSENSITIVE_ASCII));
+ EXPECT_FALSE(EndsWith(string16(), ASCIIToUTF16(".plugin"),
+ base::CompareCase::INSENSITIVE_ASCII));
+ EXPECT_FALSE(EndsWith(string16(), ASCIIToUTF16(".plugin"),
+ base::CompareCase::SENSITIVE));
+ EXPECT_TRUE(EndsWith(ASCIIToUTF16("Foo.plugin"), string16(),
+ base::CompareCase::INSENSITIVE_ASCII));
+ EXPECT_TRUE(EndsWith(ASCIIToUTF16("Foo.plugin"), string16(),
+ base::CompareCase::SENSITIVE));
+ EXPECT_TRUE(EndsWith(ASCIIToUTF16(".plugin"), ASCIIToUTF16(".plugin"),
+ base::CompareCase::INSENSITIVE_ASCII));
+ EXPECT_TRUE(EndsWith(ASCIIToUTF16(".plugin"), ASCIIToUTF16(".plugin"),
+ base::CompareCase::SENSITIVE));
+ EXPECT_TRUE(
+ EndsWith(string16(), string16(), base::CompareCase::INSENSITIVE_ASCII));
+ EXPECT_TRUE(EndsWith(string16(), string16(), base::CompareCase::SENSITIVE));
}
TEST(StringUtilTest, GetStringFWithOffsets) {
@@ -994,45 +896,6 @@
"$1 $$2 $$$3");
}
-TEST(StringUtilTest, MatchPatternTest) {
- EXPECT_TRUE(MatchPattern("www.google.com", "*.com"));
- EXPECT_TRUE(MatchPattern("www.google.com", "*"));
- EXPECT_FALSE(MatchPattern("www.google.com", "www*.g*.org"));
- EXPECT_TRUE(MatchPattern("Hello", "H?l?o"));
- EXPECT_FALSE(MatchPattern("www.google.com", "http://*)"));
- EXPECT_FALSE(MatchPattern("www.msn.com", "*.COM"));
- EXPECT_TRUE(MatchPattern("Hello*1234", "He??o\\*1*"));
- EXPECT_FALSE(MatchPattern("", "*.*"));
- EXPECT_TRUE(MatchPattern("", "*"));
- EXPECT_TRUE(MatchPattern("", "?"));
- EXPECT_TRUE(MatchPattern("", ""));
- EXPECT_FALSE(MatchPattern("Hello", ""));
- EXPECT_TRUE(MatchPattern("Hello*", "Hello*"));
- // Stop after a certain recursion depth.
- EXPECT_FALSE(MatchPattern("123456789012345678", "?????????????????*"));
-
- // Test UTF8 matching.
- EXPECT_TRUE(MatchPattern("heart: \xe2\x99\xa0", "*\xe2\x99\xa0"));
- EXPECT_TRUE(MatchPattern("heart: \xe2\x99\xa0.", "heart: ?."));
- EXPECT_TRUE(MatchPattern("hearts: \xe2\x99\xa0\xe2\x99\xa0", "*"));
- // Invalid sequences should be handled as a single invalid character.
- EXPECT_TRUE(MatchPattern("invalid: \xef\xbf\xbe", "invalid: ?"));
- // If the pattern has invalid characters, it shouldn't match anything.
- EXPECT_FALSE(MatchPattern("\xf4\x90\x80\x80", "\xf4\x90\x80\x80"));
-
- // Test UTF16 character matching.
- EXPECT_TRUE(MatchPattern(UTF8ToUTF16("www.google.com"),
- UTF8ToUTF16("*.com")));
- EXPECT_TRUE(MatchPattern(UTF8ToUTF16("Hello*1234"),
- UTF8ToUTF16("He??o\\*1*")));
-
- // This test verifies that consecutive wild cards are collapsed into 1
- // wildcard (when this doesn't occur, MatchPattern reaches it's maximum
- // recursion depth).
- EXPECT_TRUE(MatchPattern(UTF8ToUTF16("Hello"),
- UTF8ToUTF16("He********************************o")));
-}
-
TEST(StringUtilTest, LcpyTest) {
// Test the normal case where we fit in our buffer.
{
@@ -1197,6 +1060,26 @@
kWhitespaceUTF16));
}
+TEST(StringUtilTest, CompareCaseInsensitiveASCII) {
+ EXPECT_EQ(0, CompareCaseInsensitiveASCII("", ""));
+ EXPECT_EQ(0, CompareCaseInsensitiveASCII("Asdf", "aSDf"));
+
+ // Differing lengths.
+ EXPECT_EQ(-1, CompareCaseInsensitiveASCII("Asdf", "aSDfA"));
+ EXPECT_EQ(1, CompareCaseInsensitiveASCII("AsdfA", "aSDf"));
+
+ // Differing values.
+ EXPECT_EQ(-1, CompareCaseInsensitiveASCII("AsdfA", "aSDfb"));
+ EXPECT_EQ(1, CompareCaseInsensitiveASCII("Asdfb", "aSDfA"));
+}
+
+TEST(StringUtilTest, EqualsCaseInsensitiveASCII) {
+ EXPECT_TRUE(EqualsCaseInsensitiveASCII("", ""));
+ EXPECT_TRUE(EqualsCaseInsensitiveASCII("Asdf", "aSDF"));
+ EXPECT_FALSE(EqualsCaseInsensitiveASCII("bsdf", "aSDF"));
+ EXPECT_FALSE(EqualsCaseInsensitiveASCII("Asdf", "aSDFz"));
+}
+
class WriteIntoTest : public testing::Test {
protected:
static void WritesCorrectly(size_t num_chars) {
diff --git a/base/strings/string_util_win.h b/base/strings/string_util_win.h
index 61eda20..839a799 100644
--- a/base/strings/string_util_win.h
+++ b/base/strings/string_util_win.h
@@ -20,18 +20,6 @@
return _strdup(str);
}
-inline int strcasecmp(const char* s1, const char* s2) {
- return _stricmp(s1, s2);
-}
-
-inline int strncasecmp(const char* s1, const char* s2, size_t count) {
- return _strnicmp(s1, s2, count);
-}
-
-inline int strncmp16(const char16* s1, const char16* s2, size_t count) {
- return ::wcsncmp(s1, s2, count);
-}
-
inline int vsnprintf(char* buffer, size_t size,
const char* format, va_list arguments) {
int length = vsnprintf_s(buffer, size, size - 1, format, arguments);
diff --git a/base/strings/utf_string_conversions_unittest.cc b/base/strings/utf_string_conversions_unittest.cc
index a7b12ff..6abcfc0 100644
--- a/base/strings/utf_string_conversions_unittest.cc
+++ b/base/strings/utf_string_conversions_unittest.cc
@@ -185,25 +185,22 @@
#endif // defined(WCHAR_T_IS_UTF32)
TEST(UTFStringConversionsTest, ConvertMultiString) {
- static wchar_t wmulti[] = {
- L'f', L'o', L'o', L'\0',
- L'b', L'a', L'r', L'\0',
- L'b', L'a', L'z', L'\0',
- L'\0'
- };
+ static char16 multi16[] = {'f', 'o', 'o', '\0', 'b', 'a', 'r',
+ '\0', 'b', 'a', 'z', '\0', '\0'};
static char multi[] = {
'f', 'o', 'o', '\0',
'b', 'a', 'r', '\0',
'b', 'a', 'z', '\0',
'\0'
};
- std::wstring wmultistring;
- memcpy(WriteInto(&wmultistring, arraysize(wmulti)), wmulti, sizeof(wmulti));
- EXPECT_EQ(arraysize(wmulti) - 1, wmultistring.length());
+ string16 multistring16;
+ memcpy(WriteInto(&multistring16, arraysize(multi16)), multi16,
+ sizeof(multi16));
+ EXPECT_EQ(arraysize(multi16) - 1, multistring16.length());
std::string expected;
memcpy(WriteInto(&expected, arraysize(multi)), multi, sizeof(multi));
EXPECT_EQ(arraysize(multi) - 1, expected.length());
- const std::string& converted = WideToUTF8(wmultistring);
+ const std::string& converted = UTF16ToUTF8(multistring16);
EXPECT_EQ(arraysize(multi) - 1, converted.length());
EXPECT_EQ(expected, converted);
}
diff --git a/base/synchronization/condition_variable_win.cc b/base/synchronization/condition_variable_win.cc
index 5f165c8..470e564 100644
--- a/base/synchronization/condition_variable_win.cc
+++ b/base/synchronization/condition_variable_win.cc
@@ -205,10 +205,10 @@
};
WinXPCondVar::WinXPCondVar(Lock* user_lock)
- : user_lock_(*user_lock),
- run_state_(RUNNING),
- allocation_counter_(0),
- recycling_list_size_(0) {
+ : run_state_(RUNNING),
+ user_lock_(*user_lock),
+ recycling_list_size_(0),
+ allocation_counter_(0) {
DCHECK(user_lock);
}
diff --git a/base/sys_info.cc b/base/sys_info.cc
index 8640dc1..f24ebd3 100644
--- a/base/sys_info.cc
+++ b/base/sys_info.cc
@@ -41,14 +41,14 @@
// Low End Device Mode will be enabled if this client is assigned to
// one of those EnabledXXX groups.
- if (StartsWithASCII(group_name, "Enabled", true))
+ if (StartsWith(group_name, "Enabled", CompareCase::SENSITIVE))
return true;
return g_lazy_low_end_device.Get().value();
}
#endif
-#if !defined(OS_MACOSX) || defined(OS_IOS)
+#if (!defined(OS_MACOSX) || defined(OS_IOS)) && !defined(OS_ANDROID)
std::string SysInfo::HardwareModelName() {
return std::string();
}
diff --git a/base/sys_info.h b/base/sys_info.h
index 654d694..5a81dc1 100644
--- a/base/sys_info.h
+++ b/base/sys_info.h
@@ -52,9 +52,9 @@
static int64 Uptime();
// Returns a descriptive string for the current machine model or an empty
- // string if machime model is unknown or an error occured.
- // e.g. MacPro1,1 on Mac.
- // Only implemented on OS X, will return an empty string on other platforms.
+ // string if the machine model is unknown or an error occured.
+ // e.g. "MacPro1,1" on Mac, or "Nexus 5" on Android. Only implemented on OS X,
+ // Android, and Chrome OS. This returns an empty string on other platforms.
static std::string HardwareModelName();
// Returns the name of the host operating system.
@@ -129,9 +129,6 @@
// Returns the Android build ID.
static std::string GetAndroidBuildID();
- // Returns the device's name.
- static std::string GetDeviceName();
-
static int DalvikHeapSizeMB();
static int DalvikHeapGrowthLimitMB();
#endif // defined(OS_ANDROID)
diff --git a/base/sys_info_android.cc b/base/sys_info_android.cc
index 245097f..c288ae2 100644
--- a/base/sys_info_android.cc
+++ b/base/sys_info_android.cc
@@ -155,28 +155,16 @@
namespace base {
-std::string SysInfo::OperatingSystemName() {
- return "Android";
-}
-
-std::string SysInfo::GetAndroidBuildCodename() {
- char os_version_codename_str[PROP_VALUE_MAX];
- __system_property_get("ro.build.version.codename", os_version_codename_str);
- return std::string(os_version_codename_str);
-}
-
-std::string SysInfo::GetAndroidBuildID() {
- char os_build_id_str[PROP_VALUE_MAX];
- __system_property_get("ro.build.id", os_build_id_str);
- return std::string(os_build_id_str);
-}
-
-std::string SysInfo::GetDeviceName() {
+std::string SysInfo::HardwareModelName() {
char device_model_str[PROP_VALUE_MAX];
__system_property_get("ro.product.model", device_model_str);
return std::string(device_model_str);
}
+std::string SysInfo::OperatingSystemName() {
+ return "Android";
+}
+
std::string SysInfo::OperatingSystemVersion() {
int32 major, minor, bugfix;
OperatingSystemVersionNumbers(&major, &minor, &bugfix);
@@ -195,6 +183,18 @@
bugfix_version);
}
+std::string SysInfo::GetAndroidBuildCodename() {
+ char os_version_codename_str[PROP_VALUE_MAX];
+ __system_property_get("ro.build.version.codename", os_version_codename_str);
+ return std::string(os_version_codename_str);
+}
+
+std::string SysInfo::GetAndroidBuildID() {
+ char os_build_id_str[PROP_VALUE_MAX];
+ __system_property_get("ro.build.id", os_build_id_str);
+ return std::string(os_build_id_str);
+}
+
int SysInfo::DalvikHeapSizeMB() {
static int heap_size = GetDalvikHeapSizeMB();
return heap_size;
diff --git a/base/task_runner.h b/base/task_runner.h
index 7d07b8c..6369c4f 100644
--- a/base/task_runner.h
+++ b/base/task_runner.h
@@ -104,7 +104,7 @@
// public:
// void GetData() {
// scoped_refptr<DataBuffer> buffer = new DataBuffer();
- // target_thread_.message_loop_proxy()->PostTaskAndReply(
+ // target_thread_.task_runner()->PostTaskAndReply(
// FROM_HERE,
// base::Bind(&DataBuffer::AddData, buffer),
// base::Bind(&DataLoader::OnDataReceived, AsWeakPtr(), buffer));
diff --git a/base/task_runner_util.h b/base/task_runner_util.h
index b6dd0f3..da088db 100644
--- a/base/task_runner_util.h
+++ b/base/task_runner_util.h
@@ -47,7 +47,7 @@
// PostTaskAndReplyWithResult as in this example:
//
// PostTaskAndReplyWithResult(
-// target_thread_.message_loop_proxy(),
+// target_thread_.task_runner(),
// FROM_HERE,
// Bind(&DoWorkAndReturn),
// Bind(&Callback));
diff --git a/base/test/BUILD.gn b/base/test/BUILD.gn
index a8ae0cf..db0fc91 100644
--- a/base/test/BUILD.gn
+++ b/base/test/BUILD.gn
@@ -128,18 +128,24 @@
"values_test_util.h",
]
+ data = [
+ # The isolate needs this script for setting up the test. It's not actually
+ # needed to run this target locally.
+ "//testing/test_env.py",
+ ]
+
public_deps = [
":test_config",
"//base",
- "//base:i18n",
"//base:base_static",
+ "//base:i18n",
]
deps = [
"//base/third_party/dynamic_annotations",
"//testing/gmock",
"//testing/gtest",
- "//third_party/libxml",
"//third_party/icu:icuuc",
+ "//third_party/libxml",
]
if (!is_posix) {
diff --git a/base/test/android/java/src/org/chromium/base/TestUiThread.java b/base/test/android/java/src/org/chromium/base/TestUiThread.java
index 77f9660..4abec80 100644
--- a/base/test/android/java/src/org/chromium/base/TestUiThread.java
+++ b/base/test/android/java/src/org/chromium/base/TestUiThread.java
@@ -19,7 +19,7 @@
@ThreadSafe
public class TestUiThread {
private static final AtomicBoolean sStarted = new AtomicBoolean(false);
- private static final String TAG = Log.makeTag("TestUiThread");
+ private static final String TAG = "cr.TestUiThread";
@CalledByNative
private static void loop() {
diff --git a/base/test/android/javatests/src/org/chromium/base/test/BaseActivityInstrumentationTestCase.java b/base/test/android/javatests/src/org/chromium/base/test/BaseActivityInstrumentationTestCase.java
index 53dee4a..4c7f3b8 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/BaseActivityInstrumentationTestCase.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/BaseActivityInstrumentationTestCase.java
@@ -10,17 +10,8 @@
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
-import org.chromium.base.BaseChromiumApplication;
-import org.chromium.base.CommandLine;
import org.chromium.base.test.util.CommandLineFlags;
-import java.lang.reflect.AnnotatedElement;
-import java.lang.reflect.Method;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
/**
* Base class for all Activity-based Instrumentation tests.
*
@@ -43,26 +34,10 @@
super(activityClass);
}
- /**
- * Sets up the CommandLine with the appropriate flags.
- *
- * This will add the difference of the sets of flags specified by {@link CommandLineFlags.Add}
- * and {@link CommandLineFlags.Remove} to the {@link org.chromium.base.CommandLine}. Note that
- * trying to remove a flag set externally, i.e. by the command-line flags file, will not work.
- */
@Override
protected void setUp() throws Exception {
super.setUp();
-
- CommandLine.reset();
- Context targetContext = getTargetContext();
- assertNotNull("Unable to get a non-null target context.", targetContext);
-
- BaseChromiumApplication.initCommandLine(targetContext);
- Set<String> flags = getFlags(getClass().getMethod(getName()));
- for (String flag : flags) {
- CommandLine.getInstance().appendSwitch(flag);
- }
+ CommandLineFlags.setUp(getTargetContext(), getClass().getMethod(getName()));
}
/**
@@ -88,31 +63,4 @@
}
return targetContext;
}
-
- private static Set<String> getFlags(AnnotatedElement element) {
- AnnotatedElement parent = (element instanceof Method)
- ? ((Method) element).getDeclaringClass()
- : ((Class) element).getSuperclass();
- Set<String> flags = (parent == null) ? new HashSet<String>() : getFlags(parent);
-
- if (element.isAnnotationPresent(CommandLineFlags.Add.class)) {
- flags.addAll(
- Arrays.asList(element.getAnnotation(CommandLineFlags.Add.class).value()));
- }
-
- if (element.isAnnotationPresent(CommandLineFlags.Remove.class)) {
- List<String> flagsToRemove =
- Arrays.asList(element.getAnnotation(CommandLineFlags.Remove.class).value());
- for (String flagToRemove : flagsToRemove) {
- // If your test fails here, you have tried to remove a command-line flag via
- // CommandLineFlags.Remove that was loaded into CommandLine via something other
- // than CommandLineFlags.Add (probably the command-line flag file).
- assertFalse("Unable to remove command-line flag \"" + flagToRemove + "\".",
- CommandLine.getInstance().hasSwitch(flagToRemove));
- }
- flags.removeAll(flagsToRemove);
- }
-
- return flags;
- }
}
diff --git a/base/test/android/javatests/src/org/chromium/base/test/BaseInstrumentationTestRunner.java b/base/test/android/javatests/src/org/chromium/base/test/BaseInstrumentationTestRunner.java
index 8a3395a..c90ebf2 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/BaseInstrumentationTestRunner.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/BaseInstrumentationTestRunner.java
@@ -94,7 +94,7 @@
@Override
protected TestResult createTestResult() {
SkippingTestResult r = new SkippingTestResult();
- r.addSkipCheck(new MinAndroidSdkLevelSkipCheck());
+ addSkipChecks(r);
return r;
}
};
@@ -103,6 +103,13 @@
}
/**
+ * Adds the desired SkipChecks to result. Subclasses can add additional SkipChecks.
+ */
+ protected void addSkipChecks(SkippingTestResult result) {
+ result.addSkipCheck(new MinAndroidSdkLevelSkipCheck());
+ }
+
+ /**
* Checks the device's SDK level against any specified minimum requirement.
*/
public static class MinAndroidSdkLevelSkipCheck implements SkipCheck {
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/CommandLineFlags.java b/base/test/android/javatests/src/org/chromium/base/test/util/CommandLineFlags.java
index 2feb83d..4dde9e5 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/util/CommandLineFlags.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/util/CommandLineFlags.java
@@ -4,11 +4,24 @@
package org.chromium.base.test.util;
+import android.content.Context;
+
+import junit.framework.Assert;
+
+import org.chromium.base.BaseChromiumApplication;
+import org.chromium.base.CommandLine;
+
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
/**
* Provides annotations related to command-line flag handling.
@@ -44,5 +57,48 @@
String[] value();
}
+ /**
+ * Sets up the CommandLine with the appropriate flags.
+ *
+ * This will add the difference of the sets of flags specified by {@link CommandLineFlags.Add}
+ * and {@link CommandLineFlags.Remove} to the {@link org.chromium.base.CommandLine}. Note that
+ * trying to remove a flag set externally, i.e. by the command-line flags file, will not work.
+ */
+ public static void setUp(Context targetContext, AnnotatedElement element) {
+ Assert.assertNotNull("Unable to get a non-null target context.", targetContext);
+ CommandLine.reset();
+ BaseChromiumApplication.initCommandLine(targetContext);
+ Set<String> flags = getFlags(element);
+ for (String flag : flags) {
+ CommandLine.getInstance().appendSwitch(flag);
+ }
+ }
+
+ private static Set<String> getFlags(AnnotatedElement element) {
+ AnnotatedElement parent = (element instanceof Method)
+ ? ((Method) element).getDeclaringClass()
+ : ((Class) element).getSuperclass();
+ Set<String> flags = (parent == null) ? new HashSet<String>() : getFlags(parent);
+
+ if (element.isAnnotationPresent(CommandLineFlags.Add.class)) {
+ flags.addAll(Arrays.asList(element.getAnnotation(CommandLineFlags.Add.class).value()));
+ }
+
+ if (element.isAnnotationPresent(CommandLineFlags.Remove.class)) {
+ List<String> flagsToRemove =
+ Arrays.asList(element.getAnnotation(CommandLineFlags.Remove.class).value());
+ for (String flagToRemove : flagsToRemove) {
+ // If your test fails here, you have tried to remove a command-line flag via
+ // CommandLineFlags.Remove that was loaded into CommandLine via something other
+ // than CommandLineFlags.Add (probably the command-line flag file).
+ Assert.assertFalse("Unable to remove command-line flag \"" + flagToRemove + "\".",
+ CommandLine.getInstance().hasSwitch(flagToRemove));
+ }
+ flags.removeAll(flagsToRemove);
+ }
+
+ return flags;
+ }
+
private CommandLineFlags() {}
}
diff --git a/base/test/expectations/parser.cc b/base/test/expectations/parser.cc
index c7132e5..83c64c6 100644
--- a/base/test/expectations/parser.cc
+++ b/base/test/expectations/parser.cc
@@ -39,7 +39,7 @@
Parser::StateFunc Parser::Start() {
// If at the start of a line is whitespace, skip it and arrange to come back
// here.
- if (IsAsciiWhitespace(*pos_))
+ if (base::IsAsciiWhitespace(*pos_))
return SkipWhitespaceAndNewLines(&Parser::Start);
// Handle comments at the start of lines.
@@ -161,7 +161,7 @@
Parser::StateFunc Parser::ExtractString(StateFunc success) {
const char* start = pos_;
- while (!IsAsciiWhitespace(*pos_) && *pos_ != ']' && HasNext()) {
+ while (!base::IsAsciiWhitespace(*pos_) && *pos_ != ']' && HasNext()) {
++pos_;
if (*pos_ == '#') {
return SyntaxError("Unexpected start of comment");
@@ -179,7 +179,7 @@
}
Parser::StateFunc Parser::SkipWhitespaceAndNewLines(Parser::StateFunc next) {
- while (IsAsciiWhitespace(*pos_) && HasNext()) {
+ while (base::IsAsciiWhitespace(*pos_) && HasNext()) {
if (*pos_ == '\n') {
++line_number_;
}
diff --git a/base/test/histogram_tester.cc b/base/test/histogram_tester.cc
index ea738b0..0eba3a9 100644
--- a/base/test/histogram_tester.cc
+++ b/base/test/histogram_tester.cc
@@ -73,6 +73,19 @@
}
}
+std::vector<Bucket> HistogramTester::GetAllSamples(const std::string& name) {
+ std::vector<Bucket> samples;
+ scoped_ptr<HistogramSamples> snapshot =
+ GetHistogramSamplesSinceCreation(name);
+ for (auto it = snapshot->Iterator(); !it->Done(); it->Next()) {
+ HistogramBase::Sample sample;
+ HistogramBase::Count count;
+ it->Get(&sample, nullptr, &count);
+ samples.push_back(Bucket(sample, count));
+ }
+ return samples;
+}
+
scoped_ptr<HistogramSamples> HistogramTester::GetHistogramSamplesSinceCreation(
const std::string& histogram_name) {
HistogramBase* histogram = StatisticsRecorder::FindHistogram(histogram_name);
@@ -120,4 +133,12 @@
<< expected_count << "). It has (" << actual_count << ").";
}
+bool Bucket::operator==(const Bucket& other) const {
+ return min == other.min && count == other.count;
+}
+
+void PrintTo(const Bucket& bucket, std::ostream* os) {
+ *os << "Bucket " << bucket.min << ": " << bucket.count;
+}
+
} // namespace base
diff --git a/base/test/histogram_tester.h b/base/test/histogram_tester.h
index 96317f9..7ac7ca6 100644
--- a/base/test/histogram_tester.h
+++ b/base/test/histogram_tester.h
@@ -6,7 +6,10 @@
#define BASE_TEST_HISTOGRAM_TESTER_H_
#include <map>
+#include <ostream>
#include <string>
+#include <utility>
+#include <vector>
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
@@ -15,6 +18,7 @@
namespace base {
+struct Bucket;
class HistogramSamples;
// HistogramTester provides a simple interface for examining histograms, UMA
@@ -47,6 +51,24 @@
void ExpectTotalCount(const std::string& name,
base::HistogramBase::Count count) const;
+ // Returns a list of all of the buckets recorded since creation of this
+ // object, as vector<Bucket>, where the Bucket represents the min boundary of
+ // the bucket and the count of samples recorded to that bucket since creation.
+ //
+ // Example usage, using gMock:
+ // EXPECT_THAT(histogram_tester.GetAllSamples("HistogramName"),
+ // ElementsAre(Bucket(1, 5), Bucket(2, 10), Bucket(3, 5)));
+ //
+ // If you build the expected list programmatically, you can use ContainerEq:
+ // EXPECT_THAT(histogram_tester.GetAllSamples("HistogramName"),
+ // ContainerEq(expected_buckets));
+ //
+ // or EXPECT_EQ if you prefer not to depend on gMock, at the expense of a
+ // slightly less helpful failure message:
+ // EXPECT_EQ(expected_buckets,
+ // histogram_tester.GetAllSamples("HistogramName"));
+ std::vector<Bucket> GetAllSamples(const std::string& name);
+
// Access a modified HistogramSamples containing only what has been logged
// to the histogram since the creation of this object.
scoped_ptr<HistogramSamples> GetHistogramSamplesSinceCreation(
@@ -76,6 +98,18 @@
DISALLOW_COPY_AND_ASSIGN(HistogramTester);
};
+struct Bucket {
+ Bucket(base::HistogramBase::Sample min, base::HistogramBase::Count count)
+ : min(min), count(count) {}
+
+ bool operator==(const Bucket& other) const;
+
+ base::HistogramBase::Sample min;
+ base::HistogramBase::Count count;
+};
+
+void PrintTo(const Bucket& value, std::ostream* os);
+
} // namespace base
#endif // BASE_TEST_HISTOGRAM_TESTER_H_
diff --git a/base/test/histogram_tester_unittest.cc b/base/test/histogram_tester_unittest.cc
index a03ee13..f875527 100644
--- a/base/test/histogram_tester_unittest.cc
+++ b/base/test/histogram_tester_unittest.cc
@@ -5,16 +5,20 @@
#include "base/test/histogram_tester.h"
#include "base/memory/scoped_ptr.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
#include "base/metrics/histogram_samples.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
+using ::testing::ElementsAre;
+
const std::string kHistogram1 = "Test1";
const std::string kHistogram2 = "Test2";
const std::string kHistogram3 = "Test3";
const std::string kHistogram4 = "Test4";
+const std::string kHistogram5 = "Test5";
typedef testing::Test HistogramTesterTest;
@@ -78,4 +82,15 @@
tester.ExpectTotalCount(kHistogram4, 1);
}
+TEST_F(HistogramTesterTest, TestGetAllSamples) {
+ HistogramTester tester;
+ UMA_HISTOGRAM_ENUMERATION(kHistogram5, 2, 5);
+ UMA_HISTOGRAM_ENUMERATION(kHistogram5, 3, 5);
+ UMA_HISTOGRAM_ENUMERATION(kHistogram5, 3, 5);
+ UMA_HISTOGRAM_ENUMERATION(kHistogram5, 5, 5);
+
+ EXPECT_THAT(tester.GetAllSamples(kHistogram5),
+ ElementsAre(Bucket(2, 1), Bucket(3, 2), Bucket(5, 1)));
+}
+
} // namespace base
diff --git a/base/test/launcher/test_launcher.cc b/base/test/launcher/test_launcher.cc
index 7f258f5..7c0bc4e 100644
--- a/base/test/launcher/test_launcher.cc
+++ b/base/test/launcher/test_launcher.cc
@@ -25,6 +25,7 @@
#include "base/process/kill.h"
#include "base/process/launch.h"
#include "base/single_thread_task_runner.h"
+#include "base/strings/pattern.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
@@ -569,7 +570,7 @@
snippet_lines.begin() + truncated_size);
snippet_lines.insert(snippet_lines.begin(), "<truncated>");
}
- fprintf(stdout, "%s", JoinString(snippet_lines, "\n").c_str());
+ fprintf(stdout, "%s", base::JoinString(snippet_lines, "\n").c_str());
fflush(stdout);
}
diff --git a/base/test/launcher/test_launcher_ios.cc b/base/test/launcher/test_launcher_ios.cc
index ecd31ae..3179bb3 100644
--- a/base/test/launcher/test_launcher_ios.cc
+++ b/base/test/launcher/test_launcher_ios.cc
@@ -121,7 +121,7 @@
base::CommandLine cmd_line(dir_exe_.AppendASCII(test_name_ + ".app"));
cmd_line.AppendSwitchPath(switches::kTestLauncherOutput, output_file);
cmd_line.AppendSwitchASCII(base::kGTestFilterFlag,
- JoinString(test_names, ":"));
+ base::JoinString(test_names, ":"));
return cmd_line;
}
diff --git a/base/test/launcher/test_launcher_nacl_nonsfi.cc b/base/test/launcher/test_launcher_nacl_nonsfi.cc
index fa52604..173e552 100644
--- a/base/test/launcher/test_launcher_nacl_nonsfi.cc
+++ b/base/test/launcher/test_launcher_nacl_nonsfi.cc
@@ -112,8 +112,8 @@
base::CommandLine cmd_line(test_path_);
cmd_line.AppendSwitchPath(
switches::kTestLauncherOutput, output_file);
- cmd_line.AppendSwitchASCII(
- base::kGTestFilterFlag, JoinString(test_names, ":"));
+ cmd_line.AppendSwitchASCII(base::kGTestFilterFlag,
+ base::JoinString(test_names, ":"));
return cmd_line;
}
diff --git a/base/test/test_pending_task_unittest.cc b/base/test/test_pending_task_unittest.cc
index 32502f2..7623ce4 100644
--- a/base/test/test_pending_task_unittest.cc
+++ b/base/test/test_pending_task_unittest.cc
@@ -10,7 +10,7 @@
#include "testing/gtest/include/gtest/gtest-spi.h"
#include "testing/gtest/include/gtest/gtest.h"
-namespace {
+namespace base {
TEST(TestPendingTaskTest, TraceSupport) {
base::TestPendingTask task;
@@ -52,4 +52,4 @@
<< task_first << ".ShouldRunBefore(" << task_after << ")\n";
}
-} // namespace
+} // namespace base
diff --git a/base/test/test_reg_util_win.cc b/base/test/test_reg_util_win.cc
index e3b1ffc..336cc45 100644
--- a/base/test/test_reg_util_win.cc
+++ b/base/test/test_reg_util_win.cc
@@ -7,6 +7,7 @@
#include "base/guid.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -32,8 +33,10 @@
test_key_root.c_str());
for (; iterator_test_root_key.Valid(); ++iterator_test_root_key) {
base::string16 key_name = iterator_test_root_key.Name();
- std::vector<base::string16> tokens;
- if (!Tokenize(key_name, base::string16(kTimestampDelimiter), &tokens))
+ std::vector<base::string16> tokens =
+ base::SplitString(key_name, kTimestampDelimiter, base::KEEP_WHITESPACE,
+ base::SPLIT_WANT_NONEMPTY);
+ if (tokens.empty())
continue;
int64 key_name_as_number = 0;
diff --git a/base/test/test_suite.cc b/base/test/test_suite.cc
index b6fb2ef..aef6906 100644
--- a/base/test/test_suite.cc
+++ b/base/test/test_suite.cc
@@ -332,7 +332,7 @@
i18n::SetICUDefaultLocale("en_US");
#else
std::string default_locale(uloc_getDefault());
- if (EndsWith(default_locale, "POSIX", false))
+ if (EndsWith(default_locale, "POSIX", CompareCase::INSENSITIVE_ASCII))
i18n::SetICUDefaultLocale("en_US");
#endif
#endif
diff --git a/base/test/test_support_ios.mm b/base/test/test_support_ios.mm
index 3b31da6..9e82612 100644
--- a/base/test/test_support_ios.mm
+++ b/base/test/test_support_ios.mm
@@ -80,6 +80,10 @@
label.textAlignment = NSTextAlignmentCenter;
[window_ addSubview:label];
+ // An NSInternalInconsistencyException is thrown if the app doesn't have a
+ // root view controller. Set an empty one here.
+ [window_ setRootViewController:[[[UIViewController alloc] init] autorelease]];
+
if ([self shouldRedirectOutputToFile])
[self redirectOutput];
diff --git a/base/test/trace_event_analyzer.cc b/base/test/trace_event_analyzer.cc
index e46c2a5..93d7f30 100644
--- a/base/test/trace_event_analyzer.cc
+++ b/base/test/trace_event_analyzer.cc
@@ -10,6 +10,7 @@
#include "base/json/json_reader.h"
#include "base/memory/scoped_ptr.h"
+#include "base/strings/pattern.h"
#include "base/values.h"
namespace trace_analyzer {
@@ -321,17 +322,17 @@
switch (operator_) {
case OP_EQ:
if (right().is_pattern_)
- *result = MatchPattern(lhs, rhs);
+ *result = base::MatchPattern(lhs, rhs);
else if (left().is_pattern_)
- *result = MatchPattern(rhs, lhs);
+ *result = base::MatchPattern(rhs, lhs);
else
*result = (lhs == rhs);
return true;
case OP_NE:
if (right().is_pattern_)
- *result = !MatchPattern(lhs, rhs);
+ *result = !base::MatchPattern(lhs, rhs);
else if (left().is_pattern_)
- *result = !MatchPattern(rhs, lhs);
+ *result = !base::MatchPattern(rhs, lhs);
else
*result = (lhs != rhs);
return true;
diff --git a/base/third_party/nspr/prtime.cc b/base/third_party/nspr/prtime.cc
index 9335b01..a7c5a3a 100644
--- a/base/third_party/nspr/prtime.cc
+++ b/base/third_party/nspr/prtime.cc
@@ -107,9 +107,9 @@
static const PRTime kSecondsToMicroseconds = static_cast<PRTime>(1000000);
#if defined(OS_WIN)
// Create the system struct representing our exploded time.
- SYSTEMTIME st = {0};
- FILETIME ft = {0};
- ULARGE_INTEGER uli = {0};
+ SYSTEMTIME st = {};
+ FILETIME ft = {};
+ ULARGE_INTEGER uli = {};
st.wYear = exploded->tm_year;
st.wMonth = static_cast<WORD>(exploded->tm_month + 1);
diff --git a/base/third_party/symbolize/symbolize.cc b/base/third_party/symbolize/symbolize.cc
index b25f747..f4861df 100644
--- a/base/third_party/symbolize/symbolize.cc
+++ b/base/third_party/symbolize/symbolize.cc
@@ -651,7 +651,8 @@
// Handle negative numbers (only for base 10).
if (i < 0 && base == 10) {
- j = -i;
+ // This does "j = -i" while avoiding integer overflow.
+ j = static_cast<uintptr_t>(-(i + 1)) + 1;
// Make sure we can write the '-' character.
if (++n > sz) {
diff --git a/base/threading/platform_thread.h b/base/threading/platform_thread.h
index 3468f45..0b92265 100644
--- a/base/threading/platform_thread.h
+++ b/base/threading/platform_thread.h
@@ -89,6 +89,7 @@
id_(id) {
}
+ // TODO(toyoshim): Remove id() and use PlatformThread::CurrentId() instead.
PlatformThreadId id() const {
return id_;
}
@@ -112,8 +113,8 @@
const PlatformThreadId kInvalidThreadId(0);
-// Valid values for SetThreadPriority(), listed in increasing order of
-// importance.
+// Valid values for priority of Thread::Options and SimpleThread::Options, and
+// SetCurrentThreadPriority(), listed in increasing order of importance.
enum class ThreadPriority {
// Suitable for threads that shouldn't disrupt high priority work.
BACKGROUND,
@@ -176,8 +177,7 @@
PlatformThreadHandle* thread_handle);
// CreateWithPriority() does the same thing as Create() except the priority of
- // the thread is set based on |priority|. Can be used in place of Create()
- // followed by SetThreadPriority().
+ // the thread is set based on |priority|.
static bool CreateWithPriority(size_t stack_size, Delegate* delegate,
PlatformThreadHandle* thread_handle,
ThreadPriority priority);
@@ -192,15 +192,15 @@
// |thread_handle|.
static void Join(PlatformThreadHandle thread_handle);
- // Toggles the target thread's priority at runtime. Prefer
- // CreateWithPriority() to set the thread's initial priority.
- // NOTE: The call may fail if the caller thread is not the same as the
- // target thread on POSIX. For example, seccomp-bpf blocks it by default
- // in the sandbox.
- static void SetThreadPriority(PlatformThreadHandle handle,
- ThreadPriority priority);
+ // Toggles the current thread's priority at runtime. A thread may not be able
+ // to raise its priority back up after lowering it if the process does not
+ // have a proper permission, e.g. CAP_SYS_NICE on Linux.
+ // Since changing other threads' priority is not permitted in favor of
+ // security, this interface is restricted to change only the current thread
+ // priority (https://crbug.com/399473).
+ static void SetCurrentThreadPriority(ThreadPriority priority);
- static ThreadPriority GetThreadPriority(PlatformThreadHandle handle);
+ static ThreadPriority GetCurrentThreadPriority();
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(PlatformThread);
diff --git a/base/threading/platform_thread_android.cc b/base/threading/platform_thread_android.cc
index 11e5e2e..176a6bd 100644
--- a/base/threading/platform_thread_android.cc
+++ b/base/threading/platform_thread_android.cc
@@ -23,29 +23,21 @@
namespace internal {
-// These nice values are taken from Android, which uses nice values like linux,
-// but defines some preset nice values.
-// Process.THREAD_PRIORITY_AUDIO = -16
-// Process.THREAD_PRIORITY_BACKGROUND = 10
-// Process.THREAD_PRIORITY_DEFAULT = 0;
-// Process.THREAD_PRIORITY_DISPLAY = -4;
-// Process.THREAD_PRIORITY_FOREGROUND = -2;
-// Process.THREAD_PRIORITY_LESS_FAVORABLE = 1;
-// Process.THREAD_PRIORITY_LOWEST = 19;
-// Process.THREAD_PRIORITY_MORE_FAVORABLE = -1;
-// Process.THREAD_PRIORITY_URGENT_AUDIO = -19;
-// Process.THREAD_PRIORITY_URGENT_DISPLAY = -8;
-// We use -6 for display, but we may want to split this into urgent (-8) and
-// non-urgent (-4).
+// - BACKGROUND is 9 due to it being the nicest value we can use that's still
+// above an Android system threshold that enables heavy throttling starting at
+// 10; we want to be lower-priority than Chrome's other threads without
+// incurring this behavior.
+// - DISPLAY is -6 due to being midway between Android's DISPLAY (-4) and
+// URGENT_DISPLAY (-8).
+// - REALTIME_AUDIO corresponds to Android's THREAD_PRIORITY_AUDIO = -16 value.
const ThreadPriorityToNiceValuePair kThreadPriorityToNiceValueMap[4] = {
- {ThreadPriority::BACKGROUND, 10},
+ {ThreadPriority::BACKGROUND, 9},
{ThreadPriority::NORMAL, 0},
{ThreadPriority::DISPLAY, -6},
{ThreadPriority::REALTIME_AUDIO, -16},
};
-bool SetThreadPriorityForPlatform(PlatformThreadHandle handle,
- ThreadPriority priority) {
+bool SetCurrentThreadPriorityForPlatform(ThreadPriority priority) {
// On Android, we set the Audio priority through JNI as Audio priority
// will also allow the process to run while it is backgrounded.
if (priority == ThreadPriority::REALTIME_AUDIO) {
@@ -56,8 +48,8 @@
return false;
}
-bool GetThreadPriorityForPlatform(PlatformThreadHandle handle,
- ThreadPriority* priority) {
+bool GetCurrentThreadPriorityForPlatform(ThreadPriority* priority) {
+ // See http://crbug.com/505474.
NOTIMPLEMENTED();
return false;
}
@@ -88,8 +80,7 @@
void InitOnThread() {
// Threads on linux/android may inherit their priority from the thread
// where they were created. This sets all new threads to the default.
- PlatformThread::SetThreadPriority(PlatformThread::CurrentHandle(),
- ThreadPriority::NORMAL);
+ PlatformThread::SetCurrentThreadPriority(ThreadPriority::NORMAL);
}
void TerminateOnThread() {
diff --git a/base/threading/platform_thread_freebsd.cc b/base/threading/platform_thread_freebsd.cc
index f4fded0..e29e865 100644
--- a/base/threading/platform_thread_freebsd.cc
+++ b/base/threading/platform_thread_freebsd.cc
@@ -36,11 +36,8 @@
{ThreadPriority::REALTIME_AUDIO, -10},
}
-bool SetThreadPriorityForPlatform(PlatformThreadHandle handle,
- ThreadPriority priority) {
+bool SetCurrentThreadPriorityForPlatform(ThreadPriority priority) {
#if !defined(OS_NACL)
- // TODO(gab): Assess the correctness of using |pthread_self()| below instead
- // of |handle|. http://crbug.com/468793.
return priority == ThreadPriority::REALTIME_AUDIO &&
pthread_setschedparam(pthread_self(), SCHED_RR, &kRealTimePrio) == 0;
#else
@@ -48,11 +45,8 @@
#endif
}
-bool GetThreadPriorityForPlatform(PlatformThreadHandle handle,
- ThreadPriority* priority) {
+bool GetCurrentThreadPriorityForPlatform(ThreadPriority* priority) {
#if !defined(OS_NACL)
- // TODO(gab): Assess the correctness of using |pthread_self()| below instead
- // of |handle|. http://crbug.com/468793.
int maybe_sched_rr = 0;
struct sched_param maybe_realtime_prio = {0};
if (pthread_getschedparam(pthread_self(), &maybe_sched_rr,
diff --git a/base/threading/platform_thread_internal_posix.h b/base/threading/platform_thread_internal_posix.h
index 62006ce..05a8d1e 100644
--- a/base/threading/platform_thread_internal_posix.h
+++ b/base/threading/platform_thread_internal_posix.h
@@ -26,17 +26,15 @@
ThreadPriority NiceValueToThreadPriority(int nice_value);
// Allows platform specific tweaks to the generic POSIX solution for
-// SetThreadPriority. Returns true if the platform-specific implementation
-// handled this |priority| change, false if the generic implementation should
-// instead proceed.
-bool SetThreadPriorityForPlatform(PlatformThreadHandle handle,
- ThreadPriority priority);
+// SetCurrentThreadPriority. Returns true if the platform-specific
+// implementation handled this |priority| change, false if the generic
+// implementation should instead proceed.
+bool SetCurrentThreadPriorityForPlatform(ThreadPriority priority);
-// Returns true if there is a platform-specific ThreadPriority set on |handle|
-// (and returns the actual ThreadPriority via |priority|). Returns false
-// otherwise, leaving |priority| untouched.
-bool GetThreadPriorityForPlatform(PlatformThreadHandle handle,
- ThreadPriority* priority);
+// Returns true if there is a platform-specific ThreadPriority set on the
+// current thread (and returns the actual ThreadPriority via |priority|).
+// Returns false otherwise, leaving |priority| untouched.
+bool GetCurrentThreadPriorityForPlatform(ThreadPriority* priority);
} // namespace internal
diff --git a/base/threading/platform_thread_linux.cc b/base/threading/platform_thread_linux.cc
index 9f74374..48cf744 100644
--- a/base/threading/platform_thread_linux.cc
+++ b/base/threading/platform_thread_linux.cc
@@ -27,6 +27,7 @@
namespace {
#if !defined(OS_NACL)
const struct sched_param kRealTimePrio = {8};
+const struct sched_param kResetPrio = {0};
#endif
} // namespace
@@ -37,11 +38,18 @@
{ThreadPriority::REALTIME_AUDIO, -10},
};
-bool SetThreadPriorityForPlatform(PlatformThreadHandle handle,
- ThreadPriority priority) {
+bool SetCurrentThreadPriorityForPlatform(ThreadPriority priority) {
#if !defined(OS_NACL)
- // TODO(gab): Assess the correctness of using |pthread_self()| below instead
- // of |handle|. http://crbug.com/468793.
+ ThreadPriority current_priority;
+ if (priority != ThreadPriority::REALTIME_AUDIO &&
+ GetCurrentThreadPriorityForPlatform(¤t_priority) &&
+ current_priority == ThreadPriority::REALTIME_AUDIO) {
+ // If the pthread's round-robin scheduler is already enabled, and the new
+ // priority will use setpriority() instead, the pthread scheduler should be
+ // reset to use SCHED_OTHER so that setpriority() just works.
+ pthread_setschedparam(pthread_self(), SCHED_OTHER, &kResetPrio);
+ return false;
+ }
return priority == ThreadPriority::REALTIME_AUDIO &&
pthread_setschedparam(pthread_self(), SCHED_RR, &kRealTimePrio) == 0;
#else
@@ -49,13 +57,10 @@
#endif
}
-bool GetThreadPriorityForPlatform(PlatformThreadHandle handle,
- ThreadPriority* priority) {
+bool GetCurrentThreadPriorityForPlatform(ThreadPriority* priority) {
#if !defined(OS_NACL)
int maybe_sched_rr = 0;
struct sched_param maybe_realtime_prio = {0};
- // TODO(gab): Assess the correctness of using |pthread_self()| below instead
- // of |handle|. http://crbug.com/468793.
if (pthread_getschedparam(pthread_self(), &maybe_sched_rr,
&maybe_realtime_prio) == 0 &&
maybe_sched_rr == SCHED_RR &&
diff --git a/base/threading/platform_thread_mac.mm b/base/threading/platform_thread_mac.mm
index 813cae2..1ecbcd6 100644
--- a/base/threading/platform_thread_mac.mm
+++ b/base/threading/platform_thread_mac.mm
@@ -155,10 +155,10 @@
} // anonymous namespace
// static
-void PlatformThread::SetThreadPriority(PlatformThreadHandle handle,
- ThreadPriority priority) {
+void PlatformThread::SetCurrentThreadPriority(ThreadPriority priority) {
// Convert from pthread_t to mach thread identifier.
- mach_port_t mach_thread_id = pthread_mach_thread_np(handle.platform_handle());
+ mach_port_t mach_thread_id =
+ pthread_mach_thread_np(PlatformThread::CurrentHandle().platform_handle());
switch (priority) {
case ThreadPriority::NORMAL:
@@ -174,7 +174,7 @@
}
// static
-ThreadPriority PlatformThread::GetThreadPriority(PlatformThreadHandle handle) {
+ThreadPriority PlatformThread::GetCurrentThreadPriority() {
NOTIMPLEMENTED();
return ThreadPriority::NORMAL;
}
diff --git a/base/threading/platform_thread_posix.cc b/base/threading/platform_thread_posix.cc
index 0d821a9..f3a835e 100644
--- a/base/threading/platform_thread_posix.cc
+++ b/base/threading/platform_thread_posix.cc
@@ -58,10 +58,8 @@
if (!thread_params->joinable)
base::ThreadRestrictions::SetSingletonAllowed(false);
- if (thread_params->priority != ThreadPriority::NORMAL) {
- PlatformThread::SetThreadPriority(PlatformThread::CurrentHandle(),
- thread_params->priority);
- }
+ if (thread_params->priority != ThreadPriority::NORMAL)
+ PlatformThread::SetCurrentThreadPriority(thread_params->priority);
// Stash the id in the handle so the calling thread has a complete
// handle, and unblock the parent thread.
@@ -231,16 +229,15 @@
CHECK_EQ(0, pthread_join(thread_handle.platform_handle(), NULL));
}
-// Mac has its own Set/GetThreadPriority() implementations.
+// Mac has its own Set/GetCurrentThreadPriority() implementations.
#if !defined(OS_MACOSX)
// static
-void PlatformThread::SetThreadPriority(PlatformThreadHandle handle,
- ThreadPriority priority) {
+void PlatformThread::SetCurrentThreadPriority(ThreadPriority priority) {
#if defined(OS_NACL)
NOTIMPLEMENTED();
#else
- if (internal::SetThreadPriorityForPlatform(handle, priority))
+ if (internal::SetCurrentThreadPriorityForPlatform(priority))
return;
// setpriority(2) should change the whole thread group's (i.e. process)
@@ -249,39 +246,34 @@
// Linux/NPTL implementation of POSIX threads, the nice value is a per-thread
// attribute". Also, 0 is prefered to the current thread id since it is
// equivalent but makes sandboxing easier (https://crbug.com/399473).
- DCHECK_NE(handle.id(), kInvalidThreadId);
const int nice_setting = internal::ThreadPriorityToNiceValue(priority);
- const PlatformThreadId current_id = PlatformThread::CurrentId();
- if (setpriority(PRIO_PROCESS, handle.id() == current_id ? 0 : handle.id(),
- nice_setting)) {
- DVPLOG(1) << "Failed to set nice value of thread (" << handle.id()
- << ") to " << nice_setting;
+ if (setpriority(PRIO_PROCESS, 0, nice_setting)) {
+ DVPLOG(1) << "Failed to set nice value of thread ("
+ << PlatformThread::CurrentId() << ") to " << nice_setting;
}
#endif // defined(OS_NACL)
}
// static
-ThreadPriority PlatformThread::GetThreadPriority(PlatformThreadHandle handle) {
+ThreadPriority PlatformThread::GetCurrentThreadPriority() {
#if defined(OS_NACL)
NOTIMPLEMENTED();
return ThreadPriority::NORMAL;
#else
- // Mirrors SetThreadPriority()'s implementation.
+ // Mirrors SetCurrentThreadPriority()'s implementation.
ThreadPriority platform_specific_priority;
- if (internal::GetThreadPriorityForPlatform(handle,
- &platform_specific_priority)) {
+ if (internal::GetCurrentThreadPriorityForPlatform(
+ &platform_specific_priority)) {
return platform_specific_priority;
}
- DCHECK_NE(handle.id(), kInvalidThreadId);
- const PlatformThreadId current_id = PlatformThread::CurrentId();
// Need to clear errno before calling getpriority():
// http://man7.org/linux/man-pages/man2/getpriority.2.html
errno = 0;
- int nice_value =
- getpriority(PRIO_PROCESS, handle.id() == current_id ? 0 : handle.id());
+ int nice_value = getpriority(PRIO_PROCESS, 0);
if (errno != 0) {
- DVPLOG(1) << "Failed to get nice value of thread (" << handle.id() << ")";
+ DVPLOG(1) << "Failed to get nice value of thread ("
+ << PlatformThread::CurrentId() << ")";
return ThreadPriority::NORMAL;
}
diff --git a/base/threading/platform_thread_unittest.cc b/base/threading/platform_thread_unittest.cc
index c4b3d5d..0fd9bc4 100644
--- a/base/threading/platform_thread_unittest.cc
+++ b/base/threading/platform_thread_unittest.cc
@@ -8,7 +8,10 @@
#include "base/threading/platform_thread.h"
#include "testing/gtest/include/gtest/gtest.h"
-#if defined(OS_WIN)
+#if defined(OS_POSIX)
+#include <sys/types.h>
+#include <unistd.h>
+#elif defined(OS_WIN)
#include <windows.h>
#endif
@@ -16,6 +19,8 @@
// Trivial tests that thread runs and doesn't crash on create and join ---------
+namespace {
+
class TrivialThread : public PlatformThread::Delegate {
public:
TrivialThread() : did_run_(false) {}
@@ -30,6 +35,8 @@
DISALLOW_COPY_AND_ASSIGN(TrivialThread);
};
+} // namespace
+
TEST(PlatformThreadTest, Trivial) {
TrivialThread thread;
PlatformThreadHandle handle;
@@ -56,11 +63,13 @@
// Tests of basic thread functions ---------------------------------------------
+namespace {
+
class FunctionTestThread : public PlatformThread::Delegate {
public:
FunctionTestThread()
: thread_id_(kInvalidThreadId),
- thread_started_(true, false),
+ termination_ready_(true, false),
terminate_thread_(true, false),
done_(false) {}
~FunctionTestThread() override {
@@ -70,8 +79,9 @@
<< "WaitableEvent blocking the underlying thread's main.";
}
- // Grabs |thread_id_|, signals |thread_started_|, and then waits for
- // |terminate_thread_| to be signaled before exiting.
+ // Grabs |thread_id_|, runs an optional test on that thread, signals
+ // |termination_ready_|, and then waits for |terminate_thread_| to be
+ // signaled before exiting.
void ThreadMain() override {
thread_id_ = PlatformThread::CurrentId();
EXPECT_NE(thread_id_, kInvalidThreadId);
@@ -79,39 +89,44 @@
// Make sure that the thread ID is the same across calls.
EXPECT_EQ(thread_id_, PlatformThread::CurrentId());
- thread_started_.Signal();
+ // Run extra tests.
+ RunTest();
+ termination_ready_.Signal();
terminate_thread_.Wait();
done_ = true;
}
PlatformThreadId thread_id() const {
- EXPECT_TRUE(thread_started_.IsSignaled()) << "Thread ID still unknown";
+ EXPECT_TRUE(termination_ready_.IsSignaled()) << "Thread ID still unknown";
return thread_id_;
}
- bool IsRunning() const {
- return thread_started_.IsSignaled() && !done_;
- }
+ bool IsRunning() const { return termination_ready_.IsSignaled() && !done_; }
- // Blocks until this thread is started.
- void WaitForThreadStart() { thread_started_.Wait(); }
+ // Blocks until this thread is started and ready to be terminated.
+ void WaitForTerminationReady() { termination_ready_.Wait(); }
- // Mark this thread for termination (callers must then join this thread to be
+ // Marks this thread for termination (callers must then join this thread to be
// guaranteed of termination).
void MarkForTermination() { terminate_thread_.Signal(); }
private:
+ // Runs an optional test on the newly created thread.
+ virtual void RunTest() {}
+
PlatformThreadId thread_id_;
- mutable WaitableEvent thread_started_;
+ mutable WaitableEvent termination_ready_;
WaitableEvent terminate_thread_;
bool done_;
DISALLOW_COPY_AND_ASSIGN(FunctionTestThread);
};
+} // namespace
+
TEST(PlatformThreadTest, Function) {
PlatformThreadId main_thread_id = PlatformThread::CurrentId();
@@ -120,7 +135,7 @@
ASSERT_FALSE(thread.IsRunning());
ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle));
- thread.WaitForThreadStart();
+ thread.WaitForTerminationReady();
ASSERT_TRUE(thread.IsRunning());
EXPECT_NE(thread.thread_id(), main_thread_id);
@@ -144,7 +159,7 @@
for (size_t n = 0; n < arraysize(thread); n++)
ASSERT_TRUE(PlatformThread::Create(0, &thread[n], &handle[n]));
for (size_t n = 0; n < arraysize(thread); n++)
- thread[n].WaitForThreadStart();
+ thread[n].WaitForTerminationReady();
for (size_t n = 0; n < arraysize(thread); n++) {
ASSERT_TRUE(thread[n].IsRunning());
@@ -170,106 +185,85 @@
namespace {
const ThreadPriority kThreadPriorityTestValues[] = {
-// Disable non-normal priority toggling on POSIX as it appears to be broken
-// (http://crbug.com/468793). This is prefered to disabling the tests altogether
-// on POSIX as it at least provides coverage for running this code under
-// "normal" priority.
-#if !defined(OS_POSIX)
- ThreadPriority::DISPLAY,
+// The order should be higher to lower to cover as much cases as possible on
+// Linux trybots running without CAP_SYS_NICE permission.
+#if !defined(OS_ANDROID)
+ // PlatformThread::GetCurrentThreadPriority() on Android does not support
+ // REALTIME_AUDIO case. See http://crbug.com/505474.
ThreadPriority::REALTIME_AUDIO,
- // Keep BACKGROUND second to last to test backgrounding from other
- // priorities.
- ThreadPriority::BACKGROUND,
-#endif // !defined(OS_POSIX)
- // Keep NORMAL last to test unbackgrounding.
- ThreadPriority::NORMAL
+#endif
+ ThreadPriority::DISPLAY,
+ // This redundant BACKGROUND priority is to test backgrounding from other
+ // priorities, and unbackgrounding.
+ ThreadPriority::BACKGROUND, ThreadPriority::NORMAL,
+ ThreadPriority::BACKGROUND};
+
+bool IsBumpingPriorityAllowed() {
+#if defined(OS_POSIX)
+ // Only root can raise thread priority on POSIX environment. On Linux, users
+ // who have CAP_SYS_NICE permission also can raise the thread priority, but
+ // libcap.so would be needed to check the capability.
+ return geteuid() == 0;
+#else
+ return true;
+#endif
+}
+
+class ThreadPriorityTestThread : public FunctionTestThread {
+ public:
+ ThreadPriorityTestThread() = default;
+ ~ThreadPriorityTestThread() override = default;
+
+ private:
+ void RunTest() override {
+ // Confirm that the current thread's priority is as expected.
+ EXPECT_EQ(ThreadPriority::NORMAL,
+ PlatformThread::GetCurrentThreadPriority());
+
+ // Toggle each supported priority on the current thread and confirm it
+ // affects it.
+ const bool bumping_priority_allowed = IsBumpingPriorityAllowed();
+ for (size_t i = 0; i < arraysize(kThreadPriorityTestValues); ++i) {
+ SCOPED_TRACE(i);
+ if (!bumping_priority_allowed &&
+ kThreadPriorityTestValues[i] >
+ PlatformThread::GetCurrentThreadPriority()) {
+ continue;
+ }
+
+ // Alter and verify the current thread's priority.
+ PlatformThread::SetCurrentThreadPriority(kThreadPriorityTestValues[i]);
+ EXPECT_EQ(kThreadPriorityTestValues[i],
+ PlatformThread::GetCurrentThreadPriority());
+ }
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadPriorityTestThread);
};
} // namespace
-// Test changing another thread's priority.
-// NOTE: This test is partially disabled on POSIX, see note above and
-// http://crbug.com/468793.
-TEST(PlatformThreadTest, ThreadPriorityOtherThread) {
- PlatformThreadHandle current_handle(PlatformThread::CurrentHandle());
+#if defined(OS_MACOSX)
+// PlatformThread::GetCurrentThreadPriority() is not implemented on OS X.
+#define MAYBE_ThreadPriorityCurrentThread DISABLED_ThreadPriorityCurrentThread
+#else
+#define MAYBE_ThreadPriorityCurrentThread ThreadPriorityCurrentThread
+#endif
- // Confirm that the current thread's priority is as expected.
- EXPECT_EQ(ThreadPriority::NORMAL,
- PlatformThread::GetThreadPriority(current_handle));
-
- // Create a test thread.
- FunctionTestThread thread;
- PlatformThreadHandle handle;
- ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle));
- thread.WaitForThreadStart();
- EXPECT_NE(thread.thread_id(), kInvalidThreadId);
- EXPECT_NE(thread.thread_id(), PlatformThread::CurrentId());
-
- // New threads should get normal priority by default.
- EXPECT_EQ(ThreadPriority::NORMAL, PlatformThread::GetThreadPriority(handle));
-
- // Toggle each supported priority on the test thread and confirm it only
- // affects it (and not the current thread).
- for (size_t i = 0; i < arraysize(kThreadPriorityTestValues); ++i) {
- SCOPED_TRACE(i);
-
- // Alter and verify the test thread's priority.
- PlatformThread::SetThreadPriority(handle, kThreadPriorityTestValues[i]);
- EXPECT_EQ(kThreadPriorityTestValues[i],
- PlatformThread::GetThreadPriority(handle));
-
- // Make sure the current thread was otherwise unaffected.
- EXPECT_EQ(ThreadPriority::NORMAL,
- PlatformThread::GetThreadPriority(current_handle));
- }
-
- thread.MarkForTermination();
- PlatformThread::Join(handle);
-}
-
-// Test changing the current thread's priority (which has different semantics on
+// Test changing a created thread's priority (which has different semantics on
// some platforms).
-// NOTE: This test is partially disabled on POSIX, see note above and
-// http://crbug.com/468793.
-TEST(PlatformThreadTest, ThreadPriorityCurrentThread) {
- PlatformThreadHandle current_handle(PlatformThread::CurrentHandle());
-
- // Confirm that the current thread's priority is as expected.
- EXPECT_EQ(ThreadPriority::NORMAL,
- PlatformThread::GetThreadPriority(current_handle));
-
- // Create a test thread for verification purposes only.
- FunctionTestThread thread;
+TEST(PlatformThreadTest, MAYBE_ThreadPriorityCurrentThread) {
+ ThreadPriorityTestThread thread;
PlatformThreadHandle handle;
+
+ ASSERT_FALSE(thread.IsRunning());
ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle));
- thread.WaitForThreadStart();
- EXPECT_NE(thread.thread_id(), kInvalidThreadId);
- EXPECT_NE(thread.thread_id(), PlatformThread::CurrentId());
-
- // Confirm that the new thread's priority is as expected.
- EXPECT_EQ(ThreadPriority::NORMAL, PlatformThread::GetThreadPriority(handle));
-
- // Toggle each supported priority on the current thread and confirm it only
- // affects it (and not the test thread).
- for (size_t i = 0; i < arraysize(kThreadPriorityTestValues); ++i) {
- SCOPED_TRACE(i);
-
- // Alter and verify the current thread's priority.
- PlatformThread::SetThreadPriority(current_handle,
- kThreadPriorityTestValues[i]);
- EXPECT_EQ(kThreadPriorityTestValues[i],
- PlatformThread::GetThreadPriority(current_handle));
-
- // Make sure the test thread was otherwise unaffected.
- EXPECT_EQ(ThreadPriority::NORMAL,
- PlatformThread::GetThreadPriority(handle));
- }
-
- // Restore current thread priority for follow-up tests.
- PlatformThread::SetThreadPriority(current_handle, ThreadPriority::NORMAL);
+ thread.WaitForTerminationReady();
+ ASSERT_TRUE(thread.IsRunning());
thread.MarkForTermination();
PlatformThread::Join(handle);
+ ASSERT_FALSE(thread.IsRunning());
}
} // namespace base
diff --git a/base/threading/platform_thread_win.cc b/base/threading/platform_thread_win.cc
index 395fc9e..be3f410 100644
--- a/base/threading/platform_thread_win.cc
+++ b/base/threading/platform_thread_win.cc
@@ -46,6 +46,7 @@
struct ThreadParams {
PlatformThread::Delegate* delegate;
bool joinable;
+ ThreadPriority priority;
};
DWORD __stdcall ThreadFunc(void* params) {
@@ -54,6 +55,9 @@
if (!thread_params->joinable)
base::ThreadRestrictions::SetSingletonAllowed(false);
+ if (thread_params->priority != ThreadPriority::NORMAL)
+ PlatformThread::SetCurrentThreadPriority(thread_params->priority);
+
// Retrieve a copy of the thread handle to use as the key in the
// thread name mapping.
PlatformThreadHandle::Handle platform_handle;
@@ -86,12 +90,13 @@
return NULL;
}
-// CreateThreadInternal() matches PlatformThread::Create(), except that
-// |out_thread_handle| may be NULL, in which case a non-joinable thread is
+// CreateThreadInternal() matches PlatformThread::CreateWithPriority(), except
+// that |out_thread_handle| may be NULL, in which case a non-joinable thread is
// created.
bool CreateThreadInternal(size_t stack_size,
PlatformThread::Delegate* delegate,
- PlatformThreadHandle* out_thread_handle) {
+ PlatformThreadHandle* out_thread_handle,
+ ThreadPriority priority) {
unsigned int flags = 0;
if (stack_size > 0 && base::win::GetVersion() >= base::win::VERSION_XP) {
flags = STACK_SIZE_PARAM_IS_A_RESERVATION;
@@ -102,6 +107,7 @@
ThreadParams* params = new ThreadParams;
params->delegate = delegate;
params->joinable = out_thread_handle != NULL;
+ params->priority = priority;
// Using CreateThread here vs _beginthreadex makes thread creation a bit
// faster and doesn't require the loader lock to be available. Our code will
@@ -185,23 +191,22 @@
// static
bool PlatformThread::Create(size_t stack_size, Delegate* delegate,
PlatformThreadHandle* thread_handle) {
- DCHECK(thread_handle);
- return CreateThreadInternal(stack_size, delegate, thread_handle);
+ return CreateWithPriority(stack_size, delegate, thread_handle,
+ ThreadPriority::NORMAL);
}
// static
bool PlatformThread::CreateWithPriority(size_t stack_size, Delegate* delegate,
PlatformThreadHandle* thread_handle,
ThreadPriority priority) {
- bool result = Create(stack_size, delegate, thread_handle);
- if (result)
- SetThreadPriority(*thread_handle, priority);
- return result;
+ DCHECK(thread_handle);
+ return CreateThreadInternal(stack_size, delegate, thread_handle, priority);
}
// static
bool PlatformThread::CreateNonJoinable(size_t stack_size, Delegate* delegate) {
- return CreateThreadInternal(stack_size, delegate, NULL);
+ return CreateThreadInternal(stack_size, delegate, NULL,
+ ThreadPriority::NORMAL);
}
// static
@@ -231,10 +236,7 @@
}
// static
-void PlatformThread::SetThreadPriority(PlatformThreadHandle handle,
- ThreadPriority priority) {
- DCHECK(!handle.is_null());
-
+void PlatformThread::SetCurrentThreadPriority(ThreadPriority priority) {
int desired_priority = THREAD_PRIORITY_ERROR_RETURN;
switch (priority) {
case ThreadPriority::BACKGROUND:
@@ -258,16 +260,16 @@
#ifndef NDEBUG
const BOOL success =
#endif
- ::SetThreadPriority(handle.platform_handle(), desired_priority);
+ ::SetThreadPriority(PlatformThread::CurrentHandle().platform_handle(),
+ desired_priority);
DPLOG_IF(ERROR, !success) << "Failed to set thread priority to "
<< desired_priority;
}
// static
-ThreadPriority PlatformThread::GetThreadPriority(PlatformThreadHandle handle) {
- DCHECK(!handle.is_null());
-
- int priority = ::GetThreadPriority(handle.platform_handle());
+ThreadPriority PlatformThread::GetCurrentThreadPriority() {
+ int priority =
+ ::GetThreadPriority(PlatformThread::CurrentHandle().platform_handle());
switch (priority) {
case THREAD_PRIORITY_LOWEST:
return ThreadPriority::BACKGROUND;
diff --git a/base/threading/post_task_and_reply_impl.h b/base/threading/post_task_and_reply_impl.h
index a5b9580..d21ab78 100644
--- a/base/threading/post_task_and_reply_impl.h
+++ b/base/threading/post_task_and_reply_impl.h
@@ -25,6 +25,8 @@
// may want base::WorkerPool.
class PostTaskAndReplyImpl {
public:
+ virtual ~PostTaskAndReplyImpl() = default;
+
// Implementation for TaskRunner::PostTaskAndReply and
// WorkerPool::PostTaskAndReply.
bool PostTaskAndReply(const tracked_objects::Location& from_here,
diff --git a/base/threading/simple_thread.h b/base/threading/simple_thread.h
index 36548d3..2f0eb4d 100644
--- a/base/threading/simple_thread.h
+++ b/base/threading/simple_thread.h
@@ -59,8 +59,10 @@
public:
class BASE_EXPORT Options {
public:
- Options() : stack_size_(0), priority_(ThreadPriority::NORMAL) { }
- ~Options() { }
+ Options() : stack_size_(0), priority_(ThreadPriority::NORMAL) {}
+ explicit Options(ThreadPriority priority)
+ : stack_size_(0), priority_(priority) {}
+ ~Options() {}
// We use the standard compiler-supplied copy constructor.
@@ -109,12 +111,6 @@
// Overridden from PlatformThread::Delegate:
void ThreadMain() override;
- // Only set priorities with a careful understanding of the consequences.
- // This is meant for very limited use cases.
- void SetThreadPriority(ThreadPriority priority) {
- PlatformThread::SetThreadPriority(thread_, priority);
- }
-
private:
const std::string name_prefix_;
std::string name_;
diff --git a/base/threading/thread.cc b/base/threading/thread.cc
index 63b07cb..9c4535b 100644
--- a/base/threading/thread.cc
+++ b/base/threading/thread.cc
@@ -94,8 +94,9 @@
type = MessageLoop::TYPE_CUSTOM;
message_loop_timer_slack_ = options.timer_slack;
- message_loop_ = new MessageLoop(type, options.message_pump_factory);
-
+ scoped_ptr<MessageLoop> message_loop =
+ MessageLoop::CreateUnbound(type, options.message_pump_factory);
+ message_loop_ = message_loop.get();
start_event_.reset(new WaitableEvent(false, false));
// Hold the thread_lock_ while starting a new thread, so that we can make sure
@@ -111,13 +112,16 @@
}
if (!created) {
DLOG(ERROR) << "failed to create thread";
- delete message_loop_;
message_loop_ = nullptr;
start_event_.reset();
return false;
}
}
+ // The ownership of message_loop is managemed by the newly created thread
+ // within the ThreadMain.
+ ignore_result(message_loop.release());
+
DCHECK(message_loop_);
return true;
}
@@ -190,13 +194,6 @@
return running_;
}
-void Thread::SetPriority(ThreadPriority priority) {
- // The thread must be started (and id known) for this to be
- // compatible with all platforms.
- DCHECK(message_loop_ != nullptr);
- PlatformThread::SetThreadPriority(thread_, priority);
-}
-
void Thread::Run(MessageLoop* message_loop) {
message_loop->Run();
}
diff --git a/base/threading/thread.h b/base/threading/thread.h
index 0bd5d12..5126491 100644
--- a/base/threading/thread.h
+++ b/base/threading/thread.h
@@ -155,25 +155,13 @@
//
MessageLoop* message_loop() const { return message_loop_; }
- // Returns a MessageLoopProxy for this thread. Use the MessageLoopProxy's
- // PostTask methods to execute code on the thread. Returns NULL if the thread
- // is not running (e.g. before Start or after Stop have been called). Callers
- // can hold on to this even after the thread is gone; in this situation,
- // attempts to PostTask() will fail.
- //
- // Note: This method is deprecated. Callers should call task_runner() instead
- // and use the TaskRunner interfaces for safely interfacing with the Thread.
- scoped_refptr<MessageLoopProxy> message_loop_proxy() const {
- return message_loop_ ? message_loop_->message_loop_proxy() : NULL;
- }
-
// Returns a TaskRunner for this thread. Use the TaskRunner's PostTask
// methods to execute code on the thread. Returns NULL if the thread is not
// running (e.g. before Start or after Stop have been called). Callers can
// hold on to this even after the thread is gone; in this situation, attempts
// to PostTask() will fail.
scoped_refptr<SingleThreadTaskRunner> task_runner() const {
- return message_loop_->task_runner();
+ return message_loop_ ? message_loop_->task_runner() : nullptr;
}
// Returns the name of this thread (for display in debugger too).
@@ -188,9 +176,6 @@
// Returns true if the thread has been started, and not yet stopped.
bool IsRunning() const;
- // Sets the thread priority. The thread must already be started.
- void SetPriority(ThreadPriority priority);
-
protected:
// Called just prior to starting the message loop
virtual void Init() {}
diff --git a/base/threading/thread_restrictions.h b/base/threading/thread_restrictions.h
index 54f50eb..6f3d705 100644
--- a/base/threading/thread_restrictions.h
+++ b/base/threading/thread_restrictions.h
@@ -58,6 +58,7 @@
}
}
namespace net {
+class NetworkChangeNotifierMac;
namespace internal {
class AddressTrackerLinux;
}
@@ -206,6 +207,7 @@
friend class disk_cache::BackendImpl; // http://crbug.com/74623
friend class disk_cache::InFlightIO; // http://crbug.com/74623
friend class net::internal::AddressTrackerLinux; // http://crbug.com/125097
+ friend class net::NetworkChangeNotifierMac; // http://crbug.com/125097
friend class ::BrowserProcessImpl; // http://crbug.com/125207
friend class ::NativeBackendKWallet; // http://crbug.com/125331
// END USAGE THAT NEEDS TO BE FIXED.
diff --git a/base/threading/thread_unittest.cc b/base/threading/thread_unittest.cc
index e86c758..3c35416 100644
--- a/base/threading/thread_unittest.cc
+++ b/base/threading/thread_unittest.cc
@@ -234,3 +234,8 @@
EXPECT_EQ(THREAD_EVENT_CLEANUP, captured_events[1]);
EXPECT_EQ(THREAD_EVENT_MESSAGE_LOOP_DESTROYED, captured_events[2]);
}
+
+TEST_F(ThreadTest, ThreadNotStarted) {
+ Thread a("Inert");
+ EXPECT_EQ(nullptr, a.task_runner());
+}
diff --git a/base/threading/worker_pool.cc b/base/threading/worker_pool.cc
index bc016ce..71b1a2b 100644
--- a/base/threading/worker_pool.cc
+++ b/base/threading/worker_pool.cc
@@ -21,6 +21,7 @@
explicit PostTaskAndReplyWorkerPool(bool task_is_slow)
: task_is_slow_(task_is_slow) {
}
+ ~PostTaskAndReplyWorkerPool() override = default;
private:
bool PostTask(const tracked_objects::Location& from_here,
diff --git a/base/time/time_posix.cc b/base/time/time_posix.cc
index 7826fc6..fc82c62 100644
--- a/base/time/time_posix.cc
+++ b/base/time/time_posix.cc
@@ -17,7 +17,6 @@
#include "base/basictypes.h"
#include "base/logging.h"
-#include "base/port.h"
#include "build/build_config.h"
#if defined(OS_ANDROID)
diff --git a/base/time/time_win.cc b/base/time/time_win.cc
index 9144483..e904460 100644
--- a/base/time/time_win.cc
+++ b/base/time/time_win.cc
@@ -428,7 +428,7 @@
}
void InitializeNowFunctionPointers() {
- LARGE_INTEGER ticks_per_sec = {0};
+ LARGE_INTEGER ticks_per_sec = {};
if (!QueryPerformanceFrequency(&ticks_per_sec))
ticks_per_sec.QuadPart = 0;
diff --git a/base/trace_event/BUILD.gn b/base/trace_event/BUILD.gn
index e6392e2..663d7ba 100644
--- a/base/trace_event/BUILD.gn
+++ b/base/trace_event/BUILD.gn
@@ -13,6 +13,7 @@
"memory_dump_manager.cc",
"memory_dump_manager.h",
"memory_dump_provider.h",
+ "memory_dump_request_args.cc",
"memory_dump_request_args.h",
"memory_dump_session_state.cc",
"memory_dump_session_state.h",
@@ -39,6 +40,8 @@
"trace_event_impl_constants.cc",
"trace_event_memory.cc",
"trace_event_memory.h",
+ "trace_event_memory_overhead.cc",
+ "trace_event_memory_overhead.h",
"trace_event_synthetic_delay.cc",
"trace_event_synthetic_delay.h",
"trace_event_system_stats_monitor.cc",
diff --git a/base/trace_event/malloc_dump_provider.cc b/base/trace_event/malloc_dump_provider.cc
index 92d513f..4304f28 100644
--- a/base/trace_event/malloc_dump_provider.cc
+++ b/base/trace_event/malloc_dump_provider.cc
@@ -32,14 +32,13 @@
struct mallinfo info = mallinfo();
DCHECK_GE(info.arena + info.hblkhd, info.uordblks);
- // When the system allocator is implemented by tcmalloc, the total physical
+ // When the system allocator is implemented by tcmalloc, the total heap
// size is given by |arena| and |hblkhd| is 0. In case of Android's jemalloc
// |arena| is 0 and the outer pages size is reported by |hblkhd|. In case of
// dlmalloc the total is given by |arena| + |hblkhd|.
// For more details see link: http://goo.gl/fMR8lF.
MemoryAllocatorDump* outer_dump = pmd->CreateAllocatorDump("malloc");
- outer_dump->AddScalar(MemoryAllocatorDump::kNameSize,
- MemoryAllocatorDump::kUnitsBytes,
+ outer_dump->AddScalar("heap_virtual_size", MemoryAllocatorDump::kUnitsBytes,
info.arena + info.hblkhd);
// Total allocated space is given by |uordblks|.
diff --git a/base/trace_event/memory_allocator_dump.cc b/base/trace_event/memory_allocator_dump.cc
index 5d11bb0..4037f94 100644
--- a/base/trace_event/memory_allocator_dump.cc
+++ b/base/trace_event/memory_allocator_dump.cc
@@ -15,28 +15,7 @@
namespace base {
namespace trace_event {
-namespace {
-// Returns the c-string pointer from a dictionary value without performing extra
-// std::string copies. The ptr will be valid as long as the value exists.
-bool GetDictionaryValueAsCStr(const DictionaryValue* dict_value,
- const std::string& key,
- const char** out_cstr) {
- const Value* value = nullptr;
- const StringValue* str_value = nullptr;
- if (!dict_value->GetWithoutPathExpansion(key, &value))
- return false;
- if (!value->GetAsString(&str_value))
- return false;
- *out_cstr = str_value->GetString().c_str();
- return true;
-}
-} // namespace
-
-// TODO(primiano): remove kName{Inner,Outer}Size below after all the existing
-// dump providers have been rewritten.
const char MemoryAllocatorDump::kNameSize[] = "size";
-const char MemoryAllocatorDump::kNameInnerSize[] = "inner_size";
-const char MemoryAllocatorDump::kNameOuterSize[] = "outer_size";
const char MemoryAllocatorDump::kNameObjectsCount[] = "objects_count";
const char MemoryAllocatorDump::kTypeScalar[] = "scalar";
const char MemoryAllocatorDump::kTypeString[] = "string";
@@ -48,6 +27,7 @@
const MemoryAllocatorDumpGuid& guid)
: absolute_name_(absolute_name),
process_memory_dump_(process_memory_dump),
+ attributes_(new TracedValue),
guid_(guid) {
// The |absolute_name| cannot be empty.
DCHECK(!absolute_name.empty());
@@ -73,73 +53,47 @@
"%d:%s",
TraceLog::GetInstance()->process_id(),
absolute_name.c_str()))) {
+ string_conversion_buffer_.reserve(16);
}
MemoryAllocatorDump::~MemoryAllocatorDump() {
}
-void MemoryAllocatorDump::Add(const std::string& name,
- const char* type,
- const char* units,
- scoped_ptr<Value> value) {
- scoped_ptr<DictionaryValue> attribute(new DictionaryValue());
- DCHECK(!attributes_.HasKey(name));
- attribute->SetStringWithoutPathExpansion("type", type);
- attribute->SetStringWithoutPathExpansion("units", units);
- attribute->SetWithoutPathExpansion("value", value.Pass());
- attributes_.SetWithoutPathExpansion(name, attribute.Pass());
-}
-
-bool MemoryAllocatorDump::Get(const std::string& name,
- const char** out_type,
- const char** out_units,
- const Value** out_value) const {
- const DictionaryValue* attribute = nullptr;
- if (!attributes_.GetDictionaryWithoutPathExpansion(name, &attribute))
- return false;
-
- if (!GetDictionaryValueAsCStr(attribute, "type", out_type))
- return false;
-
- if (!GetDictionaryValueAsCStr(attribute, "units", out_units))
- return false;
-
- if (!attribute->GetWithoutPathExpansion("value", out_value))
- return false;
-
- return true;
-}
-
-void MemoryAllocatorDump::AddScalar(const std::string& name,
+void MemoryAllocatorDump::AddScalar(const char* name,
const char* units,
uint64 value) {
- scoped_ptr<Value> hex_value(new StringValue(StringPrintf("%" PRIx64, value)));
- Add(name, kTypeScalar, units, hex_value.Pass());
+ SStringPrintf(&string_conversion_buffer_, "%" PRIx64, value);
+ attributes_->BeginDictionary(name);
+ attributes_->SetString("type", kTypeScalar);
+ attributes_->SetString("units", units);
+ attributes_->SetString("value", string_conversion_buffer_);
+ attributes_->EndDictionary();
}
-void MemoryAllocatorDump::AddScalarF(const std::string& name,
+void MemoryAllocatorDump::AddScalarF(const char* name,
const char* units,
double value) {
- Add(name, kTypeScalar, units, make_scoped_ptr(new FundamentalValue(value)));
+ attributes_->BeginDictionary(name);
+ attributes_->SetString("type", kTypeScalar);
+ attributes_->SetString("units", units);
+ attributes_->SetDouble("value", value);
+ attributes_->EndDictionary();
}
-void MemoryAllocatorDump::AddString(const std::string& name,
+void MemoryAllocatorDump::AddString(const char* name,
const char* units,
const std::string& value) {
- scoped_ptr<Value> str_value(new StringValue(value));
- Add(name, kTypeString, units, str_value.Pass());
+ attributes_->BeginDictionary(name);
+ attributes_->SetString("type", kTypeString);
+ attributes_->SetString("units", units);
+ attributes_->SetString("value", value);
+ attributes_->EndDictionary();
}
void MemoryAllocatorDump::AsValueInto(TracedValue* value) const {
- value->BeginDictionary(absolute_name_.c_str());
+ value->BeginDictionaryWithCopiedName(absolute_name_);
value->SetString("guid", guid_.ToString());
-
- value->BeginDictionary("attrs");
-
- for (DictionaryValue::Iterator it(attributes_); !it.IsAtEnd(); it.Advance())
- value->SetValue(it.key().c_str(), it.value().CreateDeepCopy());
-
- value->EndDictionary(); // "attrs": { ... }
+ value->SetValue("attrs", *attributes_);
value->EndDictionary(); // "allocator_name/heap_subheap": { ... }
}
diff --git a/base/trace_event/memory_allocator_dump.h b/base/trace_event/memory_allocator_dump.h
index 9a151a6..2ded173 100644
--- a/base/trace_event/memory_allocator_dump.h
+++ b/base/trace_event/memory_allocator_dump.h
@@ -5,9 +5,12 @@
#ifndef BASE_TRACE_EVENT_MEMORY_ALLOCATOR_DUMP_H_
#define BASE_TRACE_EVENT_MEMORY_ALLOCATOR_DUMP_H_
+#include <string>
+
#include "base/base_export.h"
#include "base/basictypes.h"
#include "base/logging.h"
+#include "base/memory/ref_counted.h"
#include "base/trace_event/memory_allocator_dump_guid.h"
#include "base/values.h"
@@ -29,45 +32,32 @@
ProcessMemoryDump* process_memory_dump);
~MemoryAllocatorDump();
- // Standard attribute name to model allocated space.
- static const char kNameSize[];
+ // Standard attribute |name|s for the AddScalar and AddString() methods.
+ static const char kNameSize[]; // To represent allocated space.
+ static const char kNameObjectsCount[]; // To represent number of objects.
- // Standard attribute name to model total space requested by the allocator
- // (e.g., amount of pages requested to the system).
- static const char kNameOuterSize[];
-
- // Standard attribute name to model space for allocated objects, without
- // taking into account allocator metadata or fragmentation.
- static const char kNameInnerSize[];
-
- // Standard attribute name to model the number of objects allocated.
- static const char kNameObjectsCount[];
-
- static const char kTypeScalar[]; // Type name for scalar attributes.
- static const char kTypeString[]; // Type name for string attributes.
+ // Standard attribute |unit|s for the AddScalar and AddString() methods.
static const char kUnitsBytes[]; // Unit name to represent bytes.
static const char kUnitsObjects[]; // Unit name to represent #objects.
+ // Constants used only internally and by tests.
+ static const char kTypeScalar[]; // Type name for scalar attributes.
+ static const char kTypeString[]; // Type name for string attributes.
+
+ // Setters for scalar attributes. Some examples:
+ // - "size" column (all dumps are expected to have at least this one):
+ // AddScalar(kNameSize, kUnitsBytes, 1234);
+ // - Some extra-column reporting internal details of the subsystem:
+ // AddScalar("number_of_freelist_entires", kUnitsObjects, 42)
+ // - Other informational column (will not be auto-added in the UI)
+ // AddScalarF("kittens_ratio", "ratio", 42.0f)
+ void AddScalar(const char* name, const char* units, uint64 value);
+ void AddScalarF(const char* name, const char* units, double value);
+ void AddString(const char* name, const char* units, const std::string& value);
+
// Absolute name, unique within the scope of an entire ProcessMemoryDump.
const std::string& absolute_name() const { return absolute_name_; }
- // Generic attribute setter / getter.
- void Add(const std::string& name,
- const char* type,
- const char* units,
- scoped_ptr<Value> value);
- bool Get(const std::string& name,
- const char** out_type,
- const char** out_units,
- const Value** out_value) const;
-
- // Helper setter for scalar attributes.
- void AddScalar(const std::string& name, const char* units, uint64 value);
- void AddScalarF(const std::string& name, const char* units, double value);
- void AddString(const std::string& name,
- const char* units,
- const std::string& value);
-
// Called at trace generation time to populate the TracedValue.
void AsValueInto(TracedValue* value) const;
@@ -84,12 +74,18 @@
// expected to have the same guid.
const MemoryAllocatorDumpGuid& guid() const { return guid_; }
+ TracedValue* attributes_for_testing() const { return attributes_.get(); }
+
private:
const std::string absolute_name_;
ProcessMemoryDump* const process_memory_dump_; // Not owned (PMD owns this).
- DictionaryValue attributes_;
+ scoped_refptr<TracedValue> attributes_;
MemoryAllocatorDumpGuid guid_;
+ // A local buffer for Sprintf conversion on fastpath. Avoids allocating
+ // temporary strings on each AddScalar() call.
+ std::string string_conversion_buffer_;
+
DISALLOW_COPY_AND_ASSIGN(MemoryAllocatorDump);
};
diff --git a/base/trace_event/memory_allocator_dump_guid.cc b/base/trace_event/memory_allocator_dump_guid.cc
index a4ea50d..69c7de6 100644
--- a/base/trace_event/memory_allocator_dump_guid.cc
+++ b/base/trace_event/memory_allocator_dump_guid.cc
@@ -5,12 +5,21 @@
#include "base/trace_event/memory_allocator_dump_guid.h"
#include "base/format_macros.h"
-#include "base/hash.h"
+#include "base/sha1.h"
#include "base/strings/stringprintf.h"
namespace base {
namespace trace_event {
+namespace {
+uint64 HashString(const std::string& str) {
+ uint64 hash[(kSHA1Length + sizeof(uint64) - 1) / sizeof(uint64)] = {0};
+ SHA1HashBytes(reinterpret_cast<const unsigned char*>(str.data()), str.size(),
+ reinterpret_cast<unsigned char*>(hash));
+ return hash[0];
+}
+} // namespace
+
MemoryAllocatorDumpGuid::MemoryAllocatorDumpGuid(uint64 guid) : guid_(guid) {
}
@@ -19,8 +28,7 @@
}
MemoryAllocatorDumpGuid::MemoryAllocatorDumpGuid(const std::string& guid_str)
- : MemoryAllocatorDumpGuid(Hash(guid_str)) {
-}
+ : MemoryAllocatorDumpGuid(HashString(guid_str)) {}
std::string MemoryAllocatorDumpGuid::ToString() const {
return StringPrintf("%" PRIx64, guid_);
diff --git a/base/trace_event/memory_allocator_dump_guid.h b/base/trace_event/memory_allocator_dump_guid.h
index 84c12ef..634ca81 100644
--- a/base/trace_event/memory_allocator_dump_guid.h
+++ b/base/trace_event/memory_allocator_dump_guid.h
@@ -23,6 +23,8 @@
// global scope of all the traced processes.
explicit MemoryAllocatorDumpGuid(const std::string& guid_str);
+ uint64 ToUint64() const { return guid_; }
+
// Returns a (hex-encoded) string representation of the guid.
std::string ToString() const;
diff --git a/base/trace_event/memory_allocator_dump_unittest.cc b/base/trace_event/memory_allocator_dump_unittest.cc
index b9adbae..85b98d6 100644
--- a/base/trace_event/memory_allocator_dump_unittest.cc
+++ b/base/trace_event/memory_allocator_dump_unittest.cc
@@ -11,6 +11,7 @@
#include "base/trace_event/memory_dump_session_state.h"
#include "base/trace_event/process_memory_dump.h"
#include "base/trace_event/trace_event_argument.h"
+#include "base/values.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
@@ -47,20 +48,23 @@
}
};
-bool CheckAttribute(const MemoryAllocatorDump* dump,
- const std::string& name,
- const char* expected_type,
- const char* expected_units,
- const Value** out_value) {
- const char* attr_type;
- const char* attr_units;
- bool res = dump->Get(name, &attr_type, &attr_units, out_value);
- EXPECT_TRUE(res);
- if (!res)
- return false;
- EXPECT_EQ(expected_type, std::string(attr_type));
- EXPECT_EQ(expected_units, std::string(attr_units));
- return true;
+scoped_ptr<Value> CheckAttribute(const MemoryAllocatorDump* dump,
+ const std::string& name,
+ const char* expected_type,
+ const char* expected_units) {
+ scoped_ptr<Value> raw_attrs = dump->attributes_for_testing()->ToBaseValue();
+ DictionaryValue* args = nullptr;
+ DictionaryValue* arg = nullptr;
+ std::string arg_value;
+ const Value* out_value = nullptr;
+ EXPECT_TRUE(raw_attrs->GetAsDictionary(&args));
+ EXPECT_TRUE(args->GetDictionary(name, &arg));
+ EXPECT_TRUE(arg->GetString("type", &arg_value));
+ EXPECT_EQ(expected_type, arg_value);
+ EXPECT_TRUE(arg->GetString("units", &arg_value));
+ EXPECT_EQ(expected_units, arg_value);
+ EXPECT_TRUE(arg->Get("value", &out_value));
+ return out_value ? out_value->CreateDeepCopy() : scoped_ptr<Value>();
}
void CheckString(const MemoryAllocatorDump* dump,
@@ -68,12 +72,8 @@
const char* expected_type,
const char* expected_units,
const std::string& expected_value) {
- const Value* attr_value = nullptr;
std::string attr_str_value;
- bool res =
- CheckAttribute(dump, name, expected_type, expected_units, &attr_value);
- if (!res)
- return;
+ auto attr_value = CheckAttribute(dump, name, expected_type, expected_units);
EXPECT_TRUE(attr_value->GetAsString(&attr_str_value));
EXPECT_EQ(expected_value, attr_str_value);
}
@@ -90,12 +90,9 @@
const std::string& name,
const char* expected_units,
double expected_value) {
- const Value* attr_value = nullptr;
+ auto attr_value = CheckAttribute(dump, name, MemoryAllocatorDump::kTypeScalar,
+ expected_units);
double attr_double_value;
- bool res = CheckAttribute(dump, name, MemoryAllocatorDump::kTypeScalar,
- expected_units, &attr_value);
- if (!res)
- return;
EXPECT_TRUE(attr_value->GetAsDouble(&attr_double_value));
EXPECT_EQ(expected_value, attr_double_value);
}
@@ -154,15 +151,15 @@
MemoryAllocatorDump::kUnitsBytes, 1);
CheckScalar(sub_heap, MemoryAllocatorDump::kNameObjectsCount,
MemoryAllocatorDump::kUnitsObjects, 3);
-
const MemoryAllocatorDump* empty_sub_heap =
pmd.GetAllocatorDump("foobar_allocator/sub_heap/empty");
ASSERT_NE(nullptr, empty_sub_heap);
EXPECT_EQ("foobar_allocator/sub_heap/empty", empty_sub_heap->absolute_name());
- ASSERT_FALSE(empty_sub_heap->Get(MemoryAllocatorDump::kNameSize, nullptr,
- nullptr, nullptr));
- ASSERT_FALSE(empty_sub_heap->Get(MemoryAllocatorDump::kNameObjectsCount,
- nullptr, nullptr, nullptr));
+ auto raw_attrs = empty_sub_heap->attributes_for_testing()->ToBaseValue();
+ DictionaryValue* attrs = nullptr;
+ ASSERT_TRUE(raw_attrs->GetAsDictionary(&attrs));
+ ASSERT_FALSE(attrs->HasKey(MemoryAllocatorDump::kNameSize));
+ ASSERT_FALSE(attrs->HasKey(MemoryAllocatorDump::kNameObjectsCount));
// Check that the AsValueInfo doesn't hit any DCHECK.
scoped_refptr<TracedValue> traced_value(new TracedValue());
diff --git a/base/trace_event/memory_dump_manager.cc b/base/trace_event/memory_dump_manager.cc
index 15abb91..e2ca702 100644
--- a/base/trace_event/memory_dump_manager.cc
+++ b/base/trace_event/memory_dump_manager.cc
@@ -8,6 +8,8 @@
#include "base/atomic_sequence_num.h"
#include "base/compiler_specific.h"
+#include "base/hash.h"
+#include "base/thread_task_runner_handle.h"
#include "base/trace_event/memory_dump_provider.h"
#include "base/trace_event/memory_dump_session_state.h"
#include "base/trace_event/process_memory_dump.h"
@@ -40,96 +42,26 @@
// trace event synthetic delays.
const char kTraceCategory[] = TRACE_DISABLED_BY_DEFAULT("memory-infra");
-MemoryDumpManager* g_instance_for_testing = nullptr;
-const int kDumpIntervalSeconds = 2;
+// Throttle mmaps at a rate of once every kHeavyMmapsDumpsRate standard dumps.
+const int kHeavyMmapsDumpsRate = 8; // 250 ms * 8 = 2000 ms.
+const int kDumpIntervalMs = 250;
const int kTraceEventNumArgs = 1;
const char* kTraceEventArgNames[] = {"dumps"};
const unsigned char kTraceEventArgTypes[] = {TRACE_VALUE_TYPE_CONVERTABLE};
+
StaticAtomicSequenceNumber g_next_guid;
-
-const char* MemoryDumpTypeToString(const MemoryDumpType& dump_type) {
- switch (dump_type) {
- case MemoryDumpType::TASK_BEGIN:
- return "TASK_BEGIN";
- case MemoryDumpType::TASK_END:
- return "TASK_END";
- case MemoryDumpType::PERIODIC_INTERVAL:
- return "PERIODIC_INTERVAL";
- case MemoryDumpType::EXPLICITLY_TRIGGERED:
- return "EXPLICITLY_TRIGGERED";
- }
- NOTREACHED();
- return "UNKNOWN";
-}
-
-// Internal class used to hold details about ProcessMemoryDump requests for the
-// current process.
-// TODO(primiano): In the upcoming CLs, ProcessMemoryDump will become async.
-// and this class will be used to convey more details across PostTask()s.
-class ProcessMemoryDumpHolder
- : public RefCountedThreadSafe<ProcessMemoryDumpHolder> {
- public:
- ProcessMemoryDumpHolder(
- MemoryDumpRequestArgs req_args,
- const scoped_refptr<MemoryDumpSessionState>& session_state,
- MemoryDumpCallback callback)
- : process_memory_dump(session_state),
- req_args(req_args),
- callback(callback),
- task_runner(MessageLoop::current()->task_runner()),
- num_pending_async_requests(0) {}
-
- ProcessMemoryDump process_memory_dump;
- const MemoryDumpRequestArgs req_args;
-
- // Callback passed to the initial call to CreateProcessDump().
- MemoryDumpCallback callback;
-
- // Thread on which FinalizeDumpAndAddToTrace() should be called, which is the
- // same that invoked the initial CreateProcessDump().
- const scoped_refptr<SingleThreadTaskRunner> task_runner;
-
- // Number of pending ContinueAsyncProcessDump() calls.
- int num_pending_async_requests;
-
- private:
- friend class RefCountedThreadSafe<ProcessMemoryDumpHolder>;
- virtual ~ProcessMemoryDumpHolder() {}
- DISALLOW_COPY_AND_ASSIGN(ProcessMemoryDumpHolder);
-};
-
-void FinalizeDumpAndAddToTrace(
- const scoped_refptr<ProcessMemoryDumpHolder>& pmd_holder) {
- DCHECK_EQ(0, pmd_holder->num_pending_async_requests);
-
- if (!pmd_holder->task_runner->BelongsToCurrentThread()) {
- pmd_holder->task_runner->PostTask(
- FROM_HERE, Bind(&FinalizeDumpAndAddToTrace, pmd_holder));
- return;
- }
-
- scoped_refptr<ConvertableToTraceFormat> event_value(new TracedValue());
- pmd_holder->process_memory_dump.AsValueInto(
- static_cast<TracedValue*>(event_value.get()));
- const char* const event_name =
- MemoryDumpTypeToString(pmd_holder->req_args.dump_type);
-
- TRACE_EVENT_API_ADD_TRACE_EVENT(
- TRACE_EVENT_PHASE_MEMORY_DUMP,
- TraceLog::GetCategoryGroupEnabled(kTraceCategory), event_name,
- pmd_holder->req_args.dump_guid, kTraceEventNumArgs, kTraceEventArgNames,
- kTraceEventArgTypes, nullptr /* arg_values */, &event_value,
- TRACE_EVENT_FLAG_HAS_ID);
-
- if (!pmd_holder->callback.is_null()) {
- pmd_holder->callback.Run(pmd_holder->req_args.dump_guid, true);
- pmd_holder->callback.Reset();
- }
-}
+uint32 g_periodic_dumps_count = 0;
+MemoryDumpManager* g_instance_for_testing = nullptr;
+MemoryDumpProvider* g_mmaps_dump_provider = nullptr;
void RequestPeriodicGlobalDump() {
- MemoryDumpManager::GetInstance()->RequestGlobalDump(
- MemoryDumpType::PERIODIC_INTERVAL);
+ MemoryDumpType dump_type = g_periodic_dumps_count == 0
+ ? MemoryDumpType::PERIODIC_INTERVAL_WITH_MMAPS
+ : MemoryDumpType::PERIODIC_INTERVAL;
+ if (++g_periodic_dumps_count == kHeavyMmapsDumpsRate)
+ g_periodic_dumps_count = 0;
+
+ MemoryDumpManager::GetInstance()->RequestGlobalDump(dump_type);
}
} // namespace
@@ -138,6 +70,12 @@
const char* const MemoryDumpManager::kTraceCategoryForTesting = kTraceCategory;
// static
+const uint64 MemoryDumpManager::kInvalidTracingProcessId = 0;
+
+// static
+const int MemoryDumpManager::kMaxConsecutiveFailuresCount = 3;
+
+// static
MemoryDumpManager* MemoryDumpManager::GetInstance() {
if (g_instance_for_testing)
return g_instance_for_testing;
@@ -154,8 +92,10 @@
}
MemoryDumpManager::MemoryDumpManager()
- : delegate_(nullptr),
+ : did_unregister_dump_provider_(false),
+ delegate_(nullptr),
memory_tracing_enabled_(0),
+ tracing_process_id_(kInvalidTracingProcessId),
skip_core_dumpers_auto_registration_for_testing_(false) {
g_next_guid.GetNext(); // Make sure that first guid is not zero.
}
@@ -177,7 +117,8 @@
#endif
#if (defined(OS_LINUX) && !defined(FNL_MUSL)) || defined(OS_ANDROID)
- RegisterDumpProvider(ProcessMemoryMapsDumpProvider::GetInstance());
+ g_mmaps_dump_provider = ProcessMemoryMapsDumpProvider::GetInstance();
+ RegisterDumpProvider(g_mmaps_dump_provider);
RegisterDumpProvider(MallocDumpProvider::GetInstance());
#endif
@@ -199,9 +140,9 @@
void MemoryDumpManager::RegisterDumpProvider(
MemoryDumpProvider* mdp,
const scoped_refptr<SingleThreadTaskRunner>& task_runner) {
- MemoryDumpProviderInfo mdp_info(task_runner);
+ MemoryDumpProviderInfo mdp_info(mdp, task_runner);
AutoLock lock(lock_);
- dump_providers_.insert(std::make_pair(mdp, mdp_info));
+ dump_providers_.insert(mdp_info);
}
void MemoryDumpManager::RegisterDumpProvider(MemoryDumpProvider* mdp) {
@@ -211,11 +152,15 @@
void MemoryDumpManager::UnregisterDumpProvider(MemoryDumpProvider* mdp) {
AutoLock lock(lock_);
- auto it = dump_providers_.find(mdp);
- if (it == dump_providers_.end())
+ auto mdp_iter = dump_providers_.begin();
+ for (; mdp_iter != dump_providers_.end(); ++mdp_iter) {
+ if (mdp_iter->dump_provider == mdp)
+ break;
+ }
+
+ if (mdp_iter == dump_providers_.end())
return;
- const MemoryDumpProviderInfo& mdp_info = it->second;
// Unregistration of a MemoryDumpProvider while tracing is ongoing is safe
// only if the MDP has specified a thread affinity (via task_runner()) AND
// the unregistration happens on the same thread (so the MDP cannot unregister
@@ -224,13 +169,12 @@
// race-free. If you hit this DCHECK, your MDP has a bug.
DCHECK_IMPLIES(
subtle::NoBarrier_Load(&memory_tracing_enabled_),
- mdp_info.task_runner && mdp_info.task_runner->BelongsToCurrentThread())
+ mdp_iter->task_runner && mdp_iter->task_runner->BelongsToCurrentThread())
<< "The MemoryDumpProvider attempted to unregister itself in a racy way. "
- << " Please file a crbug.";
+ << "Please file a crbug.";
- // Remove from the enabled providers list. This is to deal with the case that
- // UnregisterDumpProvider is called while the trace is enabled.
- dump_providers_.erase(it);
+ dump_providers_.erase(mdp_iter);
+ did_unregister_dump_provider_ = true;
}
void MemoryDumpManager::RequestGlobalDump(
@@ -265,96 +209,179 @@
RequestGlobalDump(dump_type, MemoryDumpCallback());
}
-// Creates a memory dump for the current process and appends it to the trace.
void MemoryDumpManager::CreateProcessDump(const MemoryDumpRequestArgs& args,
const MemoryDumpCallback& callback) {
- scoped_refptr<ProcessMemoryDumpHolder> pmd_holder(
- new ProcessMemoryDumpHolder(args, session_state_, callback));
- ProcessMemoryDump* pmd = &pmd_holder->process_memory_dump;
- bool did_any_provider_dump = false;
-
- // Iterate over the active dump providers and invoke OnMemoryDump(pmd).
- // The MDM guarantees linearity (at most one MDP is active within one
- // process) and thread-safety (MDM enforces the right locking when entering /
- // leaving the MDP.OnMemoryDump() call). This is to simplify the clients'
- // design
- // and not let the MDPs worry about locking.
- // As regards thread affinity, depending on the MDP configuration (see
- // memory_dump_provider.h), the OnMemoryDump() invocation can happen:
- // - Synchronousy on the MDM thread, when MDP.task_runner() is not set.
- // - Posted on MDP.task_runner(), when MDP.task_runner() is set.
+ scoped_ptr<ProcessMemoryDumpAsyncState> pmd_async_state;
{
AutoLock lock(lock_);
- for (auto it = dump_providers_.begin(); it != dump_providers_.end(); ++it) {
- MemoryDumpProvider* mdp = it->first;
- MemoryDumpProviderInfo* mdp_info = &it->second;
- if (mdp_info->disabled)
- continue;
- if (mdp_info->task_runner) {
- // The OnMemoryDump() call must be posted.
- bool did_post_async_task = mdp_info->task_runner->PostTask(
- FROM_HERE, Bind(&MemoryDumpManager::ContinueAsyncProcessDump,
- Unretained(this), Unretained(mdp), pmd_holder));
- // The thread underlying the TaskRunner might have gone away.
- if (did_post_async_task)
- ++pmd_holder->num_pending_async_requests;
- } else {
- // Invoke the dump provider synchronously.
- did_any_provider_dump |= InvokeDumpProviderLocked(mdp, pmd);
- }
- }
- } // AutoLock
-
- // If at least one synchronous provider did dump and there are no pending
- // asynchronous requests, add the dump to the trace and invoke the callback
- // straight away (FinalizeDumpAndAddToTrace() takes care of the callback).
- if (did_any_provider_dump && pmd_holder->num_pending_async_requests == 0)
- FinalizeDumpAndAddToTrace(pmd_holder);
-}
-
-// Invokes the MemoryDumpProvider.OnMemoryDump(), taking care of the fail-safe
-// logic which disables the dumper when failing (crbug.com/461788).
-bool MemoryDumpManager::InvokeDumpProviderLocked(MemoryDumpProvider* mdp,
- ProcessMemoryDump* pmd) {
- lock_.AssertAcquired();
- bool dump_successful = mdp->OnMemoryDump(pmd);
- if (!dump_successful) {
- LOG(ERROR) << "The memory dumper failed, possibly due to sandboxing "
- "(crbug.com/461788), disabling it for current process. Try "
- "restarting chrome with the --no-sandbox switch.";
- dump_providers_.find(mdp)->second.disabled = true;
+ did_unregister_dump_provider_ = false;
+ pmd_async_state.reset(new ProcessMemoryDumpAsyncState(
+ args, dump_providers_.begin(), session_state_, callback));
}
- return dump_successful;
+
+ // Start the thread hop. |dump_providers_| are kept sorted by thread, so
+ // ContinueAsyncProcessDump will hop at most once per thread (w.r.t. thread
+ // affinity specified by the MemoryDumpProvider(s) in RegisterDumpProvider()).
+ ContinueAsyncProcessDump(pmd_async_state.Pass());
}
-// This is posted to arbitrary threads as a continuation of CreateProcessDump(),
-// when one or more MemoryDumpProvider(s) require the OnMemoryDump() call to
-// happen on a different thread.
+// At most one ContinueAsyncProcessDump() can be active at any time for a given
+// PMD, regardless of status of the |lock_|. |lock_| is used here purely to
+// ensure consistency w.r.t. (un)registrations of |dump_providers_|.
+// The linearization of dump providers' OnMemoryDump invocations is achieved by
+// means of subsequent PostTask(s).
+//
+// 1) Prologue:
+// - Check if the dump provider is disabled, if so skip the dump.
+// - Check if we are on the right thread. If not hop and continue there.
+// 2) Invoke the dump provider's OnMemoryDump() (unless skipped).
+// 3) Epilogue:
+// - Unregister the dump provider if it failed too many times consecutively.
+// - Advance the |next_dump_provider| iterator to the next dump provider.
+// - If this was the last hop, create a trace event, add it to the trace
+// and finalize (invoke callback).
+
void MemoryDumpManager::ContinueAsyncProcessDump(
- MemoryDumpProvider* mdp,
- scoped_refptr<ProcessMemoryDumpHolder> pmd_holder) {
- bool should_finalize_dump = false;
+ scoped_ptr<ProcessMemoryDumpAsyncState> pmd_async_state) {
+ // Initalizes the ThreadLocalEventBuffer to guarantee that the TRACE_EVENTs
+ // in the PostTask below don't end up registering their own dump providers
+ // (for discounting trace memory overhead) while holding the |lock_|.
+ TraceLog::GetInstance()->InitializeThreadLocalEventBufferIfSupported();
+
+ // DO NOT put any LOG() statement in the locked sections, as in some contexts
+ // (GPU process) LOG() ends up performing PostTask/IPCs.
+ MemoryDumpProvider* mdp;
+ bool skip_dump = false;
{
- // The lock here is to guarantee that different asynchronous dumps on
- // different threads are still serialized, so that the MemoryDumpProvider
- // has a consistent view of the |pmd| argument passed.
AutoLock lock(lock_);
- ProcessMemoryDump* pmd = &pmd_holder->process_memory_dump;
+ // In the unlikely event that a dump provider was unregistered while
+ // dumping, abort the dump, as that would make |next_dump_provider| invalid.
+ // Registration, on the other hand, is safe as per std::set<> contract.
+ if (did_unregister_dump_provider_) {
+ return AbortDumpLocked(pmd_async_state->callback,
+ pmd_async_state->task_runner,
+ pmd_async_state->req_args.dump_guid);
+ }
- // Check if the MemoryDumpProvider is still there. It might have been
- // destroyed and unregistered while hopping threads.
- if (dump_providers_.count(mdp))
- InvokeDumpProviderLocked(mdp, pmd);
+ auto* mdp_info = &*pmd_async_state->next_dump_provider;
+ mdp = mdp_info->dump_provider;
+ if (mdp_info->disabled) {
+ skip_dump = true;
+ } else if (mdp == g_mmaps_dump_provider &&
+ pmd_async_state->req_args.dump_type !=
+ MemoryDumpType::PERIODIC_INTERVAL_WITH_MMAPS) {
+ // Mmaps dumping is very heavyweight and cannot be performed at the same
+ // rate of other dumps. TODO(primiano): this is a hack and should be
+ // cleaned up as part of crbug.com/499731.
+ skip_dump = true;
+ } else if (mdp_info->task_runner &&
+ !mdp_info->task_runner->BelongsToCurrentThread()) {
+ // It's time to hop onto another thread.
- // Finalize the dump appending it to the trace if this was the last
- // asynchronous request pending.
- --pmd_holder->num_pending_async_requests;
- if (pmd_holder->num_pending_async_requests == 0)
- should_finalize_dump = true;
+ // Copy the callback + arguments just for the unlikley case in which
+ // PostTask fails. In such case the Bind helper will destroy the
+ // pmd_async_state and we must keep a copy of the fields to notify the
+ // abort.
+ MemoryDumpCallback callback = pmd_async_state->callback;
+ scoped_refptr<SingleThreadTaskRunner> callback_task_runner =
+ pmd_async_state->task_runner;
+ const uint64 dump_guid = pmd_async_state->req_args.dump_guid;
+
+ const bool did_post_task = mdp_info->task_runner->PostTask(
+ FROM_HERE, Bind(&MemoryDumpManager::ContinueAsyncProcessDump,
+ Unretained(this), Passed(pmd_async_state.Pass())));
+ if (did_post_task)
+ return;
+
+ // The thread is gone. At this point the best thing we can do is to
+ // disable the dump provider and abort this dump.
+ mdp_info->disabled = true;
+ return AbortDumpLocked(callback, callback_task_runner, dump_guid);
+ }
} // AutoLock(lock_)
- if (should_finalize_dump)
- FinalizeDumpAndAddToTrace(pmd_holder);
+ // Invoke the dump provider without holding the |lock_|.
+ bool finalize = false;
+ bool dump_successful = false;
+ if (!skip_dump)
+ dump_successful = mdp->OnMemoryDump(&pmd_async_state->process_memory_dump);
+
+ {
+ AutoLock lock(lock_);
+ if (did_unregister_dump_provider_) {
+ return AbortDumpLocked(pmd_async_state->callback,
+ pmd_async_state->task_runner,
+ pmd_async_state->req_args.dump_guid);
+ }
+ auto* mdp_info = &*pmd_async_state->next_dump_provider;
+ if (dump_successful) {
+ mdp_info->consecutive_failures = 0;
+ } else if (!skip_dump) {
+ ++mdp_info->consecutive_failures;
+ if (mdp_info->consecutive_failures >= kMaxConsecutiveFailuresCount) {
+ mdp_info->disabled = true;
+ }
+ }
+ ++pmd_async_state->next_dump_provider;
+ finalize = pmd_async_state->next_dump_provider == dump_providers_.end();
+ }
+
+ if (!skip_dump && !dump_successful) {
+ LOG(ERROR) << "A memory dumper failed, possibly due to sandboxing "
+ "(crbug.com/461788). Disabling dumper for current process. "
+ "Try restarting chrome with the --no-sandbox switch.";
+ }
+
+ if (finalize)
+ return FinalizeDumpAndAddToTrace(pmd_async_state.Pass());
+
+ ContinueAsyncProcessDump(pmd_async_state.Pass());
+}
+
+// static
+void MemoryDumpManager::FinalizeDumpAndAddToTrace(
+ scoped_ptr<ProcessMemoryDumpAsyncState> pmd_async_state) {
+ if (!pmd_async_state->task_runner->BelongsToCurrentThread()) {
+ scoped_refptr<SingleThreadTaskRunner> task_runner =
+ pmd_async_state->task_runner;
+ task_runner->PostTask(FROM_HERE,
+ Bind(&MemoryDumpManager::FinalizeDumpAndAddToTrace,
+ Passed(pmd_async_state.Pass())));
+ return;
+ }
+
+ scoped_refptr<ConvertableToTraceFormat> event_value(new TracedValue());
+ pmd_async_state->process_memory_dump.AsValueInto(
+ static_cast<TracedValue*>(event_value.get()));
+ const char* const event_name =
+ MemoryDumpTypeToString(pmd_async_state->req_args.dump_type);
+
+ TRACE_EVENT_API_ADD_TRACE_EVENT(
+ TRACE_EVENT_PHASE_MEMORY_DUMP,
+ TraceLog::GetCategoryGroupEnabled(kTraceCategory), event_name,
+ pmd_async_state->req_args.dump_guid, kTraceEventNumArgs,
+ kTraceEventArgNames, kTraceEventArgTypes, nullptr /* arg_values */,
+ &event_value, TRACE_EVENT_FLAG_HAS_ID);
+
+ if (!pmd_async_state->callback.is_null()) {
+ pmd_async_state->callback.Run(pmd_async_state->req_args.dump_guid,
+ true /* success */);
+ pmd_async_state->callback.Reset();
+ }
+}
+
+// static
+void MemoryDumpManager::AbortDumpLocked(
+ MemoryDumpCallback callback,
+ scoped_refptr<SingleThreadTaskRunner> task_runner,
+ uint64 dump_guid) {
+ if (callback.is_null())
+ return; // There is nothing to NACK.
+
+ // Post the callback even if we are already on the right thread to avoid
+ // invoking the callback while holding the lock_.
+ task_runner->PostTask(FROM_HERE,
+ Bind(callback, dump_guid, false /* success */));
}
void MemoryDumpManager::OnTraceLogEnabled() {
@@ -364,25 +391,33 @@
bool enabled;
TRACE_EVENT_CATEGORY_GROUP_ENABLED(kTraceCategory, &enabled);
+ // Initialize the TraceLog for the current thread. This is to avoid that the
+ // TraceLog memory dump provider is registered lazily in the PostTask() below
+ // while the |lock_| is taken;
+ TraceLog::GetInstance()->InitializeThreadLocalEventBufferIfSupported();
+
AutoLock lock(lock_);
// There is no point starting the tracing without a delegate.
if (!enabled || !delegate_) {
// Disable all the providers.
for (auto it = dump_providers_.begin(); it != dump_providers_.end(); ++it)
- it->second.disabled = true;
+ it->disabled = true;
return;
}
session_state_ = new MemoryDumpSessionState();
- for (auto it = dump_providers_.begin(); it != dump_providers_.end(); ++it)
- it->second.disabled = false;
+ for (auto it = dump_providers_.begin(); it != dump_providers_.end(); ++it) {
+ it->disabled = false;
+ it->consecutive_failures = 0;
+ }
subtle::NoBarrier_Store(&memory_tracing_enabled_, 1);
if (delegate_->IsCoordinatorProcess()) {
+ g_periodic_dumps_count = 0;
periodic_dump_timer_.Start(FROM_HERE,
- TimeDelta::FromSeconds(kDumpIntervalSeconds),
+ TimeDelta::FromMilliseconds(kDumpIntervalMs),
base::Bind(&RequestPeriodicGlobalDump));
}
}
@@ -394,12 +429,46 @@
session_state_ = nullptr;
}
-MemoryDumpManager::MemoryDumpProviderInfo::MemoryDumpProviderInfo(
- const scoped_refptr<SingleThreadTaskRunner>& task_runner)
- : task_runner(task_runner), disabled(false) {
+// static
+uint64 MemoryDumpManager::ChildProcessIdToTracingProcessId(
+ int child_process_id) {
+ return static_cast<uint64>(
+ Hash(reinterpret_cast<const char*>(&child_process_id),
+ sizeof(child_process_id))) +
+ 1;
}
+
+MemoryDumpManager::MemoryDumpProviderInfo::MemoryDumpProviderInfo(
+ MemoryDumpProvider* dump_provider,
+ const scoped_refptr<SingleThreadTaskRunner>& task_runner)
+ : dump_provider(dump_provider),
+ task_runner(task_runner),
+ consecutive_failures(0),
+ disabled(false) {}
+
MemoryDumpManager::MemoryDumpProviderInfo::~MemoryDumpProviderInfo() {
}
+bool MemoryDumpManager::MemoryDumpProviderInfo::operator<(
+ const MemoryDumpProviderInfo& other) const {
+ if (task_runner == other.task_runner)
+ return dump_provider < other.dump_provider;
+ return task_runner < other.task_runner;
+}
+
+MemoryDumpManager::ProcessMemoryDumpAsyncState::ProcessMemoryDumpAsyncState(
+ MemoryDumpRequestArgs req_args,
+ MemoryDumpProviderInfoSet::iterator next_dump_provider,
+ const scoped_refptr<MemoryDumpSessionState>& session_state,
+ MemoryDumpCallback callback)
+ : process_memory_dump(session_state),
+ req_args(req_args),
+ next_dump_provider(next_dump_provider),
+ callback(callback),
+ task_runner(MessageLoop::current()->task_runner()) {}
+
+MemoryDumpManager::ProcessMemoryDumpAsyncState::~ProcessMemoryDumpAsyncState() {
+}
+
} // namespace trace_event
} // namespace base
diff --git a/base/trace_event/memory_dump_manager.h b/base/trace_event/memory_dump_manager.h
index 3645ac1..f9ece6e 100644
--- a/base/trace_event/memory_dump_manager.h
+++ b/base/trace_event/memory_dump_manager.h
@@ -5,7 +5,7 @@
#ifndef BASE_TRACE_EVENT_MEMORY_DUMP_MANAGER_H_
#define BASE_TRACE_EVENT_MEMORY_DUMP_MANAGER_H_
-#include <vector>
+#include <set>
#include "base/atomicops.h"
#include "base/containers/hash_tables.h"
@@ -14,6 +14,7 @@
#include "base/synchronization/lock.h"
#include "base/timer/timer.h"
#include "base/trace_event/memory_dump_request_args.h"
+#include "base/trace_event/process_memory_dump.h"
#include "base/trace_event/trace_event.h"
namespace base {
@@ -22,13 +23,8 @@
namespace trace_event {
-namespace {
-class ProcessMemoryDumpHolder;
-}
-
class MemoryDumpManagerDelegate;
class MemoryDumpProvider;
-class ProcessMemoryDump;
class MemoryDumpSessionState;
// This is the interface exposed to the rest of the codebase to deal with
@@ -36,6 +32,7 @@
// RequestDumpPoint(). The extension by Un(RegisterDumpProvider).
class BASE_EXPORT MemoryDumpManager : public TraceLog::EnabledStateObserver {
public:
+ static const uint64 kInvalidTracingProcessId;
static const char* const kTraceCategoryForTesting;
static MemoryDumpManager* GetInstance();
@@ -81,27 +78,97 @@
return session_state_;
}
+ // Derives a tracing process id from a child process id. Child process ids
+ // cannot be used directly in tracing for security reasons (see: discussion in
+ // crrev.com/1173263004). This method is meant to be used when dumping
+ // cross-process shared memory from a process which knows the child process id
+ // of its endpoints. The value returned by this method is guaranteed to be
+ // equal to the value returned by tracing_process_id() in the corresponding
+ // child process.
+ // This will never return kInvalidTracingProcessId.
+ static uint64 ChildProcessIdToTracingProcessId(int child_id);
+
+ // Returns a unique id for the current process. The id can be retrieved only
+ // by child processes and only when tracing is enabled. This is intended to
+ // express cross-process sharing of memory dumps on the child-process side,
+ // without having to know its own child process id.
+ uint64 tracing_process_id() const { return tracing_process_id_; }
+
private:
- // Descriptor struct used to hold information about registered MDPs. It is
- // deliberately copyable, in order to allow to be used as hash_map value.
- struct MemoryDumpProviderInfo {
- MemoryDumpProviderInfo(
- const scoped_refptr<SingleThreadTaskRunner>& task_runner);
- ~MemoryDumpProviderInfo();
-
- scoped_refptr<SingleThreadTaskRunner> task_runner; // Optional.
- bool disabled; // For fail-safe logic (auto-disable failing MDPs).
- };
-
friend struct DefaultDeleter<MemoryDumpManager>; // For the testing instance.
friend struct DefaultSingletonTraits<MemoryDumpManager>;
friend class MemoryDumpManagerDelegate;
friend class MemoryDumpManagerTest;
+ FRIEND_TEST_ALL_PREFIXES(MemoryDumpManagerTest, DisableFailingDumpers);
- static void SetInstanceForTesting(MemoryDumpManager* instance);
+ // Descriptor struct used to hold information about registered MDPs. It is
+ // deliberately copyable, in order to allow it to be used as std::set value.
+ struct MemoryDumpProviderInfo {
+ MemoryDumpProviderInfo(
+ MemoryDumpProvider* dump_provider,
+ const scoped_refptr<SingleThreadTaskRunner>& task_runner);
+ ~MemoryDumpProviderInfo();
+
+ // Define a total order based on the thread (i.e. |task_runner|) affinity,
+ // so that all MDP belonging to the same thread are adjacent in the set.
+ bool operator<(const MemoryDumpProviderInfo& other) const;
+
+ MemoryDumpProvider* const dump_provider;
+ scoped_refptr<SingleThreadTaskRunner> task_runner; // Optional.
+
+ // For fail-safe logic (auto-disable failing MDPs). These fields are mutable
+ // as can be safely changed without impacting the order within the set.
+ mutable int consecutive_failures;
+ mutable bool disabled;
+ };
+
+ using MemoryDumpProviderInfoSet = std::set<MemoryDumpProviderInfo>;
+
+ // Holds the state of a process memory dump that needs to be carried over
+ // across threads in order to fulfil an asynchronous CreateProcessDump()
+ // request. At any time exactly one thread owns a ProcessMemoryDumpAsyncState.
+ struct ProcessMemoryDumpAsyncState {
+ ProcessMemoryDumpAsyncState(
+ MemoryDumpRequestArgs req_args,
+ MemoryDumpProviderInfoSet::iterator next_dump_provider,
+ const scoped_refptr<MemoryDumpSessionState>& session_state,
+ MemoryDumpCallback callback);
+ ~ProcessMemoryDumpAsyncState();
+
+ // The ProcessMemoryDump container, where each dump provider will dump its
+ // own MemoryAllocatorDump(s) upon the OnMemoryDump() call.
+ ProcessMemoryDump process_memory_dump;
+
+ // The arguments passed to the initial CreateProcessDump() request.
+ const MemoryDumpRequestArgs req_args;
+
+ // The |dump_providers_| iterator to the next dump provider that should be
+ // invoked (or dump_providers_.end() if at the end of the sequence).
+ MemoryDumpProviderInfoSet::iterator next_dump_provider;
+
+ // Callback passed to the initial call to CreateProcessDump().
+ MemoryDumpCallback callback;
+
+ // The thread on which FinalizeDumpAndAddToTrace() (and hence |callback|)
+ // should be invoked. This is the thread on which the initial
+ // CreateProcessDump() request was called.
+ const scoped_refptr<SingleThreadTaskRunner> task_runner;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ProcessMemoryDumpAsyncState);
+ };
+
+ static const int kMaxConsecutiveFailuresCount;
MemoryDumpManager();
- virtual ~MemoryDumpManager();
+ ~MemoryDumpManager() override;
+
+ static void SetInstanceForTesting(MemoryDumpManager* instance);
+ static void FinalizeDumpAndAddToTrace(
+ scoped_ptr<ProcessMemoryDumpAsyncState> pmd_async_state);
+ static void AbortDumpLocked(MemoryDumpCallback callback,
+ scoped_refptr<SingleThreadTaskRunner> task_runner,
+ uint64 dump_guid);
// Internal, used only by MemoryDumpManagerDelegate.
// Creates a memory dump for the current process and appends it to the trace.
@@ -110,13 +177,25 @@
void CreateProcessDump(const MemoryDumpRequestArgs& args,
const MemoryDumpCallback& callback);
- bool InvokeDumpProviderLocked(MemoryDumpProvider* mdp,
- ProcessMemoryDump* pmd);
+ // Continues the ProcessMemoryDump started by CreateProcessDump(), hopping
+ // across threads as needed as specified by MDPs in RegisterDumpProvider().
void ContinueAsyncProcessDump(
- MemoryDumpProvider* mdp,
- scoped_refptr<ProcessMemoryDumpHolder> pmd_holder);
+ scoped_ptr<ProcessMemoryDumpAsyncState> pmd_async_state);
- hash_map<MemoryDumpProvider*, MemoryDumpProviderInfo> dump_providers_;
+ // Pass kInvalidTracingProcessId to invalidate the id.
+ void set_tracing_process_id(uint64 id) {
+ DCHECK(tracing_process_id_ == kInvalidTracingProcessId ||
+ id == kInvalidTracingProcessId || tracing_process_id_ == id);
+ tracing_process_id_ = id;
+ }
+
+ // An ordererd set of registered MemoryDumpProviderInfo(s), sorted by thread
+ // affinity (MDPs belonging to the same thread are adjacent).
+ MemoryDumpProviderInfoSet dump_providers_;
+
+ // Flag used to signal that some provider was removed from |dump_providers_|
+ // and therefore the current memory dump (if any) should be aborted.
+ bool did_unregister_dump_provider_;
// Shared among all the PMDs to keep state scoped to the tracing session.
scoped_refptr<MemoryDumpSessionState> session_state_;
@@ -134,6 +213,10 @@
// For time-triggered periodic dumps.
RepeatingTimer<MemoryDumpManager> periodic_dump_timer_;
+ // The unique id of the child process. This is created only for tracing and is
+ // expected to be valid only when tracing is enabled.
+ uint64 tracing_process_id_;
+
// Skips the auto-registration of the core dumpers during Initialize().
bool skip_core_dumpers_auto_registration_for_testing_;
@@ -160,6 +243,10 @@
MemoryDumpManager::GetInstance()->CreateProcessDump(args, callback);
}
+ void set_tracing_process_id(uint64 id) {
+ MemoryDumpManager::GetInstance()->set_tracing_process_id(id);
+ }
+
private:
DISALLOW_COPY_AND_ASSIGN(MemoryDumpManagerDelegate);
};
diff --git a/base/trace_event/memory_dump_manager_unittest.cc b/base/trace_event/memory_dump_manager_unittest.cc
index c15748c..be20cec 100644
--- a/base/trace_event/memory_dump_manager_unittest.cc
+++ b/base/trace_event/memory_dump_manager_unittest.cc
@@ -8,6 +8,7 @@
#include "base/memory/scoped_vector.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
+#include "base/thread_task_runner_handle.h"
#include "base/threading/thread.h"
#include "base/trace_event/memory_dump_provider.h"
#include "base/trace_event/process_memory_dump.h"
@@ -15,6 +16,7 @@
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
+using testing::Between;
using testing::Invoke;
using testing::Return;
@@ -25,9 +27,8 @@
// instead of performing IPC dances.
class MemoryDumpManagerDelegateForTesting : public MemoryDumpManagerDelegate {
public:
- void RequestGlobalMemoryDump(
- const base::trace_event::MemoryDumpRequestArgs& args,
- const MemoryDumpCallback& callback) override {
+ void RequestGlobalMemoryDump(const MemoryDumpRequestArgs& args,
+ const MemoryDumpCallback& callback) override {
CreateProcessDump(args, callback);
}
@@ -81,7 +82,9 @@
class MockDumpProvider : public MemoryDumpProvider {
public:
- MockDumpProvider() : last_session_state_(nullptr) {}
+ MockDumpProvider()
+ : dump_provider_to_register_or_unregister(nullptr),
+ last_session_state_(nullptr) {}
// Ctor used by the RespectTaskRunnerAffinity test.
explicit MockDumpProvider(
@@ -107,6 +110,23 @@
return true;
}
+ // OnMemoryDump() override for the RegisterDumperWhileDumping test.
+ bool OnMemoryDump_RegisterExtraDumpProvider(ProcessMemoryDump* pmd) {
+ MemoryDumpManager::GetInstance()->RegisterDumpProvider(
+ dump_provider_to_register_or_unregister);
+ return true;
+ }
+
+ // OnMemoryDump() override for the UnegisterDumperWhileDumping test.
+ bool OnMemoryDump_UnregisterDumpProvider(ProcessMemoryDump* pmd) {
+ MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
+ dump_provider_to_register_or_unregister);
+ return true;
+ }
+
+ // Used by OnMemoryDump_(Un)RegisterExtraDumpProvider.
+ MemoryDumpProvider* dump_provider_to_register_or_unregister;
+
private:
MemoryDumpSessionState* last_session_state_;
scoped_refptr<SingleThreadTaskRunner> task_runner_;
@@ -250,9 +270,8 @@
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.
+// Enable both dump providers, make sure that mdp gets disabled after 3 failures
+// and not disabled after 1.
TEST_F(MemoryDumpManagerTest, DisableFailingDumpers) {
MockDumpProvider mdp1;
MockDumpProvider mdp2;
@@ -261,13 +280,78 @@
mdm_->RegisterDumpProvider(&mdp2);
EnableTracing(kTraceCategory);
- EXPECT_CALL(mdp1, OnMemoryDump(_)).Times(1).WillRepeatedly(Return(false));
- EXPECT_CALL(mdp2, OnMemoryDump(_)).Times(1).WillRepeatedly(Return(true));
- mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED);
+ EXPECT_CALL(mdp1, OnMemoryDump(_))
+ .Times(MemoryDumpManager::kMaxConsecutiveFailuresCount)
+ .WillRepeatedly(Return(false));
- EXPECT_CALL(mdp1, OnMemoryDump(_)).Times(0);
- EXPECT_CALL(mdp2, OnMemoryDump(_)).Times(1).WillRepeatedly(Return(false));
- mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED);
+ EXPECT_CALL(mdp2, OnMemoryDump(_))
+ .Times(1 + MemoryDumpManager::kMaxConsecutiveFailuresCount)
+ .WillOnce(Return(false))
+ .WillRepeatedly(Return(true));
+ for (int i = 0; i < 1 + MemoryDumpManager::kMaxConsecutiveFailuresCount;
+ i++) {
+ mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED);
+ }
+
+ DisableTracing();
+}
+
+// Sneakily register an extra memory dump provider while an existing one is
+// dumping and expect it to take part in the already active tracing session.
+TEST_F(MemoryDumpManagerTest, RegisterDumperWhileDumping) {
+ MockDumpProvider mdp1;
+ MockDumpProvider mdp2;
+
+ mdp1.dump_provider_to_register_or_unregister = &mdp2;
+ mdm_->RegisterDumpProvider(&mdp1);
+ EnableTracing(kTraceCategory);
+
+ EXPECT_CALL(mdp1, OnMemoryDump(_))
+ .Times(4)
+ .WillOnce(Return(true))
+ .WillOnce(Invoke(
+ &mdp1, &MockDumpProvider::OnMemoryDump_RegisterExtraDumpProvider))
+ .WillRepeatedly(Return(true));
+
+ // Depending on the insertion order (before or after mdp1), mdp2 might be
+ // called also immediately after it gets registered.
+ EXPECT_CALL(mdp2, OnMemoryDump(_))
+ .Times(Between(2, 3))
+ .WillRepeatedly(Return(true));
+
+ for (int i = 0; i < 4; i++) {
+ mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED);
+ }
+
+ DisableTracing();
+}
+
+// Like the above, but suddenly unregister the dump provider.
+TEST_F(MemoryDumpManagerTest, UnregisterDumperWhileDumping) {
+ MockDumpProvider mdp1;
+ MockDumpProvider mdp2;
+
+ mdm_->RegisterDumpProvider(&mdp1, ThreadTaskRunnerHandle::Get());
+ mdm_->RegisterDumpProvider(&mdp2, ThreadTaskRunnerHandle::Get());
+ mdp1.dump_provider_to_register_or_unregister = &mdp2;
+ EnableTracing(kTraceCategory);
+
+ EXPECT_CALL(mdp1, OnMemoryDump(_))
+ .Times(4)
+ .WillOnce(Return(true))
+ .WillOnce(
+ Invoke(&mdp1, &MockDumpProvider::OnMemoryDump_UnregisterDumpProvider))
+ .WillRepeatedly(Return(true));
+
+ // Depending on the insertion order (before or after mdp1), mdp2 might have
+ // been already called when OnMemoryDump_UnregisterDumpProvider happens.
+ EXPECT_CALL(mdp2, OnMemoryDump(_))
+ .Times(Between(1, 2))
+ .WillRepeatedly(Return(true));
+
+ for (int i = 0; i < 4; i++) {
+ mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED);
+ }
DisableTracing();
}
diff --git a/base/trace_event/memory_dump_request_args.cc b/base/trace_event/memory_dump_request_args.cc
new file mode 100644
index 0000000..196e98c
--- /dev/null
+++ b/base/trace_event/memory_dump_request_args.cc
@@ -0,0 +1,31 @@
+// 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/trace_event/memory_dump_request_args.h"
+
+#include "base/logging.h"
+
+namespace base {
+namespace trace_event {
+
+// static
+const char* MemoryDumpTypeToString(const MemoryDumpType& dump_type) {
+ switch (dump_type) {
+ case MemoryDumpType::TASK_BEGIN:
+ return "TASK_BEGIN";
+ case MemoryDumpType::TASK_END:
+ return "TASK_END";
+ case MemoryDumpType::PERIODIC_INTERVAL:
+ return "PERIODIC_INTERVAL";
+ case MemoryDumpType::PERIODIC_INTERVAL_WITH_MMAPS:
+ return "PERIODIC_INTERVAL_WITH_MMAPS";
+ case MemoryDumpType::EXPLICITLY_TRIGGERED:
+ return "EXPLICITLY_TRIGGERED";
+ }
+ NOTREACHED();
+ return "UNKNOWN";
+}
+
+} // namespace trace_event
+} // namespace base
diff --git a/base/trace_event/memory_dump_request_args.h b/base/trace_event/memory_dump_request_args.h
index 4d3763a..05d98be 100644
--- a/base/trace_event/memory_dump_request_args.h
+++ b/base/trace_event/memory_dump_request_args.h
@@ -20,10 +20,15 @@
TASK_BEGIN, // Dumping memory at the beginning of a message-loop task.
TASK_END, // Dumping memory at the ending of a message-loop task.
PERIODIC_INTERVAL, // Dumping memory at periodic intervals.
- EXPLICITLY_TRIGGERED, // Non maskable dump request.
- LAST = EXPLICITLY_TRIGGERED // For IPC macros.
+ PERIODIC_INTERVAL_WITH_MMAPS, // As above but w/ heavyweight mmaps dumps.
+ // Temporary workaround for crbug.com/499731.
+ EXPLICITLY_TRIGGERED, // Non maskable dump request.
+ LAST = EXPLICITLY_TRIGGERED // For IPC macros.
};
+// Returns the name in string for the dump type given.
+BASE_EXPORT const char* MemoryDumpTypeToString(const MemoryDumpType& dump_type);
+
using MemoryDumpCallback = Callback<void(uint64 dump_guid, bool success)>;
struct BASE_EXPORT MemoryDumpRequestArgs {
diff --git a/base/trace_event/process_memory_dump.h b/base/trace_event/process_memory_dump.h
index 88ce28a..3b71a2c 100644
--- a/base/trace_event/process_memory_dump.h
+++ b/base/trace_event/process_memory_dump.h
@@ -48,29 +48,6 @@
ProcessMemoryDump(const scoped_refptr<MemoryDumpSessionState>& session_state);
~ProcessMemoryDump();
- // Called at trace generation time to populate the TracedValue.
- void AsValueInto(TracedValue* value) const;
-
- // Removes all the MemoryAllocatorDump(s) contained in this instance. This
- // ProcessMemoryDump can be safely reused as if it was new once this returns.
- void Clear();
-
- // Merges all MemoryAllocatorDump(s) contained in |other| inside this
- // ProcessMemoryDump, transferring their ownership to this instance.
- // |other| will be an empty ProcessMemoryDump after this method returns.
- // This is to allow dump providers to pre-populate ProcessMemoryDump instances
- // and later move their contents into the ProcessMemoryDump passed as argument
- // of the MemoryDumpProvider::OnMemoryDump(ProcessMemoryDump*) callback.
- void TakeAllDumpsFrom(ProcessMemoryDump* other);
-
- ProcessMemoryTotals* process_totals() { return &process_totals_; }
- bool has_process_totals() const { return has_process_totals_; }
- void set_has_process_totals() { has_process_totals_ = true; }
-
- ProcessMemoryMaps* process_mmaps() { return &process_mmaps_; }
- bool has_process_mmaps() const { return has_process_mmaps_; }
- void set_has_process_mmaps() { has_process_mmaps_ = true; }
-
// Creates a new MemoryAllocatorDump with the given name and returns the
// empty object back to the caller.
// Arguments:
@@ -80,7 +57,8 @@
// Leading or trailing slashes are not allowed.
// guid: an optional identifier, unique among all processes within the
// scope of a global dump. This is only relevant when using
- // AddOwnershipEdge(). If omitted, it will be automatically generated.
+ // AddOwnershipEdge() to express memory sharing. If omitted,
+ // it will be automatically generated.
// ProcessMemoryDump handles the memory ownership of its MemoryAllocatorDumps.
MemoryAllocatorDump* CreateAllocatorDump(const std::string& absolute_name);
MemoryAllocatorDump* CreateAllocatorDump(const std::string& absolute_name,
@@ -131,6 +109,29 @@
return session_state_;
}
+ // Removes all the MemoryAllocatorDump(s) contained in this instance. This
+ // ProcessMemoryDump can be safely reused as if it was new once this returns.
+ void Clear();
+
+ // Merges all MemoryAllocatorDump(s) contained in |other| inside this
+ // ProcessMemoryDump, transferring their ownership to this instance.
+ // |other| will be an empty ProcessMemoryDump after this method returns.
+ // This is to allow dump providers to pre-populate ProcessMemoryDump instances
+ // and later move their contents into the ProcessMemoryDump passed as argument
+ // of the MemoryDumpProvider::OnMemoryDump(ProcessMemoryDump*) callback.
+ void TakeAllDumpsFrom(ProcessMemoryDump* other);
+
+ // Called at trace generation time to populate the TracedValue.
+ void AsValueInto(TracedValue* value) const;
+
+ ProcessMemoryTotals* process_totals() { return &process_totals_; }
+ bool has_process_totals() const { return has_process_totals_; }
+ void set_has_process_totals() { has_process_totals_ = true; }
+
+ ProcessMemoryMaps* process_mmaps() { return &process_mmaps_; }
+ bool has_process_mmaps() const { return has_process_mmaps_; }
+ void set_has_process_mmaps() { has_process_mmaps_ = true; }
+
private:
void AddAllocatorDumpInternal(MemoryAllocatorDump* mad);
diff --git a/base/trace_event/trace_config.cc b/base/trace_event/trace_config.cc
index ef9b892..2a15ec5 100644
--- a/base/trace_event/trace_config.cc
+++ b/base/trace_event/trace_config.cc
@@ -6,6 +6,7 @@
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
+#include "base/strings/pattern.h"
#include "base/strings/string_split.h"
#include "base/strings/string_tokenizer.h"
#include "base/strings/stringprintf.h"
@@ -147,8 +148,8 @@
if (IsCategoryEnabled(category_group_token.c_str())) {
return true;
}
- if (!MatchPattern(category_group_token.c_str(),
- TRACE_DISABLED_BY_DEFAULT("*")))
+ if (!base::MatchPattern(category_group_token.c_str(),
+ TRACE_DISABLED_BY_DEFAULT("*")))
had_enabled_by_default = true;
}
// Do a second pass to check for explicitly disabled categories
@@ -160,7 +161,7 @@
for (StringList::const_iterator ci = excluded_categories_.begin();
ci != excluded_categories_.end();
++ci) {
- if (MatchPattern(category_group_token.c_str(), ci->c_str())) {
+ if (base::MatchPattern(category_group_token.c_str(), ci->c_str())) {
// Current token of category_group_name is present in excluded_list.
// Flag the exclusion and proceed further to check if any of the
// remaining categories of category_group_name is not present in the
@@ -515,17 +516,17 @@
for (ci = disabled_categories_.begin();
ci != disabled_categories_.end();
++ci) {
- if (MatchPattern(category_name, ci->c_str()))
+ if (base::MatchPattern(category_name, ci->c_str()))
return true;
}
- if (MatchPattern(category_name, TRACE_DISABLED_BY_DEFAULT("*")))
+ if (base::MatchPattern(category_name, TRACE_DISABLED_BY_DEFAULT("*")))
return false;
for (ci = included_categories_.begin();
ci != included_categories_.end();
++ci) {
- if (MatchPattern(category_name, ci->c_str()))
+ if (base::MatchPattern(category_name, ci->c_str()))
return true;
}
diff --git a/base/trace_event/trace_event.h b/base/trace_event/trace_event.h
index 397bafc..77ec1de 100644
--- a/base/trace_event/trace_event.h
+++ b/base/trace_event/trace_event.h
@@ -852,6 +852,10 @@
#define TRACE_EVENT_FLOW_END0(category_group, name, id) \
INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, \
category_group, name, id, TRACE_EVENT_FLAG_NONE)
+#define TRACE_EVENT_FLOW_END_BIND_TO_ENCLOSING0(category_group, name, id) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, category_group, \
+ name, id, \
+ TRACE_EVENT_FLAG_BIND_TO_ENCLOSING)
#define TRACE_EVENT_FLOW_END1(category_group, name, id, arg1_name, arg1_val) \
INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, \
category_group, name, id, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
@@ -885,6 +889,14 @@
category_group, name, TRACE_ID_DONT_MANGLE(id), TRACE_EVENT_FLAG_NONE,\
"snapshot", snapshot)
+#define TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID_AND_TIMESTAMP( \
+ category_group, name, id, timestamp, snapshot) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \
+ TRACE_EVENT_PHASE_SNAPSHOT_OBJECT, category_group, name, \
+ TRACE_ID_DONT_MANGLE(id), \
+ static_cast<int>(base::PlatformThread::CurrentId()), timestamp, \
+ TRACE_EVENT_FLAG_NONE, "snapshot", snapshot)
+
#define TRACE_EVENT_OBJECT_DELETED_WITH_ID(category_group, name, id) \
INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_DELETE_OBJECT, \
category_group, name, TRACE_ID_DONT_MANGLE(id), TRACE_EVENT_FLAG_NONE)
@@ -954,7 +966,7 @@
// const char** arg_names,
// const unsigned char* arg_types,
// const unsigned long long* arg_values,
-// unsigned char flags)
+// unsigned int flags)
#define TRACE_EVENT_API_ADD_TRACE_EVENT \
base::trace_event::TraceLog::GetInstance()->AddTraceEvent
@@ -971,7 +983,7 @@
// const char** arg_names,
// const unsigned char* arg_types,
// const unsigned long long* arg_values,
-// unsigned char flags)
+// unsigned int flags)
#define TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_THREAD_ID_AND_TIMESTAMP \
base::trace_event::TraceLog::GetInstance() \
->AddTraceEventWithThreadIdAndTimestamp
@@ -1067,39 +1079,38 @@
// Implementation detail: internal macro to create static category and add
// event if the category is enabled.
-#define INTERNAL_TRACE_EVENT_ADD_WITH_ID(phase, category_group, name, id, \
- flags, ...) \
- do { \
- INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \
- if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \
- unsigned char trace_event_flags = flags | TRACE_EVENT_FLAG_HAS_ID; \
- trace_event_internal::TraceID trace_event_trace_id( \
- id, &trace_event_flags); \
- trace_event_internal::AddTraceEvent( \
- phase, INTERNAL_TRACE_EVENT_UID(category_group_enabled), \
- name, trace_event_trace_id.data(), trace_event_flags, \
- ##__VA_ARGS__); \
- } \
- } while (0)
+#define INTERNAL_TRACE_EVENT_ADD_WITH_ID(phase, category_group, name, id, \
+ flags, ...) \
+ do { \
+ INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \
+ if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \
+ unsigned int trace_event_flags = flags | TRACE_EVENT_FLAG_HAS_ID; \
+ trace_event_internal::TraceID trace_event_trace_id(id, \
+ &trace_event_flags); \
+ trace_event_internal::AddTraceEvent( \
+ phase, INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \
+ trace_event_trace_id.data(), trace_event_flags, ##__VA_ARGS__); \
+ } \
+ } while (0)
// Implementation detail: internal macro to create static category and add
// event if the category is enabled.
-#define INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(phase, \
- category_group, name, id, thread_id, timestamp, flags, ...) \
- do { \
- INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \
- if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \
- unsigned char trace_event_flags = flags | TRACE_EVENT_FLAG_HAS_ID; \
- trace_event_internal::TraceID trace_event_trace_id( \
- id, &trace_event_flags); \
- trace_event_internal::AddTraceEventWithThreadIdAndTimestamp( \
- phase, INTERNAL_TRACE_EVENT_UID(category_group_enabled), \
- name, trace_event_trace_id.data(), \
- thread_id, base::TraceTicks::FromInternalValue(timestamp), \
- trace_event_flags | TRACE_EVENT_FLAG_EXPLICIT_TIMESTAMP, \
- ##__VA_ARGS__); \
- } \
- } while (0)
+#define INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \
+ phase, category_group, name, id, thread_id, timestamp, flags, ...) \
+ do { \
+ INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \
+ if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \
+ unsigned int trace_event_flags = flags | TRACE_EVENT_FLAG_HAS_ID; \
+ trace_event_internal::TraceID trace_event_trace_id(id, \
+ &trace_event_flags); \
+ trace_event_internal::AddTraceEventWithThreadIdAndTimestamp( \
+ phase, INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \
+ trace_event_trace_id.data(), thread_id, \
+ base::TraceTicks::FromInternalValue(timestamp), \
+ trace_event_flags | TRACE_EVENT_FLAG_EXPLICIT_TIMESTAMP, \
+ ##__VA_ARGS__); \
+ } \
+ } while (0)
// Notes regarding the following definitions:
// New values can be added and propagated to third party libraries, but existing
@@ -1130,17 +1141,19 @@
#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))
-#define TRACE_EVENT_FLAG_COPY (static_cast<unsigned char>(1 << 0))
-#define TRACE_EVENT_FLAG_HAS_ID (static_cast<unsigned char>(1 << 1))
-#define TRACE_EVENT_FLAG_MANGLE_ID (static_cast<unsigned char>(1 << 2))
-#define TRACE_EVENT_FLAG_SCOPE_OFFSET (static_cast<unsigned char>(1 << 3))
-#define TRACE_EVENT_FLAG_SCOPE_EXTRA (static_cast<unsigned char>(1 << 4))
-#define TRACE_EVENT_FLAG_EXPLICIT_TIMESTAMP (static_cast<unsigned char>(1 << 5))
-#define TRACE_EVENT_FLAG_ASYNC_TTS (static_cast<unsigned char>(1 << 6))
+#define TRACE_EVENT_FLAG_NONE (static_cast<unsigned int>(0))
+#define TRACE_EVENT_FLAG_COPY (static_cast<unsigned int>(1 << 0))
+#define TRACE_EVENT_FLAG_HAS_ID (static_cast<unsigned int>(1 << 1))
+#define TRACE_EVENT_FLAG_MANGLE_ID (static_cast<unsigned int>(1 << 2))
+#define TRACE_EVENT_FLAG_SCOPE_OFFSET (static_cast<unsigned int>(1 << 3))
+#define TRACE_EVENT_FLAG_SCOPE_EXTRA (static_cast<unsigned int>(1 << 4))
+#define TRACE_EVENT_FLAG_EXPLICIT_TIMESTAMP (static_cast<unsigned int>(1 << 5))
+#define TRACE_EVENT_FLAG_ASYNC_TTS (static_cast<unsigned int>(1 << 6))
+#define TRACE_EVENT_FLAG_BIND_TO_ENCLOSING (static_cast<unsigned int>(1 << 7))
-#define TRACE_EVENT_FLAG_SCOPE_MASK (static_cast<unsigned char>( \
- TRACE_EVENT_FLAG_SCOPE_OFFSET | TRACE_EVENT_FLAG_SCOPE_EXTRA))
+#define TRACE_EVENT_FLAG_SCOPE_MASK \
+ (static_cast<unsigned int>(TRACE_EVENT_FLAG_SCOPE_OFFSET | \
+ TRACE_EVENT_FLAG_SCOPE_EXTRA))
// Type values for identifying types in the TraceValue union.
#define TRACE_VALUE_TYPE_BOOL (static_cast<unsigned char>(1))
@@ -1220,36 +1233,42 @@
private:
unsigned long long data_;
};
- TraceID(const void* id, unsigned char* flags)
- : data_(static_cast<unsigned long long>(
- reinterpret_cast<uintptr_t>(id))) {
+ TraceID(const void* id, unsigned int* flags)
+ : data_(
+ static_cast<unsigned long long>(reinterpret_cast<uintptr_t>(id))) {
*flags |= TRACE_EVENT_FLAG_MANGLE_ID;
}
- TraceID(ForceMangle id, unsigned char* flags) : data_(id.data()) {
+ TraceID(ForceMangle id, unsigned int* flags) : data_(id.data()) {
*flags |= TRACE_EVENT_FLAG_MANGLE_ID;
}
- TraceID(DontMangle id, unsigned char* flags) : data_(id.data()) {
+ TraceID(DontMangle id, unsigned int* flags) : data_(id.data()) {}
+ TraceID(unsigned long long id, unsigned int* flags) : data_(id) {
+ (void)flags;
}
- TraceID(unsigned long long id, unsigned char* flags)
- : data_(id) { (void)flags; }
- TraceID(unsigned long id, unsigned char* flags)
- : data_(id) { (void)flags; }
- TraceID(unsigned int id, unsigned char* flags)
- : data_(id) { (void)flags; }
- TraceID(unsigned short id, unsigned char* flags)
- : data_(id) { (void)flags; }
- TraceID(unsigned char id, unsigned char* flags)
- : data_(id) { (void)flags; }
- TraceID(long long id, unsigned char* flags)
- : data_(static_cast<unsigned long long>(id)) { (void)flags; }
- TraceID(long id, unsigned char* flags)
- : data_(static_cast<unsigned long long>(id)) { (void)flags; }
- TraceID(int id, unsigned char* flags)
- : data_(static_cast<unsigned long long>(id)) { (void)flags; }
- TraceID(short id, unsigned char* flags)
- : data_(static_cast<unsigned long long>(id)) { (void)flags; }
- TraceID(signed char id, unsigned char* flags)
- : data_(static_cast<unsigned long long>(id)) { (void)flags; }
+ TraceID(unsigned long id, unsigned int* flags) : data_(id) { (void)flags; }
+ TraceID(unsigned int id, unsigned int* flags) : data_(id) { (void)flags; }
+ TraceID(unsigned short id, unsigned int* flags) : data_(id) { (void)flags; }
+ TraceID(unsigned char id, unsigned int* flags) : data_(id) { (void)flags; }
+ TraceID(long long id, unsigned int* flags)
+ : data_(static_cast<unsigned long long>(id)) {
+ (void)flags;
+ }
+ TraceID(long id, unsigned int* flags)
+ : data_(static_cast<unsigned long long>(id)) {
+ (void)flags;
+ }
+ TraceID(int id, unsigned int* flags)
+ : data_(static_cast<unsigned long long>(id)) {
+ (void)flags;
+ }
+ TraceID(short id, unsigned int* flags)
+ : data_(static_cast<unsigned long long>(id)) {
+ (void)flags;
+ }
+ TraceID(signed char id, unsigned int* flags)
+ : data_(static_cast<unsigned long long>(id)) {
+ (void)flags;
+ }
unsigned long long data() const { return data_; }
@@ -1380,7 +1399,7 @@
unsigned long long id,
int thread_id,
const base::TraceTicks& timestamp,
- unsigned char flags,
+ unsigned int flags,
const char* arg1_name,
const scoped_refptr<base::trace_event::ConvertableToTraceFormat>&
arg1_val) {
@@ -1391,7 +1410,7 @@
num_args, &arg1_name, arg_types, NULL, &arg1_val, flags);
}
-template<class ARG1_TYPE>
+template <class ARG1_TYPE>
static inline base::trace_event::TraceEventHandle
AddTraceEventWithThreadIdAndTimestamp(
char phase,
@@ -1400,7 +1419,7 @@
unsigned long long id,
int thread_id,
const base::TraceTicks& timestamp,
- unsigned char flags,
+ unsigned int flags,
const char* arg1_name,
const ARG1_TYPE& arg1_val,
const char* arg2_name,
@@ -1423,7 +1442,7 @@
num_args, arg_names, arg_types, arg_values, convertable_values, flags);
}
-template<class ARG2_TYPE>
+template <class ARG2_TYPE>
static inline base::trace_event::TraceEventHandle
AddTraceEventWithThreadIdAndTimestamp(
char phase,
@@ -1432,7 +1451,7 @@
unsigned long long id,
int thread_id,
const base::TraceTicks& timestamp,
- unsigned char flags,
+ unsigned int flags,
const char* arg1_name,
const scoped_refptr<base::trace_event::ConvertableToTraceFormat>& arg1_val,
const char* arg2_name,
@@ -1463,7 +1482,7 @@
unsigned long long id,
int thread_id,
const base::TraceTicks& timestamp,
- unsigned char flags,
+ unsigned int flags,
const char* arg1_name,
const scoped_refptr<base::trace_event::ConvertableToTraceFormat>& arg1_val,
const char* arg2_name,
@@ -1489,7 +1508,7 @@
unsigned long long id,
int thread_id,
const base::TraceTicks& timestamp,
- unsigned char flags) {
+ unsigned int flags) {
return TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_THREAD_ID_AND_TIMESTAMP(
phase, category_group_enabled, name, id, thread_id, timestamp,
kZeroNumArgs, NULL, NULL, NULL, NULL, flags);
@@ -1500,14 +1519,14 @@
const unsigned char* category_group_enabled,
const char* name,
unsigned long long id,
- unsigned char flags) {
+ unsigned int flags) {
const int thread_id = static_cast<int>(base::PlatformThread::CurrentId());
const base::TraceTicks now = base::TraceTicks::Now();
return AddTraceEventWithThreadIdAndTimestamp(phase, category_group_enabled,
name, id, thread_id, now, flags);
}
-template<class ARG1_TYPE>
+template <class ARG1_TYPE>
static inline base::trace_event::TraceEventHandle
AddTraceEventWithThreadIdAndTimestamp(
char phase,
@@ -1516,7 +1535,7 @@
unsigned long long id,
int thread_id,
const base::TraceTicks& timestamp,
- unsigned char flags,
+ unsigned int flags,
const char* arg1_name,
const ARG1_TYPE& arg1_val) {
const int num_args = 1;
@@ -1528,13 +1547,13 @@
num_args, &arg1_name, arg_types, arg_values, NULL, flags);
}
-template<class ARG1_TYPE>
+template <class ARG1_TYPE>
static inline base::trace_event::TraceEventHandle AddTraceEvent(
char phase,
const unsigned char* category_group_enabled,
const char* name,
unsigned long long id,
- unsigned char flags,
+ unsigned int flags,
const char* arg1_name,
const ARG1_TYPE& arg1_val) {
int thread_id = static_cast<int>(base::PlatformThread::CurrentId());
@@ -1544,7 +1563,7 @@
arg1_name, arg1_val);
}
-template<class ARG1_TYPE, class ARG2_TYPE>
+template <class ARG1_TYPE, class ARG2_TYPE>
static inline base::trace_event::TraceEventHandle
AddTraceEventWithThreadIdAndTimestamp(
char phase,
@@ -1553,7 +1572,7 @@
unsigned long long id,
int thread_id,
const base::TraceTicks& timestamp,
- unsigned char flags,
+ unsigned int flags,
const char* arg1_name,
const ARG1_TYPE& arg1_val,
const char* arg2_name,
@@ -1569,13 +1588,13 @@
num_args, arg_names, arg_types, arg_values, NULL, flags);
}
-template<class ARG1_TYPE, class ARG2_TYPE>
+template <class ARG1_TYPE, class ARG2_TYPE>
static inline base::trace_event::TraceEventHandle AddTraceEvent(
char phase,
const unsigned char* category_group_enabled,
const char* name,
unsigned long long id,
- unsigned char flags,
+ unsigned int flags,
const char* arg1_name,
const ARG1_TYPE& arg1_val,
const char* arg2_name,
diff --git a/base/trace_event/trace_event_android.cc b/base/trace_event/trace_event_android.cc
index 465649d..4d25014 100644
--- a/base/trace_event/trace_event_android.cc
+++ b/base/trace_event/trace_event_android.cc
@@ -12,6 +12,9 @@
#include "base/synchronization/waitable_event.h"
#include "base/trace_event/trace_event.h"
+namespace base {
+namespace trace_event {
+
namespace {
int g_atrace_fd = -1;
@@ -24,28 +27,25 @@
unsigned long long id,
const char** arg_names,
const unsigned char* arg_types,
- const base::trace_event::TraceEvent::TraceValue* arg_values,
- const scoped_refptr<base::trace_event::ConvertableToTraceFormat>*
- convertable_values,
- unsigned char flags) {
- std::string out = base::StringPrintf("%c|%d|%s", phase, getpid(), name);
+ const TraceEvent::TraceValue* arg_values,
+ const scoped_refptr<ConvertableToTraceFormat>* convertable_values,
+ unsigned int flags) {
+ std::string out = StringPrintf("%c|%d|%s", phase, getpid(), name);
if (flags & TRACE_EVENT_FLAG_HAS_ID)
- base::StringAppendF(&out, "-%" PRIx64, static_cast<uint64>(id));
+ StringAppendF(&out, "-%" PRIx64, static_cast<uint64>(id));
out += '|';
- for (int i = 0; i < base::trace_event::kTraceMaxNumArgs && arg_names[i];
- ++i) {
+ for (int i = 0; i < kTraceMaxNumArgs && arg_names[i]; ++i) {
if (i)
out += ';';
out += arg_names[i];
out += '=';
std::string::size_type value_start = out.length();
- if (arg_types[i] == TRACE_VALUE_TYPE_CONVERTABLE) {
+ if (arg_types[i] == TRACE_VALUE_TYPE_CONVERTABLE)
convertable_values[i]->AppendAsTraceFormat(&out);
- } else {
- base::trace_event::TraceEvent::AppendValueAsJSON(arg_types[i],
- arg_values[i], &out);
- }
+ else
+ TraceEvent::AppendValueAsJSON(arg_types[i], arg_values[i], &out);
+
// Remove the quotes which may confuse the atrace script.
ReplaceSubstringsAfterOffset(&out, value_start, "\\\"", "'");
ReplaceSubstringsAfterOffset(&out, value_start, "\"", "");
@@ -59,25 +59,21 @@
write(g_atrace_fd, out.c_str(), out.size());
}
-void NoOpOutputCallback(base::WaitableEvent* complete_event,
- const scoped_refptr<base::RefCountedString>&,
+void NoOpOutputCallback(WaitableEvent* complete_event,
+ const scoped_refptr<RefCountedString>&,
bool has_more_events) {
if (!has_more_events)
complete_event->Signal();
}
-void EndChromeTracing(base::trace_event::TraceLog* trace_log,
- base::WaitableEvent* complete_event) {
+void EndChromeTracing(TraceLog* trace_log, WaitableEvent* complete_event) {
trace_log->SetDisabled();
// Delete the buffered trace events as they have been sent to atrace.
- trace_log->Flush(base::Bind(&NoOpOutputCallback, complete_event));
+ trace_log->Flush(Bind(&NoOpOutputCallback, complete_event));
}
} // namespace
-namespace base {
-namespace trace_event {
-
// These functions support Android systrace.py when 'webview' category is
// traced. With the new adb_profile_chrome, we may have two phases:
// - before WebView is ready for combined tracing, we can use adb_profile_chrome
diff --git a/base/trace_event/trace_event_argument.cc b/base/trace_event/trace_event_argument.cc
index e83aa73..2da6258 100644
--- a/base/trace_event/trace_event_argument.cc
+++ b/base/trace_event/trace_event_argument.cc
@@ -5,112 +5,464 @@
#include "base/trace_event/trace_event_argument.h"
#include "base/json/json_writer.h"
+#include "base/trace_event/trace_event_memory_overhead.h"
#include "base/values.h"
namespace base {
namespace trace_event {
-TracedValue::TracedValue() : root_(new DictionaryValue()) {
- stack_.push_back(root_.get());
+namespace {
+const char kTypeStartDict = '{';
+const char kTypeEndDict = '}';
+const char kTypeStartArray = '[';
+const char kTypeEndArray = ']';
+const char kTypeBool = 'b';
+const char kTypeInt = 'i';
+const char kTypeDouble = 'd';
+const char kTypeString = 's';
+const char kTypeCStr = '*';
+
+#ifndef NDEBUG
+const bool kStackTypeDict = false;
+const bool kStackTypeArray = true;
+#define DCHECK_CURRENT_CONTAINER_IS(x) DCHECK_EQ(x, nesting_stack_.back())
+#define DCHECK_CONTAINER_STACK_DEPTH_EQ(x) DCHECK_EQ(x, nesting_stack_.size())
+#define DEBUG_PUSH_CONTAINER(x) nesting_stack_.push_back(x)
+#define DEBUG_POP_CONTAINER() nesting_stack_.pop_back()
+#else
+#define DCHECK_CURRENT_CONTAINER_IS(x) \
+ do { \
+ } while (0)
+#define DCHECK_CONTAINER_STACK_DEPTH_EQ(x) \
+ do { \
+ } while (0)
+#define DEBUG_PUSH_CONTAINER(x) \
+ do { \
+ } while (0)
+#define DEBUG_POP_CONTAINER() \
+ do { \
+ } while (0)
+#endif
+
+inline void WriteKeyNameAsRawPtr(Pickle& pickle, const char* ptr) {
+ pickle.WriteBytes(&kTypeCStr, 1);
+ pickle.WriteUInt64(static_cast<uint64>(reinterpret_cast<uintptr_t>(ptr)));
+}
+
+inline void WriteKeyNameAsStdString(Pickle& pickle, const std::string& str) {
+ pickle.WriteBytes(&kTypeString, 1);
+ pickle.WriteString(str);
+}
+
+std::string ReadKeyName(PickleIterator& pickle_iterator) {
+ const char* type = nullptr;
+ bool res = pickle_iterator.ReadBytes(&type, 1);
+ std::string key_name;
+ if (res && *type == kTypeCStr) {
+ uint64 ptr_value = 0;
+ res = pickle_iterator.ReadUInt64(&ptr_value);
+ key_name = reinterpret_cast<const char*>(static_cast<uintptr_t>(ptr_value));
+ } else if (res && *type == kTypeString) {
+ res = pickle_iterator.ReadString(&key_name);
+ }
+ DCHECK(res);
+ return key_name;
+}
+} // namespace
+
+TracedValue::TracedValue() : TracedValue(0) {}
+
+TracedValue::TracedValue(size_t capacity) {
+ DEBUG_PUSH_CONTAINER(kStackTypeDict);
+ if (capacity)
+ pickle_.Reserve(capacity);
}
TracedValue::~TracedValue() {
- DCHECK_EQ(1u, stack_.size());
+ DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
+ DEBUG_POP_CONTAINER();
+ DCHECK_CONTAINER_STACK_DEPTH_EQ(0u);
}
void TracedValue::SetInteger(const char* name, int value) {
- GetCurrentDictionary()->SetInteger(name, value);
+ DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
+ pickle_.WriteBytes(&kTypeInt, 1);
+ pickle_.WriteInt(value);
+ WriteKeyNameAsRawPtr(pickle_, name);
+}
+
+void TracedValue::SetIntegerWithCopiedName(const std::string& name, int value) {
+ DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
+ pickle_.WriteBytes(&kTypeInt, 1);
+ pickle_.WriteInt(value);
+ WriteKeyNameAsStdString(pickle_, name);
}
void TracedValue::SetDouble(const char* name, double value) {
- GetCurrentDictionary()->SetDouble(name, value);
+ DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
+ pickle_.WriteBytes(&kTypeDouble, 1);
+ pickle_.WriteDouble(value);
+ WriteKeyNameAsRawPtr(pickle_, name);
+}
+
+void TracedValue::SetDoubleWithCopiedName(const std::string& name,
+ double value) {
+ DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
+ pickle_.WriteBytes(&kTypeDouble, 1);
+ pickle_.WriteDouble(value);
+ WriteKeyNameAsStdString(pickle_, name);
}
void TracedValue::SetBoolean(const char* name, bool value) {
- GetCurrentDictionary()->SetBoolean(name, value);
+ DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
+ pickle_.WriteBytes(&kTypeBool, 1);
+ pickle_.WriteBool(value);
+ WriteKeyNameAsRawPtr(pickle_, name);
+}
+
+void TracedValue::SetBooleanWithCopiedName(const std::string& name,
+ bool value) {
+ DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
+ pickle_.WriteBytes(&kTypeBool, 1);
+ pickle_.WriteBool(value);
+ WriteKeyNameAsStdString(pickle_, name);
}
void TracedValue::SetString(const char* name, const std::string& value) {
- GetCurrentDictionary()->SetString(name, value);
+ DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
+ pickle_.WriteBytes(&kTypeString, 1);
+ pickle_.WriteString(value);
+ WriteKeyNameAsRawPtr(pickle_, name);
}
-void TracedValue::SetValue(const char* name, scoped_ptr<Value> value) {
- GetCurrentDictionary()->Set(name, value.Pass());
+void TracedValue::SetStringWithCopiedName(const std::string& name,
+ const std::string& value) {
+ DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
+ pickle_.WriteBytes(&kTypeString, 1);
+ pickle_.WriteString(value);
+ WriteKeyNameAsStdString(pickle_, name);
+}
+
+void TracedValue::SetValue(const char* name, const TracedValue& value) {
+ DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
+ BeginDictionary(name);
+ pickle_.WriteBytes(value.pickle_.payload(),
+ static_cast<int>(value.pickle_.payload_size()));
+ EndDictionary();
+}
+
+void TracedValue::SetValueWithCopiedName(const std::string& name,
+ const TracedValue& value) {
+ DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
+ BeginDictionaryWithCopiedName(name);
+ pickle_.WriteBytes(value.pickle_.payload(),
+ static_cast<int>(value.pickle_.payload_size()));
+ EndDictionary();
}
void TracedValue::BeginDictionary(const char* name) {
- DictionaryValue* dictionary = new DictionaryValue();
- GetCurrentDictionary()->Set(name, make_scoped_ptr(dictionary));
- stack_.push_back(dictionary);
+ DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
+ DEBUG_PUSH_CONTAINER(kStackTypeDict);
+ pickle_.WriteBytes(&kTypeStartDict, 1);
+ WriteKeyNameAsRawPtr(pickle_, name);
+}
+
+void TracedValue::BeginDictionaryWithCopiedName(const std::string& name) {
+ DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
+ DEBUG_PUSH_CONTAINER(kStackTypeDict);
+ pickle_.WriteBytes(&kTypeStartDict, 1);
+ WriteKeyNameAsStdString(pickle_, name);
}
void TracedValue::BeginArray(const char* name) {
- ListValue* array = new ListValue();
- GetCurrentDictionary()->Set(name, make_scoped_ptr(array));
- stack_.push_back(array);
+ DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
+ DEBUG_PUSH_CONTAINER(kStackTypeArray);
+ pickle_.WriteBytes(&kTypeStartArray, 1);
+ WriteKeyNameAsRawPtr(pickle_, name);
+}
+
+void TracedValue::BeginArrayWithCopiedName(const std::string& name) {
+ DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
+ DEBUG_PUSH_CONTAINER(kStackTypeArray);
+ pickle_.WriteBytes(&kTypeStartArray, 1);
+ WriteKeyNameAsStdString(pickle_, name);
}
void TracedValue::EndDictionary() {
- DCHECK_GT(stack_.size(), 1u);
- DCHECK(GetCurrentDictionary());
- stack_.pop_back();
+ DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
+ DEBUG_POP_CONTAINER();
+ pickle_.WriteBytes(&kTypeEndDict, 1);
}
void TracedValue::AppendInteger(int value) {
- GetCurrentArray()->AppendInteger(value);
+ DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
+ pickle_.WriteBytes(&kTypeInt, 1);
+ pickle_.WriteInt(value);
}
void TracedValue::AppendDouble(double value) {
- GetCurrentArray()->AppendDouble(value);
+ DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
+ pickle_.WriteBytes(&kTypeDouble, 1);
+ pickle_.WriteDouble(value);
}
void TracedValue::AppendBoolean(bool value) {
- GetCurrentArray()->AppendBoolean(value);
+ DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
+ pickle_.WriteBytes(&kTypeBool, 1);
+ pickle_.WriteBool(value);
}
void TracedValue::AppendString(const std::string& value) {
- GetCurrentArray()->AppendString(value);
+ DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
+ pickle_.WriteBytes(&kTypeString, 1);
+ pickle_.WriteString(value);
}
void TracedValue::BeginArray() {
- ListValue* array = new ListValue();
- GetCurrentArray()->Append(array);
- stack_.push_back(array);
+ DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
+ DEBUG_PUSH_CONTAINER(kStackTypeArray);
+ pickle_.WriteBytes(&kTypeStartArray, 1);
}
void TracedValue::BeginDictionary() {
- DictionaryValue* dictionary = new DictionaryValue();
- GetCurrentArray()->Append(dictionary);
- stack_.push_back(dictionary);
+ DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
+ DEBUG_PUSH_CONTAINER(kStackTypeDict);
+ pickle_.WriteBytes(&kTypeStartDict, 1);
}
void TracedValue::EndArray() {
- DCHECK_GT(stack_.size(), 1u);
- DCHECK(GetCurrentArray());
- stack_.pop_back();
+ DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
+ DEBUG_POP_CONTAINER();
+ pickle_.WriteBytes(&kTypeEndArray, 1);
}
-DictionaryValue* TracedValue::GetCurrentDictionary() {
- DCHECK(!stack_.empty());
- DictionaryValue* dictionary = NULL;
- stack_.back()->GetAsDictionary(&dictionary);
- DCHECK(dictionary);
- return dictionary;
+void TracedValue::SetValue(const char* name, scoped_ptr<base::Value> value) {
+ SetBaseValueWithCopiedName(name, *value);
}
-ListValue* TracedValue::GetCurrentArray() {
- DCHECK(!stack_.empty());
- ListValue* list = NULL;
- stack_.back()->GetAsList(&list);
- DCHECK(list);
- return list;
+void TracedValue::SetBaseValueWithCopiedName(const std::string& name,
+ const base::Value& value) {
+ DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
+ switch (value.GetType()) {
+ case base::Value::TYPE_NULL:
+ case base::Value::TYPE_BINARY:
+ NOTREACHED();
+ break;
+
+ case base::Value::TYPE_BOOLEAN: {
+ bool bool_value;
+ value.GetAsBoolean(&bool_value);
+ SetBooleanWithCopiedName(name, bool_value);
+ } break;
+
+ case base::Value::TYPE_INTEGER: {
+ int int_value;
+ value.GetAsInteger(&int_value);
+ SetIntegerWithCopiedName(name, int_value);
+ } break;
+
+ case base::Value::TYPE_DOUBLE: {
+ double double_value;
+ value.GetAsDouble(&double_value);
+ SetDoubleWithCopiedName(name, double_value);
+ } break;
+
+ case base::Value::TYPE_STRING: {
+ const StringValue* string_value;
+ value.GetAsString(&string_value);
+ SetStringWithCopiedName(name, string_value->GetString());
+ } break;
+
+ case base::Value::TYPE_DICTIONARY: {
+ const DictionaryValue* dict_value;
+ value.GetAsDictionary(&dict_value);
+ BeginDictionaryWithCopiedName(name);
+ for (DictionaryValue::Iterator it(*dict_value); !it.IsAtEnd();
+ it.Advance()) {
+ SetBaseValueWithCopiedName(it.key(), it.value());
+ }
+ EndDictionary();
+ } break;
+
+ case base::Value::TYPE_LIST: {
+ const ListValue* list_value;
+ value.GetAsList(&list_value);
+ BeginArrayWithCopiedName(name);
+ for (base::Value* base_value : *list_value)
+ AppendBaseValue(*base_value);
+ EndArray();
+ } break;
+ }
+}
+
+void TracedValue::AppendBaseValue(const base::Value& value) {
+ DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
+ switch (value.GetType()) {
+ case base::Value::TYPE_NULL:
+ case base::Value::TYPE_BINARY:
+ NOTREACHED();
+ break;
+
+ case base::Value::TYPE_BOOLEAN: {
+ bool bool_value;
+ value.GetAsBoolean(&bool_value);
+ AppendBoolean(bool_value);
+ } break;
+
+ case base::Value::TYPE_INTEGER: {
+ int int_value;
+ value.GetAsInteger(&int_value);
+ AppendInteger(int_value);
+ } break;
+
+ case base::Value::TYPE_DOUBLE: {
+ double double_value;
+ value.GetAsDouble(&double_value);
+ AppendDouble(double_value);
+ } break;
+
+ case base::Value::TYPE_STRING: {
+ const StringValue* string_value;
+ value.GetAsString(&string_value);
+ AppendString(string_value->GetString());
+ } break;
+
+ case base::Value::TYPE_DICTIONARY: {
+ const DictionaryValue* dict_value;
+ value.GetAsDictionary(&dict_value);
+ BeginDictionary();
+ for (DictionaryValue::Iterator it(*dict_value); !it.IsAtEnd();
+ it.Advance()) {
+ SetBaseValueWithCopiedName(it.key(), it.value());
+ }
+ EndDictionary();
+ } break;
+
+ case base::Value::TYPE_LIST: {
+ const ListValue* list_value;
+ value.GetAsList(&list_value);
+ BeginArray();
+ for (base::Value* base_value : *list_value)
+ AppendBaseValue(*base_value);
+ EndArray();
+ } break;
+ }
+}
+
+scoped_ptr<base::Value> TracedValue::ToBaseValue() const {
+ scoped_ptr<DictionaryValue> root(new DictionaryValue);
+ DictionaryValue* cur_dict = root.get();
+ ListValue* cur_list = nullptr;
+ std::vector<Value*> stack;
+ PickleIterator it(pickle_);
+ const char* type;
+
+ while (it.ReadBytes(&type, 1)) {
+ DCHECK((cur_dict && !cur_list) || (cur_list && !cur_dict));
+ switch (*type) {
+ case kTypeStartDict: {
+ auto new_dict = new DictionaryValue();
+ if (cur_dict) {
+ cur_dict->Set(ReadKeyName(it), make_scoped_ptr(new_dict));
+ stack.push_back(cur_dict);
+ cur_dict = new_dict;
+ } else {
+ cur_list->Append(make_scoped_ptr(new_dict));
+ stack.push_back(cur_list);
+ cur_list = nullptr;
+ cur_dict = new_dict;
+ }
+ } break;
+
+ case kTypeEndArray:
+ case kTypeEndDict: {
+ if (stack.back()->GetAsDictionary(&cur_dict)) {
+ cur_list = nullptr;
+ } else if (stack.back()->GetAsList(&cur_list)) {
+ cur_dict = nullptr;
+ }
+ stack.pop_back();
+ } break;
+
+ case kTypeStartArray: {
+ auto new_list = new ListValue();
+ if (cur_dict) {
+ cur_dict->Set(ReadKeyName(it), make_scoped_ptr(new_list));
+ stack.push_back(cur_dict);
+ cur_dict = nullptr;
+ cur_list = new_list;
+ } else {
+ cur_list->Append(make_scoped_ptr(new_list));
+ stack.push_back(cur_list);
+ cur_list = new_list;
+ }
+ } break;
+
+ case kTypeBool: {
+ bool value;
+ CHECK(it.ReadBool(&value));
+ if (cur_dict) {
+ cur_dict->SetBoolean(ReadKeyName(it), value);
+ } else {
+ cur_list->AppendBoolean(value);
+ }
+ } break;
+
+ case kTypeInt: {
+ int value;
+ CHECK(it.ReadInt(&value));
+ if (cur_dict) {
+ cur_dict->SetInteger(ReadKeyName(it), value);
+ } else {
+ cur_list->AppendInteger(value);
+ }
+ } break;
+
+ case kTypeDouble: {
+ double value;
+ CHECK(it.ReadDouble(&value));
+ if (cur_dict) {
+ cur_dict->SetDouble(ReadKeyName(it), value);
+ } else {
+ cur_list->AppendDouble(value);
+ }
+ } break;
+
+ case kTypeString: {
+ std::string value;
+ CHECK(it.ReadString(&value));
+ if (cur_dict) {
+ cur_dict->SetString(ReadKeyName(it), value);
+ } else {
+ cur_list->AppendString(value);
+ }
+ } break;
+
+ default:
+ NOTREACHED();
+ }
+ }
+ DCHECK(stack.empty());
+ return root.Pass();
}
void TracedValue::AppendAsTraceFormat(std::string* out) const {
+ DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
+ DCHECK_CONTAINER_STACK_DEPTH_EQ(1u);
+
+ // TODO(primiano): this could be smarter, skip the ToBaseValue encoding and
+ // produce the JSON on its own. This will require refactoring JSONWriter
+ // to decouple the base::Value traversal from the JSON writing bits
std::string tmp;
- JSONWriter::Write(*stack_.front(), &tmp);
+ JSONWriter::Write(*ToBaseValue(), &tmp);
*out += tmp;
- DCHECK_EQ(1u, stack_.size()) << tmp;
+}
+
+void TracedValue::EstimateTraceMemoryOverhead(
+ TraceEventMemoryOverhead* overhead) {
+ overhead->Add("TracedValue",
+ pickle_.GetTotalAllocatedSize() /* allocated size */,
+ pickle_.size() /* resident size */);
}
} // namespace trace_event
diff --git a/base/trace_event/trace_event_argument.h b/base/trace_event/trace_event_argument.h
index 56f7c61..aab58bc 100644
--- a/base/trace_event/trace_event_argument.h
+++ b/base/trace_event/trace_event_argument.h
@@ -9,11 +9,11 @@
#include <vector>
#include "base/memory/scoped_ptr.h"
+#include "base/pickle.h"
#include "base/trace_event/trace_event.h"
namespace base {
-class DictionaryValue;
-class ListValue;
+
class Value;
namespace trace_event {
@@ -21,18 +21,31 @@
class BASE_EXPORT TracedValue : public ConvertableToTraceFormat {
public:
TracedValue();
+ explicit TracedValue(size_t capacity);
void EndDictionary();
void EndArray();
+ // These methods assume that |name| is a long lived "quoted" string.
void SetInteger(const char* name, int value);
void SetDouble(const char* name, double value);
void SetBoolean(const char* name, bool value);
void SetString(const char* name, const std::string& value);
- void SetValue(const char* name, scoped_ptr<Value> value);
+ void SetValue(const char* name, const TracedValue& value);
void BeginDictionary(const char* name);
void BeginArray(const char* name);
+ // These, instead, can be safely passed a temporary string.
+ void SetIntegerWithCopiedName(const std::string& name, int value);
+ void SetDoubleWithCopiedName(const std::string& name, double value);
+ void SetBooleanWithCopiedName(const std::string& name, bool value);
+ void SetStringWithCopiedName(const std::string& name,
+ const std::string& value);
+ void SetValueWithCopiedName(const std::string& name,
+ const TracedValue& value);
+ void BeginDictionaryWithCopiedName(const std::string& name);
+ void BeginArrayWithCopiedName(const std::string& name);
+
void AppendInteger(int);
void AppendDouble(double);
void AppendBoolean(bool);
@@ -40,16 +53,33 @@
void BeginArray();
void BeginDictionary();
+ // ConvertableToTraceFormat implementation.
void AppendAsTraceFormat(std::string* out) const override;
+ void EstimateTraceMemoryOverhead(TraceEventMemoryOverhead* overhead) override;
+
+ // DEPRECATED: do not use, here only for legacy reasons. These methods causes
+ // a copy-and-translation of the base::Value into the equivalent TracedValue.
+ // TODO(primiano): migrate the (three) existing clients to the cheaper
+ // SetValue(TracedValue) API. crbug.com/495628.
+ void SetValue(const char* name, scoped_ptr<base::Value> value);
+ void SetBaseValueWithCopiedName(const std::string& name,
+ const base::Value& value);
+ void AppendBaseValue(const base::Value& value);
+
+ // Public for tests only.
+ scoped_ptr<base::Value> ToBaseValue() const;
+
private:
~TracedValue() override;
- DictionaryValue* GetCurrentDictionary();
- ListValue* GetCurrentArray();
+ Pickle pickle_;
- scoped_ptr<base::Value> root_;
- std::vector<Value*> stack_; // Weak references.
+#ifndef NDEBUG
+ // In debug builds checks the pairings of {Start,End}{Dictionary,Array}
+ std::vector<bool> nesting_stack_;
+#endif
+
DISALLOW_COPY_AND_ASSIGN(TracedValue);
};
diff --git a/base/trace_event/trace_event_argument_unittest.cc b/base/trace_event/trace_event_argument_unittest.cc
index c59910e..cb1cf2e 100644
--- a/base/trace_event/trace_event_argument_unittest.cc
+++ b/base/trace_event/trace_event_argument_unittest.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "base/trace_event/trace_event_argument.h"
+#include "base/values.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
@@ -14,10 +15,11 @@
value->SetDouble("double", 0.0);
value->SetBoolean("bool", true);
value->SetString("string", "string");
- std::string json;
+ std::string json = "PREFIX";
value->AppendAsTraceFormat(&json);
- EXPECT_EQ("{\"bool\":true,\"double\":0.0,\"int\":2014,\"string\":\"string\"}",
- json);
+ EXPECT_EQ(
+ "PREFIX{\"bool\":true,\"double\":0.0,\"int\":2014,\"string\":\"string\"}",
+ json);
}
TEST(TraceEventArgumentTest, Hierarchy) {
@@ -49,5 +51,96 @@
json);
}
+TEST(TraceEventArgumentTest, LongStrings) {
+ std::string kLongString = "supercalifragilisticexpialidocious";
+ std::string kLongString2 = "0123456789012345678901234567890123456789";
+ char kLongString3[4096];
+ for (size_t i = 0; i < sizeof(kLongString3); ++i)
+ kLongString3[i] = 'a' + (i % 25);
+ kLongString3[sizeof(kLongString3) - 1] = '\0';
+
+ scoped_refptr<TracedValue> value = new TracedValue();
+ value->SetString("a", "short");
+ value->SetString("b", kLongString);
+ value->BeginArray("c");
+ value->AppendString(kLongString2);
+ value->AppendString("");
+ value->BeginDictionary();
+ value->SetString("a", kLongString3);
+ value->EndDictionary();
+ value->EndArray();
+
+ std::string json;
+ value->AppendAsTraceFormat(&json);
+ EXPECT_EQ("{\"a\":\"short\",\"b\":\"" + kLongString + "\",\"c\":[\"" +
+ kLongString2 + "\",\"\",{\"a\":\"" + kLongString3 + "\"}]}",
+ json);
+}
+
+TEST(TraceEventArgumentTest, PassBaseValue) {
+ FundamentalValue int_value(42);
+ FundamentalValue bool_value(true);
+ FundamentalValue double_value(42.0f);
+
+ auto dict_value = make_scoped_ptr(new DictionaryValue);
+ dict_value->SetBoolean("bool", true);
+ dict_value->SetInteger("int", 42);
+ dict_value->SetDouble("double", 42.0f);
+ dict_value->SetString("string", std::string("a") + "b");
+ dict_value->SetString("string", std::string("a") + "b");
+
+ auto list_value = make_scoped_ptr(new ListValue);
+ list_value->AppendBoolean(false);
+ list_value->AppendInteger(1);
+ list_value->AppendString("in_list");
+ list_value->Append(dict_value.Pass());
+
+ scoped_refptr<TracedValue> value = new TracedValue();
+ value->BeginDictionary("outer_dict");
+ value->SetValue("inner_list", list_value.Pass());
+ value->EndDictionary();
+
+ dict_value.reset();
+ list_value.reset();
+
+ std::string json;
+ value->AppendAsTraceFormat(&json);
+ EXPECT_EQ(
+ "{\"outer_dict\":{\"inner_list\":[false,1,\"in_list\",{\"bool\":true,"
+ "\"double\":42.0,\"int\":42,\"string\":\"ab\"}]}}",
+ json);
+}
+
+TEST(TraceEventArgumentTest, PassTracedValue) {
+ auto dict_value = make_scoped_refptr(new TracedValue);
+ dict_value->SetInteger("a", 1);
+
+ auto nested_dict_value = make_scoped_refptr(new TracedValue);
+ nested_dict_value->SetInteger("b", 2);
+ nested_dict_value->BeginArray("c");
+ nested_dict_value->AppendString("foo");
+ nested_dict_value->EndArray();
+
+ dict_value->SetValue("e", *nested_dict_value);
+
+ // Check the merged result.
+ std::string json;
+ dict_value->AppendAsTraceFormat(&json);
+ EXPECT_EQ("{\"a\":1,\"e\":{\"b\":2,\"c\":[\"foo\"]}}", json);
+
+ // Check that the passed nestd dict was left unouthced.
+ json = "";
+ nested_dict_value->AppendAsTraceFormat(&json);
+ EXPECT_EQ("{\"b\":2,\"c\":[\"foo\"]}", json);
+
+ // And that it is still usable.
+ nested_dict_value->SetInteger("f", 3);
+ nested_dict_value->BeginDictionary("g");
+ nested_dict_value->EndDictionary();
+ json = "";
+ nested_dict_value->AppendAsTraceFormat(&json);
+ EXPECT_EQ("{\"b\":2,\"c\":[\"foo\"],\"f\":3,\"g\":{}}", json);
+}
+
} // namespace trace_event
} // namespace base
diff --git a/base/trace_event/trace_event_etw_export_win.cc b/base/trace_event/trace_event_etw_export_win.cc
index d199bf5..98e4553 100644
--- a/base/trace_event/trace_event_etw_export_win.cc
+++ b/base/trace_event/trace_event_etw_export_win.cc
@@ -7,6 +7,7 @@
#include "base/command_line.h"
#include "base/logging.h"
#include "base/memory/singleton.h"
+#include "base/strings/string_tokenizer.h"
#include "base/strings/utf_string_conversions.h"
#include "base/trace_event/trace_event.h"
#include "base/trace_event/trace_event_impl.h"
@@ -40,6 +41,55 @@
tEventRegister EventRegisterProc = nullptr;
tEventWrite EventWriteProc = nullptr;
tEventUnregister EventUnregisterProc = nullptr;
+
+// |filtered_event_group_names| contains the event categories that can be
+// exported individually. These categories can be enabled by passing the correct
+// keyword when starting the trace. A keyword is a 64-bit flag and we attribute
+// one bit per category. We can therefore enable a particular category by
+// setting its corresponding bit in the keyword. For events that are not present
+// in |filtered_event_group_names|, we have two bits that control their
+// behaviour. When bit 61 is enabled, any event that is not disabled by default
+// (ie. doesn't start with disabled-by-default-) will be exported. Likewise,
+// when bit 62 is enabled, any event that is disabled by default will be
+// exported.
+//
+// Note that bit 63 (MSB) must always be set, otherwise tracing will be disabled
+// by ETW. Therefore, the keyword will always be greater than
+// 0x8000000000000000.
+//
+// Examples of passing keywords to the provider using xperf:
+// # This exports "benchmark" and "cc" events
+// xperf -start chrome -on Chrome:0x8000000000000009
+//
+// # This exports "gpu", "netlog" and all other events that are not disabled by
+// # default
+// xperf -start chrome -on Chrome:0xA0000000000000A0
+//
+// More info about starting a trace and keyword can be obtained by using the
+// help section of xperf (xperf -help start). Note that xperf documentation
+// refers to keywords as flags and there are two ways to enable them, using
+// group names or the hex representation. We only support the latter. Also, we
+// ignore the level.
+const char* const filtered_event_group_names[] = {
+ "benchmark", // 0x1
+ "blink", // 0x2
+ "browser", // 0x4
+ "cc", // 0x8
+ "evdev", // 0x10
+ "gpu", // 0x20
+ "input", // 0x40
+ "netlog", // 0x80
+ "renderer.scheduler", // 0x100
+ "toplevel", // 0x200
+ "v8", // 0x400
+ "disabled-by-default-cc.debug", // 0x800
+ "disabled-by-default-cc.debug.picture", // 0x1000
+ "disabled-by-default-toplevel.flow"}; // 0x2000
+const char* other_events_group_name = "__OTHER_EVENTS"; // 0x2000000000000000
+const char* disabled_other_events_group_name =
+ "__DISABLED_OTHER_EVENTS"; // 0x4000000000000000
+uint64 other_events_keyword_bit = 1ULL << 61;
+uint64 disabled_other_events_keyword_bit = 1ULL << 62;
} // namespace
// Redirector function for EventRegister. Called by macros in
@@ -77,7 +127,8 @@
namespace base {
namespace trace_event {
-TraceEventETWExport::TraceEventETWExport() : ETWExportEnabled_(false) {
+TraceEventETWExport::TraceEventETWExport()
+ : etw_export_enabled_(false), etw_match_any_keyword_(0ULL) {
// Find Advapi32.dll. This should always succeed.
HMODULE AdvapiDLL = ::LoadLibraryW(L"Advapi32.dll");
if (AdvapiDLL) {
@@ -92,6 +143,8 @@
// Register the ETW provider. If registration fails then the event logging
// calls will fail (on XP this call will do nothing).
EventRegisterChrome();
+
+ UpdateEnabledCategories();
}
}
@@ -108,13 +161,18 @@
// static
void TraceEventETWExport::EnableETWExport() {
if (GetInstance())
- GetInstance()->ETWExportEnabled_ = true;
+ GetInstance()->etw_export_enabled_ = true;
}
// static
void TraceEventETWExport::DisableETWExport() {
if (GetInstance())
- GetInstance()->ETWExportEnabled_ = false;
+ GetInstance()->etw_export_enabled_ = false;
+}
+
+// static
+bool TraceEventETWExport::IsETWExportEnabled() {
+ return (GetInstance() && GetInstance()->etw_export_enabled_);
}
// static
@@ -129,7 +187,7 @@
const unsigned long long* arg_values,
const scoped_refptr<ConvertableToTraceFormat>* convertable_values) {
// We bail early in case exporting is disabled or no consumer is listening.
- if (!GetInstance() || !GetInstance()->ETWExportEnabled_ ||
+ if (!GetInstance() || !GetInstance()->etw_export_enabled_ ||
!EventEnabledChromeEvent())
return;
@@ -211,7 +269,7 @@
// *total* process CPU time when ETW tracing, and many of the strings
// created exceed WPA's 4094 byte limit and are shown as:
// "Unable to parse data". See crbug.com/488257
- //convertable_values[i]->AppendAsTraceFormat(arg_values_string + i);
+ // convertable_values[i]->AppendAsTraceFormat(arg_values_string + i);
} else {
TraceEvent::TraceValue trace_event;
trace_event.as_uint = arg_values[i];
@@ -236,7 +294,7 @@
const char* arg_value_2,
const char* arg_name_3,
const char* arg_value_3) {
- if (!GetInstance() || !GetInstance()->ETWExportEnabled_ ||
+ if (!GetInstance() || !GetInstance()->etw_export_enabled_ ||
!EventEnabledChromeEvent())
return;
@@ -244,8 +302,78 @@
arg_value_2, arg_name_3, arg_value_3);
}
-void TraceEventETWExport::Resurrect() {
- StaticMemorySingletonTraits<TraceEventETWExport>::Resurrect();
+// static
+bool TraceEventETWExport::IsCategoryGroupEnabled(
+ const char* category_group_name) {
+ DCHECK(category_group_name);
+ auto instance = GetInstance();
+ if (instance == nullptr)
+ return false;
+
+ if (!instance->IsETWExportEnabled())
+ return false;
+
+ CStringTokenizer category_group_tokens(
+ category_group_name, category_group_name + strlen(category_group_name),
+ ",");
+ while (category_group_tokens.GetNext()) {
+ std::string category_group_token = category_group_tokens.token();
+ if (instance->IsCategoryEnabled(category_group_token.c_str())) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool TraceEventETWExport::UpdateEnabledCategories() {
+ if (etw_match_any_keyword_ == CHROME_Context.MatchAnyKeyword)
+ return false;
+
+ // If the keyword has changed, update each category.
+ // Chrome_Context.MatchAnyKeyword is set by UIforETW (or other ETW trace
+ // recording tools) using the ETW infrastructure. This value will be set in
+ // all Chrome processes that have registered their ETW provider.
+ etw_match_any_keyword_ = CHROME_Context.MatchAnyKeyword;
+ for (int i = 0; i < ARRAYSIZE(filtered_event_group_names); i++) {
+ if (etw_match_any_keyword_ & (1ULL << i)) {
+ categories_status_[filtered_event_group_names[i]] = true;
+ } else {
+ categories_status_[filtered_event_group_names[i]] = false;
+ }
+ }
+
+ // Also update the two default categories.
+ if (etw_match_any_keyword_ & other_events_keyword_bit) {
+ categories_status_[other_events_group_name] = true;
+ } else {
+ categories_status_[other_events_group_name] = false;
+ }
+ if (etw_match_any_keyword_ & disabled_other_events_keyword_bit) {
+ categories_status_[disabled_other_events_group_name] = true;
+ } else {
+ categories_status_[disabled_other_events_group_name] = false;
+ }
+
+ return true;
+}
+
+bool TraceEventETWExport::IsCategoryEnabled(const char* category_name) const {
+ // Try to find the category and return its status if found
+ auto it = categories_status_.find(category_name);
+ if (it != categories_status_.end())
+ return it->second;
+
+ // Otherwise return the corresponding default status by first checking if the
+ // category is disabled by default.
+ if (StringPiece(category_name).starts_with("disabled-by-default")) {
+ DCHECK(categories_status_.find(disabled_other_events_group_name) !=
+ categories_status_.end());
+ return categories_status_.find(disabled_other_events_group_name)->second;
+ } else {
+ DCHECK(categories_status_.find(other_events_group_name) !=
+ categories_status_.end());
+ return categories_status_.find(other_events_group_name)->second;
+ }
}
} // namespace trace_event
diff --git a/base/trace_event/trace_event_etw_export_win.h b/base/trace_event/trace_event_etw_export_win.h
index eefe820..9f73d78 100644
--- a/base/trace_event/trace_event_etw_export_win.h
+++ b/base/trace_event/trace_event_etw_export_win.h
@@ -6,7 +6,10 @@
#ifndef BASE_TRACE_EVENT_TRACE_EVENT_ETW_EXPORT_WIN_H_
#define BASE_TRACE_EVENT_TRACE_EVENT_ETW_EXPORT_WIN_H_
+#include <map>
+
#include "base/base_export.h"
+#include "base/strings/string_piece.h"
#include "base/trace_event/trace_event_impl.h"
// Fwd.
@@ -29,9 +32,9 @@
static void EnableETWExport();
static void DisableETWExport();
- static bool isETWExportEnabled() {
- return (GetInstance() && GetInstance()->ETWExportEnabled_);
- }
+ // Returns true if ETW is enabled. For now, this is true if the command line
+ // flag is specified.
+ static bool IsETWExportEnabled();
// Exports an event to ETW. This is mainly used in
// TraceLog::AddTraceEventWithThreadIdAndTimestamp to export internal events.
@@ -50,7 +53,7 @@
// to ETW. Supports three arguments to be passed to ETW.
// TODO(georgesak): Allow different providers.
static void AddCustomEvent(const char* name,
- char const* phase,
+ const char* phase,
const char* arg_name_1,
const char* arg_value_1,
const char* arg_name_2,
@@ -58,14 +61,30 @@
const char* arg_name_3,
const char* arg_value_3);
- void Resurrect();
+ // Returns true if any category in the group is enabled.
+ static bool IsCategoryGroupEnabled(const char* category_group_name);
private:
- bool ETWExportEnabled_;
// Ensure only the provider can construct us.
friend struct StaticMemorySingletonTraits<TraceEventETWExport>;
TraceEventETWExport();
+ // Updates the list of enabled categories by consulting the ETW keyword.
+ // Returns true if there was a change, false otherwise.
+ bool UpdateEnabledCategories();
+
+ // Returns true if the category is enabled.
+ bool IsCategoryEnabled(const char* category_name) const;
+
+ // True if ETW is enabled. Allows hiding the exporting behind a flag.
+ bool etw_export_enabled_;
+
+ // Maps category names to their status (enabled/disabled).
+ std::map<base::StringPiece, bool> categories_status_;
+
+ // Local copy of the ETW keyword.
+ uint64 etw_match_any_keyword_;
+
DISALLOW_COPY_AND_ASSIGN(TraceEventETWExport);
};
diff --git a/base/trace_event/trace_event_impl.cc b/base/trace_event/trace_event_impl.cc
index 3d58d87..96858aa 100644
--- a/base/trace_event/trace_event_impl.cc
+++ b/base/trace_event/trace_event_impl.cc
@@ -33,6 +33,9 @@
#include "base/threading/thread_id_name_manager.h"
#include "base/threading/worker_pool.h"
#include "base/time/time.h"
+#include "base/trace_event/memory_dump_manager.h"
+#include "base/trace_event/memory_dump_provider.h"
+#include "base/trace_event/process_memory_dump.h"
#include "base/trace_event/trace_event.h"
#include "base/trace_event/trace_event_synthetic_delay.h"
@@ -214,6 +217,18 @@
return cloned_buffer.Pass();
}
+ void EstimateTraceMemoryOverhead(
+ TraceEventMemoryOverhead* overhead) override {
+ overhead->Add("TraceBufferRingBuffer", sizeof(*this));
+ for (size_t queue_index = queue_head_; queue_index != queue_tail_;
+ queue_index = NextQueueIndex(queue_index)) {
+ size_t chunk_index = recyclable_chunks_queue_[queue_index];
+ if (chunk_index >= chunks_.size()) // Skip uninitialized chunks.
+ continue;
+ chunks_[chunk_index]->EstimateTraceMemoryOverhead(overhead);
+ }
+ }
+
private:
class ClonedTraceBuffer : public TraceBuffer {
public:
@@ -242,6 +257,10 @@
NOTIMPLEMENTED();
return scoped_ptr<TraceBuffer>();
}
+ void EstimateTraceMemoryOverhead(
+ TraceEventMemoryOverhead* overhead) override {
+ NOTIMPLEMENTED();
+ }
size_t current_iteration_index_;
ScopedVector<TraceBufferChunk> chunks_;
@@ -350,6 +369,23 @@
return scoped_ptr<TraceBuffer>();
}
+ void EstimateTraceMemoryOverhead(
+ TraceEventMemoryOverhead* overhead) override {
+ const size_t chunks_ptr_vector_allocated_size =
+ sizeof(*this) + max_chunks_ * sizeof(decltype(chunks_)::value_type);
+ const size_t chunks_ptr_vector_resident_size =
+ sizeof(*this) + chunks_.size() * sizeof(decltype(chunks_)::value_type);
+ overhead->Add("TraceBufferVector", chunks_ptr_vector_allocated_size,
+ chunks_ptr_vector_resident_size);
+ for (size_t i = 0; i < chunks_.size(); ++i) {
+ TraceBufferChunk* chunk = chunks_[i];
+ // Skip the in-flight (nullptr) chunks. They will be accounted by the
+ // per-thread-local dumpers, see ThreadLocalEventBuffer::OnMemoryDump.
+ if (chunk)
+ chunk->EstimateTraceMemoryOverhead(overhead);
+ }
+ }
+
private:
size_t in_flight_chunk_count_;
size_t current_iteration_index_;
@@ -398,11 +434,16 @@
} // namespace
+TraceBufferChunk::TraceBufferChunk(uint32 seq) : next_free_(0), seq_(seq) {}
+
+TraceBufferChunk::~TraceBufferChunk() {}
+
void TraceBufferChunk::Reset(uint32 new_seq) {
for (size_t i = 0; i < next_free_; ++i)
chunk_[i].Reset();
next_free_ = 0;
seq_ = new_seq;
+ cached_overhead_estimate_when_full_.reset();
}
TraceEvent* TraceBufferChunk::AddTraceEvent(size_t* event_index) {
@@ -419,6 +460,31 @@
return cloned_chunk.Pass();
}
+void TraceBufferChunk::EstimateTraceMemoryOverhead(
+ TraceEventMemoryOverhead* overhead) {
+ if (cached_overhead_estimate_when_full_) {
+ DCHECK(IsFull());
+ overhead->Update(*cached_overhead_estimate_when_full_);
+ return;
+ }
+
+ // Cache the memory overhead estimate only if the chunk is full.
+ TraceEventMemoryOverhead* estimate = overhead;
+ if (IsFull()) {
+ cached_overhead_estimate_when_full_.reset(new TraceEventMemoryOverhead);
+ estimate = cached_overhead_estimate_when_full_.get();
+ }
+
+ estimate->Add("TraceBufferChunk", sizeof(*this));
+ for (size_t i = 0; i < next_free_; ++i)
+ chunk_[i].EstimateTraceMemoryOverhead(estimate);
+
+ if (IsFull()) {
+ estimate->AddSelf();
+ overhead->Update(*estimate);
+ }
+}
+
// A helper class that allows the lock to be acquired in the middle of the scope
// and unlocks at the end of scope if locked.
class TraceLog::OptionalAutoLock {
@@ -529,7 +595,7 @@
const unsigned char* arg_types,
const unsigned long long* arg_values,
const scoped_refptr<ConvertableToTraceFormat>* convertable_values,
- unsigned char flags) {
+ unsigned int flags) {
timestamp_ = timestamp;
thread_timestamp_ = thread_timestamp;
duration_ = TimeDelta::FromInternalValue(-1);
@@ -610,6 +676,7 @@
parameter_copy_storage_ = NULL;
for (int i = 0; i < kTraceMaxNumArgs; ++i)
convertable_values_[i] = NULL;
+ cached_memory_overhead_estimate_.reset();
}
void TraceEvent::UpdateDuration(const TraceTicks& now,
@@ -619,6 +686,29 @@
thread_duration_ = thread_now - thread_timestamp_;
}
+void TraceEvent::EstimateTraceMemoryOverhead(
+ TraceEventMemoryOverhead* overhead) {
+ if (!cached_memory_overhead_estimate_) {
+ cached_memory_overhead_estimate_.reset(new TraceEventMemoryOverhead);
+ cached_memory_overhead_estimate_->Add("TraceEvent", sizeof(*this));
+ // TODO(primiano): parameter_copy_storage_ is refcounted and, in theory,
+ // could be shared by several events and we might overcount. In practice
+ // this is unlikely but it's worth checking.
+ if (parameter_copy_storage_) {
+ cached_memory_overhead_estimate_->AddRefCountedString(
+ *parameter_copy_storage_.get());
+ }
+ for (size_t i = 0; i < kTraceMaxNumArgs; ++i) {
+ if (arg_types_[i] == TRACE_VALUE_TYPE_CONVERTABLE) {
+ convertable_values_[i]->EstimateTraceMemoryOverhead(
+ cached_memory_overhead_estimate_.get());
+ }
+ }
+ cached_memory_overhead_estimate_->AddSelf();
+ }
+ overhead->Update(*cached_memory_overhead_estimate_);
+}
+
// static
void TraceEvent::AppendValueAsJSON(unsigned char type,
TraceEvent::TraceValue value,
@@ -697,34 +787,34 @@
DCHECK(!strchr(name_, '"'));
StringAppendF(out, "{\"pid\":%i,\"tid\":%i,\"ts\":%" PRId64
","
- "\"ph\":\"%c\",\"cat\":\"%s\",\"name\":\"%s\",\"args\":{",
+ "\"ph\":\"%c\",\"cat\":\"%s\",\"name\":\"%s\",\"args\":",
process_id, thread_id_, time_int64, phase_, category_group_name,
name_);
// Output argument names and values, stop at first NULL argument name.
- if (arg_names_[0]) {
- bool allow_args = argument_filter_predicate.is_null() ||
- argument_filter_predicate.Run(category_group_name, name_);
+ bool strip_args = arg_names_[0] && !argument_filter_predicate.is_null() &&
+ !argument_filter_predicate.Run(category_group_name, name_);
- if (allow_args) {
- for (int i = 0; i < kTraceMaxNumArgs && arg_names_[i]; ++i) {
- if (i > 0)
- *out += ",";
- *out += "\"";
- *out += arg_names_[i];
- *out += "\":";
+ if (strip_args) {
+ *out += "\"__stripped__\"";
+ } else {
+ *out += "{";
- if (arg_types_[i] == TRACE_VALUE_TYPE_CONVERTABLE)
- convertable_values_[i]->AppendAsTraceFormat(out);
- else
- AppendValueAsJSON(arg_types_[i], arg_values_[i], out);
- }
- } else {
- *out += "\"stripped\":1";
+ for (int i = 0; i < kTraceMaxNumArgs && arg_names_[i]; ++i) {
+ if (i > 0)
+ *out += ",";
+ *out += "\"";
+ *out += arg_names_[i];
+ *out += "\":";
+
+ if (arg_types_[i] == TRACE_VALUE_TYPE_CONVERTABLE)
+ convertable_values_[i]->AppendAsTraceFormat(out);
+ else
+ AppendValueAsJSON(arg_types_[i], arg_values_[i], out);
}
- }
- *out += "}";
+ *out += "}";
+ }
if (phase_ == TRACE_EVENT_PHASE_COMPLETE) {
int64 duration = duration_.ToInternalValue();
@@ -753,6 +843,9 @@
if (flags_ & TRACE_EVENT_FLAG_HAS_ID)
StringAppendF(out, ",\"id\":\"0x%" PRIx64 "\"", static_cast<uint64>(id_));
+ if (flags_ & TRACE_EVENT_FLAG_BIND_TO_ENCLOSING)
+ StringAppendF(out, ",\"bp\":\"e\"");
+
// Instant events also output their scope.
if (phase_ == TRACE_EVENT_PHASE_INSTANT) {
char scope = '?';
@@ -984,7 +1077,8 @@
////////////////////////////////////////////////////////////////////////////////
class TraceLog::ThreadLocalEventBuffer
- : public MessageLoop::DestructionObserver {
+ : public MessageLoop::DestructionObserver,
+ public MemoryDumpProvider {
public:
ThreadLocalEventBuffer(TraceLog* trace_log);
~ThreadLocalEventBuffer() override;
@@ -1008,6 +1102,9 @@
// MessageLoop::DestructionObserver
void WillDestroyCurrentMessageLoop() override;
+ // MemoryDumpProvider implementation.
+ bool OnMemoryDump(ProcessMemoryDump* pmd) override;
+
void FlushWhileLocked();
void CheckThisIsCurrentBuffer() const {
@@ -1036,6 +1133,10 @@
MessageLoop* message_loop = MessageLoop::current();
message_loop->AddDestructionObserver(this);
+ // This is to report the local memory usage when memory-infra is enabled.
+ MemoryDumpManager::GetInstance()->RegisterDumpProvider(
+ this, ThreadTaskRunnerHandle::Get());
+
AutoLock lock(trace_log->lock_);
trace_log->thread_message_loops_.insert(message_loop);
}
@@ -1043,6 +1144,7 @@
TraceLog::ThreadLocalEventBuffer::~ThreadLocalEventBuffer() {
CheckThisIsCurrentBuffer();
MessageLoop::current()->RemoveDestructionObserver(this);
+ MemoryDumpManager::GetInstance()->UnregisterDumpProvider(this);
// Zero event_count_ happens in either of the following cases:
// - no event generated for the thread;
@@ -1119,6 +1221,17 @@
delete this;
}
+bool TraceLog::ThreadLocalEventBuffer::OnMemoryDump(ProcessMemoryDump* pmd) {
+ if (!chunk_)
+ return true;
+ std::string dump_base_name = StringPrintf(
+ "tracing/thread_%d", static_cast<int>(PlatformThread::CurrentId()));
+ TraceEventMemoryOverhead overhead;
+ chunk_->EstimateTraceMemoryOverhead(&overhead);
+ overhead.DumpInto(dump_base_name.c_str(), pmd);
+ return true;
+}
+
void TraceLog::ThreadLocalEventBuffer::FlushWhileLocked() {
if (!chunk_)
return;
@@ -1195,11 +1308,46 @@
#endif
logged_events_.reset(CreateTraceBuffer());
+
+ MemoryDumpManager::GetInstance()->RegisterDumpProvider(this);
}
TraceLog::~TraceLog() {
}
+void TraceLog::InitializeThreadLocalEventBufferIfSupported() {
+ // A ThreadLocalEventBuffer needs the message loop
+ // - to know when the thread exits;
+ // - to handle the final flush.
+ // For a thread without a message loop or the message loop may be blocked, the
+ // trace events will be added into the main buffer directly.
+ if (thread_blocks_message_loop_.Get() || !MessageLoop::current())
+ return;
+ auto thread_local_event_buffer = thread_local_event_buffer_.Get();
+ if (thread_local_event_buffer &&
+ !CheckGeneration(thread_local_event_buffer->generation())) {
+ delete thread_local_event_buffer;
+ thread_local_event_buffer = NULL;
+ }
+ if (!thread_local_event_buffer) {
+ thread_local_event_buffer = new ThreadLocalEventBuffer(this);
+ thread_local_event_buffer_.Set(thread_local_event_buffer);
+ }
+}
+
+bool TraceLog::OnMemoryDump(ProcessMemoryDump* pmd) {
+ TraceEventMemoryOverhead overhead;
+ overhead.Add("TraceLog", sizeof(*this));
+ {
+ AutoLock lock(lock_);
+ if (logged_events_)
+ logged_events_->EstimateTraceMemoryOverhead(&overhead);
+ }
+ overhead.AddSelf();
+ overhead.DumpInto("tracing/main_trace_log", pmd);
+ return true;
+}
+
const unsigned char* TraceLog::GetCategoryGroupEnabled(
const char* category_group) {
TraceLog* tracelog = GetInstance();
@@ -1239,8 +1387,10 @@
event_callback_trace_config_.IsCategoryGroupEnabled(category_group))
enabled_flag |= ENABLED_FOR_EVENT_CALLBACK;
#if defined(OS_WIN)
- if (base::trace_event::TraceEventETWExport::isETWExportEnabled())
+ if (base::trace_event::TraceEventETWExport::IsCategoryGroupEnabled(
+ category_group)) {
enabled_flag |= ENABLED_FOR_ETW_EXPORT;
+ }
#endif
g_category_group_enabled[category_index] = enabled_flag;
@@ -1627,6 +1777,17 @@
// 4. If any thread hasn't finish its flush in time, finish the flush.
void TraceLog::Flush(const TraceLog::OutputCallback& cb,
bool use_worker_thread) {
+ FlushInternal(cb, use_worker_thread, false);
+}
+
+void TraceLog::CancelTracing(const OutputCallback& cb) {
+ SetDisabled();
+ FlushInternal(cb, false, true);
+}
+
+void TraceLog::FlushInternal(const TraceLog::OutputCallback& cb,
+ bool use_worker_thread,
+ bool discard_events) {
use_worker_thread_ = use_worker_thread;
if (IsEnabled()) {
// Can't flush when tracing is enabled because otherwise PostTask would
@@ -1670,17 +1831,17 @@
if (thread_message_loop_task_runners.size()) {
for (size_t i = 0; i < thread_message_loop_task_runners.size(); ++i) {
thread_message_loop_task_runners[i]->PostTask(
- FROM_HERE,
- Bind(&TraceLog::FlushCurrentThread, Unretained(this), generation));
+ FROM_HERE, Bind(&TraceLog::FlushCurrentThread, Unretained(this),
+ generation, discard_events));
}
flush_task_runner_->PostDelayedTask(
- FROM_HERE,
- Bind(&TraceLog::OnFlushTimeout, Unretained(this), generation),
+ FROM_HERE, Bind(&TraceLog::OnFlushTimeout, Unretained(this), generation,
+ discard_events),
TimeDelta::FromMilliseconds(kThreadFlushTimeoutMs));
return;
}
- FinishFlush(generation);
+ FinishFlush(generation, discard_events);
}
// Usually it runs on a different thread.
@@ -1693,28 +1854,24 @@
// The callback need to be called at least once even if there is no events
// to let the caller know the completion of flush.
- bool has_more_events = true;
- do {
- scoped_refptr<RefCountedString> json_events_str_ptr =
- new RefCountedString();
-
- while (json_events_str_ptr->size() < kTraceEventBufferSizeInBytes) {
- const TraceBufferChunk* chunk = logged_events->NextChunk();
- has_more_events = chunk != NULL;
- if (!chunk)
- break;
- for (size_t j = 0; j < chunk->size(); ++j) {
- if (json_events_str_ptr->size())
- json_events_str_ptr->data().append(",\n");
- chunk->GetEventAt(j)->AppendAsJSON(&(json_events_str_ptr->data()),
- argument_filter_predicate);
+ scoped_refptr<RefCountedString> json_events_str_ptr = new RefCountedString();
+ while (const TraceBufferChunk* chunk = logged_events->NextChunk()) {
+ for (size_t j = 0; j < chunk->size(); ++j) {
+ size_t size = json_events_str_ptr->size();
+ if (size > kTraceEventBufferSizeInBytes) {
+ flush_output_callback.Run(json_events_str_ptr, true);
+ json_events_str_ptr = new RefCountedString();
+ } else if (size) {
+ json_events_str_ptr->data().append(",\n");
}
+ chunk->GetEventAt(j)->AppendAsJSON(&(json_events_str_ptr->data()),
+ argument_filter_predicate);
}
- flush_output_callback.Run(json_events_str_ptr, has_more_events);
- } while (has_more_events);
+ }
+ flush_output_callback.Run(json_events_str_ptr, false);
}
-void TraceLog::FinishFlush(int generation) {
+void TraceLog::FinishFlush(int generation, bool discard_events) {
scoped_ptr<TraceBuffer> previous_logged_events;
OutputCallback flush_output_callback;
TraceEvent::ArgumentFilterPredicate argument_filter_predicate;
@@ -1739,6 +1896,14 @@
}
}
+ if (discard_events) {
+ if (!flush_output_callback.is_null()) {
+ scoped_refptr<RefCountedString> empty_result = new RefCountedString;
+ flush_output_callback.Run(empty_result, false);
+ }
+ return;
+ }
+
if (use_worker_thread_ &&
WorkerPool::PostTask(
FROM_HERE, Bind(&TraceLog::ConvertTraceEventsToTraceFormat,
@@ -1754,7 +1919,7 @@
}
// Run in each thread holding a local event buffer.
-void TraceLog::FlushCurrentThread(int generation) {
+void TraceLog::FlushCurrentThread(int generation, bool discard_events) {
{
AutoLock lock(lock_);
if (!CheckGeneration(generation) || !flush_task_runner_) {
@@ -1772,10 +1937,11 @@
return;
flush_task_runner_->PostTask(
- FROM_HERE, Bind(&TraceLog::FinishFlush, Unretained(this), generation));
+ FROM_HERE, Bind(&TraceLog::FinishFlush, Unretained(this), generation,
+ discard_events));
}
-void TraceLog::OnFlushTimeout(int generation) {
+void TraceLog::OnFlushTimeout(int generation, bool discard_events) {
{
AutoLock lock(lock_);
if (!CheckGeneration(generation) || !flush_task_runner_) {
@@ -1794,7 +1960,7 @@
LOG(WARNING) << "Thread: " << (*it)->thread_name();
}
}
- FinishFlush(generation);
+ FinishFlush(generation, discard_events);
}
void TraceLog::FlushButLeaveBufferIntact(
@@ -1839,7 +2005,7 @@
const unsigned char* arg_types,
const unsigned long long* arg_values,
const scoped_refptr<ConvertableToTraceFormat>* convertable_values,
- unsigned char flags) {
+ unsigned int flags) {
int thread_id = static_cast<int>(base::PlatformThread::CurrentId());
base::TraceTicks now = base::TraceTicks::Now();
return AddTraceEventWithThreadIdAndTimestamp(phase, category_group_enabled,
@@ -1861,7 +2027,7 @@
const unsigned char* arg_types,
const unsigned long long* arg_values,
const scoped_refptr<ConvertableToTraceFormat>* convertable_values,
- unsigned char flags) {
+ unsigned int flags) {
TraceEventHandle handle = { 0, 0, 0 };
if (!*category_group_enabled)
return handle;
@@ -1885,24 +2051,10 @@
OffsetNow() : offset_event_timestamp;
ThreadTicks thread_now = ThreadNow();
- ThreadLocalEventBuffer* thread_local_event_buffer = NULL;
- // A ThreadLocalEventBuffer needs the message loop
- // - to know when the thread exits;
- // - to handle the final flush.
- // For a thread without a message loop or the message loop may be blocked, the
- // trace events will be added into the main buffer directly.
- if (!thread_blocks_message_loop_.Get() && MessageLoop::current()) {
- thread_local_event_buffer = thread_local_event_buffer_.Get();
- if (thread_local_event_buffer &&
- !CheckGeneration(thread_local_event_buffer->generation())) {
- delete thread_local_event_buffer;
- thread_local_event_buffer = NULL;
- }
- if (!thread_local_event_buffer) {
- thread_local_event_buffer = new ThreadLocalEventBuffer(this);
- thread_local_event_buffer_.Set(thread_local_event_buffer);
- }
- }
+ // |thread_local_event_buffer_| can be null if the current thread doesn't have
+ // a message loop or the message loop is blocked.
+ InitializeThreadLocalEventBufferIfSupported();
+ auto thread_local_event_buffer = thread_local_event_buffer_.Get();
// Check and update the current thread name only if the event is for the
// current thread to avoid locks in most cases.
@@ -1927,8 +2079,9 @@
} else {
// This is a thread id that we've seen before, but potentially with a
// new name.
- std::vector<StringPiece> existing_names;
- Tokenize(existing_name->second, ",", &existing_names);
+ std::vector<StringPiece> existing_names = base::SplitStringPiece(
+ existing_name->second, ",", base::KEEP_WHITESPACE,
+ base::SPLIT_WANT_NONEMPTY);
bool found = std::find(existing_names.begin(),
existing_names.end(),
new_name) != existing_names.end();
@@ -2194,9 +2347,8 @@
labels.push_back(it->second);
}
InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, false),
- current_thread_id,
- "process_labels", "labels",
- JoinString(labels, ','));
+ current_thread_id, "process_labels", "labels",
+ base::JoinString(labels, ","));
}
// Thread sort indices.
@@ -2334,6 +2486,11 @@
}
}
+void ConvertableToTraceFormat::EstimateTraceMemoryOverhead(
+ TraceEventMemoryOverhead* overhead) {
+ overhead->Add("ConvertableToTraceFormat(Unknown)", sizeof(*this));
+}
+
} // namespace trace_event
} // namespace base
diff --git a/base/trace_event/trace_event_impl.h b/base/trace_event/trace_event_impl.h
index 7fdc5e3..a6e95f4 100644
--- a/base/trace_event/trace_event_impl.h
+++ b/base/trace_event/trace_event_impl.h
@@ -24,7 +24,9 @@
#include "base/synchronization/lock.h"
#include "base/threading/thread.h"
#include "base/threading/thread_local.h"
+#include "base/trace_event/memory_dump_provider.h"
#include "base/trace_event/trace_config.h"
+#include "base/trace_event/trace_event_memory_overhead.h"
// Older style trace macros with explicit id and extra data
// Only these macros result in publishing data to ETW as currently implemented.
@@ -65,6 +67,8 @@
// appended.
virtual void AppendAsTraceFormat(std::string* out) const = 0;
+ virtual void EstimateTraceMemoryOverhead(TraceEventMemoryOverhead* overhead);
+
std::string ToString() const {
std::string result;
AppendAsTraceFormat(&result);
@@ -117,12 +121,14 @@
const unsigned char* arg_types,
const unsigned long long* arg_values,
const scoped_refptr<ConvertableToTraceFormat>* convertable_values,
- unsigned char flags);
+ unsigned int flags);
void Reset();
void UpdateDuration(const TraceTicks& now, const ThreadTicks& thread_now);
+ void EstimateTraceMemoryOverhead(TraceEventMemoryOverhead*);
+
// Serialize event data to JSON
typedef base::Callback<bool(const char* category_group_name,
const char* event_name)> ArgumentFilterPredicate;
@@ -142,7 +148,7 @@
TimeDelta duration() const { return duration_; }
TimeDelta thread_duration() const { return thread_duration_; }
unsigned long long id() const { return id_; }
- unsigned char flags() const { return flags_; }
+ unsigned int flags() const { return flags_; }
// Exposed for unittesting:
@@ -168,6 +174,7 @@
TimeDelta thread_duration_;
// id_ can be used to store phase-specific data.
unsigned long long id_;
+ scoped_ptr<TraceEventMemoryOverhead> cached_memory_overhead_estimate_;
TraceValue arg_values_[kTraceMaxNumArgs];
const char* arg_names_[kTraceMaxNumArgs];
scoped_refptr<ConvertableToTraceFormat> convertable_values_[kTraceMaxNumArgs];
@@ -176,7 +183,7 @@
scoped_refptr<base::RefCountedString> parameter_copy_storage_;
int thread_id_;
char phase_;
- unsigned char flags_;
+ unsigned int flags_;
unsigned char arg_types_[kTraceMaxNumArgs];
DISALLOW_COPY_AND_ASSIGN(TraceEvent);
@@ -185,10 +192,8 @@
// TraceBufferChunk is the basic unit of TraceBuffer.
class BASE_EXPORT TraceBufferChunk {
public:
- explicit TraceBufferChunk(uint32 seq)
- : next_free_(0),
- seq_(seq) {
- }
+ explicit TraceBufferChunk(uint32 seq);
+ ~TraceBufferChunk();
void Reset(uint32 new_seq);
TraceEvent* AddTraceEvent(size_t* event_index);
@@ -209,10 +214,13 @@
scoped_ptr<TraceBufferChunk> Clone() const;
+ void EstimateTraceMemoryOverhead(TraceEventMemoryOverhead* overhead);
+
static const size_t kTraceBufferChunkSize = 64;
private:
size_t next_free_;
+ scoped_ptr<TraceEventMemoryOverhead> cached_overhead_estimate_when_full_;
TraceEvent chunk_[kTraceBufferChunkSize];
uint32 seq_;
};
@@ -235,6 +243,11 @@
virtual const TraceBufferChunk* NextChunk() = 0;
virtual scoped_ptr<TraceBuffer> CloneForIteration() const = 0;
+
+ // Computes an estimate of the size of the buffer, including all the retained
+ // objects.
+ virtual void EstimateTraceMemoryOverhead(
+ TraceEventMemoryOverhead* overhead) = 0;
};
// TraceResultBuffer collects and converts trace fragments returned by TraceLog
@@ -289,7 +302,7 @@
size_t event_count;
};
-class BASE_EXPORT TraceLog {
+class BASE_EXPORT TraceLog : public MemoryDumpProvider {
public:
enum Mode {
DISABLED = 0,
@@ -321,6 +334,10 @@
// Retrieves a copy (for thread-safety) of the current TraceConfig.
TraceConfig GetCurrentTraceConfig() const;
+ // Initializes the thread-local event buffer, if not already initialized and
+ // if the current thread supports that (has a message loop).
+ void InitializeThreadLocalEventBufferIfSupported();
+
// Enables normal tracing (recording trace events in the trace buffer).
// See TraceConfig comments for details on how to control what categories
// will be traced. If tracing has already been enabled, |category_filter| will
@@ -350,6 +367,8 @@
// on-demand.
class BASE_EXPORT EnabledStateObserver {
public:
+ virtual ~EnabledStateObserver() = default;
+
// Called just after the tracing system becomes enabled, outside of the
// |lock_|. TraceLog::IsEnabled() is true at this point.
virtual void OnTraceLogEnabled() = 0;
@@ -365,6 +384,10 @@
TraceLogStatus GetStatus() const;
bool BufferIsFull() const;
+ // Computes an estimate of the size of the TraceLog including all the retained
+ // objects.
+ void EstimateTraceMemoryOverhead(TraceEventMemoryOverhead* overhead);
+
// Not using base::Callback because of its limited by 7 parameters.
// Also, using primitive type allows directly passing callback from WebCore.
// WARNING: It is possible for the previously set callback to be called
@@ -383,7 +406,7 @@
const char* const arg_names[],
const unsigned char arg_types[],
const unsigned long long arg_values[],
- unsigned char flags);
+ unsigned int flags);
// Enable tracing for EventCallback.
void SetEventCallbackEnabled(const TraceConfig& trace_config,
@@ -407,6 +430,9 @@
void Flush(const OutputCallback& cb, bool use_worker_thread = false);
void FlushButLeaveBufferIntact(const OutputCallback& flush_output_callback);
+ // Cancels tracing and discards collected data.
+ void CancelTracing(const OutputCallback& cb);
+
// Called by TRACE_EVENT* macros, don't call this directly.
// The name parameter is a category group for example:
// TRACE_EVENT0("renderer,webkit", "WebViewImpl::HandleInputEvent")
@@ -427,7 +453,7 @@
const unsigned char* arg_types,
const unsigned long long* arg_values,
const scoped_refptr<ConvertableToTraceFormat>* convertable_values,
- unsigned char flags);
+ unsigned int flags);
TraceEventHandle AddTraceEventWithThreadIdAndTimestamp(
char phase,
const unsigned char* category_group_enabled,
@@ -440,7 +466,7 @@
const unsigned char* arg_types,
const unsigned long long* arg_values,
const scoped_refptr<ConvertableToTraceFormat>* convertable_values,
- unsigned char flags);
+ unsigned int flags);
static void AddTraceEventEtw(char phase,
const char* category_group,
const void* id,
@@ -528,6 +554,9 @@
// by the Singleton class.
friend struct DefaultSingletonTraits<TraceLog>;
+ // MemoryDumpProvider implementation.
+ bool OnMemoryDump(ProcessMemoryDump* pmd) override;
+
// Enable/disable each category group based on the current mode_,
// category_filter_, event_callback_ and event_callback_category_filter_.
// Enable the category group in the enabled mode if category_filter_ matches
@@ -547,7 +576,7 @@
class OptionalAutoLock;
TraceLog();
- ~TraceLog();
+ ~TraceLog() override;
const unsigned char* GetCategoryGroupEnabledInternal(const char* name);
void AddMetadataEventsWhileLocked();
@@ -572,16 +601,20 @@
TraceEvent* GetEventByHandleInternal(TraceEventHandle handle,
OptionalAutoLock* lock);
+ void FlushInternal(const OutputCallback& cb,
+ bool use_worker_thread,
+ bool discard_events);
+
// |generation| is used in the following callbacks to check if the callback
// is called for the flush of the current |logged_events_|.
- void FlushCurrentThread(int generation);
+ void FlushCurrentThread(int generation, bool discard_events);
// Usually it runs on a different thread.
static void ConvertTraceEventsToTraceFormat(
scoped_ptr<TraceBuffer> logged_events,
const TraceLog::OutputCallback& flush_output_callback,
const TraceEvent::ArgumentFilterPredicate& argument_filter_predicate);
- void FinishFlush(int generation);
- void OnFlushTimeout(int generation);
+ void FinishFlush(int generation, bool discard_events);
+ void OnFlushTimeout(int generation, bool discard_events);
int generation() const {
return static_cast<int>(subtle::NoBarrier_Load(&generation_));
diff --git a/base/trace_event/trace_event_memory.cc b/base/trace_event/trace_event_memory.cc
index 8959589..73c8536 100644
--- a/base/trace_event/trace_event_memory.cc
+++ b/base/trace_event/trace_event_memory.cc
@@ -11,6 +11,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/threading/thread_local_storage.h"
#include "base/trace_event/trace_event.h"
@@ -318,9 +319,9 @@
input_string.assign(input);
}
- std::vector<std::string> lines;
- size_t line_count = Tokenize(input_string, "\n", &lines);
- if (line_count == 0) {
+ std::vector<std::string> lines = base::SplitString(
+ input_string, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+ if (lines.empty()) {
DLOG(WARNING) << "No lines found";
return;
}
@@ -330,10 +331,8 @@
AppendHeapProfileTotalsAsTraceFormat(lines[0], output);
// Handle the following stack trace lines.
- for (size_t i = 1; i < line_count; ++i) {
- const std::string& line = lines[i];
- AppendHeapProfileLineAsTraceFormat(line, output);
- }
+ for (size_t i = 1; i < lines.size(); i++)
+ AppendHeapProfileLineAsTraceFormat(lines[i], output);
output->append("]\n");
}
@@ -348,8 +347,8 @@
// 55227 = Outstanding bytes (malloc bytes - free bytes)
// 14653 = Total allocations (mallocs)
// 2624014 = Total bytes (malloc bytes)
- std::vector<std::string> tokens;
- Tokenize(line, " :[]@", &tokens);
+ std::vector<std::string> tokens = base::SplitString(
+ line, " :[]@", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
if (tokens.size() < 4) {
DLOG(WARNING) << "Invalid totals line " << line;
return;
@@ -378,8 +377,8 @@
// 0x7fa7fa9b9ba0 0x7fa7f4b3be13 = Stack trace represented as pointers to
// static strings from trace event categories
// and names.
- std::vector<std::string> tokens;
- Tokenize(line, " :[]@", &tokens);
+ std::vector<std::string> tokens = base::SplitString(
+ line, " :[]@", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
// It's valid to have no stack addresses, so only require 4 tokens.
if (tokens.size() < 4) {
DLOG(WARNING) << "Invalid line " << line;
diff --git a/base/trace_event/trace_event_memory.h b/base/trace_event/trace_event_memory.h
index e2b3ae9..5b63db0 100644
--- a/base/trace_event/trace_event_memory.h
+++ b/base/trace_event/trace_event_memory.h
@@ -43,7 +43,7 @@
HeapProfilerStartFunction heap_profiler_start_function,
HeapProfilerStopFunction heap_profiler_stop_function,
GetHeapProfileFunction get_heap_profile_function);
- virtual ~TraceMemoryController();
+ ~TraceMemoryController() override;
// base::trace_event::TraceLog::EnabledStateChangedObserver overrides:
void OnTraceLogEnabled() override;
diff --git a/base/trace_event/trace_event_memory_overhead.cc b/base/trace_event/trace_event_memory_overhead.cc
new file mode 100644
index 0000000..9aea5c1
--- /dev/null
+++ b/base/trace_event/trace_event_memory_overhead.cc
@@ -0,0 +1,151 @@
+// 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/trace_event/trace_event_memory_overhead.h"
+
+#include <algorithm>
+
+#include "base/memory/ref_counted_memory.h"
+#include "base/strings/stringprintf.h"
+#include "base/trace_event/memory_allocator_dump.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "base/values.h"
+
+namespace {
+size_t RoundUp(size_t size, size_t alignment) {
+ return (size + alignment - 1) & ~(alignment - 1);
+}
+} // namespace
+
+namespace base {
+namespace trace_event {
+
+TraceEventMemoryOverhead::TraceEventMemoryOverhead() {}
+
+TraceEventMemoryOverhead::~TraceEventMemoryOverhead() {}
+
+void TraceEventMemoryOverhead::AddOrCreateInternal(
+ const char* object_type,
+ size_t count,
+ size_t allocated_size_in_bytes,
+ size_t resident_size_in_bytes) {
+ auto it = allocated_objects_.find(object_type);
+ if (it == allocated_objects_.end()) {
+ allocated_objects_.insert(std::make_pair(
+ object_type, ObjectCountAndSize({count, allocated_size_in_bytes,
+ resident_size_in_bytes})));
+ return;
+ }
+ it->second.count += count;
+ it->second.allocated_size_in_bytes += allocated_size_in_bytes;
+ it->second.resident_size_in_bytes += resident_size_in_bytes;
+}
+
+void TraceEventMemoryOverhead::Add(const char* object_type,
+ size_t allocated_size_in_bytes) {
+ Add(object_type, allocated_size_in_bytes, allocated_size_in_bytes);
+}
+
+void TraceEventMemoryOverhead::Add(const char* object_type,
+ size_t allocated_size_in_bytes,
+ size_t resident_size_in_bytes) {
+ AddOrCreateInternal(object_type, 1, allocated_size_in_bytes,
+ resident_size_in_bytes);
+}
+
+void TraceEventMemoryOverhead::AddString(const std::string& str) {
+ // The number below are empirical and mainly based on profiling of real-world
+ // std::string implementations:
+ // - even short string end up malloc()-inc at least 32 bytes.
+ // - longer stings seem to malloc() multiples of 16 bytes.
+ Add("std::string",
+ sizeof(std::string) + std::max<size_t>(RoundUp(str.capacity(), 16), 32u));
+}
+
+void TraceEventMemoryOverhead::AddRefCountedString(
+ const RefCountedString& str) {
+ Add("RefCountedString", sizeof(RefCountedString));
+ AddString(str.data());
+}
+
+void TraceEventMemoryOverhead::AddValue(const Value& value) {
+ switch (value.GetType()) {
+ case Value::TYPE_NULL:
+ case Value::TYPE_BOOLEAN:
+ case Value::TYPE_INTEGER:
+ case Value::TYPE_DOUBLE:
+ Add("FundamentalValue", sizeof(Value));
+ break;
+
+ case Value::TYPE_STRING: {
+ const StringValue* string_value = nullptr;
+ value.GetAsString(&string_value);
+ Add("StringValue", sizeof(StringValue));
+ AddString(string_value->GetString());
+ } break;
+
+ case Value::TYPE_BINARY: {
+ const BinaryValue* binary_value = nullptr;
+ value.GetAsBinary(&binary_value);
+ Add("BinaryValue", sizeof(BinaryValue) + binary_value->GetSize());
+ } break;
+
+ case Value::TYPE_DICTIONARY: {
+ const DictionaryValue* dictionary_value = nullptr;
+ value.GetAsDictionary(&dictionary_value);
+ Add("DictionaryValue", sizeof(DictionaryValue));
+ for (DictionaryValue::Iterator it(*dictionary_value); !it.IsAtEnd();
+ it.Advance()) {
+ AddString(it.key());
+ AddValue(it.value());
+ }
+ } break;
+
+ case Value::TYPE_LIST: {
+ const ListValue* list_value = nullptr;
+ value.GetAsList(&list_value);
+ Add("ListValue", sizeof(ListValue));
+ for (const Value* v : *list_value)
+ AddValue(*v);
+ } break;
+
+ default:
+ NOTREACHED();
+ }
+}
+
+void TraceEventMemoryOverhead::AddSelf() {
+ size_t estimated_size = sizeof(*this);
+ // If the SmallMap did overflow its static capacity, its elements will be
+ // allocated on the heap and have to be accounted separately.
+ if (allocated_objects_.UsingFullMap())
+ estimated_size += sizeof(map_type::value_type) * allocated_objects_.size();
+ Add("TraceEventMemoryOverhead", estimated_size);
+}
+
+void TraceEventMemoryOverhead::Update(const TraceEventMemoryOverhead& other) {
+ for (const auto& it : other.allocated_objects_) {
+ AddOrCreateInternal(it.first, it.second.count,
+ it.second.allocated_size_in_bytes,
+ it.second.resident_size_in_bytes);
+ }
+}
+
+void TraceEventMemoryOverhead::DumpInto(const char* base_name,
+ ProcessMemoryDump* pmd) const {
+ for (const auto& it : allocated_objects_) {
+ std::string dump_name = StringPrintf("%s/%s", base_name, it.first);
+ MemoryAllocatorDump* mad = pmd->CreateAllocatorDump(dump_name);
+ mad->AddScalar(MemoryAllocatorDump::kNameSize,
+ MemoryAllocatorDump::kUnitsBytes,
+ it.second.allocated_size_in_bytes);
+ mad->AddScalar("resident_size", MemoryAllocatorDump::kUnitsBytes,
+ it.second.resident_size_in_bytes);
+ mad->AddScalar(MemoryAllocatorDump::kNameObjectsCount,
+ MemoryAllocatorDump::kUnitsObjects, it.second.count);
+ }
+}
+
+} // namespace trace_event
+} // namespace base
diff --git a/base/trace_event/trace_event_memory_overhead.h b/base/trace_event/trace_event_memory_overhead.h
new file mode 100644
index 0000000..8ecf12d
--- /dev/null
+++ b/base/trace_event/trace_event_memory_overhead.h
@@ -0,0 +1,71 @@
+// 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_TRACE_EVENT_TRACE_EVENT_MEMORY_OVERHEAD_H_
+#define BASE_TRACE_EVENT_TRACE_EVENT_MEMORY_OVERHEAD_H_
+
+#include "base/base_export.h"
+#include "base/containers/hash_tables.h"
+#include "base/containers/small_map.h"
+
+namespace base {
+
+class RefCountedString;
+class Value;
+
+namespace trace_event {
+
+class ProcessMemoryDump;
+
+// Used to estimate the memory overhead of the tracing infrastructure.
+class BASE_EXPORT TraceEventMemoryOverhead {
+ public:
+ TraceEventMemoryOverhead();
+ ~TraceEventMemoryOverhead();
+
+ // Use this method to account the overhead of an object for which an estimate
+ // is known for both the allocated and resident memory.
+ void Add(const char* object_type,
+ size_t allocated_size_in_bytes,
+ size_t resident_size_in_bytes);
+
+ // Similar to Add() above, but assumes that
+ // |resident_size_in_bytes| == |allocated_size_in_bytes|.
+ void Add(const char* object_type, size_t allocated_size_in_bytes);
+
+ // Specialized profiling functions for commonly used object types.
+ void AddString(const std::string& str);
+ void AddValue(const Value& value);
+ void AddRefCountedString(const RefCountedString& str);
+
+ // Call this after all the Add* methods above to account the memory used by
+ // this TraceEventMemoryOverhead instance itself.
+ void AddSelf();
+
+ // Adds up and merges all the values from |other| to this instance.
+ void Update(const TraceEventMemoryOverhead& other);
+
+ void DumpInto(const char* base_name, ProcessMemoryDump* pmd) const;
+
+ private:
+ struct ObjectCountAndSize {
+ size_t count;
+ size_t allocated_size_in_bytes;
+ size_t resident_size_in_bytes;
+ };
+ using map_type = SmallMap<hash_map<const char*, ObjectCountAndSize>, 16>;
+ map_type allocated_objects_;
+
+ void AddOrCreateInternal(const char* object_type,
+ size_t count,
+ size_t allocated_size_in_bytes,
+ size_t resident_size_in_bytes);
+
+ DISALLOW_COPY_AND_ASSIGN(TraceEventMemoryOverhead);
+};
+
+} // namespace trace_event
+} // namespace base
+
+#endif // BASE_TRACE_EVENT_TRACE_EVENT_MEMORY_OVERHEAD_H_
diff --git a/base/trace_event/trace_event_system_stats_monitor.h b/base/trace_event/trace_event_system_stats_monitor.h
index 051669a..7a63a14 100644
--- a/base/trace_event/trace_event_system_stats_monitor.h
+++ b/base/trace_event/trace_event_system_stats_monitor.h
@@ -33,7 +33,7 @@
explicit TraceEventSystemStatsMonitor(
scoped_refptr<SingleThreadTaskRunner> task_runner);
- virtual ~TraceEventSystemStatsMonitor();
+ ~TraceEventSystemStatsMonitor() override;
// base::trace_event::TraceLog::EnabledStateChangedObserver overrides:
void OnTraceLogEnabled() override;
diff --git a/base/trace_event/trace_event_unittest.cc b/base/trace_event/trace_event_unittest.cc
index 2449d54..8248f28 100644
--- a/base/trace_event/trace_event_unittest.cc
+++ b/base/trace_event/trace_event_unittest.cc
@@ -15,6 +15,7 @@
#include "base/memory/singleton.h"
#include "base/process/process_handle.h"
#include "base/single_thread_task_runner.h"
+#include "base/strings/pattern.h"
#include "base/strings/stringprintf.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/platform_thread.h"
@@ -47,6 +48,8 @@
const char kAsyncIdStr[] = "0x5";
const int kAsyncId2 = 6;
const char kAsyncId2Str[] = "0x6";
+const int kFlowId = 7;
+const char kFlowIdStr[] = "0x7";
const char kRecordAllCategoryFilter[] = "*";
@@ -85,7 +88,14 @@
TraceLog::RECORDING_MODE);
}
+ void CancelTrace() {
+ WaitableEvent flush_complete_event(false, false);
+ CancelTraceAsync(&flush_complete_event);
+ flush_complete_event.Wait();
+ }
+
void EndTraceAndFlush() {
+ num_flush_callbacks_ = 0;
WaitableEvent flush_complete_event(false, false);
EndTraceAndFlushAsync(&flush_complete_event);
flush_complete_event.Wait();
@@ -103,6 +113,13 @@
flush_complete_event.Wait();
}
+ void CancelTraceAsync(WaitableEvent* flush_complete_event) {
+ TraceLog::GetInstance()->CancelTracing(
+ base::Bind(&TraceEventTestFixture::OnTraceDataCollected,
+ base::Unretained(static_cast<TraceEventTestFixture*>(this)),
+ base::Unretained(flush_complete_event)));
+ }
+
void EndTraceAndFlushAsync(WaitableEvent* flush_complete_event) {
TraceLog::GetInstance()->SetDisabled();
TraceLog::GetInstance()->Flush(
@@ -134,6 +151,7 @@
ASSERT_FALSE(tracelog->IsEnabled());
trace_buffer_.SetOutputCallback(json_output_.GetCallback());
event_watch_notification_ = 0;
+ num_flush_callbacks_ = 0;
}
void TearDown() override {
if (TraceLog::GetInstance())
@@ -150,6 +168,7 @@
TraceResultBuffer trace_buffer_;
TraceResultBuffer::SimpleOutput json_output_;
int event_watch_notification_;
+ size_t num_flush_callbacks_;
private:
// We want our singleton torn down after each test.
@@ -161,6 +180,10 @@
WaitableEvent* flush_complete_event,
const scoped_refptr<base::RefCountedString>& events_str,
bool has_more_events) {
+ num_flush_callbacks_++;
+ if (num_flush_callbacks_ > 1) {
+ EXPECT_FALSE(events_str->data().empty());
+ }
AutoLock lock(lock_);
json_output_.json_output.clear();
trace_buffer_.Start();
@@ -430,6 +453,12 @@
"name1", "value1",
"name2", "value2");
+ TRACE_EVENT_FLOW_BEGIN0("all", "TRACE_EVENT_FLOW_BEGIN0 call", kFlowId);
+ TRACE_EVENT_FLOW_STEP0("all", "TRACE_EVENT_FLOW_STEP0 call", kFlowId,
+ "step1");
+ TRACE_EVENT_FLOW_END_BIND_TO_ENCLOSING0(
+ "all", "TRACE_EVENT_FLOW_END_BIND_TO_ENCLOSING0 call", kFlowId);
+
TRACE_EVENT_BEGIN_ETW("TRACE_EVENT_BEGIN_ETW0 call", kAsyncId, NULL);
TRACE_EVENT_BEGIN_ETW("TRACE_EVENT_BEGIN_ETW1 call", kAsyncId, "value");
TRACE_EVENT_END_ETW("TRACE_EVENT_END_ETW0 call", kAsyncId, NULL);
@@ -613,6 +642,17 @@
EXPECT_SUB_FIND_("name2");
EXPECT_SUB_FIND_("value2");
+ EXPECT_FIND_("TRACE_EVENT_FLOW_BEGIN0 call");
+ EXPECT_SUB_FIND_("id");
+ EXPECT_SUB_FIND_(kFlowIdStr);
+ EXPECT_FIND_("TRACE_EVENT_FLOW_STEP0 call");
+ EXPECT_SUB_FIND_("id");
+ EXPECT_SUB_FIND_(kFlowIdStr);
+ EXPECT_SUB_FIND_("step1");
+ EXPECT_FIND_("TRACE_EVENT_FLOW_END_BIND_TO_ENCLOSING0 call");
+ EXPECT_SUB_FIND_("id");
+ EXPECT_SUB_FIND_(kFlowIdStr);
+
EXPECT_FIND_("TRACE_EVENT_BEGIN_ETW0 call");
EXPECT_SUB_FIND_("id");
EXPECT_SUB_FIND_(kAsyncIdStr);
@@ -882,6 +922,19 @@
ValidateAllTraceMacrosCreatedData(trace_parsed_);
}
+// Emit some events and validate that only empty strings are received
+// if we tell Flush() to discard events.
+TEST_F(TraceEventTestFixture, DataDiscarded) {
+ TraceLog::GetInstance()->SetEnabled(TraceConfig(kRecordAllCategoryFilter, ""),
+ TraceLog::RECORDING_MODE);
+
+ TraceWithAllMacroVariants(NULL);
+
+ CancelTrace();
+
+ EXPECT_TRUE(trace_parsed_.empty());
+}
+
class MockEnabledStateChangedObserver :
public TraceLog::EnabledStateObserver {
public:
@@ -968,7 +1021,7 @@
: public TraceLog::EnabledStateObserver {
public:
AfterStateChangeEnabledStateObserver() {}
- virtual ~AfterStateChangeEnabledStateObserver() {}
+ ~AfterStateChangeEnabledStateObserver() override {}
// TraceLog::EnabledStateObserver overrides:
void OnTraceLogEnabled() override {
@@ -999,7 +1052,7 @@
: public TraceLog::EnabledStateObserver {
public:
SelfRemovingEnabledStateObserver() {}
- virtual ~SelfRemovingEnabledStateObserver() {}
+ ~SelfRemovingEnabledStateObserver() override {}
// TraceLog::EnabledStateObserver overrides:
void OnTraceLogEnabled() override {}
@@ -1052,6 +1105,43 @@
EndTraceAndFlush();
}
+TEST_F(TraceEventTestFixture, TestTraceFlush) {
+ size_t min_traces = 1;
+ size_t max_traces = 1;
+ do {
+ max_traces *= 2;
+ TraceLog::GetInstance()->SetEnabled(TraceConfig(),
+ TraceLog::RECORDING_MODE);
+ for (size_t i = 0; i < max_traces; i++) {
+ TRACE_EVENT_INSTANT0("x", "y", TRACE_EVENT_SCOPE_THREAD);
+ }
+ EndTraceAndFlush();
+ } while (num_flush_callbacks_ < 2);
+
+ while (min_traces + 50 < max_traces) {
+ size_t traces = (min_traces + max_traces) / 2;
+ TraceLog::GetInstance()->SetEnabled(TraceConfig(),
+ TraceLog::RECORDING_MODE);
+ for (size_t i = 0; i < traces; i++) {
+ TRACE_EVENT_INSTANT0("x", "y", TRACE_EVENT_SCOPE_THREAD);
+ }
+ EndTraceAndFlush();
+ if (num_flush_callbacks_ < 2) {
+ min_traces = traces - 10;
+ } else {
+ max_traces = traces + 10;
+ }
+ }
+
+ for (size_t traces = min_traces; traces < max_traces; traces++) {
+ TraceLog::GetInstance()->SetEnabled(TraceConfig(),
+ TraceLog::RECORDING_MODE);
+ for (size_t i = 0; i < traces; i++) {
+ TRACE_EVENT_INSTANT0("x", "y", TRACE_EVENT_SCOPE_THREAD);
+ }
+ EndTraceAndFlush();
+ }
+}
// Test that categories work.
TEST_F(TraceEventTestFixture, Categories) {
@@ -2149,8 +2239,8 @@
bool IsTraceEventArgsWhitelisted(const char* category_group_name,
const char* event_name) {
- if (MatchPattern(category_group_name, "toplevel") &&
- MatchPattern(event_name, "*")) {
+ if (base::MatchPattern(category_group_name, "toplevel") &&
+ base::MatchPattern(event_name, "*")) {
return true;
}
@@ -2187,7 +2277,10 @@
dict->GetDictionary("args", &args_dict);
ASSERT_TRUE(args_dict);
EXPECT_FALSE(args_dict->GetInteger("int_two", &int_value));
- EXPECT_TRUE(args_dict->GetInteger("stripped", &int_value));
+
+ std::string args_string;
+ EXPECT_TRUE(dict->GetString("args", &args_string));
+ EXPECT_EQ(args_string, "__stripped__");
}
class TraceEventCallbackTest : public TraceEventTestFixture {
@@ -2247,7 +2340,7 @@
const char* const arg_names[],
const unsigned char arg_types[],
const unsigned long long arg_values[],
- unsigned char flags) {
+ unsigned int flags) {
s_instance->collected_events_phases_.push_back(phase);
s_instance->collected_events_categories_.push_back(
TraceLog::GetCategoryGroupName(category_group_enabled));
diff --git a/base/trace_event/trace_event_win.cc b/base/trace_event/trace_event_win.cc
index ebb55c8..fdbd35a 100644
--- a/base/trace_event/trace_event_win.cc
+++ b/base/trace_event/trace_event_win.cc
@@ -16,16 +16,24 @@
// {3DADA31D-19EF-4dc1-B345-037927193422}
const GUID kChromeTraceProviderName = {
- 0x3dada31d, 0x19ef, 0x4dc1, 0xb3, 0x45, 0x3, 0x79, 0x27, 0x19, 0x34, 0x22 };
+ 0x3dada31d,
+ 0x19ef,
+ 0x4dc1,
+ {0xb3, 0x45, 0x3, 0x79, 0x27, 0x19, 0x34, 0x22}};
// {B967AE67-BB22-49d7-9406-55D91EE1D560}
const GUID kTraceEventClass32 = {
- 0xb967ae67, 0xbb22, 0x49d7, 0x94, 0x6, 0x55, 0xd9, 0x1e, 0xe1, 0xd5, 0x60 };
+ 0xb967ae67,
+ 0xbb22,
+ 0x49d7,
+ {0x94, 0x6, 0x55, 0xd9, 0x1e, 0xe1, 0xd5, 0x60}};
// {97BE602D-2930-4ac3-8046-B6763B631DFE}
const GUID kTraceEventClass64 = {
- 0x97be602d, 0x2930, 0x4ac3, 0x80, 0x46, 0xb6, 0x76, 0x3b, 0x63, 0x1d, 0xfe};
-
+ 0x97be602d,
+ 0x2930,
+ 0x4ac3,
+ {0x80, 0x46, 0xb6, 0x76, 0x3b, 0x63, 0x1d, 0xfe}};
TraceEventETWProvider::TraceEventETWProvider() :
EtwTraceProvider(kChromeTraceProviderName) {
diff --git a/base/trace_event/winheap_dump_provider_win.cc b/base/trace_event/winheap_dump_provider_win.cc
index 82bb016..d56d8d3 100644
--- a/base/trace_event/winheap_dump_provider_win.cc
+++ b/base/trace_event/winheap_dump_provider_win.cc
@@ -6,6 +6,7 @@
#include <windows.h>
+#include "base/debug/profiler.h"
#include "base/trace_event/process_memory_dump.h"
#include "base/win/windows_version.h"
@@ -59,6 +60,13 @@
if (base::win::GetVersion() < base::win::VERSION_VISTA)
return false;
+// Disable this dump provider for the SyzyASan instrumented build
+// because they don't support the heap walking functions yet.
+#if defined(SYZYASAN)
+ if (base::debug::IsBinaryInstrumented())
+ return false;
+#endif
+
// Retrieves the number of heaps in the current process.
DWORD number_of_heaps = ::GetProcessHeaps(0, NULL);
WinHeapInfo all_heap_info = {0};
diff --git a/base/values.cc b/base/values.cc
index 4534d27..5374d6c 100644
--- a/base/values.cc
+++ b/base/values.cc
@@ -482,27 +482,29 @@
SetWithoutPathExpansion(path, new StringValue(in_value));
}
-bool DictionaryValue::Get(const std::string& path,
- const Value** out_value) const {
+bool DictionaryValue::Get(StringPiece path, const Value** out_value) const {
DCHECK(IsStringUTF8(path));
- std::string current_path(path);
+ StringPiece current_path(path);
const DictionaryValue* current_dictionary = this;
for (size_t delimiter_position = current_path.find('.');
delimiter_position != std::string::npos;
delimiter_position = current_path.find('.')) {
const DictionaryValue* child_dictionary = NULL;
- if (!current_dictionary->GetDictionary(
- current_path.substr(0, delimiter_position), &child_dictionary))
+ if (!current_dictionary->GetDictionaryWithoutPathExpansion(
+ current_path.substr(0, delimiter_position).as_string(),
+ &child_dictionary)) {
return false;
+ }
current_dictionary = child_dictionary;
- current_path.erase(0, delimiter_position + 1);
+ current_path = current_path.substr(delimiter_position + 1);
}
- return current_dictionary->GetWithoutPathExpansion(current_path, out_value);
+ return current_dictionary->GetWithoutPathExpansion(current_path.as_string(),
+ out_value);
}
-bool DictionaryValue::Get(const std::string& path, Value** out_value) {
+bool DictionaryValue::Get(StringPiece path, Value** out_value) {
return static_cast<const DictionaryValue&>(*this).Get(
path,
const_cast<const Value**>(out_value));
@@ -588,7 +590,7 @@
const_cast<const BinaryValue**>(out_value));
}
-bool DictionaryValue::GetDictionary(const std::string& path,
+bool DictionaryValue::GetDictionary(StringPiece path,
const DictionaryValue** out_value) const {
const Value* value;
bool result = Get(path, &value);
@@ -601,7 +603,7 @@
return true;
}
-bool DictionaryValue::GetDictionary(const std::string& path,
+bool DictionaryValue::GetDictionary(StringPiece path,
DictionaryValue** out_value) {
return static_cast<const DictionaryValue&>(*this).GetDictionary(
path,
diff --git a/base/values.h b/base/values.h
index 7feef9d..0ff6217 100644
--- a/base/values.h
+++ b/base/values.h
@@ -30,6 +30,7 @@
#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/string16.h"
+#include "base/strings/string_piece.h"
namespace base {
@@ -270,8 +271,8 @@
// Otherwise, it will return false and |out_value| will be untouched.
// Note that the dictionary always owns the value that's returned.
// |out_value| is optional and will only be set if non-NULL.
- bool Get(const std::string& path, const Value** out_value) const;
- bool Get(const std::string& path, Value** out_value);
+ bool Get(StringPiece path, const Value** out_value) const;
+ bool Get(StringPiece path, Value** out_value);
// These are convenience forms of Get(). The value will be retrieved
// and the return value will be true if the path is valid and the value at
@@ -287,9 +288,8 @@
bool GetStringASCII(const std::string& path, std::string* out_value) const;
bool GetBinary(const std::string& path, const BinaryValue** out_value) const;
bool GetBinary(const std::string& path, BinaryValue** out_value);
- bool GetDictionary(const std::string& path,
- const DictionaryValue** out_value) const;
- bool GetDictionary(const std::string& path, DictionaryValue** out_value);
+ bool GetDictionary(StringPiece path, const DictionaryValue** out_value) const;
+ bool GetDictionary(StringPiece path, DictionaryValue** out_value);
bool GetList(const std::string& path, const ListValue** out_value) const;
bool GetList(const std::string& path, ListValue** out_value);
diff --git a/base/version.cc b/base/version.cc
index ede8a45..228dcb8 100644
--- a/base/version.cc
+++ b/base/version.cc
@@ -24,15 +24,17 @@
// parsed successfully, false otherwise.
bool ParseVersionNumbers(const std::string& version_str,
std::vector<uint32_t>* parsed) {
- std::vector<std::string> numbers;
- SplitString(version_str, '.', &numbers);
+ std::vector<StringPiece> numbers =
+ SplitStringPiece(version_str, ".", KEEP_WHITESPACE, SPLIT_WANT_ALL);
if (numbers.empty())
return false;
- for (std::vector<std::string>::const_iterator it = numbers.begin();
- it != numbers.end(); ++it) {
- if (StartsWithASCII(*it, "+", false))
+ for (auto it = numbers.begin(); it != numbers.end(); ++it) {
+ if (StartsWith(*it, "+", CompareCase::SENSITIVE))
return false;
+
+ // TODO(brettw) when we have a StringPiece version of StringToUint, delete
+ // this string conversion.
unsigned int num;
if (!StringToUint(*it, &num))
return false;
@@ -98,8 +100,8 @@
// static
bool Version::IsValidWildcardString(const std::string& wildcard_string) {
std::string version_string = wildcard_string;
- if (EndsWith(wildcard_string.c_str(), ".*", false))
- version_string = wildcard_string.substr(0, wildcard_string.size() - 2);
+ if (EndsWith(version_string, ".*", CompareCase::SENSITIVE))
+ version_string.resize(version_string.size() - 2);
Version version(version_string);
return version.IsValid();
@@ -117,7 +119,7 @@
DCHECK(Version::IsValidWildcardString(wildcard_string));
// Default behavior if the string doesn't end with a wildcard.
- if (!EndsWith(wildcard_string.c_str(), ".*", false)) {
+ if (!EndsWith(wildcard_string, ".*", CompareCase::SENSITIVE)) {
Version version(wildcard_string);
DCHECK(version.IsValid());
return CompareTo(version);
diff --git a/base/win/iat_patch_function.cc b/base/win/iat_patch_function.cc
index 13acd65..2e6ed40 100644
--- a/base/win/iat_patch_function.cc
+++ b/base/win/iat_patch_function.cc
@@ -219,10 +219,9 @@
IATPatchFunction::IATPatchFunction()
: module_handle_(NULL),
+ intercept_function_(NULL),
original_function_(NULL),
- iat_thunk_(NULL),
- intercept_function_(NULL) {
-}
+ iat_thunk_(NULL) {}
IATPatchFunction::~IATPatchFunction() {
if (NULL != intercept_function_) {
diff --git a/base/win/scoped_comptr.h b/base/win/scoped_comptr.h
index 373c0c3..ade12fe 100644
--- a/base/win/scoped_comptr.h
+++ b/base/win/scoped_comptr.h
@@ -52,24 +52,24 @@
// Note that this function equates to IUnknown::Release and should not
// be confused with e.g. scoped_ptr::release().
void Release() {
- if (ptr_ != NULL) {
- ptr_->Release();
- ptr_ = NULL;
+ if (this->ptr_ != NULL) {
+ this->ptr_->Release();
+ this->ptr_ = NULL;
}
}
// Sets the internal pointer to NULL and returns the held object without
// releasing the reference.
Interface* Detach() {
- Interface* p = ptr_;
- ptr_ = NULL;
+ Interface* p = this->ptr_;
+ this->ptr_ = NULL;
return p;
}
// Accepts an interface pointer that has already been addref-ed.
void Attach(Interface* p) {
- DCHECK(!ptr_);
- ptr_ = p;
+ DCHECK(!this->ptr_);
+ this->ptr_ = p;
}
// Retrieves the pointer address.
@@ -77,8 +77,8 @@
// The function DCHECKs on the current value being NULL.
// Usage: Foo(p.Receive());
Interface** Receive() {
- DCHECK(!ptr_) << "Object leak. Pointer must be NULL";
- return &ptr_;
+ DCHECK(!this->ptr_) << "Object leak. Pointer must be NULL";
+ return &this->ptr_;
}
// A convenience for whenever a void pointer is needed as an out argument.
@@ -89,18 +89,18 @@
template <class Query>
HRESULT QueryInterface(Query** p) {
DCHECK(p != NULL);
- DCHECK(ptr_ != NULL);
+ DCHECK(this->ptr_ != NULL);
// IUnknown already has a template version of QueryInterface
// so the iid parameter is implicit here. The only thing this
// function adds are the DCHECKs.
- return ptr_->QueryInterface(p);
+ return this->ptr_->QueryInterface(p);
}
// QI for times when the IID is not associated with the type.
HRESULT QueryInterface(const IID& iid, void** obj) {
DCHECK(obj != NULL);
- DCHECK(ptr_ != NULL);
- return ptr_->QueryInterface(iid, obj);
+ DCHECK(this->ptr_ != NULL);
+ return this->ptr_->QueryInterface(iid, obj);
}
// Queries |other| for the interface this object wraps and returns the
@@ -113,18 +113,18 @@
// Convenience wrapper around CoCreateInstance
HRESULT CreateInstance(const CLSID& clsid, IUnknown* outer = NULL,
DWORD context = CLSCTX_ALL) {
- DCHECK(!ptr_);
+ DCHECK(!this->ptr_);
HRESULT hr = ::CoCreateInstance(clsid, outer, context, *interface_id,
- reinterpret_cast<void**>(&ptr_));
+ reinterpret_cast<void**>(&this->ptr_));
return hr;
}
// Checks if the identity of |other| and this object is the same.
bool IsSameObject(IUnknown* other) {
- if (!other && !ptr_)
+ if (!other && !this->ptr_)
return true;
- if (!other || !ptr_)
+ if (!other || !this->ptr_)
return false;
ScopedComPtr<IUnknown> my_identity;
@@ -147,8 +147,8 @@
// by statically casting the ScopedComPtr instance to the wrapped interface
// and then making the call... but generally that shouldn't be necessary.
BlockIUnknownMethods* operator->() const {
- DCHECK(ptr_ != NULL);
- return reinterpret_cast<BlockIUnknownMethods*>(ptr_);
+ DCHECK(this->ptr_ != NULL);
+ return reinterpret_cast<BlockIUnknownMethods*>(this->ptr_);
}
// Pull in operator=() from the parent class.
diff --git a/base/win/scoped_comptr_unittest.cc b/base/win/scoped_comptr_unittest.cc
index d38752d..23090b0 100644
--- a/base/win/scoped_comptr_unittest.cc
+++ b/base/win/scoped_comptr_unittest.cc
@@ -25,8 +25,10 @@
};
extern const IID dummy_iid;
-const IID dummy_iid = { 0x12345678u, 0x1234u, 0x5678u, 01, 23, 45, 67, 89,
- 01, 23, 45 };
+const IID dummy_iid = {0x12345678u,
+ 0x1234u,
+ 0x5678u,
+ {01, 23, 45, 67, 89, 01, 23, 45}};
} // namespace
diff --git a/base/win/scoped_variant.cc b/base/win/scoped_variant.cc
index f57ab93..2cf2657 100644
--- a/base/win/scoped_variant.cc
+++ b/base/win/scoped_variant.cc
@@ -9,7 +9,7 @@
namespace win {
// Global, const instance of an empty variant.
-const VARIANT ScopedVariant::kEmptyVariant = { VT_EMPTY };
+const VARIANT ScopedVariant::kEmptyVariant = {{{VT_EMPTY}}};
ScopedVariant::~ScopedVariant() {
COMPILE_ASSERT(sizeof(ScopedVariant) == sizeof(VARIANT), ScopedVariantSize);
@@ -82,7 +82,7 @@
}
VARIANT ScopedVariant::Copy() const {
- VARIANT ret = { VT_EMPTY };
+ VARIANT ret = {{{VT_EMPTY}}};
::VariantCopy(&ret, &var_);
return ret;
}
diff --git a/base/win/shortcut.cc b/base/win/shortcut.cc
index f8b2182..57f8e61 100644
--- a/base/win/shortcut.cc
+++ b/base/win/shortcut.cc
@@ -5,13 +5,18 @@
#include "base/win/shortcut.h"
#include <shellapi.h>
+#include <shldisp.h>
#include <shlobj.h>
#include <propkey.h>
#include "base/files/file_util.h"
+#include "base/strings/string_util.h"
#include "base/threading/thread_restrictions.h"
+#include "base/win/scoped_bstr.h"
#include "base/win/scoped_comptr.h"
+#include "base/win/scoped_handle.h"
#include "base/win/scoped_propvariant.h"
+#include "base/win/scoped_variant.h"
#include "base/win/win_util.h"
#include "base/win/windows_version.h"
@@ -20,6 +25,90 @@
namespace {
+// String resource IDs in shell32.dll.
+const uint32_t kPinToTaskbarID = 5386;
+const uint32_t kUnpinFromTaskbarID = 5387;
+
+// Traits for a GenericScopedHandle that will free a module on closure.
+struct ModuleTraits {
+ typedef HMODULE Handle;
+ static Handle NullHandle() { return nullptr; }
+ static bool IsHandleValid(Handle module) { return !!module; }
+ static bool CloseHandle(Handle module) { return !!::FreeLibrary(module); }
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ModuleTraits);
+};
+
+// An object that will free a module when it goes out of scope.
+using ScopedLibrary = GenericScopedHandle<ModuleTraits, DummyVerifierTraits>;
+
+// Returns the shell resource string identified by |resource_id|, or an empty
+// string on error.
+string16 LoadShellResourceString(uint32_t resource_id) {
+ ScopedLibrary shell32(::LoadLibrary(L"shell32.dll"));
+ if (!shell32.IsValid())
+ return string16();
+
+ const wchar_t* resource_ptr = nullptr;
+ int length = ::LoadStringW(shell32.Get(), resource_id,
+ reinterpret_cast<wchar_t*>(&resource_ptr), 0);
+ if (!length || !resource_ptr)
+ return string16();
+ return string16(resource_ptr, length);
+}
+
+// Uses the shell to perform the verb identified by |resource_id| on |path|.
+bool DoVerbOnFile(uint32_t resource_id, const FilePath& path) {
+ string16 verb_name(LoadShellResourceString(resource_id));
+ if (verb_name.empty())
+ return false;
+
+ ScopedComPtr<IShellDispatch> shell_dispatch;
+ HRESULT hresult =
+ shell_dispatch.CreateInstance(CLSID_Shell, nullptr, CLSCTX_INPROC_SERVER);
+ if (FAILED(hresult) || !shell_dispatch.get())
+ return false;
+
+ ScopedComPtr<Folder> folder;
+ hresult = shell_dispatch->NameSpace(
+ ScopedVariant(path.DirName().value().c_str()), folder.Receive());
+ if (FAILED(hresult) || !folder.get())
+ return false;
+
+ ScopedComPtr<FolderItem> item;
+ hresult = folder->ParseName(ScopedBstr(path.BaseName().value().c_str()),
+ item.Receive());
+ if (FAILED(hresult) || !item.get())
+ return false;
+
+ ScopedComPtr<FolderItemVerbs> verbs;
+ hresult = item->Verbs(verbs.Receive());
+ if (FAILED(hresult) || !verbs.get())
+ return false;
+
+ long verb_count = 0;
+ hresult = verbs->get_Count(&verb_count);
+ if (FAILED(hresult))
+ return false;
+
+ for (long i = 0; i < verb_count; ++i) {
+ ScopedComPtr<FolderItemVerb> verb;
+ hresult = verbs->Item(ScopedVariant(i, VT_I4), verb.Receive());
+ if (FAILED(hresult) || !verb.get())
+ continue;
+ ScopedBstr name;
+ hresult = verb->get_Name(name.Receive());
+ if (FAILED(hresult))
+ continue;
+ if (StringPiece16(name, name.Length()) == verb_name) {
+ hresult = verb->DoIt();
+ return SUCCEEDED(hresult);
+ }
+ }
+ return false;
+}
+
// Initializes |i_shell_link| and |i_persist_file| (releasing them first if they
// are already initialized).
// If |shortcut| is not NULL, loads |shortcut| into |i_persist_file|.
@@ -314,28 +403,24 @@
return true;
}
-bool TaskbarPinShortcutLink(const wchar_t* shortcut) {
+bool TaskbarPinShortcutLink(const FilePath& shortcut) {
base::ThreadRestrictions::AssertIOAllowed();
// "Pin to taskbar" is only supported after Win7.
if (GetVersion() < VERSION_WIN7)
return false;
- intptr_t result = reinterpret_cast<intptr_t>(
- ShellExecute(NULL, L"taskbarpin", shortcut, NULL, NULL, 0));
- return result > 32;
+ return DoVerbOnFile(kPinToTaskbarID, shortcut);
}
-bool TaskbarUnpinShortcutLink(const wchar_t* shortcut) {
+bool TaskbarUnpinShortcutLink(const FilePath& shortcut) {
base::ThreadRestrictions::AssertIOAllowed();
// "Unpin from taskbar" is only supported after Win7.
if (GetVersion() < VERSION_WIN7)
return false;
- intptr_t result = reinterpret_cast<intptr_t>(
- ShellExecute(NULL, L"taskbarunpin", shortcut, NULL, NULL, 0));
- return result > 32;
+ return DoVerbOnFile(kUnpinFromTaskbarID, shortcut);
}
} // namespace win
diff --git a/base/win/shortcut.h b/base/win/shortcut.h
index 6f7d10c..6c85f01 100644
--- a/base/win/shortcut.h
+++ b/base/win/shortcut.h
@@ -155,12 +155,12 @@
// Pins a shortcut to the Windows 7 taskbar. The shortcut file must already
// exist and be a shortcut that points to an executable. The app id of the
// shortcut is used to group windows and must be set correctly.
-BASE_EXPORT bool TaskbarPinShortcutLink(const wchar_t* shortcut);
+BASE_EXPORT bool TaskbarPinShortcutLink(const FilePath& shortcut);
// Unpins a shortcut from the Windows 7 taskbar. The shortcut must exist and
// already be pinned to the taskbar. The app id of the shortcut is used as the
// identifier for the taskbar item to remove and must be set correctly.
-BASE_EXPORT bool TaskbarUnpinShortcutLink(const wchar_t* shortcut);
+BASE_EXPORT bool TaskbarUnpinShortcutLink(const FilePath& shortcut);
} // namespace win
} // namespace base
diff --git a/base/win/win_util.cc b/base/win/win_util.cc
index c5b06c4..7b4f612 100644
--- a/base/win/win_util.cc
+++ b/base/win/win_util.cc
@@ -19,11 +19,14 @@
#include <signal.h>
#include <stdlib.h>
+#include "base/base_switches.h"
+#include "base/command_line.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "base/win/metro.h"
#include "base/win/registry.h"
@@ -32,13 +35,16 @@
#include "base/win/scoped_propvariant.h"
#include "base/win/windows_version.h"
+namespace base {
+namespace win {
+
namespace {
// Sets the value of |property_key| to |property_value| in |property_store|.
bool SetPropVariantValueForPropertyStore(
IPropertyStore* property_store,
const PROPERTYKEY& property_key,
- const base::win::ScopedPropVariant& property_value) {
+ const ScopedPropVariant& property_value) {
DCHECK(property_store);
HRESULT result = property_store->SetValue(property_key, property_value.get());
@@ -48,31 +54,56 @@
}
void __cdecl ForceCrashOnSigAbort(int) {
- *((int*)0) = 0x1337;
+ *((volatile int*)0) = 0x1337;
}
const wchar_t kWindows8OSKRegPath[] =
L"Software\\Classes\\CLSID\\{054AAE20-4BEA-4347-8A35-64A533254A9D}"
L"\\LocalServer32";
+} // namespace
+
// 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 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() {
+bool IsKeyboardPresentOnSlate(std::string* reason) {
+ bool result = false;
+
+ if (GetVersion() < VERSION_WIN7) {
+ *reason = "Detection not supported";
+ return false;
+ }
+
// This function is only supported for Windows 8 and up.
- DCHECK(base::win::GetVersion() >= base::win::VERSION_WIN8);
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableUsbKeyboardDetect)) {
+ if (reason)
+ *reason = "Detection disabled";
+ return false;
+ }
// This function should be only invoked for machines with touch screens.
if ((GetSystemMetrics(SM_DIGITIZER) & NID_INTEGRATED_TOUCH)
!= NID_INTEGRATED_TOUCH) {
- return true;
+ if (reason) {
+ *reason += "NID_INTEGRATED_TOUCH\n";
+ result = true;
+ } else {
+ return true;
+ }
}
// If the device is docked, the user is treating the device as a PC.
- if (GetSystemMetrics(SM_SYSTEMDOCKED) != 0)
- return true;
+ if (GetSystemMetrics(SM_SYSTEMDOCKED) != 0) {
+ if (reason) {
+ *reason += "SM_SYSTEMDOCKED\n";
+ result = true;
+ } else {
+ return true;
+ }
+ }
// To determine whether a keyboard is present on the device, we do the
// following:-
@@ -103,7 +134,13 @@
// If there is no auto rotation sensor or rotation is not supported in
// the current configuration, then we can assume that this is a desktop
// or a traditional laptop.
- return true;
+ if (reason) {
+ *reason += (auto_rotation_state & AR_NOSENSOR) ? "AR_NOSENSOR\n"
+ : "AR_NOT_SUPPORTED\n";
+ result = true;
+ } else {
+ return true;
+ }
}
}
@@ -114,8 +151,15 @@
POWER_PLATFORM_ROLE role = PowerDeterminePlatformRole();
if (((role == PlatformRoleMobile) || (role == PlatformRoleSlate)) &&
- (GetSystemMetrics(SM_CONVERTIBLESLATEMODE) == 0))
- return false;
+ (GetSystemMetrics(SM_CONVERTIBLESLATEMODE) == 0)) {
+ if (reason) {
+ *reason += (role == PlatformRoleMobile) ? "PlatformRoleMobile\n"
+ : "PlatformRoleSlate\n";
+ // Don't change result here if it's already true.
+ } else {
+ return false;
+ }
+ }
const GUID KEYBOARD_CLASS_GUID =
{ 0x4D36E96B, 0xE325, 0x11CE,
@@ -124,13 +168,15 @@
// Query for all the keyboard devices.
HDEVINFO device_info =
SetupDiGetClassDevs(&KEYBOARD_CLASS_GUID, NULL, NULL, DIGCF_PRESENT);
- if (device_info == INVALID_HANDLE_VALUE)
- return false;
+ if (device_info == INVALID_HANDLE_VALUE) {
+ if (reason)
+ *reason += "No keyboard info\n";
+ return result;
+ }
// Enumerate all keyboards and look for ACPI\PNP and HID\VID devices. If
// the count is more than 1 we assume that a keyboard is present. This is
// under the assumption that there will always be one keyboard device.
- int keyboard_count = 0;
for (DWORD i = 0;; ++i) {
SP_DEVINFO_DATA device_info_data = { 0 };
device_info_data.cbSize = sizeof(device_info_data);
@@ -146,23 +192,24 @@
if (status == CR_SUCCESS) {
// To reduce the scope of the hack we only look for ACPI and HID\\VID
// prefixes in the keyboard device ids.
- if (StartsWith(device_id, L"ACPI", false) ||
- StartsWith(device_id, L"HID\\VID", false)) {
- keyboard_count++;
+ if (StartsWith(device_id, L"ACPI", CompareCase::INSENSITIVE_ASCII) ||
+ StartsWith(device_id, L"HID\\VID", CompareCase::INSENSITIVE_ASCII)) {
+ if (reason) {
+ *reason += "device: ";
+ *reason += WideToUTF8(device_id);
+ *reason += '\n';
+ }
+ // 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.
+ result = true;
}
}
}
- // 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 result;
}
-} // namespace
-
-namespace base {
-namespace win {
-
static bool g_crash_on_process_detach = false;
void GetNonClientMetrics(NONCLIENTMETRICS_XP* metrics) {
@@ -181,7 +228,7 @@
HANDLE token = NULL;
if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &token))
return false;
- base::win::ScopedHandle token_scoped(token);
+ ScopedHandle token_scoped(token);
DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE;
scoped_ptr<BYTE[]> user_bytes(new BYTE[size]);
@@ -226,11 +273,11 @@
// This can be slow if Windows ends up going to disk. Should watch this key
// for changes and only read it once, preferably on the file thread.
// http://code.google.com/p/chromium/issues/detail?id=61644
- base::ThreadRestrictions::ScopedAllowIO allow_io;
+ ThreadRestrictions::ScopedAllowIO allow_io;
- base::win::RegKey key(HKEY_LOCAL_MACHINE,
- L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System",
- KEY_READ);
+ RegKey key(HKEY_LOCAL_MACHINE,
+ L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System",
+ KEY_READ);
DWORD uac_enabled;
if (key.ReadValueDW(L"EnableLUA", &uac_enabled) != ERROR_SUCCESS)
return true;
@@ -284,20 +331,20 @@
bool AddCommandToAutoRun(HKEY root_key, const string16& name,
const string16& command) {
- base::win::RegKey autorun_key(root_key, kAutoRunKeyPath, KEY_SET_VALUE);
+ RegKey autorun_key(root_key, kAutoRunKeyPath, KEY_SET_VALUE);
return (autorun_key.WriteValue(name.c_str(), command.c_str()) ==
ERROR_SUCCESS);
}
bool RemoveCommandFromAutoRun(HKEY root_key, const string16& name) {
- base::win::RegKey autorun_key(root_key, kAutoRunKeyPath, KEY_SET_VALUE);
+ RegKey autorun_key(root_key, kAutoRunKeyPath, KEY_SET_VALUE);
return (autorun_key.DeleteValue(name.c_str()) == ERROR_SUCCESS);
}
bool ReadCommandFromAutoRun(HKEY root_key,
const string16& name,
string16* command) {
- base::win::RegKey autorun_key(root_key, kAutoRunKeyPath, KEY_QUERY_VALUE);
+ RegKey autorun_key(root_key, kAutoRunKeyPath, KEY_QUERY_VALUE);
return (autorun_key.ReadValue(name.c_str(), command) == ERROR_SUCCESS);
}
@@ -326,8 +373,8 @@
if (GetSystemMetrics(SM_MAXIMUMTOUCHES) == 0)
return false;
- base::win::Version version = base::win::GetVersion();
- if (version == base::win::VERSION_XP)
+ Version version = GetVersion();
+ if (version == VERSION_XP)
return (GetSystemMetrics(SM_TABLETPC) != 0);
// If the device is docked, the user is treating the device as a PC.
@@ -339,7 +386,7 @@
POWER_PLATFORM_ROLE role = PowerDeterminePlatformRole();
bool mobile_power_profile = (role == PlatformRoleMobile);
bool slate_power_profile = false;
- if (version >= base::win::VERSION_WIN8)
+ if (version >= VERSION_WIN8)
slate_power_profile = (role == PlatformRoleSlate);
if (mobile_power_profile || slate_power_profile)
@@ -349,14 +396,13 @@
}
bool DisplayVirtualKeyboard() {
- if (base::win::GetVersion() < base::win::VERSION_WIN8)
+ if (GetVersion() < VERSION_WIN8)
return false;
- if (IsKeyboardPresentOnSlate())
+ if (IsKeyboardPresentOnSlate(nullptr))
return false;
- static base::LazyInstance<string16>::Leaky osk_path =
- LAZY_INSTANCE_INITIALIZER;
+ static LazyInstance<string16>::Leaky osk_path = LAZY_INSTANCE_INITIALIZER;
if (osk_path.Get().empty()) {
// We need to launch TabTip.exe from the location specified under the
@@ -367,9 +413,8 @@
// We don't want to launch TabTip.exe from
// c:\program files (x86)\common files\microsoft shared\ink. This path is
// normally found on 64 bit Windows.
- base::win::RegKey key(HKEY_LOCAL_MACHINE,
- kWindows8OSKRegPath,
- KEY_READ | KEY_WOW64_64KEY);
+ RegKey key(HKEY_LOCAL_MACHINE, kWindows8OSKRegPath,
+ KEY_READ | KEY_WOW64_64KEY);
DWORD osk_path_length = 1024;
if (key.ReadValue(NULL,
WriteInto(&osk_path.Get(), osk_path_length),
@@ -410,7 +455,7 @@
common_program_files_path = common_program_files_wow6432.get();
DCHECK(!common_program_files_path.empty());
} else {
- base::win::ScopedCoMem<wchar_t> common_program_files;
+ ScopedCoMem<wchar_t> common_program_files;
if (FAILED(SHGetKnownFolderPath(FOLDERID_ProgramFilesCommon, 0, NULL,
&common_program_files))) {
return false;
@@ -432,7 +477,7 @@
}
bool DismissVirtualKeyboard() {
- if (base::win::GetVersion() < base::win::VERSION_WIN8)
+ if (GetVersion() < VERSION_WIN8)
return false;
// We dismiss the virtual keyboard by generating the ESC keystroke
@@ -474,21 +519,21 @@
}
bool MaybeHasSHA256Support() {
- const base::win::OSInfo* os_info = base::win::OSInfo::GetInstance();
+ const OSInfo* os_info = OSInfo::GetInstance();
- if (os_info->version() == base::win::VERSION_PRE_XP)
+ if (os_info->version() == VERSION_PRE_XP)
return false; // Too old to have it and this OS is not supported anyway.
- if (os_info->version() == base::win::VERSION_XP)
+ if (os_info->version() == VERSION_XP)
return os_info->service_pack().major >= 3; // Windows XP SP3 has it.
// Assume it is missing in this case, although it may not be. This category
// includes Windows XP x64, and Windows Server, where a hotfix could be
// deployed.
- if (os_info->version() == base::win::VERSION_SERVER_2003)
+ if (os_info->version() == VERSION_SERVER_2003)
return false;
- DCHECK(os_info->version() >= base::win::VERSION_VISTA);
+ DCHECK(os_info->version() >= VERSION_VISTA);
return true; // New enough to have SHA-256 support.
}
diff --git a/base/win/win_util.h b/base/win/win_util.h
index 8513f62..9f42e44 100644
--- a/base/win/win_util.h
+++ b/base/win/win_util.h
@@ -132,6 +132,11 @@
// insight into how users use Chrome.
BASE_EXPORT bool IsTabletDevice();
+// A slate is a touch device that may have a keyboard attached. This function
+// returns true if a keyboard is attached and optionally will set the reason
+// parameter to the detection method that was used to detect the keyboard.
+BASE_EXPORT bool IsKeyboardPresentOnSlate(std::string* reason);
+
// Get the size of a struct up to and including the specified member.
// This is necessary to set compatible struct sizes for different versions
// of certain Windows APIs (e.g. SystemParametersInfo).
diff --git a/base/win/windows_version.cc b/base/win/windows_version.cc
index fc2def3..35cdbb3 100644
--- a/base/win/windows_version.cc
+++ b/base/win/windows_version.cc
@@ -73,7 +73,7 @@
service_pack_.major = version_info.wServicePackMajor;
service_pack_.minor = version_info.wServicePackMinor;
- SYSTEM_INFO system_info = { 0 };
+ SYSTEM_INFO system_info = {};
::GetNativeSystemInfo(&system_info);
switch (system_info.wProcessorArchitecture) {
case PROCESSOR_ARCHITECTURE_INTEL: architecture_ = X86_ARCHITECTURE; break;
diff --git a/crypto/random_unittest.cc b/crypto/random_unittest.cc
index 846d9b6..00d4b2b 100644
--- a/crypto/random_unittest.cc
+++ b/crypto/random_unittest.cc
@@ -22,6 +22,6 @@
TEST(RandBytes, RandBytes) {
std::string bytes(16, '\0');
- crypto::RandBytes(WriteInto(&bytes, bytes.size()), bytes.size());
+ crypto::RandBytes(base::WriteInto(&bytes, bytes.size()), bytes.size());
EXPECT_TRUE(!IsTrivial(bytes));
}
diff --git a/gin/modules/console.cc b/gin/modules/console.cc
index d172373..e471312 100644
--- a/gin/modules/console.cc
+++ b/gin/modules/console.cc
@@ -6,6 +6,7 @@
#include <iostream>
+#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "gin/arguments.h"
#include "gin/converter.h"
@@ -25,7 +26,7 @@
args->ThrowError();
return;
}
- std::cout << JoinString(messages, ' ') << std::endl;
+ std::cout << base::JoinString(messages, " ") << std::endl;
}
WrapperInfo g_wrapper_info = { kEmbedderNativeGin };
diff --git a/gin/per_isolate_data.cc b/gin/per_isolate_data.cc
index 99c928c..9d2d6b2 100644
--- a/gin/per_isolate_data.cc
+++ b/gin/per_isolate_data.cc
@@ -3,7 +3,8 @@
// found in the LICENSE file.
#include "base/logging.h"
-#include "base/message_loop/message_loop_proxy.h"
+#include "base/message_loop/message_loop.h"
+#include "base/single_thread_task_runner.h"
#include "gin/per_isolate_data.h"
#include "gin/public/gin_embedders.h"
@@ -21,7 +22,7 @@
ArrayBuffer::Allocator* allocator)
: isolate_(isolate),
allocator_(allocator),
- message_loop_proxy_(base::MessageLoopProxy::current()) {
+ task_runner_(base::MessageLoop::current()->task_runner()) {
isolate_->SetData(kEmbedderNativeGin, this);
}
diff --git a/gin/per_isolate_data.h b/gin/per_isolate_data.h
index bffe5fb..175161c 100644
--- a/gin/per_isolate_data.h
+++ b/gin/per_isolate_data.h
@@ -14,7 +14,7 @@
#include "v8/include/v8.h"
namespace base {
-class MessageLoopProxy;
+class SingleThreadTaskRunner;
}
namespace gin {
@@ -65,9 +65,7 @@
v8::Isolate* isolate() { return isolate_; }
v8::ArrayBuffer::Allocator* allocator() { return allocator_; }
- base::MessageLoopProxy* message_loop_proxy() {
- return message_loop_proxy_.get();
- }
+ base::SingleThreadTaskRunner* task_runner() { return task_runner_.get(); }
private:
typedef std::map<
@@ -87,7 +85,7 @@
FunctionTemplateMap function_templates_;
IndexedPropertyInterceptorMap indexed_interceptors_;
NamedPropertyInterceptorMap named_interceptors_;
- scoped_refptr<base::MessageLoopProxy> message_loop_proxy_;
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
DISALLOW_COPY_AND_ASSIGN(PerIsolateData);
};
diff --git a/gin/v8_platform.cc b/gin/v8_platform.cc
index c8ee770..244534f 100644
--- a/gin/v8_platform.cc
+++ b/gin/v8_platform.cc
@@ -6,7 +6,6 @@
#include "base/bind.h"
#include "base/location.h"
-#include "base/message_loop/message_loop_proxy.h"
#include "base/threading/thread.h"
#include "gin/per_isolate_data.h"
@@ -32,13 +31,12 @@
background_thread_.reset(new base::Thread("gin_background"));
background_thread_->Start();
}
- background_thread_->message_loop_proxy()->PostTask(
- FROM_HERE,
- base::Bind(&v8::Task::Run, base::Owned(task)));
+ background_thread_->task_runner()->PostTask(
+ FROM_HERE, base::Bind(&v8::Task::Run, base::Owned(task)));
}
void V8Platform::CallOnForegroundThread(v8::Isolate* isolate, v8::Task* task) {
- PerIsolateData::From(isolate)->message_loop_proxy()->PostTask(
+ PerIsolateData::From(isolate)->task_runner()->PostTask(
FROM_HERE, base::Bind(&v8::Task::Run, base::Owned(task)));
}
diff --git a/gpu/command_buffer/service/async_pixel_transfer_manager_egl.cc b/gpu/command_buffer/service/async_pixel_transfer_manager_egl.cc
index 977822e..c612644 100644
--- a/gpu/command_buffer/service/async_pixel_transfer_manager_egl.cc
+++ b/gpu/command_buffer/service/async_pixel_transfer_manager_egl.cc
@@ -88,10 +88,11 @@
class TransferThread : public base::Thread {
public:
TransferThread() : base::Thread(kAsyncTransferThreadName) {
- Start();
+ base::Thread::Options options;
#if defined(OS_ANDROID) || defined(OS_LINUX)
- SetPriority(base::ThreadPriority::BACKGROUND);
+ options.priority = base::ThreadPriority::BACKGROUND;
#endif
+ StartWithOptions(options);
}
~TransferThread() override { Stop(); }
@@ -122,8 +123,8 @@
base::LazyInstance<TransferThread>
g_transfer_thread = LAZY_INSTANCE_INITIALIZER;
-base::MessageLoopProxy* transfer_message_loop_proxy() {
- return g_transfer_thread.Pointer()->message_loop_proxy().get();
+base::TaskRunner* transfer_task_runner() {
+ return g_transfer_thread.Pointer()->task_runner().get();
}
// Class which holds async pixel transfers state (EGLImage).
@@ -353,8 +354,8 @@
eglDestroyImageKHR(display, egl_image_);
}
if (thread_texture_id_) {
- transfer_message_loop_proxy()->PostTask(FROM_HERE,
- base::Bind(&DeleteTexture, thread_texture_id_));
+ transfer_task_runner()->PostTask(
+ FROM_HERE, base::Bind(&DeleteTexture, thread_texture_id_));
}
}
@@ -466,16 +467,8 @@
void AsyncPixelTransferDelegateEGL::WaitForTransferCompletion() {
if (state_->TransferIsInProgress()) {
-#if defined(OS_ANDROID) || defined(OS_LINUX)
- g_transfer_thread.Pointer()->SetPriority(base::ThreadPriority::BACKGROUND);
-#endif
-
state_->WaitForTransferCompletion();
DCHECK(!state_->TransferIsInProgress());
-
-#if defined(OS_ANDROID) || defined(OS_LINUX)
- g_transfer_thread.Pointer()->SetPriority(base::ThreadPriority::BACKGROUND);
-#endif
}
}
@@ -501,13 +494,10 @@
// Duplicate the shared memory so there is no way we can get
// a use-after-free of the raw pixels.
- transfer_message_loop_proxy()->PostTask(FROM_HERE,
- base::Bind(
- &TransferStateInternal::PerformAsyncTexImage2D,
- state_,
- tex_params,
- mem_params,
- shared_state_->texture_upload_stats));
+ transfer_task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&TransferStateInternal::PerformAsyncTexImage2D, state_,
+ tex_params, mem_params, shared_state_->texture_upload_stats));
DCHECK(CHECK_GL());
}
@@ -533,13 +523,10 @@
// Duplicate the shared memory so there are no way we can get
// a use-after-free of the raw pixels.
- transfer_message_loop_proxy()->PostTask(FROM_HERE,
- base::Bind(
- &TransferStateInternal::PerformAsyncTexSubImage2D,
- state_,
- tex_params,
- mem_params,
- shared_state_->texture_upload_stats));
+ transfer_task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&TransferStateInternal::PerformAsyncTexSubImage2D, state_,
+ tex_params, mem_params, shared_state_->texture_upload_stats));
DCHECK(CHECK_GL());
}
@@ -702,11 +689,9 @@
AsyncPixelTransferCompletionObserver* observer) {
// Post a PerformNotifyCompletion task to the upload thread. This task
// will run after all async transfers are complete.
- transfer_message_loop_proxy()->PostTask(
- FROM_HERE,
- base::Bind(&PerformNotifyCompletion,
- mem_params,
- make_scoped_refptr(observer)));
+ transfer_task_runner()->PostTask(
+ FROM_HERE, base::Bind(&PerformNotifyCompletion, mem_params,
+ make_scoped_refptr(observer)));
}
uint32 AsyncPixelTransferManagerEGL::GetTextureUploadCount() {
diff --git a/gpu/command_buffer/service/async_pixel_transfer_manager_share_group.cc b/gpu/command_buffer/service/async_pixel_transfer_manager_share_group.cc
index 45e5e5c..eb007bc 100644
--- a/gpu/command_buffer/service/async_pixel_transfer_manager_share_group.cc
+++ b/gpu/command_buffer/service/async_pixel_transfer_manager_share_group.cc
@@ -11,6 +11,7 @@
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
#include "base/synchronization/cancellation_flag.h"
#include "base/synchronization/lock.h"
#include "base/synchronization/waitable_event.h"
@@ -45,10 +46,12 @@
TransferThread()
: base::Thread(kAsyncTransferThreadName),
initialized_(false) {
+ base::Thread::Options options;
Start();
#if defined(OS_ANDROID) || defined(OS_LINUX)
- SetPriority(base::ThreadPriority::BACKGROUND);
+ options.priority = base::ThreadPriority::BACKGROUND;
#endif
+ StartWithOptions(options);
}
~TransferThread() override {
@@ -62,12 +65,11 @@
return;
base::WaitableEvent wait_for_init(true, false);
- message_loop_proxy()->PostTask(
- FROM_HERE,
- base::Bind(&TransferThread::InitializeOnTransferThread,
- base::Unretained(this),
- base::Unretained(parent_context),
- &wait_for_init));
+ task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&TransferThread::InitializeOnTransferThread,
+ base::Unretained(this), base::Unretained(parent_context),
+ &wait_for_init));
wait_for_init.Wait();
}
@@ -124,8 +126,8 @@
base::LazyInstance<TransferThread>::Leaky
g_transfer_thread = LAZY_INSTANCE_INITIALIZER;
-base::MessageLoopProxy* transfer_message_loop_proxy() {
- return g_transfer_thread.Pointer()->message_loop_proxy().get();
+base::SingleThreadTaskRunner* transfer_task_runner() {
+ return g_transfer_thread.Pointer()->task_runner().get();
}
class PendingTask : public base::RefCountedThreadSafe<PendingTask> {
@@ -247,10 +249,9 @@
tex_params,
mem_params,
texture_upload_stats));
- transfer_message_loop_proxy()->PostTask(
- FROM_HERE,
- base::Bind(
- &PendingTask::BindAndRun, pending_upload_task_, texture_id_));
+ transfer_task_runner()->PostTask(
+ FROM_HERE, base::Bind(&PendingTask::BindAndRun, pending_upload_task_,
+ texture_id_));
// Save the late bind callback, so we can notify the client when it is
// bound.
@@ -268,10 +269,9 @@
tex_params,
mem_params,
texture_upload_stats));
- transfer_message_loop_proxy()->PostTask(
- FROM_HERE,
- base::Bind(
- &PendingTask::BindAndRun, pending_upload_task_, texture_id_));
+ transfer_task_runner()->PostTask(
+ FROM_HERE, base::Bind(&PendingTask::BindAndRun, pending_upload_task_,
+ texture_id_));
}
private:
@@ -506,11 +506,9 @@
AsyncPixelTransferCompletionObserver* observer) {
// Post a PerformNotifyCompletion task to the upload thread. This task
// will run after all async transfers are complete.
- transfer_message_loop_proxy()->PostTask(
- FROM_HERE,
- base::Bind(&PerformNotifyCompletion,
- mem_params,
- make_scoped_refptr(observer)));
+ transfer_task_runner()->PostTask(
+ FROM_HERE, base::Bind(&PerformNotifyCompletion, mem_params,
+ make_scoped_refptr(observer)));
}
uint32 AsyncPixelTransferManagerShareGroup::GetTextureUploadCount() {
diff --git a/gpu/command_buffer/service/feature_info.cc b/gpu/command_buffer/service/feature_info.cc
index f28a8fb..8caca94 100644
--- a/gpu/command_buffer/service/feature_info.cc
+++ b/gpu/command_buffer/service/feature_info.cc
@@ -51,8 +51,8 @@
}
void Init(const std::string& str) {
- std::vector<std::string> tokens;
- Tokenize(str, " ", &tokens);
+ std::vector<std::string> tokens = base::SplitString(
+ str, " ", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
string_set_.insert(tokens.begin(), tokens.end());
}
diff --git a/gpu/command_buffer/service/in_process_command_buffer.cc b/gpu/command_buffer/service/in_process_command_buffer.cc
index 2036ed0..f1b9b29 100644
--- a/gpu/command_buffer/service/in_process_command_buffer.cc
+++ b/gpu/command_buffer/service/in_process_command_buffer.cc
@@ -14,7 +14,6 @@
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/memory/weak_ptr.h"
-#include "base/message_loop/message_loop_proxy.h"
#include "base/sequence_checker.h"
#include "base/synchronization/condition_variable.h"
#include "base/threading/thread.h"
@@ -956,12 +955,13 @@
namespace {
-void PostCallback(const scoped_refptr<base::MessageLoopProxy>& loop,
- const base::Closure& callback) {
- // The loop.get() check is to support using InProcessCommandBuffer on a thread
- // without a message loop.
- if (loop.get() && !loop->BelongsToCurrentThread()) {
- loop->PostTask(FROM_HERE, callback);
+void PostCallback(
+ const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
+ const base::Closure& callback) {
+ // The task_runner.get() check is to support using InProcessCommandBuffer on a
+ // thread without a message loop.
+ if (task_runner.get() && !task_runner->BelongsToCurrentThread()) {
+ task_runner->PostTask(FROM_HERE, callback);
} else {
callback.Run();
}
@@ -982,7 +982,7 @@
base::Closure callback_on_client_thread =
base::Bind(&RunOnTargetThread, base::Passed(&scoped_callback));
base::Closure wrapped_callback =
- base::Bind(&PostCallback, base::MessageLoopProxy::current(),
+ base::Bind(&PostCallback, base::MessageLoop::current()->task_runner(),
callback_on_client_thread);
return wrapped_callback;
}
diff --git a/gpu/command_buffer/service/program_manager.cc b/gpu/command_buffer/service/program_manager.cc
index 9187c88..0fd414a 100644
--- a/gpu/command_buffer/service/program_manager.cc
+++ b/gpu/command_buffer/service/program_manager.cc
@@ -782,7 +782,8 @@
found = uniform->findInfoByMappedName(name, &info, original_name);
if (found) {
const std::string kArraySpec("[0]");
- if (info->arraySize > 0 && !EndsWith(name, kArraySpec, true)) {
+ if (info->arraySize > 0 &&
+ !base::EndsWith(name, kArraySpec, base::CompareCase::SENSITIVE)) {
*corrected_name = name + kArraySpec;
*original_name += kArraySpec;
} else {
diff --git a/gpu/config/gpu_control_list.cc b/gpu/config/gpu_control_list.cc
index 6ea07f2..eaa2e61 100644
--- a/gpu/config/gpu_control_list.cc
+++ b/gpu/config/gpu_control_list.cc
@@ -1015,7 +1015,7 @@
gl_type = kGLTypeGLES;
if (segments.size() > 3 &&
- StartsWithASCII(segments[3], "(ANGLE", false)) {
+ base::StartsWithASCII(segments[3], "(ANGLE", false)) {
gl_type = kGLTypeANGLE;
}
} else {
diff --git a/gpu/config/gpu_info_collector_linux.cc b/gpu/config/gpu_info_collector_linux.cc
index 023c08b..413e949 100644
--- a/gpu/config/gpu_info_collector_linux.cc
+++ b/gpu/config/gpu_info_collector_linux.cc
@@ -56,7 +56,7 @@
base::StringTokenizer t(contents, "\r\n");
while (t.GetNext()) {
std::string line = t.token();
- if (StartsWithASCII(line, "ReleaseVersion=", true)) {
+ if (base::StartsWithASCII(line, "ReleaseVersion=", true)) {
size_t begin = line.find_first_of("0123456789");
if (begin != std::string::npos) {
size_t end = line.find_first_not_of("0123456789.", begin);
@@ -244,7 +244,7 @@
DCHECK(gpu_info);
std::string gl_version = gpu_info->gl_version;
- if (StartsWithASCII(gl_version, "OpenGL ES", true))
+ if (base::StartsWithASCII(gl_version, "OpenGL ES", true))
gl_version = gl_version.substr(10);
std::vector<std::string> pieces;
base::SplitStringAlongWhitespace(gl_version, &pieces);
diff --git a/gpu/config/gpu_test_expectations_parser.cc b/gpu/config/gpu_test_expectations_parser.cc
index dec42c5..9a6e191 100644
--- a/gpu/config/gpu_test_expectations_parser.cc
+++ b/gpu/config/gpu_test_expectations_parser.cc
@@ -129,9 +129,9 @@
};
Token ParseToken(const std::string& word) {
- if (StartsWithASCII(word, "//", false))
+ if (base::StartsWithASCII(word, "//", false))
return kTokenComment;
- if (StartsWithASCII(word, "0x", false))
+ if (base::StartsWithASCII(word, "0x", false))
return kConfigGPUDeviceID;
for (int32 i = 0; i < kNumberOfExactMatchTokens; ++i) {
diff --git a/mojo/android/javatests/src/org/chromium/mojo/MojoTestCase.java b/mojo/android/javatests/src/org/chromium/mojo/MojoTestCase.java
index 4af9a28..6edf95f 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/MojoTestCase.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/MojoTestCase.java
@@ -25,8 +25,9 @@
@Override
protected void setUp() throws Exception {
super.setUp();
- LibraryLoader.get(LibraryProcessType.PROCESS_BROWSER).ensureInitialized();
- nativeInitApplicationContext(getInstrumentation().getTargetContext());
+ Context context = getInstrumentation().getTargetContext();
+ LibraryLoader.get(LibraryProcessType.PROCESS_BROWSER).ensureInitialized(context);
+ nativeInitApplicationContext(context);
mTestEnvironmentPointer = nativeSetupTestEnvironment();
}
diff --git a/mojo/dart/embedder/dart_controller.cc b/mojo/dart/embedder/dart_controller.cc
index 98e2688..e3779a1 100644
--- a/mojo/dart/embedder/dart_controller.cc
+++ b/mojo/dart/embedder/dart_controller.cc
@@ -264,7 +264,7 @@
Dart_Handle url) {
if (tag == Dart_kCanonicalizeUrl) {
std::string string = tonic::StdStringFromDart(url);
- if (StartsWithASCII(string, "dart:", true))
+ if (base::StartsWith(string, "dart:", base::CompareCase::SENSITIVE))
return url;
}
return tonic::DartLibraryLoader::HandleLibraryTag(tag, library, url);
@@ -451,7 +451,8 @@
// If it's a file URI, strip the scheme.
const char* file_scheme = "file://";
- if (StartsWithASCII(script_uri_string, file_scheme, true)) {
+ if (base::StartsWith(script_uri_string, file_scheme,
+ base::CompareCase::SENSITIVE)) {
script_uri_string = script_uri_string.substr(strlen(file_scheme));
}
diff --git a/mojo/dart/unittests/embedder_tester/validation_unittest.cc b/mojo/dart/unittests/embedder_tester/validation_unittest.cc
index 1300ca9..74674ad 100644
--- a/mojo/dart/unittests/embedder_tester/validation_unittest.cc
+++ b/mojo/dart/unittests/embedder_tester/validation_unittest.cc
@@ -79,7 +79,8 @@
std::string source;
bool r;
std::string filename = base::FilePath(test_name).BaseName().value();
- if (!StartsWithASCII(filename, "conformance_", true)) {
+ if (!base::StartsWith(filename, "conformance_",
+ base::CompareCase::SENSITIVE)) {
// Only include conformance tests.
continue;
}
diff --git a/services/authenticating_url_loader_interceptor/authenticating_url_loader_interceptor_apptest.cc b/services/authenticating_url_loader_interceptor/authenticating_url_loader_interceptor_apptest.cc
index 9bf7aa2..4f4a9a8 100644
--- a/services/authenticating_url_loader_interceptor/authenticating_url_loader_interceptor_apptest.cc
+++ b/services/authenticating_url_loader_interceptor/authenticating_url_loader_interceptor_apptest.cc
@@ -7,7 +7,7 @@
#include "base/atomic_sequence_num.h"
#include "base/macros.h"
#include "base/run_loop.h"
-#include "base/strings/string_util.h"
+#include "base/strings/string_split.h"
#include "base/strings/stringprintf.h"
#include "mojo/public/cpp/application/application_connection.h"
#include "mojo/public/cpp/application/application_impl.h"
@@ -206,8 +206,9 @@
HttpHeaderPtr header = request->headers[0].Pass();
EXPECT_EQ(kAuthenticationHeaderName, header->name);
- std::vector<std::string> auth_value_components;
- Tokenize(header->value, " ", &auth_value_components);
+ std::vector<std::string> auth_value_components =
+ base::SplitString(header->value.To<std::string>(), " ",
+ base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
EXPECT_EQ(2u, auth_value_components.size());
if (auth_value_components.size() == 2) {
EXPECT_EQ(kAuthenticationHeaderValuePrefix, auth_value_components[0]);
diff --git a/services/dart/content_handler_main.cc b/services/dart/content_handler_main.cc
index 4d73d83..84c723a 100644
--- a/services/dart/content_handler_main.cc
+++ b/services/dart/content_handler_main.cc
@@ -5,7 +5,7 @@
#include "base/bind.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
-#include "base/strings/string_util.h"
+#include "base/strings/string_split.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/platform_thread.h"
#include "base/trace_event/trace_event.h"
@@ -36,7 +36,7 @@
static bool IsDartZip(std::string url) {
// If the url doesn't end with ".dart" we assume it is a zipped up
// dart application.
- return !EndsWith(url, ".dart", false);
+ return !base::EndsWith(url, ".dart", base::CompareCase::INSENSITIVE_ASCII);
}
class DartContentHandlerApp;
@@ -158,8 +158,8 @@
bool strict_compilation = false;
GURL url(requestedUrl);
if (url.has_query()) {
- std::vector<std::string> query_parameters;
- Tokenize(url.query(), "&", &query_parameters);
+ std::vector<std::string> query_parameters = base::SplitString(
+ url.query(), "&", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
strict_compilation =
std::find(query_parameters.begin(), query_parameters.end(),
"strict=true") != query_parameters.end();
diff --git a/services/python/content_handler/content_handler_main.cc b/services/python/content_handler/content_handler_main.cc
index d05de8b..1f21f00 100644
--- a/services/python/content_handler/content_handler_main.cc
+++ b/services/python/content_handler/content_handler_main.cc
@@ -8,7 +8,7 @@
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
#include "base/i18n/icu_util.h"
-#include "base/strings/string_util.h"
+#include "base/strings/string_split.h"
#include "base/strings/stringprintf.h"
#include "mojo/application/application_runner_chromium.h"
#include "mojo/application/content_handler_factory.h"
@@ -224,8 +224,8 @@
bool IsDebug(const std::string& requestedUrl) {
GURL url(requestedUrl);
if (url.has_query()) {
- std::vector<std::string> query_parameters;
- Tokenize(url.query(), "&", &query_parameters);
+ std::vector<std::string> query_parameters = base::SplitString(
+ url.query(), "&", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
return std::find(query_parameters.begin(), query_parameters.end(),
"debug=true") != query_parameters.end();
}
diff --git a/services/url_response_disk_cache/url_response_disk_cache_impl.cc b/services/url_response_disk_cache/url_response_disk_cache_impl.cc
index 7cbca7c..318a469 100644
--- a/services/url_response_disk_cache/url_response_disk_cache_impl.cc
+++ b/services/url_response_disk_cache/url_response_disk_cache_impl.cc
@@ -237,7 +237,8 @@
// Returns whether the given |entry| is valid.
bool IsCacheEntryValid(const CacheEntryPtr& entry) {
- return entry && PathExists(base::FilePath(entry->response_body_path));
+ return entry &&
+ PathExists(base::FilePath::FromUTF8Unsafe(entry->response_body_path));
}
// Returns whether the given directory |entry| is valid and its content can be
@@ -285,7 +286,8 @@
iterator->GetNext(&key, &entry);
if (last_key && last_key->request_origin == key->request_origin &&
last_key->url == key->url) {
- base::FilePath entry_directory = base::FilePath(entry->entry_directory);
+ base::FilePath entry_directory =
+ base::FilePath::FromUTF8Unsafe(entry->entry_directory);
if (base::DeleteFile(entry_directory, true))
db->Delete(key.Clone());
}
@@ -390,10 +392,11 @@
callback.Run(URLResponsePtr(), nullptr, nullptr);
return;
}
- callback.Run(entry->response.Pass(),
- PathToArray(base::FilePath(entry->response_body_path)),
- PathToArray(GetConsumerCacheDirectory(
- base::FilePath(entry->entry_directory))));
+ callback.Run(
+ entry->response.Pass(),
+ PathToArray(base::FilePath::FromUTF8Unsafe(entry->response_body_path)),
+ PathToArray(GetConsumerCacheDirectory(
+ base::FilePath::FromUTF8Unsafe(entry->entry_directory))));
UpdateLastInvalidation(db_, key.Pass(), base::Time::Now());
}
@@ -441,9 +444,9 @@
CacheKeyPtr key;
CacheEntryPtr entry = db_->GetNewest(request_origin_, url, &key);
if (IsCacheEntryFresh(response, entry)) {
- callback.Run(
- base::FilePath(entry->response_body_path),
- GetConsumerCacheDirectory(base::FilePath(entry->entry_directory)));
+ callback.Run(base::FilePath::FromUTF8Unsafe(entry->response_body_path),
+ GetConsumerCacheDirectory(
+ base::FilePath::FromUTF8Unsafe(entry->entry_directory)));
UpdateLastInvalidation(db_, key.Pass(), base::Time::Max());
return;
}
diff --git a/shell/BUILD.gn b/shell/BUILD.gn
index 9d5f5bf..879a23e 100644
--- a/shell/BUILD.gn
+++ b/shell/BUILD.gn
@@ -655,6 +655,7 @@
"//base",
"//mojo/application",
"//mojo/application:test_support",
+ "//mojo/converters/base",
"//mojo/data_pipe_utils",
"//mojo/public/cpp/bindings:callback",
"//mojo/public/cpp/environment",
diff --git a/shell/android/apk/src/org/chromium/mojo/shell/MojoShellApplication.java b/shell/android/apk/src/org/chromium/mojo/shell/MojoShellApplication.java
index 0dbdeab..5d2f02b 100644
--- a/shell/android/apk/src/org/chromium/mojo/shell/MojoShellApplication.java
+++ b/shell/android/apk/src/org/chromium/mojo/shell/MojoShellApplication.java
@@ -40,7 +40,8 @@
*/
private void initializeNative() {
try {
- LibraryLoader.get(LibraryProcessType.PROCESS_BROWSER).ensureInitialized();
+ LibraryLoader.get(LibraryProcessType.PROCESS_BROWSER)
+ .ensureInitialized(getApplicationContext());
} catch (ProcessInitException e) {
Log.e(TAG, "libmojo_shell initialization failed.", e);
throw new RuntimeException(e);
diff --git a/shell/android/url_response_disk_cache_delegate_impl.cc b/shell/android/url_response_disk_cache_delegate_impl.cc
index 5e7d0ac..badacab 100644
--- a/shell/android/url_response_disk_cache_delegate_impl.cc
+++ b/shell/android/url_response_disk_cache_delegate_impl.cc
@@ -83,7 +83,8 @@
base::Bind(&AAssetDir_close, base::Unretained(dir)));
while (const char* filename = AAssetDir_getNextFileName(dir)) {
std::string file_name_string = filename;
- if (EndsWith(file_name_string, kMojoApplicationSuffix, true)) {
+ if (base::EndsWith(file_name_string, kMojoApplicationSuffix,
+ base::CompareCase::SENSITIVE)) {
std::string base_name = file_name_string.substr(
0,
file_name_string.size() - (arraysize(kMojoApplicationSuffix) - 1));
diff --git a/shell/application_manager/application_manager.cc b/shell/application_manager/application_manager.cc
index 77d657d..b4f8876 100644
--- a/shell/application_manager/application_manager.cc
+++ b/shell/application_manager/application_manager.cc
@@ -210,9 +210,11 @@
// connecting to those apps would result in a recursive loop, so it has to be
// explicitly avoided here. What this means in practice is that these apps
// cannot themselves require authentication to obtain.
- if (EndsWith(resolved_url.path(), "/authentication.mojo", true) ||
- EndsWith(resolved_url.path(),
- "/authenticating_url_loader_interceptor.mojo", true)) {
+ if (base::EndsWith(resolved_url.path(), "/authentication.mojo",
+ base::CompareCase::SENSITIVE) ||
+ base::EndsWith(resolved_url.path(),
+ "/authenticating_url_loader_interceptor.mojo",
+ base::CompareCase::SENSITIVE)) {
network_service = network_service_.get();
} else if (!initialized_authentication_interceptor_) {
#ifndef NO_AUTHENTICATION
diff --git a/shell/context.cc b/shell/context.cc
index 6056730..4f0d693 100644
--- a/shell/context.cc
+++ b/shell/context.cc
@@ -150,7 +150,7 @@
// (to embed a comma into a string escape it using "\,")
// Whatever takes 'parameters' and constructs a CommandLine is failing to
// un-escape the commas, we need to move this fix to that file.
- ReplaceSubstringsAfterOffset(&handlers_spec, 0, "\\,", ",");
+ base::ReplaceSubstringsAfterOffset(&handlers_spec, 0, "\\,", ",");
#endif
std::vector<std::string> parts;
@@ -296,7 +296,7 @@
mojo_shell_child_path_ = shell_child_path;
task_runners_.reset(
- new TaskRunners(base::MessageLoop::current()->message_loop_proxy()));
+ new TaskRunners(base::MessageLoop::current()->task_runner()));
#if !defined(OS_MACOSX)
application_manager()->SetLoaderForURL(
diff --git a/shell/crash/crash_upload.cc b/shell/crash/crash_upload.cc
index 17ecc5a..a2316c8 100644
--- a/shell/crash/crash_upload.cc
+++ b/shell/crash/crash_upload.cc
@@ -161,7 +161,7 @@
// Find multipart boundary.
std::string start_of_file;
base::ReadFileToString(minidump, &start_of_file, kMaxBoundarySize);
- if (!StartsWithASCII(start_of_file, "--", true)) {
+ if (!base::StartsWith(start_of_file, "--", base::CompareCase::SENSITIVE)) {
LOG(WARNING) << "Corrupted minidump: " << minidump.value();
base::DeleteFile(minidump, false);
return MinidumpAndBoundary();
diff --git a/shell/filename_util.cc b/shell/filename_util.cc
index 2dbdd11..936acb0 100644
--- a/shell/filename_util.cc
+++ b/shell/filename_util.cc
@@ -36,21 +36,21 @@
// This must be the first substitution since others will introduce percents as
// the escape character
- ReplaceSubstringsAfterOffset(&url_string, 0, FILE_PATH_LITERAL("%"),
- FILE_PATH_LITERAL("%25"));
+ base::ReplaceSubstringsAfterOffset(&url_string, 0, FILE_PATH_LITERAL("%"),
+ FILE_PATH_LITERAL("%25"));
// A semicolon is supposed to be some kind of separator according to RFC 2396.
- ReplaceSubstringsAfterOffset(&url_string, 0, FILE_PATH_LITERAL(";"),
- FILE_PATH_LITERAL("%3B"));
+ base::ReplaceSubstringsAfterOffset(&url_string, 0, FILE_PATH_LITERAL(";"),
+ FILE_PATH_LITERAL("%3B"));
- ReplaceSubstringsAfterOffset(&url_string, 0, FILE_PATH_LITERAL("#"),
- FILE_PATH_LITERAL("%23"));
+ base::ReplaceSubstringsAfterOffset(&url_string, 0, FILE_PATH_LITERAL("#"),
+ FILE_PATH_LITERAL("%23"));
- ReplaceSubstringsAfterOffset(&url_string, 0, FILE_PATH_LITERAL("?"),
- FILE_PATH_LITERAL("%3F"));
+ base::ReplaceSubstringsAfterOffset(&url_string, 0, FILE_PATH_LITERAL("?"),
+ FILE_PATH_LITERAL("%3F"));
- ReplaceSubstringsAfterOffset(&url_string, 0, FILE_PATH_LITERAL("\\"),
- FILE_PATH_LITERAL("%5C"));
+ base::ReplaceSubstringsAfterOffset(&url_string, 0, FILE_PATH_LITERAL("\\"),
+ FILE_PATH_LITERAL("%5C"));
return GURL(url_string);
}
diff --git a/shell/shell_apptest.cc b/shell/shell_apptest.cc
index 2829155..09988e5 100644
--- a/shell/shell_apptest.cc
+++ b/shell/shell_apptest.cc
@@ -10,6 +10,7 @@
#include "base/run_loop.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
+#include "mojo/converters/base/base_type_converters.h"
#include "mojo/data_pipe_utils/data_pipe_utils.h"
#include "mojo/public/cpp/application/application_impl.h"
#include "mojo/public/cpp/application/application_test_base.h"
@@ -45,7 +46,8 @@
const mojo::Callback<void(http_server::HttpResponsePtr)>&
callback) override {
http_server::HttpResponsePtr response;
- if (StartsWithASCII(request->relative_url, "/app", true)) {
+ if (base::StartsWith(request->relative_url.To<base::StringPiece>(), "/app",
+ base::CompareCase::SENSITIVE)) {
response = http_server::CreateHttpResponse(
200, std::string(shell::test::kPingable.data,
shell::test::kPingable.size));
@@ -188,7 +190,9 @@
application_impl()->ConnectToService("mojo:pingable_app?foo", &pingable);
auto callback = [](const String& app_url, const String& connection_url,
const String& message) {
- EXPECT_TRUE(EndsWith(app_url, "/pingable_app.mojo", true));
+ EXPECT_TRUE(base::EndsWith(app_url.To<base::StringPiece>(),
+ "/pingable_app.mojo",
+ base::CompareCase::SENSITIVE));
EXPECT_EQ(app_url.To<std::string>() + "?foo", connection_url);
EXPECT_EQ("hello", message);
base::MessageLoop::current()->Quit();
@@ -202,7 +206,9 @@
ConnectToService(app_connector, "mojo:pingable_app", &pingable);
auto callback = [](const String& app_url, const String& connection_url,
const String& message) {
- EXPECT_TRUE(EndsWith(app_url, "/pingable_app.mojo", true));
+ EXPECT_TRUE(base::EndsWith(app_url.To<base::StringPiece>(),
+ "/pingable_app.mojo",
+ base::CompareCase::SENSITIVE));
EXPECT_EQ(app_url, connection_url);
EXPECT_EQ("hello", message);
base::MessageLoop::current()->Quit();
diff --git a/testing/android/native_test/java/src/org/chromium/native_test/NativeUnitTestActivity.java b/testing/android/native_test/java/src/org/chromium/native_test/NativeUnitTestActivity.java
index 2a2a8dd..b15599c 100644
--- a/testing/android/native_test/java/src/org/chromium/native_test/NativeUnitTestActivity.java
+++ b/testing/android/native_test/java/src/org/chromium/native_test/NativeUnitTestActivity.java
@@ -27,11 +27,6 @@
// Needed by path_utils_unittest.cc
PathUtils.setPrivateDataDirectorySuffix("chrome", getApplicationContext());
- ResourceExtractor resourceExtractor = ResourceExtractor.get(getApplicationContext());
- resourceExtractor.setExtractAllPaksAndV8SnapshotForTesting();
- resourceExtractor.startExtractingResources();
- resourceExtractor.waitForCompletion();
-
// Needed by system_monitor_unittest.cc
PowerMonitor.createForTests(this);
diff --git a/third_party/zlib/google/zip_reader.cc b/third_party/zlib/google/zip_reader.cc
index 59d96da..7df46e7 100644
--- a/third_party/zlib/google/zip_reader.cc
+++ b/third_party/zlib/google/zip_reader.cc
@@ -10,6 +10,7 @@
#include "base/message_loop/message_loop.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/thread_task_runner_handle.h"
#include "third_party/zlib/google/zip_internal.h"
#if defined(USE_SYSTEM_MINIZIP)
@@ -130,7 +131,8 @@
original_size_ = raw_file_info.uncompressed_size;
// Directory entries in zip files end with "/".
- is_directory_ = EndsWith(file_name_in_zip, "/", false);
+ is_directory_ =
+ base::EndsWith(file_name_in_zip, "/", base::CompareCase::SENSITIVE);
// Check the file name here for directory traversal issues.
is_unsafe_ = file_path_.ReferencesParent();
@@ -144,7 +146,8 @@
// We also consider that the file name is unsafe, if it's absolute.
// On Windows, IsAbsolute() returns false for paths starting with "/".
- if (file_path_.IsAbsolute() || StartsWithASCII(file_name_in_zip, "/", false))
+ if (file_path_.IsAbsolute() ||
+ base::StartsWith(file_name_in_zip, "/", base::CompareCase::SENSITIVE))
is_unsafe_ = true;
// Construct the last modified time. The timezone info is not present in
@@ -355,24 +358,26 @@
// If this is a directory, just create it and return.
if (current_entry_info()->is_directory()) {
if (base::CreateDirectory(output_file_path)) {
- base::MessageLoopProxy::current()->PostTask(FROM_HERE, success_callback);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ success_callback);
} else {
DVLOG(1) << "Unzip failed: unable to create directory.";
- base::MessageLoopProxy::current()->PostTask(FROM_HERE, failure_callback);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ failure_callback);
}
return;
}
if (unzOpenCurrentFile(zip_file_) != UNZ_OK) {
DVLOG(1) << "Unzip failed: unable to open current zip entry.";
- base::MessageLoopProxy::current()->PostTask(FROM_HERE, failure_callback);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, failure_callback);
return;
}
base::FilePath output_dir_path = output_file_path.DirName();
if (!base::CreateDirectory(output_dir_path)) {
DVLOG(1) << "Unzip failed: unable to create containing directory.";
- base::MessageLoopProxy::current()->PostTask(FROM_HERE, failure_callback);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, failure_callback);
return;
}
@@ -382,7 +387,7 @@
if (!output_file.IsValid()) {
DVLOG(1) << "Unzip failed: unable to create platform file at "
<< output_file_path.value();
- base::MessageLoopProxy::current()->PostTask(FROM_HERE, failure_callback);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, failure_callback);
return;
}
diff --git a/tonic/dart_library_provider_files.cc b/tonic/dart_library_provider_files.cc
index 133cd57..25a3001 100644
--- a/tonic/dart_library_provider_files.cc
+++ b/tonic/dart_library_provider_files.cc
@@ -62,17 +62,17 @@
}
std::string DartLibraryProviderFiles::CanonicalizePackageURL(std::string url) {
- DCHECK(StartsWithASCII(url, "package:", true));
- ReplaceFirstSubstringAfterOffset(&url, 0, "package:", "");
+ DCHECK(base::StartsWithASCII(url, "package:", true));
+ base::ReplaceFirstSubstringAfterOffset(&url, 0, "package:", "");
return package_root_.Append(url).AsUTF8Unsafe();
}
Dart_Handle DartLibraryProviderFiles::CanonicalizeURL(Dart_Handle library,
Dart_Handle url) {
std::string string = StdStringFromDart(url);
- if (StartsWithASCII(string, "dart:", true))
+ if (base::StartsWithASCII(string, "dart:", true))
return url;
- if (StartsWithASCII(string, "package:", true))
+ if (base::StartsWithASCII(string, "package:", true))
return StdStringToDart(CanonicalizePackageURL(string));
base::FilePath base_path(StdStringFromDart(Dart_LibraryUrl(library)));
base::FilePath resolved_path = base_path.DirName().Append(string);
diff --git a/tonic/dart_library_provider_network.cc b/tonic/dart_library_provider_network.cc
index 890ca53..635d07e 100644
--- a/tonic/dart_library_provider_network.cc
+++ b/tonic/dart_library_provider_network.cc
@@ -81,11 +81,12 @@
Dart_Handle DartLibraryProviderNetwork::CanonicalizeURL(Dart_Handle library,
Dart_Handle url) {
std::string string = StdStringFromDart(url);
- if (StartsWithASCII(string, "dart:", true))
+ if (base::StartsWithASCII(string, "dart:", true))
return url;
// TODO(abarth): The package root should be configurable.
- if (StartsWithASCII(string, "package:", true))
- ReplaceFirstSubstringAfterOffset(&string, 0, "package:", "/packages/");
+ if (base::StartsWithASCII(string, "package:", true))
+ base::ReplaceFirstSubstringAfterOffset(&string, 0, "package:",
+ "/packages/");
GURL library_url(StdStringFromDart(Dart_LibraryUrl(library)));
GURL resolved_url = library_url.Resolve(string);
return StdStringToDart(resolved_url.spec());
diff --git a/tools/android/forwarder2/device_controller.cc b/tools/android/forwarder2/device_controller.cc
index a4cb9c7..d831c2a 100644
--- a/tools/android/forwarder2/device_controller.cc
+++ b/tools/android/forwarder2/device_controller.cc
@@ -11,7 +11,6 @@
#include "base/callback_helpers.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
-#include "base/message_loop/message_loop_proxy.h"
#include "base/single_thread_task_runner.h"
#include "tools/android/forwarder2/command.h"
#include "tools/android/forwarder2/device_listener.h"
@@ -49,16 +48,15 @@
int exit_notifier_fd)
: host_socket_(host_socket.Pass()),
exit_notifier_fd_(exit_notifier_fd),
- construction_task_runner_(base::MessageLoopProxy::current()),
+ construction_task_runner_(base::MessageLoop::current()->task_runner()),
weak_ptr_factory_(this) {
host_socket_->AddEventFd(exit_notifier_fd);
}
void DeviceController::AcceptHostCommandSoon() {
- base::MessageLoopProxy::current()->PostTask(
- FROM_HERE,
- base::Bind(&DeviceController::AcceptHostCommandInternal,
- base::Unretained(this)));
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&DeviceController::AcceptHostCommandInternal,
+ base::Unretained(this)));
}
void DeviceController::AcceptHostCommandInternal() {
diff --git a/tools/android/forwarder2/device_forwarder_main.cc b/tools/android/forwarder2/device_forwarder_main.cc
index 8b5df26..30da954 100644
--- a/tools/android/forwarder2/device_forwarder_main.cc
+++ b/tools/android/forwarder2/device_forwarder_main.cc
@@ -55,15 +55,13 @@
// thread. Make sure that it gets deleted on that same thread. Note that
// DeleteSoon() is not used here since it would imply reading |controller_|
// from the main thread while it's set on the internal thread.
- controller_thread_->message_loop_proxy()->PostTask(
- FROM_HERE,
- base::Bind(&ServerDelegate::DeleteControllerOnInternalThread,
- base::Unretained(this)));
+ controller_thread_->task_runner()->PostTask(
+ FROM_HERE, base::Bind(&ServerDelegate::DeleteControllerOnInternalThread,
+ base::Unretained(this)));
}
void DeleteControllerOnInternalThread() {
- DCHECK(
- controller_thread_->message_loop_proxy()->RunsTasksOnCurrentThread());
+ DCHECK(controller_thread_->task_runner()->RunsTasksOnCurrentThread());
controller_.reset();
}
diff --git a/tools/android/forwarder2/device_listener.cc b/tools/android/forwarder2/device_listener.cc
index b48a746..c1b3015 100644
--- a/tools/android/forwarder2/device_listener.cc
+++ b/tools/android/forwarder2/device_listener.cc
@@ -9,7 +9,6 @@
#include "base/callback.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
-#include "base/message_loop/message_loop_proxy.h"
#include "base/single_thread_task_runner.h"
#include "tools/android/forwarder2/command.h"
#include "tools/android/forwarder2/forwarder.h"
@@ -51,7 +50,7 @@
}
void DeviceListener::SetAdbDataSocket(scoped_ptr<Socket> adb_data_socket) {
- thread_.message_loop_proxy()->PostTask(
+ thread_.task_runner()->PostTask(
FROM_HERE,
base::Bind(&DeviceListener::OnAdbDataSocketReceivedOnInternalThread,
base::Unretained(this), base::Passed(&adb_data_socket)));
@@ -65,7 +64,7 @@
listener_socket_(listener_socket.Pass()),
host_socket_(host_socket.Pass()),
listener_port_(port),
- deletion_task_runner_(base::MessageLoopProxy::current()),
+ deletion_task_runner_(base::MessageLoop::current()->task_runner()),
thread_("DeviceListener") {
CHECK(host_socket_.get());
DCHECK(deletion_task_runner_.get());
@@ -74,10 +73,9 @@
}
void DeviceListener::AcceptNextClientSoon() {
- thread_.message_loop_proxy()->PostTask(
- FROM_HERE,
- base::Bind(&DeviceListener::AcceptClientOnInternalThread,
- base::Unretained(this)));
+ thread_.task_runner()->PostTask(
+ FROM_HERE, base::Bind(&DeviceListener::AcceptClientOnInternalThread,
+ base::Unretained(this)));
}
void DeviceListener::AcceptClientOnInternalThread() {
diff --git a/tools/android/forwarder2/forwarders_manager.cc b/tools/android/forwarder2/forwarders_manager.cc
index 0c0c904..3148e22 100644
--- a/tools/android/forwarder2/forwarders_manager.cc
+++ b/tools/android/forwarder2/forwarders_manager.cc
@@ -14,8 +14,8 @@
#include "base/callback_helpers.h"
#include "base/location.h"
#include "base/logging.h"
-#include "base/message_loop/message_loop_proxy.h"
#include "base/posix/eintr_wrapper.h"
+#include "base/task_runner.h"
#include "tools/android/forwarder2/forwarder.h"
#include "tools/android/forwarder2/socket.h"
@@ -35,7 +35,7 @@
scoped_ptr<Socket> socket2) {
// Note that the internal Forwarder vector is populated on the internal thread
// which is the only thread from which it's accessed.
- thread_.message_loop_proxy()->PostTask(
+ thread_.task_runner()->PostTask(
FROM_HERE,
base::Bind(&ForwardersManager::CreateNewForwarderOnInternalThread,
base::Unretained(this), base::Passed(&socket1),
@@ -49,19 +49,18 @@
void ForwardersManager::CreateNewForwarderOnInternalThread(
scoped_ptr<Socket> socket1,
scoped_ptr<Socket> socket2) {
- DCHECK(thread_.message_loop_proxy()->RunsTasksOnCurrentThread());
+ DCHECK(thread_.task_runner()->RunsTasksOnCurrentThread());
forwarders_.push_back(new Forwarder(socket1.Pass(), socket2.Pass()));
}
void ForwardersManager::WaitForEventsOnInternalThreadSoon() {
- thread_.message_loop_proxy()->PostTask(
- FROM_HERE,
- base::Bind(&ForwardersManager::WaitForEventsOnInternalThread,
- base::Unretained(this)));
+ thread_.task_runner()->PostTask(
+ FROM_HERE, base::Bind(&ForwardersManager::WaitForEventsOnInternalThread,
+ base::Unretained(this)));
}
void ForwardersManager::WaitForEventsOnInternalThread() {
- DCHECK(thread_.message_loop_proxy()->RunsTasksOnCurrentThread());
+ DCHECK(thread_.task_runner()->RunsTasksOnCurrentThread());
fd_set read_fds;
fd_set write_fds;
diff --git a/tools/android/forwarder2/host_controller.cc b/tools/android/forwarder2/host_controller.cc
index 94e63ec..dc70606 100644
--- a/tools/android/forwarder2/host_controller.cc
+++ b/tools/android/forwarder2/host_controller.cc
@@ -79,15 +79,13 @@
global_exit_notifier_fd_(exit_notifier_fd),
adb_control_socket_(adb_control_socket.Pass()),
delete_controller_notifier_(delete_controller_notifier.Pass()),
- deletion_task_runner_(base::MessageLoopProxy::current()),
- thread_("HostControllerThread") {
-}
+ deletion_task_runner_(base::MessageLoop::current()->task_runner()),
+ thread_("HostControllerThread") {}
void HostController::ReadNextCommandSoon() {
- thread_.message_loop_proxy()->PostTask(
- FROM_HERE,
- base::Bind(&HostController::ReadCommandOnInternalThread,
- base::Unretained(this)));
+ thread_.task_runner()->PostTask(
+ FROM_HERE, base::Bind(&HostController::ReadCommandOnInternalThread,
+ base::Unretained(this)));
}
void HostController::ReadCommandOnInternalThread() {
diff --git a/tools/android/forwarder2/host_forwarder_main.cc b/tools/android/forwarder2/host_forwarder_main.cc
index 16a7b51..3e08321 100644
--- a/tools/android/forwarder2/host_forwarder_main.cc
+++ b/tools/android/forwarder2/host_forwarder_main.cc
@@ -93,8 +93,7 @@
if (!thread_.get())
return;
// Delete the controllers on the thread they were created on.
- thread_->message_loop_proxy()->DeleteSoon(
- FROM_HERE, controllers_.release());
+ thread_->task_runner()->DeleteSoon(FROM_HERE, controllers_.release());
}
void HandleRequest(const std::string& adb_path,
@@ -104,7 +103,7 @@
scoped_ptr<Socket> client_socket) {
// Lazy initialize so that the CLI process doesn't get this thread created.
InitOnce();
- thread_->message_loop_proxy()->PostTask(
+ thread_->task_runner()->PostTask(
FROM_HERE,
base::Bind(&HostControllersManager::HandleRequestOnInternalThread,
base::Unretained(this), adb_path, device_serial, device_port,
@@ -143,7 +142,7 @@
// then all the controllers (including |controller|) were also deleted.
return;
}
- DCHECK(manager->thread_->message_loop_proxy()->RunsTasksOnCurrentThread());
+ DCHECK(manager->thread_->task_runner()->RunsTasksOnCurrentThread());
// Note that this will delete |controller| which is owned by the map.
DeleteRefCountedValueInMap(
MakeHostControllerMapKey(
diff --git a/tools/android/forwarder2/self_deleter_helper.h b/tools/android/forwarder2/self_deleter_helper.h
index d96903d..51fcff1 100644
--- a/tools/android/forwarder2/self_deleter_helper.h
+++ b/tools/android/forwarder2/self_deleter_helper.h
@@ -13,7 +13,6 @@
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
-#include "base/message_loop/message_loop_proxy.h"
namespace base {
@@ -99,11 +98,10 @@
SelfDeleterHelper(T* self_deleting_object,
const DeletionCallback& deletion_callback)
- : construction_runner_(base::MessageLoopProxy::current()),
+ : construction_runner_(base::MessageLoop::current()->task_runner()),
self_deleting_object_(self_deleting_object),
deletion_callback_(deletion_callback),
- weak_ptr_factory_(this) {
- }
+ weak_ptr_factory_(this) {}
~SelfDeleterHelper() {
DCHECK(construction_runner_->RunsTasksOnCurrentThread());
diff --git a/ui/events/devices/device_util_linux.cc b/ui/events/devices/device_util_linux.cc
index 858ee84..9f1f632 100644
--- a/ui/events/devices/device_util_linux.cc
+++ b/ui/events/devices/device_util_linux.cc
@@ -17,7 +17,9 @@
InputDeviceType GetInputDeviceTypeFromPath(const base::FilePath& path) {
DCHECK(!base::MessageLoopForUI::IsCurrent());
std::string event_node = path.BaseName().value();
- if (event_node.empty() || !StartsWithASCII(event_node, "event", false))
+ if (event_node.empty() ||
+ !base::StartsWith(event_node, "event",
+ base::CompareCase::INSENSITIVE_ASCII))
return InputDeviceType::INPUT_DEVICE_UNKNOWN;
// Find sysfs device path for this device.
diff --git a/ui/gl/gl_gl_api_implementation.cc b/ui/gl/gl_gl_api_implementation.cc
index 5bea3f1..bffa64e 100644
--- a/ui/gl/gl_gl_api_implementation.cc
+++ b/ui/gl/gl_gl_api_implementation.cc
@@ -8,6 +8,7 @@
#include <vector>
#include "base/command_line.h"
+#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_implementation.h"
@@ -437,8 +438,8 @@
DCHECK(real_context->IsCurrent(NULL));
std::string ext_string(
reinterpret_cast<const char*>(driver_->fn.glGetStringFn(GL_EXTENSIONS)));
- std::vector<std::string> ext;
- Tokenize(ext_string, " ", &ext);
+ std::vector<std::string> ext = base::SplitString(
+ ext_string, " ", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
std::vector<std::string>::iterator it;
// We can't support GL_EXT_occlusion_query_boolean which is
@@ -448,7 +449,7 @@
if (it != ext.end())
ext.erase(it);
- extensions_ = JoinString(ext, " ");
+ extensions_ = base::JoinString(ext, " ");
}
bool VirtualGLApi::MakeCurrent(GLContext* virtual_context, GLSurface* surface) {
diff --git a/ui/gl/gl_version_info.cc b/ui/gl/gl_version_info.cc
index e2aa0de..375580f 100644
--- a/ui/gl/gl_version_info.cc
+++ b/ui/gl/gl_version_info.cc
@@ -35,7 +35,8 @@
is_es3 = true;
}
if (renderer_str) {
- is_angle = StartsWithASCII(renderer_str, "ANGLE", true);
+ is_angle =
+ base::StartsWith(renderer_str, "ANGLE", base::CompareCase::SENSITIVE);
}
}
diff --git a/url/origin.cc b/url/origin.cc
index 8aaa173..cebf5dd 100644
--- a/url/origin.cc
+++ b/url/origin.cc
@@ -4,14 +4,15 @@
#include "url/origin.h"
-#include "base/strings/string_util.h"
+#include "base/logging.h"
+#include "base/strings/pattern.h"
namespace url {
Origin::Origin() : string_("null") {}
Origin::Origin(const std::string& origin) : string_(origin) {
- DCHECK(origin == "null" || MatchPattern(origin, "?*://?*"));
+ DCHECK(origin == "null" || base::MatchPattern(origin, "?*://?*"));
DCHECK_GT(origin.size(), 0u);
DCHECK(origin == "file://" || origin[origin.size() - 1] != '/');
}