diff options
author | Allan Sandfeld Jensen <allan.jensen@theqtcompany.com> | 2015-06-18 14:10:49 +0200 |
---|---|---|
committer | Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com> | 2015-06-18 13:53:24 +0000 |
commit | 813fbf95af77a531c57a8c497345ad2c61d475b3 (patch) | |
tree | 821b2c8de8365f21b6c9ba17a236fb3006a1d506 /chromium/base | |
parent | af6588f8d723931a298c995fa97259bb7f7deb55 (diff) | |
download | qtwebengine-chromium-813fbf95af77a531c57a8c497345ad2c61d475b3.tar.gz |
BASELINE: Update chromium to 44.0.2403.47
Change-Id: Ie056fedba95cf5e5c76b30c4b2c80fca4764aa2f
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com>
Diffstat (limited to 'chromium/base')
695 files changed, 23322 insertions, 21464 deletions
diff --git a/chromium/base/BUILD.gn b/chromium/base/BUILD.gn index a0cdf662285..de282cfe35d 100644 --- a/chromium/base/BUILD.gn +++ b/chromium/base/BUILD.gn @@ -3,25 +3,75 @@ # found in the LICENSE file. import("//build/config/ui.gni") +import("//testing/test.gni") if (is_android) { import("//build/config/android/rules.gni") } +config("base_implementation") { + defines = [ "BASE_IMPLEMENTATION" ] +} + +if (is_win) { + # This is in a separate config so the flags can be applied to dependents. + # ldflags in GN aren't automatically inherited. + config("base_win_linker_flags") { + ldflags = [ + "/DELAYLOAD:cfgmgr32.dll", + "/DELAYLOAD:powrprof.dll", + "/DELAYLOAD:setupapi.dll", + ] + } +} + +source_set("base_paths") { + sources = [ + "base_paths.cc", + "base_paths.h", + "base_paths_android.cc", + "base_paths_android.h", + "base_paths_mac.h", + "base_paths_mac.mm", + "base_paths_posix.cc", + "base_paths_posix.h", + "base_paths_win.cc", + "base_paths_win.h", + ] + + if (is_android || is_mac) { + sources -= [ "base_paths_posix.cc" ] + } + + if (is_nacl) { + sources -= [ + "base_paths.cc", + "base_paths_posix.cc", + ] + } + + configs += [ ":base_implementation" ] + + deps = [ + "//base/memory", + "//base/process", + ] + + visibility = [ ":base" ] +} + component("base") { sources = [ - "third_party/dmg_fp/dmg_fp.h", - "third_party/dmg_fp/g_fmt.cc", - "third_party/dmg_fp/dtoa_wrapper.cc", - "third_party/icu/icu_utf.cc", - "third_party/icu/icu_utf.h", - "third_party/superfasthash/superfasthash.c", "allocator/allocator_extension.cc", "allocator/allocator_extension.h", "allocator/type_profiler_control.cc", "allocator/type_profiler_control.h", + "android/animation_frame_time_histogram.cc", + "android/animation_frame_time_histogram.h", "android/application_status_listener.cc", "android/application_status_listener.h", + "android/base_jni_onload.cc", + "android/base_jni_onload.h", "android/base_jni_registrar.cc", "android/base_jni_registrar.h", "android/build_info.cc", @@ -39,10 +89,10 @@ component("base") { "android/fifo_utils.h", "android/important_file_writer_android.cc", "android/important_file_writer_android.h", - "android/locale_utils.cc", - "android/locale_utils.h", - "android/scoped_java_ref.cc", - "android/scoped_java_ref.h", + "android/java_handler_thread.cc", + "android/java_handler_thread.h", + "android/java_runtime.cc", + "android/java_runtime.h", "android/jni_android.cc", "android/jni_android.h", "android/jni_array.cc", @@ -55,17 +105,25 @@ component("base") { "android/jni_utils.h", "android/jni_weak_ref.cc", "android/jni_weak_ref.h", + "android/library_loader/library_load_from_apk_status_codes.h", "android/library_loader/library_loader_hooks.cc", "android/library_loader/library_loader_hooks.h", - "android/library_loader/library_load_from_apk_status_codes.h", + "android/library_loader/library_prefetcher.cc", + "android/library_loader/library_prefetcher.h", + "android/locale_utils.cc", + "android/locale_utils.h", "android/memory_pressure_listener_android.cc", "android/memory_pressure_listener_android.h", - "android/java_handler_thread.cc", - "android/java_handler_thread.h", "android/path_service_android.cc", "android/path_service_android.h", "android/path_utils.cc", "android/path_utils.h", + "android/record_histogram.cc", + "android/record_histogram.h", + "android/record_user_action.cc", + "android/record_user_action.h", + "android/scoped_java_ref.cc", + "android/scoped_java_ref.h", "android/sys_utils.cc", "android/sys_utils.h", "android/thread_utils.h", @@ -88,20 +146,10 @@ component("base") { "auto_reset.h", "barrier_closure.cc", "barrier_closure.h", - "base_export.h", - "base_paths.cc", - "base_paths.h", - "base_paths_android.cc", - "base_paths_android.h", - "base_paths_mac.h", - "base_paths_mac.mm", - "base_paths_posix.cc", - "base_paths_posix.h", - "base_paths_win.cc", - "base_paths_win.h", - "base_switches.h", "base64.cc", "base64.h", + "base_export.h", + "base_switches.h", "basictypes.h", "big_endian.cc", "big_endian.h", @@ -119,6 +167,8 @@ component("base") { "callback_internal.cc", "callback_internal.h", "cancelable_callback.h", + "chromeos/memory_pressure_monitor.cc", + "chromeos/memory_pressure_monitor.h", "command_line.cc", "command_line.h", "compiler_specific.h", @@ -126,63 +176,17 @@ component("base") { "containers/hash_tables.h", "containers/linked_list.h", "containers/mru_cache.h", + "containers/scoped_ptr_hash_map.h", "containers/small_map.h", "containers/stack_container.h", "cpu.cc", "cpu.h", "critical_closure.h", "critical_closure_internal_ios.mm", - "debug/alias.cc", - "debug/alias.h", - "debug/asan_invalid_access.cc", - "debug/asan_invalid_access.h", - "debug/crash_logging.cc", - "debug/crash_logging.h", - "debug/debugger.cc", - "debug/debugger.h", - "debug/debugger_posix.cc", - "debug/debugger_win.cc", - "debug/dump_without_crashing.cc", - "debug/dump_without_crashing.h", - "debug/gdi_debug_util_win.cc", - "debug/gdi_debug_util_win.h", - # This file depends on files from the "allocator" target, - # but this target does not depend on "allocator" (see - # allocator.gyp for details). - "debug/leak_annotations.h", - "debug/leak_tracker.h", - "debug/proc_maps_linux.cc", - "debug/proc_maps_linux.h", - "debug/profiler.cc", - "debug/profiler.h", - "debug/stack_trace.cc", - "debug/stack_trace.h", - "debug/stack_trace_android.cc", - "debug/stack_trace_posix.cc", - "debug/stack_trace_win.cc", - "debug/task_annotator.cc", - "debug/task_annotator.h", - "debug/trace_event.h", - "debug/trace_event_android.cc", - "debug/trace_event_argument.cc", - "debug/trace_event_argument.h", - "debug/trace_event_impl.cc", - "debug/trace_event_impl.h", - "debug/trace_event_impl_constants.cc", - "debug/trace_event_memory.cc", - "debug/trace_event_memory.h", - "debug/trace_event_synthetic_delay.cc", - "debug/trace_event_synthetic_delay.h", - "debug/trace_event_system_stats_monitor.cc", - "debug/trace_event_system_stats_monitor.h", - "debug/trace_event_win.cc", "deferred_sequenced_task_runner.cc", "deferred_sequenced_task_runner.h", "environment.cc", "environment.h", - "event_recorder.h", - "event_recorder_stubs.cc", - "event_recorder_win.cc", "file_descriptor_posix.h", "file_version_info.h", "file_version_info_mac.h", @@ -193,8 +197,6 @@ component("base") { "files/dir_reader_linux.h", "files/dir_reader_posix.h", "files/file.cc", - "files/file_posix.cc", - "files/file_win.cc", "files/file_enumerator.cc", "files/file_enumerator.h", "files/file_enumerator_posix.cc", @@ -211,8 +213,11 @@ component("base") { "files/file_path_watcher_linux.cc", "files/file_path_watcher_mac.cc", "files/file_path_watcher_win.cc", + "files/file_posix.cc", "files/file_proxy.cc", "files/file_proxy.h", + "files/file_tracing.cc", + "files/file_tracing.h", "files/file_util.cc", "files/file_util.h", "files/file_util_android.cc", @@ -222,6 +227,7 @@ component("base") { "files/file_util_proxy.cc", "files/file_util_proxy.h", "files/file_util_win.cc", + "files/file_win.cc", "files/important_file_writer.cc", "files/important_file_writer.h", "files/memory_mapped_file.cc", @@ -232,7 +238,6 @@ component("base") { "files/scoped_file.h", "files/scoped_temp_dir.cc", "files/scoped_temp_dir.h", - "float_util.h", "format_macros.h", "gtest_prod_util.h", "guid.cc", @@ -248,19 +253,8 @@ component("base") { "ios/ios_util.mm", "ios/scoped_critical_action.h", "ios/scoped_critical_action.mm", - "json/json_file_value_serializer.cc", - "json/json_file_value_serializer.h", - "json/json_parser.cc", - "json/json_parser.h", - "json/json_reader.cc", - "json/json_reader.h", - "json/json_string_value_serializer.cc", - "json/json_string_value_serializer.h", - "json/json_value_converter.h", - "json/json_writer.cc", - "json/json_writer.h", - "json/string_escape.cc", - "json/string_escape.h", + "ios/weak_nsobject.h", + "ios/weak_nsobject.mm", "lazy_instance.cc", "lazy_instance.h", "linux_util.cc", @@ -277,6 +271,8 @@ component("base") { "mac/bundle_locations.h", "mac/bundle_locations.mm", "mac/cocoa_protocols.h", + "mac/dispatch_source_mach.cc", + "mac/dispatch_source_mach.h", "mac/foundation_util.h", "mac/foundation_util.mm", "mac/launch_services_util.cc", @@ -285,12 +281,14 @@ component("base") { "mac/launchd.h", "mac/libdispatch_task_runner.cc", "mac/libdispatch_task_runner.h", - "mac/mac_logging.h", "mac/mac_logging.cc", + "mac/mac_logging.h", "mac/mac_util.h", "mac/mac_util.mm", "mac/mach_logging.cc", "mac/mach_logging.h", + "mac/memory_pressure_monitor.cc", + "mac/memory_pressure_monitor.h", "mac/objc_property_releaser.h", "mac/objc_property_releaser.mm", "mac/os_crash_dumps.cc", @@ -316,50 +314,10 @@ component("base") { "mac/scoped_sending_event.h", "mac/scoped_sending_event.mm", "mac/sdk_forward_declarations.h", + "mac/sdk_forward_declarations.mm", "macros.h", "md5.cc", "md5.h", - "memory/aligned_memory.cc", - "memory/aligned_memory.h", - "memory/discardable_memory.cc", - "memory/discardable_memory.h", - "memory/discardable_memory_android.cc", - "memory/discardable_memory_emulated.cc", - "memory/discardable_memory_emulated.h", - "memory/discardable_memory_linux.cc", - "memory/discardable_memory_mac.cc", - "memory/discardable_memory_manager.cc", - "memory/discardable_memory_manager.h", - "memory/discardable_memory_shmem.cc", - "memory/discardable_memory_shmem.h", - "memory/discardable_memory_shmem_allocator.cc", - "memory/discardable_memory_shmem_allocator.h", - "memory/discardable_memory_win.cc", - "memory/discardable_shared_memory.cc", - "memory/discardable_shared_memory.h", - "memory/linked_ptr.h", - "memory/manual_constructor.h", - "memory/memory_pressure_listener.cc", - "memory/memory_pressure_listener.h", - "memory/raw_scoped_refptr_mismatch_checker.h", - "memory/ref_counted.cc", - "memory/ref_counted.h", - "memory/ref_counted_delete_on_message_loop.h", - "memory/ref_counted_memory.cc", - "memory/ref_counted_memory.h", - "memory/scoped_open_process.h", - "memory/scoped_policy.h", - "memory/scoped_ptr.h", - "memory/scoped_vector.h", - "memory/shared_memory.h", - "memory/shared_memory_android.cc", - "memory/shared_memory_nacl.cc", - "memory/shared_memory_posix.cc", - "memory/shared_memory_win.cc", - "memory/singleton.cc", - "memory/singleton.h", - "memory/weak_ptr.cc", - "memory/weak_ptr.h", "message_loop/incoming_task_queue.cc", "message_loop/incoming_task_queue.h", "message_loop/message_loop.cc", @@ -384,49 +342,20 @@ component("base") { "message_loop/message_pump_mac.mm", "message_loop/message_pump_win.cc", "message_loop/message_pump_win.h", - "metrics/field_trial.cc", - "metrics/field_trial.h", - "metrics/sample_map.cc", - "metrics/sample_map.h", - "metrics/sample_vector.cc", - "metrics/sample_vector.h", - "metrics/bucket_ranges.cc", - "metrics/bucket_ranges.h", - "metrics/histogram.cc", - "metrics/histogram.h", - "metrics/histogram_base.cc", - "metrics/histogram_base.h", - "metrics/histogram_delta_serialization.cc", - "metrics/histogram_delta_serialization.", - "metrics/histogram_flattener.h", - "metrics/histogram_samples.cc", - "metrics/histogram_samples.h", - "metrics/histogram_snapshot_manager.cc", - "metrics/histogram_snapshot_manager.h", - "metrics/sparse_histogram.cc", - "metrics/sparse_histogram.h", - "metrics/statistics_recorder.cc", - "metrics/statistics_recorder.h", - "metrics/stats_counters.cc", - "metrics/stats_counters.h", - "metrics/stats_table.cc", - "metrics/stats_table.h", - "metrics/user_metrics.cc", - "metrics/user_metrics.h", - "metrics/user_metrics_action.h", "move.h", "native_library.h", + "native_library_ios.mm", "native_library_mac.mm", "native_library_posix.cc", "native_library_win.cc", - "numerics/safe_conversions.h", - "numerics/safe_conversions_impl.h", - "numerics/safe_math.h", - "numerics/safe_math_impl.h", "nix/mime_util_xdg.cc", "nix/mime_util_xdg.h", "nix/xdg_util.cc", "nix/xdg_util.h", + "numerics/safe_conversions.h", + "numerics/safe_conversions_impl.h", + "numerics/safe_math.h", + "numerics/safe_math_impl.h", "observer_list.h", "observer_list_threadsafe.h", "os_compat_android.cc", @@ -460,60 +389,18 @@ component("base") { "power_monitor/power_monitor_source.cc", "power_monitor/power_monitor_source.h", "power_monitor/power_observer.h", - "process/internal_linux.cc", - "process/internal_linux.h", - "process/kill.cc", - "process/kill.h", - "process/kill_mac.cc", - "process/kill_posix.cc", - "process/kill_win.cc", - "process/launch.cc", - "process/launch.h", - "process/launch_ios.cc", - "process/launch_mac.cc", - "process/launch_posix.cc", - "process/launch_win.cc", - "process/memory.cc", - "process/memory.h", - "process/memory_linux.cc", - "process/memory_mac.mm", - "process/memory_win.cc", - "process/process.h", - "process/process_handle_freebsd.cc", - "process/process_handle_linux.cc", - "process/process_handle_mac.cc", - "process/process_handle_openbsd.cc", - "process/process_handle_posix.cc", - "process/process_handle_win.cc", - "process/process_info.h", - "process/process_info_linux.cc", - "process/process_info_mac.cc", - "process/process_info_win.cc", - "process/process_iterator.cc", - "process/process_iterator.h", - "process/process_iterator_freebsd.cc", - "process/process_iterator_linux.cc", - "process/process_iterator_mac.cc", - "process/process_iterator_openbsd.cc", - "process/process_iterator_win.cc", - "process/process_linux.cc", - "process/process_metrics.cc", - "process/process_metrics.h", - "process/process_metrics_freebsd.cc", - "process/process_metrics_ios.cc", - "process/process_metrics_linux.cc", - "process/process_metrics_mac.cc", - "process/process_metrics_openbsd.cc", - "process/process_metrics_posix.cc", - "process/process_metrics_win.cc", - "process/process_posix.cc", - "process/process_win.cc", "profiler/alternate_timer.cc", "profiler/alternate_timer.h", + "profiler/native_stack_sampler.cc", + "profiler/native_stack_sampler.h", "profiler/scoped_profile.cc", "profiler/scoped_profile.h", "profiler/scoped_tracker.cc", "profiler/scoped_tracker.h", + "profiler/stack_sampling_profiler.cc", + "profiler/stack_sampling_profiler.h", + "profiler/stack_sampling_profiler_posix.cc", + "profiler/stack_sampling_profiler_win.cc", "profiler/tracked_time.cc", "profiler/tracked_time.h", "rand_util.cc", @@ -549,11 +436,11 @@ component("base") { "strings/string16.cc", "strings/string16.h", "strings/string_number_conversions.cc", - "strings/string_split.cc", - "strings/string_split.h", "strings/string_number_conversions.h", "strings/string_piece.cc", "strings/string_piece.h", + "strings/string_split.cc", + "strings/string_split.h", "strings/string_tokenizer.h", "strings/string_util.cc", "strings/string_util.h", @@ -595,8 +482,6 @@ component("base") { "synchronization/waitable_event_watcher_posix.cc", "synchronization/waitable_event_watcher_win.cc", "synchronization/waitable_event_win.cc", - "system_monitor/system_monitor.cc", - "system_monitor/system_monitor.h", "sys_byteorder.h", "sys_info.cc", "sys_info.h", @@ -609,12 +494,22 @@ component("base") { "sys_info_openbsd.cc", "sys_info_posix.cc", "sys_info_win.cc", + "system_monitor/system_monitor.cc", + "system_monitor/system_monitor.h", "task/cancelable_task_tracker.cc", "task/cancelable_task_tracker.h", "task_runner.cc", "task_runner.h", "task_runner_util.h", "template_util.h", + "third_party/dmg_fp/dmg_fp.h", + "third_party/dmg_fp/dtoa_wrapper.cc", + "third_party/dmg_fp/g_fmt.cc", + "third_party/icu/icu_utf.cc", + "third_party/icu/icu_utf.h", + "third_party/nspr/prtime.cc", + "third_party/nspr/prtime.h", + "third_party/superfasthash/superfasthash.c", "thread_task_runner_handle.cc", "thread_task_runner_handle.h", "threading/non_thread_safe.h", @@ -622,6 +517,8 @@ component("base") { "threading/non_thread_safe_impl.h", "threading/platform_thread.h", "threading/platform_thread_android.cc", + "threading/platform_thread_internal_posix.cc", + "threading/platform_thread_internal_posix.h", "threading/platform_thread_linux.cc", "threading/platform_thread_mac.mm", "threading/platform_thread_posix.cc", @@ -649,12 +546,12 @@ component("base") { "threading/thread_local_storage_posix.cc", "threading/thread_local_storage_win.cc", "threading/thread_local_win.cc", - "threading/thread_restrictions.h", "threading/thread_restrictions.cc", + "threading/thread_restrictions.h", "threading/watchdog.cc", "threading/watchdog.h", - "threading/worker_pool.h", "threading/worker_pool.cc", + "threading/worker_pool.h", "threading/worker_pool_posix.cc", "threading/worker_pool_posix.h", "threading/worker_pool_win.cc", @@ -685,10 +582,10 @@ component("base") { "tracking_info.cc", "tracking_info.h", "tuple.h", - "values.cc", - "values.h", "value_conversions.cc", "value_conversions.h", + "values.cc", + "values.h", "version.cc", "version.h", "vlog.cc", @@ -706,6 +603,8 @@ component("base") { "win/iat_patch_function.h", "win/iunknown_impl.cc", "win/iunknown_impl.h", + "win/memory_pressure_monitor.cc", + "win/memory_pressure_monitor.h", "win/message_window.cc", "win/message_window.h", "win/metro.cc", @@ -744,55 +643,43 @@ component("base") { "win/wrapped_window_proc.h", ] - if (is_nacl) { - sources += [ "files/file_path_watcher_stub.cc" ] - } - sources -= [ - "process/process_handle_freebsd.cc", - "process/process_handle_openbsd.cc", - "process/process_iterator_freebsd.cc", - "process/process_iterator_openbsd.cc", - "process/process_metrics_freebsd.cc", - "process/process_metrics_openbsd.cc", "sys_info_freebsd.cc", "sys_info_openbsd.cc", ] - defines = [ - "BASE_IMPLEMENTATION", - ] + configs += [ ":base_implementation" ] deps = [ ":base_static", "//base/allocator:allocator_extension_thunks", "//base/third_party/dynamic_annotations", - "//base/third_party/nspr", "//third_party/modp_b64", ] + public_deps = [ + ":base_paths", + "//base/debug", + "//base/json", + "//base/memory", + "//base/metrics", + "//base/process", + "//base/trace_event", + ] + + # Allow more direct string conversions on platforms with native utf8 + # strings + if (is_mac || is_ios || is_chromeos) { + defines = [ "SYSTEM_NATIVE_UTF8" ] + } + if (is_android) { - sources += [ - "memory/discardable_memory_ashmem_allocator.cc", - "memory/discardable_memory_ashmem_allocator.h", - "memory/discardable_memory_ashmem.cc", - "memory/discardable_memory_ashmem.h", - ] - sources -= [ - "base_paths_posix.cc", - "power_monitor/power_monitor_device_source_posix.cc", - ] + sources -= [ "power_monitor/power_monitor_device_source_posix.cc" ] # Android uses some Linux sources, put those back. set_sources_assignment_filter([]) sources += [ - "debug/proc_maps_linux.cc", "files/file_path_watcher_linux.cc", - "process/memory_linux.cc", - "process/internal_linux.cc", - "process/process_handle_linux.cc", - "process/process_iterator_linux.cc", - "process/process_metrics_linux.cc", "posix/unix_domain_socket_linux.cc", "sys_info_linux.cc", ] @@ -801,64 +688,72 @@ component("base") { deps += [ ":base_jni_headers", "//third_party/ashmem", - "//third_party/android_tools:cpu_features" + "//third_party/android_tools:cpu_features", ] # logging.cc uses the Android logging library. libs = [ "log" ] - - sources -= [ - "debug/stack_trace_posix.cc", - ] } if (is_chromeos) { - sources -= [ - "power_monitor/power_monitor_device_source_posix.cc", - ] + sources -= [ "power_monitor/power_monitor_device_source_posix.cc" ] } if (is_nacl) { - # These things would otherwise be built on a Posix build but aren't - # supported on NaCl. + # We reset sources_assignment_filter in order to explicitly include + # the linux file (which would otherwise be filtered out). + set_sources_assignment_filter([]) + sources += [ + "files/file_path_watcher_stub.cc", + "sync_socket_nacl.cc", + "threading/platform_thread_linux.cc", + ] + set_sources_assignment_filter(sources_assignment_filter) + sources -= [ - "debug/stack_trace_posix.cc", + "allocator/type_profiler_control.cc", + "allocator/type_profiler_control.h", + "async_socket_io_handler_posix.cc", + "cpu.cc", "files/file_enumerator_posix.cc", + "files/file_proxy.cc", + "files/file_util.cc", "files/file_util_posix.cc", + "files/file_util_proxy.cc", + "files/important_file_writer.cc", + "files/important_file_writer.h", + "files/scoped_temp_dir.cc", "message_loop/message_pump_libevent.cc", - "process/kill_posix.cc", - "process/launch_posix.cc", - "process/process_metrics_posix.cc", - "process/process_posix.cc", - "metrics/field_trial.cc", "native_library_posix.cc", - "memory/shared_memory_posix.cc", + "path_service.cc", + "rand_util_posix.cc", + "scoped_native_library.cc", "sync_socket_posix.cc", + "sys_info.cc", "sys_info_posix.cc", ] } else { - # Remove nacl stuff. + # Remove NaCl stuff. sources -= [ "os_compat_nacl.cc", "os_compat_nacl.h", "rand_util_nacl.cc", - "memory/shared_memory_nacl.cc", ] } # Windows. if (is_win) { sources -= [ - "event_recorder_stubs.cc", "message_loop/message_pump_libevent.cc", "strings/string16.cc", + # Not using sha1_win.cc because it may have caused a # regression to page cycler moz. "sha1_win.cc", ] # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. - cflags = [ "/wd4267" ] + configs += [ "//build/config/compiler:no_size_t_to_int_warning" ] libs = [ "cfgmgr32.lib", @@ -866,11 +761,7 @@ component("base") { "powrprof.lib", "setupapi.lib", ] - ldflags = [ - "/DELAYLOAD:cfgmgr32.dll", - "/DELAYLOAD:powrprof.dll", - "/DELAYLOAD:setupapi.dll", - ] + all_dependent_configs = [ ":base_win_linker_flags" ] } else if (!is_nacl) { # Non-Windows. deps += [ "//third_party/libevent" ] @@ -878,16 +769,11 @@ component("base") { # Mac. if (is_mac) { - sources += [ - "memory/discardable_memory_mach.cc", - "memory/discardable_memory_mach.h", - ] sources -= [ - "base_paths_posix.cc", "native_library_posix.cc", "strings/sys_string_conversions_posix.cc", + "threading/platform_thread_internal_posix.cc", ] - deps += [ "//third_party/mach_override" ] } else { # Non-Mac. sources -= [ @@ -901,19 +787,17 @@ component("base") { # Linux. if (is_linux) { # TODO(brettw) this will need to be parameterized at some point. - linux_configs = [ - "//build/config/linux:glib", - ] + linux_configs = [] + if (use_glib) { + linux_configs += [ "//build/config/linux:glib" ] + } configs += linux_configs all_dependent_configs = linux_configs - defines += [ "USE_SYMBOLIZE" ] - # These dependencies are not required on Android, and in the case # of xdg_mime must be excluded due to licensing restrictions. deps += [ - "//base/third_party/symbolize", "//base/third_party/xdg_mime", "//base/third_party/xdg_user_dirs", ] @@ -946,6 +830,8 @@ component("base") { configs -= [ "//build/config/compiler:optimize" ] configs += [ "//build/config/compiler:optimize_max" ] } + + allow_circular_includes_from = public_deps } # This is the subset of files from base that should not be used with a dynamic @@ -1017,13 +903,68 @@ component("i18n") { configs += [ "//build/config/compiler:optimize_max" ] } - if (is_win) { - # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. - cflags = [ "/wd4267" ] + # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. + configs += [ "//build/config/compiler:no_size_t_to_int_warning" ] +} + +if (is_win || (is_linux && !is_chromeos)) { + # TODO(GYP): Figure out which of these work and are needed on other platforms. + test("base_perftests") { + sources = [ + "message_loop/message_pump_perftest.cc", + + # "test/run_all_unittests.cc", + "threading/thread_perftest.cc", + ] + deps = [ + ":base", + "//base/test:test_support", + "//base/test:test_support_perf", + "//testing/perf", + "//testing/gtest", + ] + + if (is_android) { + deps += [ "//testing/android/native_test:native_test_native_code" ] + } + } + + test("base_i18n_perftests") { + sources = [ + "i18n/streaming_utf8_validator_perftest.cc", + ] + deps = [ + ":base", + ":i18n", + "//base/test:test_support", + "//base/test:test_support_perf", + "//testing/gtest", + ] + } + + if (!is_ios) { + executable("build_utf8_validator_tables") { + sources = [ + "i18n/build_utf8_validator_tables.cc", + ] + deps = [ + ":base", + "//third_party/icu:icuuc", + ] + } + + executable("check_example") { + sources = [ + "check_example.cc", + ] + deps = [ + ":base", + ] + } } } -source_set("prefs") { +component("prefs") { sources = [ "prefs/base_prefs_export.h", "prefs/default_pref_store.cc", @@ -1065,7 +1006,9 @@ source_set("prefs") { defines = [ "BASE_PREFS_IMPLEMENTATION" ] - deps = [ ":base" ] + deps = [ + ":base", + ] if (is_android && !is_debug) { configs -= [ "//build/config/compiler:optimize" ] @@ -1086,9 +1029,11 @@ source_set("prefs_test_support") { "prefs/testing_pref_store.h", ] + public_deps = [ + ":prefs", + ] deps = [ ":base", - ":prefs", "//testing/gmock", "//testing/gtest", ] @@ -1107,12 +1052,33 @@ source_set("message_loop_tests") { ] } +if (is_win) { + # Target to manually rebuild pe_image_test.dll which is checked into + # base/test/data/pe_image. + shared_library("pe_image_test") { + sources = [ + "win/pe_image_test.cc", + ] + ldflags = [ + "/DELAYLOAD:cfgmgr32.dll", + "/DELAYLOAD:shell32.dll", + "/SUBSYSTEM:WINDOWS", + ] + libs = [ + "cfgmgr32.lib", + "shell32.lib", + ] + } +} + test("base_unittests") { sources = [ "android/application_status_listener_unittest.cc", + "android/content_uri_utils_unittest.cc", "android/jni_android_unittest.cc", "android/jni_array_unittest.cc", "android/jni_string_unittest.cc", + "android/library_loader/library_prefetcher_unittest.cc", "android/path_utils_unittest.cc", "android/scoped_java_ref_unittest.cc", "android/sys_utils_unittest.cc", @@ -1132,31 +1098,28 @@ test("base_unittests") { "callback_unittest.cc", "callback_unittest.nc", "cancelable_callback_unittest.cc", + "chromeos/memory_pressure_monitor_unittest.cc", "command_line_unittest.cc", "containers/adapters_unittest.cc", "containers/hash_tables_unittest.cc", "containers/linked_list_unittest.cc", "containers/mru_cache_unittest.cc", + "containers/scoped_ptr_hash_map_unittest.cc", "containers/small_map_unittest.cc", "containers/stack_container_unittest.cc", "cpu_unittest.cc", "debug/crash_logging_unittest.cc", + "debug/debugger_unittest.cc", "debug/leak_tracker_unittest.cc", "debug/proc_maps_linux_unittest.cc", "debug/stack_trace_unittest.cc", "debug/task_annotator_unittest.cc", - "debug/trace_event_argument_unittest.cc", - "debug/trace_event_memory_unittest.cc", - "debug/trace_event_synthetic_delay_unittest.cc", - "debug/trace_event_system_stats_monitor_unittest.cc", - "debug/trace_event_unittest.cc", - "debug/trace_event_unittest.h", - "debug/trace_event_win_unittest.cc", "deferred_sequenced_task_runner_unittest.cc", "environment_unittest.cc", "file_version_info_unittest.cc", "files/dir_reader_posix_unittest.cc", "files/file_path_unittest.cc", + "files/file_path_watcher_unittest.cc", "files/file_proxy_unittest.cc", "files/file_unittest.cc", "files/file_util_proxy_unittest.cc", @@ -1166,10 +1129,9 @@ test("base_unittests") { "gmock_unittest.cc", "guid_unittest.cc", "hash_unittest.cc", - "id_map_unittest.cc", "i18n/break_iterator_unittest.cc", - "i18n/char_iterator_unittest.cc", "i18n/case_conversion_unittest.cc", + "i18n/char_iterator_unittest.cc", "i18n/file_util_icu_unittest.cc", "i18n/icu_string_conversions_unittest.cc", "i18n/number_formatting_unittest.cc", @@ -1178,7 +1140,9 @@ test("base_unittests") { "i18n/string_search_unittest.cc", "i18n/time_formatting_unittest.cc", "i18n/timezone_unittest.cc", + "id_map_unittest.cc", "ios/device_util_unittest.mm", + "ios/weak_nsobject_unittest.mm", "json/json_parser_unittest.cc", "json/json_reader_unittest.cc", "json/json_value_converter_unittest.cc", @@ -1188,6 +1152,7 @@ test("base_unittests") { "lazy_instance_unittest.cc", "logging_unittest.cc", "mac/bind_objc_block_unittest.mm", + "mac/dispatch_source_mach_unittest.cc", "mac/foundation_util_unittest.mm", "mac/libdispatch_task_runner_unittest.cc", "mac/mac_util_unittest.mm", @@ -1197,8 +1162,6 @@ test("base_unittests") { "mac/scoped_sending_event_unittest.mm", "md5_unittest.cc", "memory/aligned_memory_unittest.cc", - "memory/discardable_memory_manager_unittest.cc", - "memory/discardable_memory_unittest.cc", "memory/discardable_shared_memory_unittest.cc", "memory/linked_ptr_unittest.cc", "memory/ref_counted_memory_unittest.cc", @@ -1215,17 +1178,18 @@ test("base_unittests") { "message_loop/message_loop_unittest.cc", "message_loop/message_pump_glib_unittest.cc", "message_loop/message_pump_io_ios_unittest.cc", - "metrics/sample_map_unittest.cc", - "metrics/sample_vector_unittest.cc", "metrics/bucket_ranges_unittest.cc", "metrics/field_trial_unittest.cc", "metrics/histogram_base_unittest.cc", "metrics/histogram_delta_serialization_unittest.cc", "metrics/histogram_snapshot_manager_unittest.cc", "metrics/histogram_unittest.cc", + "metrics/sample_map_unittest.cc", + "metrics/sample_vector_unittest.cc", "metrics/sparse_histogram_unittest.cc", - "metrics/stats_table_unittest.cc", "metrics/statistics_recorder_unittest.cc", + "move_unittest.cc", + "numerics/safe_numerics_unittest.cc", "observer_list_unittest.cc", "os_compat_android_unittest.cc", "path_service_unittest.cc", @@ -1251,9 +1215,9 @@ test("base_unittests") { "process/process_unittest.cc", "process/process_util_unittest.cc", "process/process_util_unittest_ios.cc", + "profiler/stack_sampling_profiler_unittest.cc", "profiler/tracked_time_unittest.cc", "rand_util_unittest.cc", - "numerics/safe_numerics_unittest.cc", "scoped_clear_errno_unittest.cc", "scoped_generic_unittest.cc", "scoped_native_library_unittest.cc", @@ -1264,13 +1228,13 @@ test("base_unittests") { "strings/nullable_string16_unittest.cc", "strings/safe_sprintf_unittest.cc", "strings/string16_unittest.cc", - "strings/stringprintf_unittest.cc", "strings/string_number_conversions_unittest.cc", "strings/string_piece_unittest.cc", "strings/string_split_unittest.cc", "strings/string_tokenizer_unittest.cc", "strings/string_util_unittest.cc", "strings/stringize_macros_unittest.cc", + "strings/stringprintf_unittest.cc", "strings/sys_string_conversions_mac_unittest.mm", "strings/sys_string_conversions_unittest.cc", "strings/utf_offset_string_conversions_unittest.cc", @@ -1292,6 +1256,7 @@ test("base_unittests") { "test/histogram_tester_unittest.cc", "test/test_reg_util_win_unittest.cc", "test/trace_event_analyzer_unittest.cc", + "test/user_action_tester_unittest.cc", "threading/non_thread_safe_unittest.cc", "threading/platform_thread_unittest.cc", "threading/sequenced_worker_pool_unittest.cc", @@ -1324,6 +1289,7 @@ test("base_unittests") { "win/event_trace_provider_unittest.cc", "win/i18n_unittest.cc", "win/iunknown_impl_unittest.cc", + "win/memory_pressure_monitor_unittest.cc", "win/message_window_unittest.cc", "win/object_watcher_unittest.cc", "win/pe_image_unittest.cc", @@ -1348,14 +1314,27 @@ test("base_unittests") { "//base/test:run_all_unittests", "//base/test:test_support", "//base/third_party/dynamic_annotations", + "//base/trace_event:trace_event_unittests", "//testing/gmock", "//testing/gtest", "//third_party/icu", ] + # Allow more direct string conversions on platforms with native utf8 + # strings + if (is_mac || is_ios || is_chromeos) { + defines = [ "SYSTEM_NATIVE_UTF8" ] + } + + if (is_android) { + apk_deps = [ + ":base_java", + ":base_java_unittest_support", + ] + } + if (is_ios) { sources -= [ - "metrics/stats_table_uinittest.cc", # Requires spawning a process. "process/memory_unittest.cc", "process/memory_unittest_mac.h", "process/memory_unittest_mac.mm", @@ -1381,8 +1360,11 @@ test("base_unittests") { if (is_linux) { sources -= [ "file_version_info_unittest.cc" ] sources += [ "nix/xdg_util_unittest.cc" ] - defines = [ "USE_SYMBOLIZE" ] - configs += [ "//build/config/linux:glib" ] + deps += [ "//base/test:malloc_wrapper" ] + + if (use_glib) { + configs += [ "//build/config/linux:glib" ] + } } if (!is_linux || use_ozone) { @@ -1395,19 +1377,21 @@ test("base_unittests") { } if (is_android) { - deps += [ - "//testing/android:native_test_native_code", - ] + deps += [ "//testing/android/native_test:native_test_native_code" ] set_sources_assignment_filter([]) sources += [ "debug/proc_maps_linux_unittest.cc" ] set_sources_assignment_filter(sources_assignment_filter) } + + # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. + configs += [ "//build/config/compiler:no_size_t_to_int_warning" ] } if (is_android) { # GYP: //base.gyp:base_jni_headers generate_jni("base_jni_headers") { sources = [ + "android/java/src/org/chromium/base/AnimationFrameTimeHistogram.java", "android/java/src/org/chromium/base/ApplicationStatus.java", "android/java/src/org/chromium/base/BuildInfo.java", "android/java/src/org/chromium/base/CommandLine.java", @@ -1417,19 +1401,32 @@ if (is_android) { "android/java/src/org/chromium/base/FieldTrialList.java", "android/java/src/org/chromium/base/ImportantFileWriterAndroid.java", "android/java/src/org/chromium/base/JNIUtils.java", - "android/java/src/org/chromium/base/library_loader/LibraryLoader.java", + "android/java/src/org/chromium/base/JavaHandlerThread.java", "android/java/src/org/chromium/base/LocaleUtils.java", "android/java/src/org/chromium/base/MemoryPressureListener.java", - "android/java/src/org/chromium/base/JavaHandlerThread.java", "android/java/src/org/chromium/base/PathService.java", "android/java/src/org/chromium/base/PathUtils.java", "android/java/src/org/chromium/base/PowerMonitor.java", - "android/java/src/org/chromium/base/SystemMessageHandler.java", "android/java/src/org/chromium/base/SysUtils.java", + "android/java/src/org/chromium/base/SystemMessageHandler.java", "android/java/src/org/chromium/base/ThreadUtils.java", "android/java/src/org/chromium/base/TraceEvent.java", + "android/java/src/org/chromium/base/library_loader/LibraryLoader.java", + "android/java/src/org/chromium/base/metrics/RecordHistogram.java", + "android/java/src/org/chromium/base/metrics/RecordUserAction.java", + ] + + deps = [ + ":android_runtime_jni_headers", ] + + jni_package = "base" + } + + # GYP: //base.gyp:android_runtime_jni_headers + generate_jar_jni("android_runtime_jni_headers") { jni_package = "base" + classes = [ "java/lang/Runtime.class" ] } # GYP: //base.gyp:base_java @@ -1440,7 +1437,7 @@ if (is_android) { ] deps = [ - "//third_party/jsr-305:jsr_305_javalib" + "//third_party/jsr-305:jsr_305_javalib", ] DEPRECATED_java_in_dir = "android/java/src" @@ -1466,22 +1463,35 @@ if (is_android) { android_library("base_java_test_support") { deps = [ ":base_java", + "//testing/android/reporter:reporter_java", ] DEPRECATED_java_in_dir = "test/android/javatests/src" } + # GYP: //base.gyp:base_junit_tests + junit_binary("base_junit_tests") { + java_files = [ "android/junit/src/org/chromium/base/LogTest.java" ] + deps = [ + ":base_java", + ":base_java_test_support", + ] + } + # GYP: //base.gyp:base_java_application_state # GYP: //base.gyp:base_java_library_load_from_apk_status_codes + # GYP: //base.gyp:base_java_library_process_type # GYP: //base.gyp:base_java_memory_pressure_level java_cpp_enum("base_android_java_enums_srcjar") { sources = [ "android/application_status_listener.h", "android/library_loader/library_load_from_apk_status_codes.h", + "android/library_loader/library_loader_hooks.h", "memory/memory_pressure_listener.h", ] outputs = [ "org/chromium/base/ApplicationState.java", "org/chromium/base/library_loader/LibraryLoadFromApkStatusCodes.java", + "org/chromium/base/library_loader/LibraryProcessType.java", "org/chromium/base/MemoryPressureLevel.java", ] } @@ -1496,19 +1506,10 @@ if (is_android) { # GYP: //base.gyp:base_java_unittest_support android_library("base_java_unittest_support") { - deps = [":base_java"] - java_files = [ - "test/android/java/src/org/chromium/base/ContentUriTestUtils.java" - ] - } - - # GYP: //base.gyp:base_unittests_apk - unittest_apk("base_unittests_apk") { deps = [ ":base_java", - ":base_java_unittest_support", - ":base_unittests", ] - unittests_dep = ":base_unittests" + java_files = + [ "test/android/java/src/org/chromium/base/ContentUriTestUtils.java" ] } } diff --git a/chromium/base/DEPS b/chromium/base/DEPS index 2407baaa53c..c632e35d83a 100644 --- a/chromium/base/DEPS +++ b/chromium/base/DEPS @@ -4,7 +4,6 @@ include_rules = [ "+third_party/apple_apsl", "+third_party/libevent", "+third_party/dmg_fp", - "+third_party/google_toolbox_for_mac/src", "+third_party/mach_override", "+third_party/modp_b64", "+third_party/tcmalloc", diff --git a/chromium/base/OWNERS b/chromium/base/OWNERS index 92844b617f3..9890491e471 100644 --- a/chromium/base/OWNERS +++ b/chromium/base/OWNERS @@ -1,6 +1,5 @@ mark@chromium.org darin@chromium.org -ajwong@chromium.org thakis@chromium.org danakj@chromium.org rvargas@chromium.org @@ -24,24 +23,9 @@ willchan@chromium.org per-file *.isolate=csharp@chromium.org per-file *.isolate=maruel@chromium.org -per-file bind.h=ajwong@chromium.org -per-file bind_helpers.cc=ajwong@chromium.org -per-file bind_helpers.h=ajwong@chromium.org -per-file bind_helpers_unittest.cc=ajwong@chromium.org -per-file bind.h.pump=ajwong@chromium.org -per-file bind_internal.h=ajwong@chromium.org -per-file bind_internal.h.pump=ajwong@chromium.org -per-file bind_internal_win.h=ajwong@chromium.org -per-file bind_internal_win.h.pump=ajwong@chromium.org -per-file bind_unittest.cc=ajwong@chromium.org -per-file bind_unittest.nc=ajwong@chromium.org -per-file callback_forward.h=ajwong@chromium.org -per-file callback.h=ajwong@chromium.org -per-file callback_helpers.h=ajwong@chromium.org -per-file callback.h.pump=ajwong@chromium.org -per-file callback_internal.cc=ajwong@chromium.org -per-file callback_internal.h=ajwong@chromium.org -per-file callback_unittest.cc=ajwong@chromium.org -per-file callback_unittest.h=ajwong@chromium.org -per-file callback_unittest.nc=ajwong@chromium.org per-file security_unittest.cc=jln@chromium.org + +# For Android-specific changes: +per-file *android*=nyquist@chromium.org +per-file *android*=rmcilroy@chromium.org +per-file *android*=yfriedman@chromium.org diff --git a/chromium/base/PRESUBMIT.py b/chromium/base/PRESUBMIT.py index 46ab02e3264..7fc8107658c 100644 --- a/chromium/base/PRESUBMIT.py +++ b/chromium/base/PRESUBMIT.py @@ -5,7 +5,7 @@ """Chromium presubmit script for src/base. See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts -for more details on the presubmit API built into gcl. +for more details on the presubmit API built into depot_tools. """ def _CheckNoInterfacesInBase(input_api, output_api): diff --git a/chromium/base/allocator/BUILD.gn b/chromium/base/allocator/BUILD.gn index 510c3d38f52..a07a356e5d8 100644 --- a/chromium/base/allocator/BUILD.gn +++ b/chromium/base/allocator/BUILD.gn @@ -10,7 +10,9 @@ import("//build/config/allocator.gni") # to the build settings. group("allocator") { if (use_allocator == "tcmalloc") { - deps = [ ":tcmalloc" ] + deps = [ + ":tcmalloc", + ] } } @@ -28,20 +30,27 @@ if (is_win) { action("prep_libc") { script = "prep_libc.py" - outputs = [ "$target_gen_dir/allocator/libcmt.lib" ] + outputs = [ + "$target_gen_dir/allocator/libcmt.lib", + ] args = [ visual_studio_path + "/vc/lib", rebase_path("$target_gen_dir/allocator"), - cpu_arch, + current_cpu, ] } } -if (!is_android) { +if (use_allocator == "tcmalloc") { # tcmalloc currently won't compile on Android. source_set("tcmalloc") { tcmalloc_dir = "//third_party/tcmalloc/chromium" + # Don't check tcmalloc's includes. These files include various files like + # base/foo.h and they actually refer to tcmalloc's forked copy of base + # rather than the regular one, which confuses the header checker. + check_includes = false + sources = [ # Generated for our configuration from tcmalloc"s build # and checked in. @@ -54,6 +63,7 @@ if (!is_android) { "$tcmalloc_dir/src/base/abort.cc", "$tcmalloc_dir/src/base/abort.h", "$tcmalloc_dir/src/base/arm_instruction_set_select.h", + # We don't list dynamic_annotations.c since its copy is already # present in the dynamic_annotations target. "$tcmalloc_dir/src/base/elf_mem_image.cc", @@ -79,6 +89,7 @@ if (!is_android) { "$tcmalloc_dir/src/central_freelist.h", "$tcmalloc_dir/src/common.cc", "$tcmalloc_dir/src/common.h", + # #included by debugallocation_shim.cc #"$tcmalloc_dir/src/debugallocation.cc", "$tcmalloc_dir/src/deep-heap-profile.cc", @@ -120,15 +131,14 @@ if (!is_android) { "$tcmalloc_dir/src/symbolize.h", "$tcmalloc_dir/src/system-alloc.cc", "$tcmalloc_dir/src/system-alloc.h", + # #included by debugallocation_shim.cc #"$tcmalloc_dir/src/tcmalloc.cc", "$tcmalloc_dir/src/thread_cache.cc", "$tcmalloc_dir/src/thread_cache.h", "$tcmalloc_dir/src/windows/port.cc", "$tcmalloc_dir/src/windows/port.h", - "allocator_shim.cc", - "allocator_shim.h", "debugallocation_shim.cc", # These are both #included by allocator_shim for maximal linking. @@ -170,10 +180,10 @@ if (!is_android) { # cpuprofiler "$tcmalloc_dir/src/base/thread_lister.c", "$tcmalloc_dir/src/base/thread_lister.h", - "$tcmalloc_dir/src/profiledata.cc", - "$tcmalloc_dir/src/profiledata.h", "$tcmalloc_dir/src/profile-handler.cc", "$tcmalloc_dir/src/profile-handler.h", + "$tcmalloc_dir/src/profiledata.cc", + "$tcmalloc_dir/src/profiledata.h", "$tcmalloc_dir/src/profiler.cc", ] defines += [ "PERFTOOLS_DLL_DECL=" ] @@ -186,9 +196,7 @@ if (!is_android) { public_configs = [ ":nocmt" ] - deps += [ - ":prep_libc", - ] + deps += [ ":prep_libc" ] } if (is_linux || is_android) { @@ -215,6 +223,7 @@ if (!is_android) { # Don't let linker rip this symbol out, otherwise the heap&cpu # profilers will not initialize properly on startup. "-Wl,-uIsHeapProfilerRunning,-uProfilerStart", + # Do the same for heap leak checker. "-Wl,-u_Z21InitialMallocHook_NewPKvj,-u_Z22InitialMallocHook_MMapPKvS0_jiiix,-u_Z22InitialMallocHook_SbrkPKvi", "-Wl,-u_Z21InitialMallocHook_NewPKvm,-u_Z22InitialMallocHook_MMapPKvS0_miiil,-u_Z22InitialMallocHook_SbrkPKvl", @@ -229,9 +238,7 @@ if (!is_android) { configs += [ "//build/config/compiler:optimize_max" ] } - deps += [ - "//base/third_party/dynamic_annotations", - ] + deps += [ "//base/third_party/dynamic_annotations" ] if (is_win) { ldflags = [ "/ignore:4006:4221" ] diff --git a/chromium/base/allocator/allocator.gyp b/chromium/base/allocator/allocator.gyp index 323d79ede60..d426c9c3f17 100644 --- a/chromium/base/allocator/allocator.gyp +++ b/chromium/base/allocator/allocator.gyp @@ -8,7 +8,7 @@ # This code gets run a lot and debugged rarely, so it should be fast # by default. See http://crbug.com/388949. 'debug_optimize': '2', - 'win_debug_Optimization': '2', + 'win_debug_Optimization': '0', # Run time checks are incompatible with any level of optimizations. 'win_debug_RuntimeChecks': '0', }, @@ -24,17 +24,6 @@ { 'target_name': 'allocator', 'type': 'static_library', - # Make sure the allocation library is optimized to - # the hilt in official builds. - 'variables': { - 'optimize': 'max', - }, - 'include_dirs': [ - '.', - '<(tcmalloc_dir)/src/base', - '<(tcmalloc_dir)/src', - '../..', - ], 'direct_dependent_settings': { 'configurations': { 'Common_Base': { @@ -56,242 +45,6 @@ }], ], }, - 'sources': [ - # Generated for our configuration from tcmalloc's build - # and checked in. - '<(tcmalloc_dir)/src/config.h', - '<(tcmalloc_dir)/src/config_android.h', - '<(tcmalloc_dir)/src/config_linux.h', - '<(tcmalloc_dir)/src/config_win.h', - - # all tcmalloc native and forked files - '<(tcmalloc_dir)/src/addressmap-inl.h', - '<(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-linuxppc.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-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. - '<(tcmalloc_dir)/src/base/dynamic_annotations.h', - '<(tcmalloc_dir)/src/base/elf_mem_image.cc', - '<(tcmalloc_dir)/src/base/elf_mem_image.h', - '<(tcmalloc_dir)/src/base/elfcore.h', - '<(tcmalloc_dir)/src/base/googleinit.h', - '<(tcmalloc_dir)/src/base/linux_syscall_support.h', - '<(tcmalloc_dir)/src/base/linuxthreads.cc', - '<(tcmalloc_dir)/src/base/linuxthreads.h', - '<(tcmalloc_dir)/src/base/logging.cc', - '<(tcmalloc_dir)/src/base/logging.h', - '<(tcmalloc_dir)/src/base/low_level_alloc.cc', - '<(tcmalloc_dir)/src/base/low_level_alloc.h', - '<(tcmalloc_dir)/src/base/simple_mutex.h', - '<(tcmalloc_dir)/src/base/spinlock.cc', - '<(tcmalloc_dir)/src/base/spinlock.h', - '<(tcmalloc_dir)/src/base/spinlock_internal.cc', - '<(tcmalloc_dir)/src/base/spinlock_internal.h', - '<(tcmalloc_dir)/src/base/spinlock_linux-inl.h', - '<(tcmalloc_dir)/src/base/spinlock_posix-inl.h', - '<(tcmalloc_dir)/src/base/spinlock_win32-inl.h', - '<(tcmalloc_dir)/src/base/stl_allocator.h', - '<(tcmalloc_dir)/src/base/synchronization_profiling.h', - '<(tcmalloc_dir)/src/base/sysinfo.cc', - '<(tcmalloc_dir)/src/base/sysinfo.h', - '<(tcmalloc_dir)/src/base/thread_annotations.h', - '<(tcmalloc_dir)/src/base/thread_lister.c', - '<(tcmalloc_dir)/src/base/thread_lister.h', - '<(tcmalloc_dir)/src/base/vdso_support.cc', - '<(tcmalloc_dir)/src/base/vdso_support.h', - '<(tcmalloc_dir)/src/central_freelist.cc', - '<(tcmalloc_dir)/src/central_freelist.h', - '<(tcmalloc_dir)/src/common.cc', - '<(tcmalloc_dir)/src/common.h', - '<(tcmalloc_dir)/src/debugallocation.cc', - '<(tcmalloc_dir)/src/deep-heap-profile.cc', - '<(tcmalloc_dir)/src/deep-heap-profile.h', - '<(tcmalloc_dir)/src/free_list.cc', - '<(tcmalloc_dir)/src/free_list.h', - '<(tcmalloc_dir)/src/getpc.h', - '<(tcmalloc_dir)/src/gperftools/heap-checker.h', - '<(tcmalloc_dir)/src/gperftools/heap-profiler.h', - '<(tcmalloc_dir)/src/gperftools/malloc_extension.h', - '<(tcmalloc_dir)/src/gperftools/malloc_extension_c.h', - '<(tcmalloc_dir)/src/gperftools/malloc_hook.h', - '<(tcmalloc_dir)/src/gperftools/malloc_hook_c.h', - '<(tcmalloc_dir)/src/gperftools/profiler.h', - '<(tcmalloc_dir)/src/gperftools/stacktrace.h', - '<(tcmalloc_dir)/src/gperftools/tcmalloc.h', - '<(tcmalloc_dir)/src/heap-checker-bcad.cc', - '<(tcmalloc_dir)/src/heap-checker.cc', - '<(tcmalloc_dir)/src/heap-profile-table.cc', - '<(tcmalloc_dir)/src/heap-profile-table.h', - '<(tcmalloc_dir)/src/heap-profiler.cc', - '<(tcmalloc_dir)/src/internal_logging.cc', - '<(tcmalloc_dir)/src/internal_logging.h', - '<(tcmalloc_dir)/src/libc_override.h', - '<(tcmalloc_dir)/src/libc_override_gcc_and_weak.h', - '<(tcmalloc_dir)/src/libc_override_glibc.h', - '<(tcmalloc_dir)/src/libc_override_osx.h', - '<(tcmalloc_dir)/src/libc_override_redefine.h', - '<(tcmalloc_dir)/src/linked_list.h', - '<(tcmalloc_dir)/src/malloc_extension.cc', - '<(tcmalloc_dir)/src/malloc_hook-inl.h', - '<(tcmalloc_dir)/src/malloc_hook.cc', - '<(tcmalloc_dir)/src/malloc_hook_mmap_freebsd.h', - '<(tcmalloc_dir)/src/malloc_hook_mmap_linux.h', - '<(tcmalloc_dir)/src/maybe_threads.cc', - '<(tcmalloc_dir)/src/maybe_threads.h', - '<(tcmalloc_dir)/src/memfs_malloc.cc', - '<(tcmalloc_dir)/src/memory_region_map.cc', - '<(tcmalloc_dir)/src/memory_region_map.h', - '<(tcmalloc_dir)/src/packed-cache-inl.h', - '<(tcmalloc_dir)/src/page_heap.cc', - '<(tcmalloc_dir)/src/page_heap.h', - '<(tcmalloc_dir)/src/page_heap_allocator.h', - '<(tcmalloc_dir)/src/pagemap.h', - '<(tcmalloc_dir)/src/profile-handler.cc', - '<(tcmalloc_dir)/src/profile-handler.h', - '<(tcmalloc_dir)/src/profiledata.cc', - '<(tcmalloc_dir)/src/profiledata.h', - '<(tcmalloc_dir)/src/profiler.cc', - '<(tcmalloc_dir)/src/raw_printer.cc', - '<(tcmalloc_dir)/src/raw_printer.h', - '<(tcmalloc_dir)/src/sampler.cc', - '<(tcmalloc_dir)/src/sampler.h', - '<(tcmalloc_dir)/src/span.cc', - '<(tcmalloc_dir)/src/span.h', - '<(tcmalloc_dir)/src/stack_trace_table.cc', - '<(tcmalloc_dir)/src/stack_trace_table.h', - '<(tcmalloc_dir)/src/stacktrace.cc', - '<(tcmalloc_dir)/src/stacktrace_arm-inl.h', - '<(tcmalloc_dir)/src/stacktrace_config.h', - '<(tcmalloc_dir)/src/stacktrace_generic-inl.h', - '<(tcmalloc_dir)/src/stacktrace_libunwind-inl.h', - '<(tcmalloc_dir)/src/stacktrace_powerpc-inl.h', - '<(tcmalloc_dir)/src/stacktrace_win32-inl.h', - '<(tcmalloc_dir)/src/stacktrace_with_context.cc', - '<(tcmalloc_dir)/src/stacktrace_x86-inl.h', - '<(tcmalloc_dir)/src/static_vars.cc', - '<(tcmalloc_dir)/src/static_vars.h', - '<(tcmalloc_dir)/src/symbolize.cc', - '<(tcmalloc_dir)/src/symbolize.h', - '<(tcmalloc_dir)/src/system-alloc.cc', - '<(tcmalloc_dir)/src/system-alloc.h', - '<(tcmalloc_dir)/src/tcmalloc.cc', - '<(tcmalloc_dir)/src/tcmalloc_guard.h', - '<(tcmalloc_dir)/src/thread_cache.cc', - '<(tcmalloc_dir)/src/thread_cache.h', - '<(tcmalloc_dir)/src/windows/config.h', - '<(tcmalloc_dir)/src/windows/get_mangled_names.cc', - '<(tcmalloc_dir)/src/windows/gperftools/tcmalloc.h', - '<(tcmalloc_dir)/src/windows/ia32_modrm_map.cc', - '<(tcmalloc_dir)/src/windows/ia32_opcode_map.cc', - '<(tcmalloc_dir)/src/windows/mingw.h', - '<(tcmalloc_dir)/src/windows/mini_disassembler.cc', - '<(tcmalloc_dir)/src/windows/mini_disassembler.h', - '<(tcmalloc_dir)/src/windows/mini_disassembler_types.h', - '<(tcmalloc_dir)/src/windows/override_functions.cc', - '<(tcmalloc_dir)/src/windows/patch_functions.cc', - '<(tcmalloc_dir)/src/windows/port.cc', - '<(tcmalloc_dir)/src/windows/port.h', - '<(tcmalloc_dir)/src/windows/preamble_patcher.cc', - '<(tcmalloc_dir)/src/windows/preamble_patcher.h', - '<(tcmalloc_dir)/src/windows/preamble_patcher_with_stub.cc', - - 'allocator_shim.cc', - 'allocator_shim.h', - 'debugallocation_shim.cc', - 'generic_allocators.cc', - 'win_allocator.cc', - ], - # sources! means that these are not compiled directly. - 'sources!': [ - # Included by allocator_shim.cc for maximal inlining. - 'generic_allocators.cc', - 'win_allocator.cc', - - # Included by debugallocation_shim.cc. - '<(tcmalloc_dir)/src/debugallocation.cc', - '<(tcmalloc_dir)/src/tcmalloc.cc', - - # We simply don't use these, but list them above so that IDE - # users can view the full available source for reference, etc. - '<(tcmalloc_dir)/src/addressmap-inl.h', - '<(tcmalloc_dir)/src/base/atomicops-internals-linuxppc.h', - '<(tcmalloc_dir)/src/base/atomicops-internals-macosx.h', - '<(tcmalloc_dir)/src/base/atomicops-internals-x86-msvc.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', - '<(tcmalloc_dir)/src/base/elf_mem_image.h', - '<(tcmalloc_dir)/src/base/elfcore.h', - '<(tcmalloc_dir)/src/base/googleinit.h', - '<(tcmalloc_dir)/src/base/linux_syscall_support.h', - '<(tcmalloc_dir)/src/base/simple_mutex.h', - '<(tcmalloc_dir)/src/base/spinlock_linux-inl.h', - '<(tcmalloc_dir)/src/base/spinlock_posix-inl.h', - '<(tcmalloc_dir)/src/base/spinlock_win32-inl.h', - '<(tcmalloc_dir)/src/base/stl_allocator.h', - '<(tcmalloc_dir)/src/base/thread_annotations.h', - '<(tcmalloc_dir)/src/getpc.h', - '<(tcmalloc_dir)/src/gperftools/heap-checker.h', - '<(tcmalloc_dir)/src/gperftools/heap-profiler.h', - '<(tcmalloc_dir)/src/gperftools/malloc_extension.h', - '<(tcmalloc_dir)/src/gperftools/malloc_extension_c.h', - '<(tcmalloc_dir)/src/gperftools/malloc_hook.h', - '<(tcmalloc_dir)/src/gperftools/malloc_hook_c.h', - '<(tcmalloc_dir)/src/gperftools/profiler.h', - '<(tcmalloc_dir)/src/gperftools/stacktrace.h', - '<(tcmalloc_dir)/src/gperftools/tcmalloc.h', - '<(tcmalloc_dir)/src/heap-checker-bcad.cc', - '<(tcmalloc_dir)/src/heap-checker.cc', - '<(tcmalloc_dir)/src/libc_override.h', - '<(tcmalloc_dir)/src/libc_override_gcc_and_weak.h', - '<(tcmalloc_dir)/src/libc_override_glibc.h', - '<(tcmalloc_dir)/src/libc_override_osx.h', - '<(tcmalloc_dir)/src/libc_override_redefine.h', - '<(tcmalloc_dir)/src/malloc_hook_mmap_freebsd.h', - '<(tcmalloc_dir)/src/malloc_hook_mmap_linux.h', - '<(tcmalloc_dir)/src/memfs_malloc.cc', - '<(tcmalloc_dir)/src/packed-cache-inl.h', - '<(tcmalloc_dir)/src/page_heap_allocator.h', - '<(tcmalloc_dir)/src/pagemap.h', - '<(tcmalloc_dir)/src/stacktrace_arm-inl.h', - '<(tcmalloc_dir)/src/stacktrace_config.h', - '<(tcmalloc_dir)/src/stacktrace_generic-inl.h', - '<(tcmalloc_dir)/src/stacktrace_libunwind-inl.h', - '<(tcmalloc_dir)/src/stacktrace_powerpc-inl.h', - '<(tcmalloc_dir)/src/stacktrace_win32-inl.h', - '<(tcmalloc_dir)/src/stacktrace_with_context.cc', - '<(tcmalloc_dir)/src/stacktrace_x86-inl.h', - '<(tcmalloc_dir)/src/tcmalloc_guard.h', - '<(tcmalloc_dir)/src/windows/config.h', - '<(tcmalloc_dir)/src/windows/gperftools/tcmalloc.h', - '<(tcmalloc_dir)/src/windows/get_mangled_names.cc', - '<(tcmalloc_dir)/src/windows/ia32_modrm_map.cc', - '<(tcmalloc_dir)/src/windows/ia32_opcode_map.cc', - '<(tcmalloc_dir)/src/windows/mingw.h', - '<(tcmalloc_dir)/src/windows/mini_disassembler.cc', - '<(tcmalloc_dir)/src/windows/mini_disassembler.h', - '<(tcmalloc_dir)/src/windows/mini_disassembler_types.h', - '<(tcmalloc_dir)/src/windows/override_functions.cc', - '<(tcmalloc_dir)/src/windows/patch_functions.cc', - '<(tcmalloc_dir)/src/windows/preamble_patcher.cc', - '<(tcmalloc_dir)/src/windows/preamble_patcher.h', - '<(tcmalloc_dir)/src/windows/preamble_patcher_with_stub.cc', - ], 'dependencies': [ '../third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', ], @@ -318,8 +71,7 @@ 'disable_debugallocation%': 0, }, 'conditions': [ - # TODO(phajdan.jr): Also enable on Windows. - ['disable_debugallocation==0 and OS!="win"', { + ['disable_debugallocation==0', { 'defines': [ # Use debugallocation for Debug builds to catch problems early # and cleanly, http://crbug.com/30715 . @@ -329,76 +81,257 @@ ], }, }, - # Disable the heap checker in tcmalloc. - 'defines': [ - 'NO_HEAP_CHECK', - ], 'conditions': [ - ['OS=="linux" and clang_type_profiler==1', { - 'dependencies': [ - 'type_profiler_tcmalloc', - ], - # It is undoing dependencies and cflags_cc for type_profiler which - # build/common.gypi injects into all targets. - 'dependencies!': [ - 'type_profiler', - ], - 'cflags_cc!': [ - '-fintercept-allocation-functions', - ], - }], - ['OS=="win"', { + ['use_allocator=="tcmalloc"', { + # Disable the heap checker in tcmalloc. 'defines': [ - 'PERFTOOLS_DLL_DECL=', - ], - 'defines!': [ - # tcmalloc source files unconditionally define this, remove it from - # the list of defines that common.gypi defines globally. - 'NOMINMAX', - ], - 'dependencies': [ - 'libcmt', + 'NO_HEAP_CHECK', ], 'include_dirs': [ - '<(tcmalloc_dir)/src/windows', + '.', + '<(tcmalloc_dir)/src/base', + '<(tcmalloc_dir)/src', + '../..', ], - 'sources!': [ + 'sources': [ + # Generated for our configuration from tcmalloc's build + # and checked in. + '<(tcmalloc_dir)/src/config.h', + '<(tcmalloc_dir)/src/config_android.h', + '<(tcmalloc_dir)/src/config_linux.h', + '<(tcmalloc_dir)/src/config_win.h', + + # all tcmalloc native and forked files + '<(tcmalloc_dir)/src/addressmap-inl.h', + '<(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. + '<(tcmalloc_dir)/src/base/dynamic_annotations.h', '<(tcmalloc_dir)/src/base/elf_mem_image.cc', '<(tcmalloc_dir)/src/base/elf_mem_image.h', + '<(tcmalloc_dir)/src/base/elfcore.h', + '<(tcmalloc_dir)/src/base/googleinit.h', + '<(tcmalloc_dir)/src/base/linux_syscall_support.h', '<(tcmalloc_dir)/src/base/linuxthreads.cc', '<(tcmalloc_dir)/src/base/linuxthreads.h', + '<(tcmalloc_dir)/src/base/logging.cc', + '<(tcmalloc_dir)/src/base/logging.h', + '<(tcmalloc_dir)/src/base/low_level_alloc.cc', + '<(tcmalloc_dir)/src/base/low_level_alloc.h', + '<(tcmalloc_dir)/src/base/simple_mutex.h', + '<(tcmalloc_dir)/src/base/spinlock.cc', + '<(tcmalloc_dir)/src/base/spinlock.h', + '<(tcmalloc_dir)/src/base/spinlock_internal.cc', + '<(tcmalloc_dir)/src/base/spinlock_internal.h', + '<(tcmalloc_dir)/src/base/spinlock_linux-inl.h', + '<(tcmalloc_dir)/src/base/spinlock_posix-inl.h', + '<(tcmalloc_dir)/src/base/spinlock_win32-inl.h', + '<(tcmalloc_dir)/src/base/stl_allocator.h', + '<(tcmalloc_dir)/src/base/synchronization_profiling.h', + '<(tcmalloc_dir)/src/base/sysinfo.cc', + '<(tcmalloc_dir)/src/base/sysinfo.h', + '<(tcmalloc_dir)/src/base/thread_annotations.h', + '<(tcmalloc_dir)/src/base/thread_lister.c', + '<(tcmalloc_dir)/src/base/thread_lister.h', '<(tcmalloc_dir)/src/base/vdso_support.cc', '<(tcmalloc_dir)/src/base/vdso_support.h', + '<(tcmalloc_dir)/src/central_freelist.cc', + '<(tcmalloc_dir)/src/central_freelist.h', + '<(tcmalloc_dir)/src/common.cc', + '<(tcmalloc_dir)/src/common.h', + '<(tcmalloc_dir)/src/debugallocation.cc', + '<(tcmalloc_dir)/src/deep-heap-profile.cc', + '<(tcmalloc_dir)/src/deep-heap-profile.h', + '<(tcmalloc_dir)/src/free_list.cc', + '<(tcmalloc_dir)/src/free_list.h', + '<(tcmalloc_dir)/src/getpc.h', + '<(tcmalloc_dir)/src/gperftools/heap-checker.h', + '<(tcmalloc_dir)/src/gperftools/heap-profiler.h', + '<(tcmalloc_dir)/src/gperftools/malloc_extension.h', + '<(tcmalloc_dir)/src/gperftools/malloc_extension_c.h', + '<(tcmalloc_dir)/src/gperftools/malloc_hook.h', + '<(tcmalloc_dir)/src/gperftools/malloc_hook_c.h', + '<(tcmalloc_dir)/src/gperftools/profiler.h', + '<(tcmalloc_dir)/src/gperftools/stacktrace.h', + '<(tcmalloc_dir)/src/gperftools/tcmalloc.h', + '<(tcmalloc_dir)/src/heap-checker-bcad.cc', + '<(tcmalloc_dir)/src/heap-checker.cc', + '<(tcmalloc_dir)/src/heap-profile-table.cc', + '<(tcmalloc_dir)/src/heap-profile-table.h', + '<(tcmalloc_dir)/src/heap-profiler.cc', + '<(tcmalloc_dir)/src/internal_logging.cc', + '<(tcmalloc_dir)/src/internal_logging.h', + '<(tcmalloc_dir)/src/libc_override.h', + '<(tcmalloc_dir)/src/libc_override_gcc_and_weak.h', + '<(tcmalloc_dir)/src/libc_override_glibc.h', + '<(tcmalloc_dir)/src/libc_override_osx.h', + '<(tcmalloc_dir)/src/libc_override_redefine.h', + '<(tcmalloc_dir)/src/linked_list.h', + '<(tcmalloc_dir)/src/malloc_extension.cc', + '<(tcmalloc_dir)/src/malloc_hook-inl.h', + '<(tcmalloc_dir)/src/malloc_hook.cc', + '<(tcmalloc_dir)/src/malloc_hook_mmap_freebsd.h', + '<(tcmalloc_dir)/src/malloc_hook_mmap_linux.h', '<(tcmalloc_dir)/src/maybe_threads.cc', '<(tcmalloc_dir)/src/maybe_threads.h', + '<(tcmalloc_dir)/src/memfs_malloc.cc', + '<(tcmalloc_dir)/src/memory_region_map.cc', + '<(tcmalloc_dir)/src/memory_region_map.h', + '<(tcmalloc_dir)/src/packed-cache-inl.h', + '<(tcmalloc_dir)/src/page_heap.cc', + '<(tcmalloc_dir)/src/page_heap.h', + '<(tcmalloc_dir)/src/page_heap_allocator.h', + '<(tcmalloc_dir)/src/pagemap.h', + '<(tcmalloc_dir)/src/profile-handler.cc', + '<(tcmalloc_dir)/src/profile-handler.h', + '<(tcmalloc_dir)/src/profiledata.cc', + '<(tcmalloc_dir)/src/profiledata.h', + '<(tcmalloc_dir)/src/profiler.cc', + '<(tcmalloc_dir)/src/raw_printer.cc', + '<(tcmalloc_dir)/src/raw_printer.h', + '<(tcmalloc_dir)/src/sampler.cc', + '<(tcmalloc_dir)/src/sampler.h', + '<(tcmalloc_dir)/src/span.cc', + '<(tcmalloc_dir)/src/span.h', + '<(tcmalloc_dir)/src/stack_trace_table.cc', + '<(tcmalloc_dir)/src/stack_trace_table.h', + '<(tcmalloc_dir)/src/stacktrace.cc', + '<(tcmalloc_dir)/src/stacktrace_arm-inl.h', + '<(tcmalloc_dir)/src/stacktrace_config.h', + '<(tcmalloc_dir)/src/stacktrace_generic-inl.h', + '<(tcmalloc_dir)/src/stacktrace_libunwind-inl.h', + '<(tcmalloc_dir)/src/stacktrace_powerpc-inl.h', + '<(tcmalloc_dir)/src/stacktrace_win32-inl.h', + '<(tcmalloc_dir)/src/stacktrace_with_context.cc', + '<(tcmalloc_dir)/src/stacktrace_x86-inl.h', + '<(tcmalloc_dir)/src/static_vars.cc', + '<(tcmalloc_dir)/src/static_vars.h', + '<(tcmalloc_dir)/src/symbolize.cc', '<(tcmalloc_dir)/src/symbolize.h', '<(tcmalloc_dir)/src/system-alloc.cc', '<(tcmalloc_dir)/src/system-alloc.h', + '<(tcmalloc_dir)/src/tcmalloc.cc', + '<(tcmalloc_dir)/src/tcmalloc_guard.h', + '<(tcmalloc_dir)/src/thread_cache.cc', + '<(tcmalloc_dir)/src/thread_cache.h', - # included by allocator_shim.cc 'debugallocation_shim.cc', ], + # sources! means that these are not compiled directly. + 'sources!': [ + # We simply don't use these, but list them above so that IDE + # users can view the full available source for reference, etc. + '<(tcmalloc_dir)/src/addressmap-inl.h', + '<(tcmalloc_dir)/src/base/atomicops-internals-linuxppc.h', + '<(tcmalloc_dir)/src/base/atomicops-internals-macosx.h', + '<(tcmalloc_dir)/src/base/atomicops-internals-x86-msvc.h', + '<(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', + '<(tcmalloc_dir)/src/base/elf_mem_image.h', + '<(tcmalloc_dir)/src/base/elfcore.h', + '<(tcmalloc_dir)/src/base/googleinit.h', + '<(tcmalloc_dir)/src/base/linux_syscall_support.h', + '<(tcmalloc_dir)/src/base/simple_mutex.h', + '<(tcmalloc_dir)/src/base/spinlock_linux-inl.h', + '<(tcmalloc_dir)/src/base/spinlock_posix-inl.h', + '<(tcmalloc_dir)/src/base/spinlock_win32-inl.h', + '<(tcmalloc_dir)/src/base/stl_allocator.h', + '<(tcmalloc_dir)/src/base/thread_annotations.h', + '<(tcmalloc_dir)/src/getpc.h', + '<(tcmalloc_dir)/src/gperftools/heap-checker.h', + '<(tcmalloc_dir)/src/gperftools/heap-profiler.h', + '<(tcmalloc_dir)/src/gperftools/malloc_extension.h', + '<(tcmalloc_dir)/src/gperftools/malloc_extension_c.h', + '<(tcmalloc_dir)/src/gperftools/malloc_hook.h', + '<(tcmalloc_dir)/src/gperftools/malloc_hook_c.h', + '<(tcmalloc_dir)/src/gperftools/profiler.h', + '<(tcmalloc_dir)/src/gperftools/stacktrace.h', + '<(tcmalloc_dir)/src/gperftools/tcmalloc.h', + '<(tcmalloc_dir)/src/heap-checker-bcad.cc', + '<(tcmalloc_dir)/src/heap-checker.cc', + '<(tcmalloc_dir)/src/libc_override.h', + '<(tcmalloc_dir)/src/libc_override_gcc_and_weak.h', + '<(tcmalloc_dir)/src/libc_override_glibc.h', + '<(tcmalloc_dir)/src/libc_override_osx.h', + '<(tcmalloc_dir)/src/libc_override_redefine.h', + '<(tcmalloc_dir)/src/malloc_hook_mmap_freebsd.h', + '<(tcmalloc_dir)/src/malloc_hook_mmap_linux.h', + '<(tcmalloc_dir)/src/memfs_malloc.cc', + '<(tcmalloc_dir)/src/packed-cache-inl.h', + '<(tcmalloc_dir)/src/page_heap_allocator.h', + '<(tcmalloc_dir)/src/pagemap.h', + '<(tcmalloc_dir)/src/stacktrace_arm-inl.h', + '<(tcmalloc_dir)/src/stacktrace_config.h', + '<(tcmalloc_dir)/src/stacktrace_generic-inl.h', + '<(tcmalloc_dir)/src/stacktrace_libunwind-inl.h', + '<(tcmalloc_dir)/src/stacktrace_powerpc-inl.h', + '<(tcmalloc_dir)/src/stacktrace_win32-inl.h', + '<(tcmalloc_dir)/src/stacktrace_with_context.cc', + '<(tcmalloc_dir)/src/stacktrace_x86-inl.h', + '<(tcmalloc_dir)/src/tcmalloc_guard.h', + + # Included by debugallocation_shim.cc. + '<(tcmalloc_dir)/src/debugallocation.cc', + '<(tcmalloc_dir)/src/tcmalloc.cc', + ] + },{ + 'include_dirs': [ + '.', + '../..', + ], + }], + ['OS=="linux" and clang_type_profiler==1', { + 'dependencies': [ + 'type_profiler_tcmalloc', + ], + # It is undoing dependencies and cflags_cc for type_profiler which + # build/common.gypi injects into all targets. + 'dependencies!': [ + 'type_profiler', + ], + 'cflags_cc!': [ + '-fintercept-allocation-functions', + ], + }], + ['OS=="win"', { + 'dependencies': [ + 'libcmt', + ], + 'sources': [ + 'allocator_shim_win.cc', + ], }], - ['OS=="win" or profiling!=1', { + ['profiling!=1', { 'sources!': [ # cpuprofiler '<(tcmalloc_dir)/src/base/thread_lister.c', '<(tcmalloc_dir)/src/base/thread_lister.h', - '<(tcmalloc_dir)/src/profiledata.cc', - '<(tcmalloc_dir)/src/profiledata.h', '<(tcmalloc_dir)/src/profile-handler.cc', '<(tcmalloc_dir)/src/profile-handler.h', + '<(tcmalloc_dir)/src/profiledata.cc', + '<(tcmalloc_dir)/src/profiledata.h', '<(tcmalloc_dir)/src/profiler.cc', ], }], ['OS=="linux" or OS=="freebsd" or OS=="solaris" or OS=="android"', { 'sources!': [ '<(tcmalloc_dir)/src/system-alloc.h', - '<(tcmalloc_dir)/src/windows/port.cc', - '<(tcmalloc_dir)/src/windows/port.h', - - # TODO(willchan): Support allocator shim later on. - 'allocator_shim.cc', ], # We enable all warnings by default, but upstream disables a few. # Keep "-Wno-*" flags in sync with upstream by comparing against: @@ -498,31 +431,12 @@ ], 'include_dirs': [ '.', - '<(tcmalloc_dir)/src/base', - '<(tcmalloc_dir)/src', '../..', ], 'sources': [ - 'allocator_unittest.cc', '../profiler/alternate_timer.cc', '../profiler/alternate_timer.h', - ], - }, - { - 'target_name': 'tcmalloc_unittest', - 'type': 'executable', - 'sources': [ - 'tcmalloc_unittest.cc', - ], - 'include_dirs': [ - '../..', - # For constants of TCMalloc. - '<(tcmalloc_dir)/src', - ], - 'dependencies': [ - '../../testing/gtest.gyp:gtest', - '../base.gyp:base', - 'allocator', + 'allocator_unittest.cc', ], }, ], @@ -585,10 +499,10 @@ '../..', ], 'sources': [ - 'type_profiler_tcmalloc.cc', - 'type_profiler_tcmalloc.h', '<(tcmalloc_dir)/src/gperftools/type_profiler_map.h', '<(tcmalloc_dir)/src/type_profiler_map.cc', + 'type_profiler_tcmalloc.cc', + 'type_profiler_tcmalloc.h', ], }, { @@ -628,9 +542,28 @@ '../..', ], 'sources': [ - 'type_profiler_map_unittest.cc', '<(tcmalloc_dir)/src/gperftools/type_profiler_map.h', '<(tcmalloc_dir)/src/type_profiler_map.cc', + 'type_profiler_map_unittest.cc', + ], + }, + ], + }], + ['use_allocator=="tcmalloc"', { + 'targets': [ + { + 'target_name': 'tcmalloc_unittest', + 'type': 'executable', + 'sources': [ + 'tcmalloc_unittest.cc', + ], + 'include_dirs': [ + '<(tcmalloc_dir)/src', + '../..', + ], + 'dependencies': [ + '../../testing/gtest.gyp:gtest', + 'allocator', ], }, ], diff --git a/chromium/base/allocator/allocator_extension.h b/chromium/base/allocator/allocator_extension.h index de3119f85e6..e65822b359e 100644 --- a/chromium/base/allocator/allocator_extension.h +++ b/chromium/base/allocator/allocator_extension.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_ALLOCATOR_ALLOCATOR_EXTENSION_H -#define BASE_ALLOCATOR_ALLOCATOR_EXTENSION_H +#ifndef BASE_ALLOCATOR_ALLOCATOR_EXTENSION_H_ +#define BASE_ALLOCATOR_ALLOCATOR_EXTENSION_H_ #include <stddef.h> // for size_t @@ -56,4 +56,4 @@ BASE_EXPORT void SetReleaseFreeMemoryFunction( } // namespace allocator } // namespace base -#endif +#endif // BASE_ALLOCATOR_ALLOCATOR_EXTENSION_H_ diff --git a/chromium/base/allocator/allocator_extension_thunks.h b/chromium/base/allocator/allocator_extension_thunks.h index 1e97a84b63c..4e5027b2921 100644 --- a/chromium/base/allocator/allocator_extension_thunks.h +++ b/chromium/base/allocator/allocator_extension_thunks.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_ALLOCATOR_ALLOCATOR_THUNKS_EXTENSION_H -#define BASE_ALLOCATOR_ALLOCATOR_THUNKS_EXTENSION_H +#ifndef BASE_ALLOCATOR_ALLOCATOR_EXTENSION_THUNKS_H_ +#define BASE_ALLOCATOR_ALLOCATOR_EXTENSION_THUNKS_H_ #include <stddef.h> // for size_t @@ -33,4 +33,4 @@ ReleaseFreeMemoryFunction GetReleaseFreeMemoryFunction(); } // namespace allocator } // namespace base -#endif +#endif // BASE_ALLOCATOR_ALLOCATOR_EXTENSION_THUNKS_H_ diff --git a/chromium/base/allocator/allocator_shim.cc b/chromium/base/allocator/allocator_shim.cc deleted file mode 100644 index 961cda4c5c4..00000000000 --- a/chromium/base/allocator/allocator_shim.cc +++ /dev/null @@ -1,378 +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/allocator/allocator_shim.h" - -#include <config.h> -#include "base/allocator/allocator_extension_thunks.h" -#include "base/profiler/alternate_timer.h" -#include "base/sysinfo.h" - -// This shim make it possible to use different allocators via an environment -// variable set before running the program. This may reduce the -// amount of inlining that we get with malloc/free/etc. - -// TODO(mbelshe): Ensure that all calls to tcmalloc have the proper call depth -// from the "user code" so that debugging tools (HeapChecker) can work. - -// new_mode behaves similarly to MSVC's _set_new_mode. -// If flag is 0 (default), calls to malloc will behave normally. -// If flag is 1, calls to malloc will behave like calls to new, -// and the std_new_handler will be invoked on failure. -// Can be set by calling _set_new_mode(). -static int new_mode = 0; - -typedef enum { - TCMALLOC, // TCMalloc is the default allocator. - WINHEAP, // Windows Heap (standard Windows allocator). - WINLFH, // Windows LFH Heap. -} Allocator; - -// This is the default allocator. This value can be changed at startup by -// specifying environment variables shown below it. -// See SetupSubprocessAllocator() to specify a default secondary (subprocess) -// allocator. -// TODO(jar): Switch to using TCMALLOC for the renderer as well. -#if defined(SYZYASAN) -// SyzyASan requires the use of "WINHEAP". -static Allocator allocator = WINHEAP; -#else -static Allocator allocator = TCMALLOC; -#endif -// The names of the environment variables that can optionally control the -// selection of the allocator. The primary may be used to control overall -// allocator selection, and the secondary can be used to specify an allocator -// to use in sub-processes. -static const char primary_name[] = "CHROME_ALLOCATOR"; -static const char secondary_name[] = "CHROME_ALLOCATOR_2"; - -// We include tcmalloc and the win_allocator to get as much inlining as -// possible. -#include "debugallocation_shim.cc" -#include "win_allocator.cc" - -// Call the new handler, if one has been set. -// Returns true on successfully calling the handler, false otherwise. -inline bool call_new_handler(bool nothrow) { - // Get the current new handler. NB: this function is not - // thread-safe. We make a feeble stab at making it so here, but - // this lock only protects against tcmalloc interfering with - // itself, not with other libraries calling set_new_handler. - std::new_handler nh; - { - SpinLockHolder h(&set_new_handler_lock); - nh = std::set_new_handler(0); - (void) std::set_new_handler(nh); - } -#if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \ - (defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS) - if (!nh) - return false; - // Since exceptions are disabled, we don't really know if new_handler - // failed. Assume it will abort if it fails. - (*nh)(); - return false; // break out of the retry loop. -#else - // If no new_handler is established, the allocation failed. - if (!nh) { - if (nothrow) - return false; - throw std::bad_alloc(); - } - // Otherwise, try the new_handler. If it returns, retry the - // allocation. If it throws std::bad_alloc, fail the allocation. - // if it throws something else, don't interfere. - try { - (*nh)(); - } catch (const std::bad_alloc&) { - if (!nothrow) - throw; - return true; - } -#endif // (defined(__GNUC__) && !defined(__EXCEPTIONS)) || (defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS) - return false; -} - -extern "C" { -void* malloc(size_t size) { - void* ptr; - for (;;) { - switch (allocator) { - case WINHEAP: - case WINLFH: - ptr = win_heap_malloc(size); - break; - case TCMALLOC: - default: - ptr = do_malloc(size); - break; - } - if (ptr) - return ptr; - - if (!new_mode || !call_new_handler(true)) - break; - } - return ptr; -} - -void free(void* p) { - switch (allocator) { - case WINHEAP: - case WINLFH: - win_heap_free(p); - return; - case TCMALLOC: - do_free(p); - return; - } -} - -void* realloc(void* ptr, size_t size) { - // Webkit is brittle for allocators that return NULL for malloc(0). The - // realloc(0, 0) code path does not guarantee a non-NULL return, so be sure - // to call malloc for this case. - if (!ptr) - return malloc(size); - - void* new_ptr; - for (;;) { - switch (allocator) { - case WINHEAP: - case WINLFH: - new_ptr = win_heap_realloc(ptr, size); - break; - case TCMALLOC: - default: - new_ptr = do_realloc(ptr, size); - break; - } - - // Subtle warning: NULL return does not alwas indicate out-of-memory. If - // the requested new size is zero, realloc should free the ptr and return - // NULL. - if (new_ptr || !size) - return new_ptr; - if (!new_mode || !call_new_handler(true)) - break; - } - return new_ptr; -} - -// TODO(mbelshe): Implement this for other allocators. -void malloc_stats(void) { - switch (allocator) { - case WINHEAP: - case WINLFH: - // No stats. - return; - case TCMALLOC: - tc_malloc_stats(); - return; - } -} - -#ifdef WIN32 - -extern "C" size_t _msize(void* p) { - switch (allocator) { - case WINHEAP: - case WINLFH: - return win_heap_msize(p); - } - - // TCMALLOC - return MallocExtension::instance()->GetAllocatedSize(p); -} - -// This is included to resolve references from libcmt. -extern "C" intptr_t _get_heap_handle() { - return 0; -} - -static bool get_allocator_waste_size_thunk(size_t* size) { - switch (allocator) { - case WINHEAP: - case WINLFH: - // TODO(alexeif): Implement for allocators other than tcmalloc. - return false; - } - size_t heap_size, allocated_bytes, unmapped_bytes; - MallocExtension* ext = MallocExtension::instance(); - if (ext->GetNumericProperty("generic.heap_size", &heap_size) && - ext->GetNumericProperty("generic.current_allocated_bytes", - &allocated_bytes) && - ext->GetNumericProperty("tcmalloc.pageheap_unmapped_bytes", - &unmapped_bytes)) { - *size = heap_size - allocated_bytes - unmapped_bytes; - return true; - } - return false; -} - -static void get_stats_thunk(char* buffer, int buffer_length) { - MallocExtension::instance()->GetStats(buffer, buffer_length); -} - -static void release_free_memory_thunk() { - MallocExtension::instance()->ReleaseFreeMemory(); -} - -// The CRT heap initialization stub. -extern "C" int _heap_init() { -// Don't use the environment variable if SYZYASAN is defined, as the -// implementation requires Winheap to be the allocator. -#if !defined(SYZYASAN) - const char* environment_value = GetenvBeforeMain(primary_name); - if (environment_value) { - if (!stricmp(environment_value, "winheap")) - allocator = WINHEAP; - else if (!stricmp(environment_value, "winlfh")) - allocator = WINLFH; - else if (!stricmp(environment_value, "tcmalloc")) - allocator = TCMALLOC; - } -#endif - - switch (allocator) { - case WINHEAP: - return win_heap_init(false) ? 1 : 0; - case WINLFH: - return win_heap_init(true) ? 1 : 0; - case TCMALLOC: - default: - // fall through - break; - } - - // Initializing tcmalloc. - // We intentionally leak this object. It lasts for the process - // lifetime. Trying to teardown at _heap_term() is so late that - // you can't do anything useful anyway. - new TCMallocGuard(); - - // Provide optional hook for monitoring allocation quantities on a per-thread - // basis. Only set the hook if the environment indicates this needs to be - // enabled. - const char* profiling = - GetenvBeforeMain(tracked_objects::kAlternateProfilerTime); - if (profiling && *profiling == '1') { - tracked_objects::SetAlternateTimeSource( - tcmalloc::ThreadCache::GetBytesAllocatedOnCurrentThread, - tracked_objects::TIME_SOURCE_TYPE_TCMALLOC); - } - - base::allocator::thunks::SetGetAllocatorWasteSizeFunction( - get_allocator_waste_size_thunk); - base::allocator::thunks::SetGetStatsFunction(get_stats_thunk); - base::allocator::thunks::SetReleaseFreeMemoryFunction( - release_free_memory_thunk); - - return 1; -} - -// The CRT heap cleanup stub. -extern "C" void _heap_term() {} - -// We set this to 1 because part of the CRT uses a check of _crtheap != 0 -// to test whether the CRT has been initialized. Once we've ripped out -// the allocators from libcmt, we need to provide this definition so that -// the rest of the CRT is still usable. -extern "C" void* _crtheap = reinterpret_cast<void*>(1); - -// Provide support for aligned memory through Windows only _aligned_malloc(). -void* _aligned_malloc(size_t size, size_t alignment) { - // _aligned_malloc guarantees parameter validation, so do so here. These - // checks are somewhat stricter than _aligned_malloc() since we're effectively - // using memalign() under the hood. - DCHECK_GT(size, 0U); - DCHECK_EQ(alignment & (alignment - 1), 0U); - DCHECK_EQ(alignment % sizeof(void*), 0U); - - void* ptr; - for (;;) { - switch (allocator) { - case WINHEAP: - case WINLFH: - ptr = win_heap_memalign(alignment, size); - break; - case TCMALLOC: - default: - ptr = tc_memalign(alignment, size); - break; - } - - if (ptr) { - // Sanity check alignment. - DCHECK_EQ(reinterpret_cast<uintptr_t>(ptr) & (alignment - 1), 0U); - return ptr; - } - - if (!new_mode || !call_new_handler(true)) - break; - } - return ptr; -} - -void _aligned_free(void* p) { - // TCMalloc returns pointers from memalign() that are safe to use with free(). - // Pointers allocated with win_heap_memalign() MUST be freed via - // win_heap_memalign_free() since the aligned pointer is not the real one. - switch (allocator) { - case WINHEAP: - case WINLFH: - win_heap_memalign_free(p); - return; - case TCMALLOC: - do_free(p); - } -} - -#endif // WIN32 - -#include "generic_allocators.cc" - -} // extern C - -namespace base { -namespace allocator { - -void SetupSubprocessAllocator() { - size_t primary_length = 0; - getenv_s(&primary_length, NULL, 0, primary_name); - - size_t secondary_length = 0; - char buffer[20]; - getenv_s(&secondary_length, buffer, sizeof(buffer), secondary_name); - DCHECK_GT(sizeof(buffer), secondary_length); - buffer[sizeof(buffer) - 1] = '\0'; - - if (secondary_length || !primary_length) { -// Don't use the environment variable if SYZYASAN is defined, as the -// implementation require Winheap to be the allocator. -#if !defined(SYZYASAN) - const char* secondary_value = secondary_length ? buffer : "TCMALLOC"; - // Force renderer (or other subprocesses) to use secondary_value. -#else - const char* secondary_value = "WINHEAP"; -#endif - int ret_val = _putenv_s(primary_name, secondary_value); - DCHECK_EQ(0, ret_val); - } -} - -void* TCMallocDoMallocForTest(size_t size) { - return do_malloc(size); -} - -void TCMallocDoFreeForTest(void* ptr) { - do_free(ptr); -} - -size_t ExcludeSpaceForMarkForTest(size_t size) { - return ExcludeSpaceForMark(size); -} - -} // namespace allocator. -} // namespace base. diff --git a/chromium/base/allocator/allocator_shim.h b/chromium/base/allocator/allocator_shim.h deleted file mode 100644 index ca70ab0e10f..00000000000 --- a/chromium/base/allocator/allocator_shim.h +++ /dev/null @@ -1,27 +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_ALLOCATOR_ALLOCATOR_SHIM_H_ -#define BASE_ALLOCATOR_ALLOCATOR_SHIM_H_ - -#include <stddef.h> - -namespace base { -namespace allocator { - -// Resets the environment variable CHROME_ALLOCATOR to specify the choice to -// be used by subprocesses. Priority is given to the current value of -// CHROME_ALLOCATOR_2 (if specified), then CHROME_ALLOCATOR (if specified), and -// then a default value (typically set to TCMALLOC). -void SetupSubprocessAllocator(); - -// Expose some of tcmalloc functions for test. -void* TCMallocDoMallocForTest(size_t size); -void TCMallocDoFreeForTest(void* ptr); -size_t ExcludeSpaceForMarkForTest(size_t size); - -} // namespace allocator. -} // namespace base. - -#endif // BASE_ALLOCATOR_ALLOCATOR_SHIM_H_ diff --git a/chromium/base/allocator/allocator_shim_win.cc b/chromium/base/allocator/allocator_shim_win.cc new file mode 100644 index 00000000000..a1473e5fd29 --- /dev/null +++ b/chromium/base/allocator/allocator_shim_win.cc @@ -0,0 +1,319 @@ +// 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 <malloc.h> +#include <new.h> +#include <windows.h> + +#include "base/basictypes.h" + +// This shim make it possible to perform additional checks on allocations +// before passing them to the Heap functions. + +// Heap functions are stripped from libcmt.lib using the prep_libc.py +// for each object file stripped, we re-implement them here to allow us to +// perform additional checks: +// 1. Enforcing the maximum size that can be allocated to 2Gb. +// 2. Calling new_handler if malloc fails. + +extern "C" { +// We set this to 1 because part of the CRT uses a check of _crtheap != 0 +// to test whether the CRT has been initialized. Once we've ripped out +// the allocators from libcmt, we need to provide this definition so that +// the rest of the CRT is still usable. +// heapinit.c +void* _crtheap = reinterpret_cast<void*>(1); +} + +namespace { + +const size_t kWindowsPageSize = 4096; +const size_t kMaxWindowsAllocation = INT_MAX - kWindowsPageSize; +int new_mode = 0; + +// VS2013 crt uses the process heap as its heap, so we do the same here. +// See heapinit.c in VS CRT sources. +bool win_heap_init() { + // Set the _crtheap global here. THis allows us to offload most of the + // memory management to the CRT, except the functions we need to shim. + _crtheap = GetProcessHeap(); + if (_crtheap == NULL) + return false; + + ULONG enable_lfh = 2; + // NOTE: Setting LFH may fail. Vista already has it enabled. + // And under the debugger, it won't use LFH. So we + // ignore any errors. + HeapSetInformation(_crtheap, HeapCompatibilityInformation, &enable_lfh, + sizeof(enable_lfh)); + + return true; +} + +void* win_heap_malloc(size_t size) { + if (size < kMaxWindowsAllocation) + return HeapAlloc(_crtheap, 0, size); + return NULL; +} + +void win_heap_free(void* size) { + HeapFree(_crtheap, 0, size); +} + +void* win_heap_realloc(void* ptr, size_t size) { + if (!ptr) + return win_heap_malloc(size); + if (!size) { + win_heap_free(ptr); + return NULL; + } + if (size < kMaxWindowsAllocation) + return HeapReAlloc(_crtheap, 0, ptr, size); + return NULL; +} + +void win_heap_term() { + _crtheap = NULL; +} + +// Call the new handler, if one has been set. +// Returns true on successfully calling the handler, false otherwise. +inline bool call_new_handler(bool nothrow, size_t size) { + // Get the current new handler. + _PNH nh = _query_new_handler(); +#if defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS + if (!nh) + return false; + // Since exceptions are disabled, we don't really know if new_handler + // failed. Assume it will abort if it fails. + return nh(size); +#else +#error "Exceptions in allocator shim are not supported!" +#endif // defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS + return false; +} + +// Implement a C++ style allocation, which always calls the new_handler +// on failure. +inline void* generic_cpp_alloc(size_t size, bool nothrow) { + void* ptr; + for (;;) { + ptr = malloc(size); + if (ptr) + return ptr; + if (!call_new_handler(nothrow, size)) + break; + } + return ptr; +} + +} // namespace + +// new.cpp +void* operator new(size_t size) { + return generic_cpp_alloc(size, false); +} + +// delete.cpp +void operator delete(void* p) throw() { + free(p); +} + +// new2.cpp +void* operator new[](size_t size) { + return generic_cpp_alloc(size, false); +} + +// delete2.cpp +void operator delete[](void* p) throw() { + free(p); +} + +// newopnt.cpp +void* operator new(size_t size, const std::nothrow_t& nt) { + return generic_cpp_alloc(size, true); +} + +// newaopnt.cpp +void* operator new[](size_t size, const std::nothrow_t& nt) { + return generic_cpp_alloc(size, true); +} + +// This function behaves similarly to MSVC's _set_new_mode. +// If flag is 0 (default), calls to malloc will behave normally. +// If flag is 1, calls to malloc will behave like calls to new, +// and the std_new_handler will be invoked on failure. +// Returns the previous mode. +// new_mode.cpp +int _set_new_mode(int flag) throw() { + int old_mode = new_mode; + new_mode = flag; + return old_mode; +} + +// new_mode.cpp +int _query_new_mode() { + return new_mode; +} + +extern "C" { +// malloc.c +void* malloc(size_t size) { + void* ptr; + for (;;) { + ptr = win_heap_malloc(size); + if (ptr) + return ptr; + + if (!new_mode || !call_new_handler(true, size)) + break; + } + return ptr; +} + +// free.c +void free(void* p) { + win_heap_free(p); + return; +} + +// realloc.c +void* realloc(void* ptr, size_t size) { + // Webkit is brittle for allocators that return NULL for malloc(0). The + // realloc(0, 0) code path does not guarantee a non-NULL return, so be sure + // to call malloc for this case. + if (!ptr) + return malloc(size); + + void* new_ptr; + for (;;) { + new_ptr = win_heap_realloc(ptr, size); + + // Subtle warning: NULL return does not alwas indicate out-of-memory. If + // the requested new size is zero, realloc should free the ptr and return + // NULL. + if (new_ptr || !size) + return new_ptr; + if (!new_mode || !call_new_handler(true, size)) + break; + } + return new_ptr; +} + +// heapinit.c +intptr_t _get_heap_handle() { + return reinterpret_cast<intptr_t>(_crtheap); +} + +// heapinit.c +int _heap_init() { + return win_heap_init() ? 1 : 0; +} + +// heapinit.c +void _heap_term() { + win_heap_term(); +} + +// calloc.c +void* calloc(size_t n, size_t elem_size) { + // Overflow check. + const size_t size = n * elem_size; + if (elem_size != 0 && size / elem_size != n) + return NULL; + + void* result = malloc(size); + if (result != NULL) { + memset(result, 0, size); + } + return result; +} + +// recalloc.c +void* _recalloc(void* p, size_t n, size_t elem_size) { + if (!p) + return calloc(n, elem_size); + + // This API is a bit odd. + // Note: recalloc only guarantees zeroed memory when p is NULL. + // Generally, calls to malloc() have padding. So a request + // to malloc N bytes actually malloc's N+x bytes. Later, if + // that buffer is passed to recalloc, we don't know what N + // was anymore. We only know what N+x is. As such, there is + // no way to know what to zero out. + const size_t size = n * elem_size; + if (elem_size != 0 && size / elem_size != n) + return NULL; + return realloc(p, size); +} + +// calloc_impl.c +void* _calloc_impl(size_t n, size_t size) { + return calloc(n, size); +} + +#ifndef NDEBUG +#undef malloc +#undef free +#undef calloc + +static int error_handler(int reportType) { + switch (reportType) { + case 0: // _CRT_WARN + __debugbreak(); + return 0; + + case 1: // _CRT_ERROR + __debugbreak(); + return 0; + + case 2: // _CRT_ASSERT + __debugbreak(); + return 0; + } + char* p = NULL; + *p = '\0'; + return 0; +} + +int _CrtDbgReport(int reportType, + const char*, + int, + const char*, + const char*, + ...) { + return error_handler(reportType); +} + +int _CrtDbgReportW(int reportType, + const wchar_t*, + int, + const wchar_t*, + const wchar_t*, + ...) { + return error_handler(reportType); +} + +int _CrtSetReportMode(int, int) { + return 0; +} + +void* _malloc_dbg(size_t size, int, const char*, int) { + return malloc(size); +} + +void* _realloc_dbg(void* ptr, size_t size, int, const char*, int) { + return realloc(ptr, size); +} + +void _free_dbg(void* ptr, int) { + free(ptr); +} + +void* _calloc_dbg(size_t n, size_t size, int, const char*, int) { + return calloc(n, size); +} +#endif // NDEBUG + +} // extern C diff --git a/chromium/base/allocator/allocator_unittest.cc b/chromium/base/allocator/allocator_unittest.cc index a39b8384452..a1d1ef0c639 100644 --- a/chromium/base/allocator/allocator_unittest.cc +++ b/chromium/base/allocator/allocator_unittest.cc @@ -6,7 +6,7 @@ #include <stdlib.h> #include <algorithm> // for min() -#include "base/atomicops.h" +#include "base/macros.h" #include "testing/gtest/include/gtest/gtest.h" // Number of bits in a size_t. @@ -15,10 +15,6 @@ static const int kSizeBits = 8 * sizeof(size_t); static const size_t kMaxSize = ~static_cast<size_t>(0); // Maximum positive size of a size_t if it were signed. static const size_t kMaxSignedSize = ((size_t(1) << (kSizeBits-1)) - 1); -// An allocation size which is not too big to be reasonable. -static const size_t kNotTooBig = 100000; -// An allocation size which is just too big. -static const size_t kTooBig = ~static_cast<size_t>(0); namespace { @@ -82,197 +78,6 @@ static int NextSize(int size) { } } -template <class AtomicType> -static void TestAtomicIncrement() { - // For now, we just test single threaded execution - - // use a guard value to make sure the NoBarrier_AtomicIncrement doesn't go - // outside the expected address bounds. This is in particular to - // test that some future change to the asm code doesn't cause the - // 32-bit NoBarrier_AtomicIncrement to do the wrong thing on 64-bit machines. - struct { - AtomicType prev_word; - AtomicType count; - AtomicType next_word; - } s; - - AtomicType prev_word_value, next_word_value; - memset(&prev_word_value, 0xFF, sizeof(AtomicType)); - memset(&next_word_value, 0xEE, sizeof(AtomicType)); - - s.prev_word = prev_word_value; - s.count = 0; - s.next_word = next_word_value; - - EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, 1), 1); - EXPECT_EQ(s.count, 1); - EXPECT_EQ(s.prev_word, prev_word_value); - EXPECT_EQ(s.next_word, next_word_value); - - EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, 2), 3); - EXPECT_EQ(s.count, 3); - EXPECT_EQ(s.prev_word, prev_word_value); - EXPECT_EQ(s.next_word, next_word_value); - - EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, 3), 6); - EXPECT_EQ(s.count, 6); - EXPECT_EQ(s.prev_word, prev_word_value); - EXPECT_EQ(s.next_word, next_word_value); - - EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, -3), 3); - EXPECT_EQ(s.count, 3); - EXPECT_EQ(s.prev_word, prev_word_value); - EXPECT_EQ(s.next_word, next_word_value); - - EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, -2), 1); - EXPECT_EQ(s.count, 1); - EXPECT_EQ(s.prev_word, prev_word_value); - EXPECT_EQ(s.next_word, next_word_value); - - EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, -1), 0); - EXPECT_EQ(s.count, 0); - EXPECT_EQ(s.prev_word, prev_word_value); - EXPECT_EQ(s.next_word, next_word_value); - - EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, -1), -1); - EXPECT_EQ(s.count, -1); - EXPECT_EQ(s.prev_word, prev_word_value); - EXPECT_EQ(s.next_word, next_word_value); - - EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, -4), -5); - EXPECT_EQ(s.count, -5); - EXPECT_EQ(s.prev_word, prev_word_value); - EXPECT_EQ(s.next_word, next_word_value); - - EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, 5), 0); - EXPECT_EQ(s.count, 0); - EXPECT_EQ(s.prev_word, prev_word_value); - EXPECT_EQ(s.next_word, next_word_value); -} - - -#define NUM_BITS(T) (sizeof(T) * 8) - - -template <class AtomicType> -static void TestCompareAndSwap() { - AtomicType value = 0; - AtomicType prev = base::subtle::NoBarrier_CompareAndSwap(&value, 0, 1); - EXPECT_EQ(1, value); - EXPECT_EQ(0, prev); - - // Use test value that has non-zero bits in both halves, more for testing - // 64-bit implementation on 32-bit platforms. - const AtomicType k_test_val = (static_cast<uint64_t>(1) << - (NUM_BITS(AtomicType) - 2)) + 11; - value = k_test_val; - prev = base::subtle::NoBarrier_CompareAndSwap(&value, 0, 5); - EXPECT_EQ(k_test_val, value); - EXPECT_EQ(k_test_val, prev); - - value = k_test_val; - prev = base::subtle::NoBarrier_CompareAndSwap(&value, k_test_val, 5); - EXPECT_EQ(5, value); - EXPECT_EQ(k_test_val, prev); -} - - -template <class AtomicType> -static void TestAtomicExchange() { - AtomicType value = 0; - AtomicType new_value = base::subtle::NoBarrier_AtomicExchange(&value, 1); - EXPECT_EQ(1, value); - EXPECT_EQ(0, new_value); - - // Use test value that has non-zero bits in both halves, more for testing - // 64-bit implementation on 32-bit platforms. - const AtomicType k_test_val = (static_cast<uint64_t>(1) << - (NUM_BITS(AtomicType) - 2)) + 11; - value = k_test_val; - new_value = base::subtle::NoBarrier_AtomicExchange(&value, k_test_val); - EXPECT_EQ(k_test_val, value); - EXPECT_EQ(k_test_val, new_value); - - value = k_test_val; - new_value = base::subtle::NoBarrier_AtomicExchange(&value, 5); - EXPECT_EQ(5, value); - EXPECT_EQ(k_test_val, new_value); -} - - -template <class AtomicType> -static void TestAtomicIncrementBounds() { - // Test increment at the half-width boundary of the atomic type. - // It is primarily for testing at the 32-bit boundary for 64-bit atomic type. - AtomicType test_val = static_cast<uint64_t>(1) << (NUM_BITS(AtomicType) / 2); - AtomicType value = test_val - 1; - AtomicType new_value = base::subtle::NoBarrier_AtomicIncrement(&value, 1); - EXPECT_EQ(test_val, value); - EXPECT_EQ(value, new_value); - - base::subtle::NoBarrier_AtomicIncrement(&value, -1); - EXPECT_EQ(test_val - 1, value); -} - -// This is a simple sanity check that values are correct. Not testing -// atomicity -template <class AtomicType> -static void TestStore() { - const AtomicType kVal1 = static_cast<AtomicType>(0xa5a5a5a5a5a5a5a5LL); - const AtomicType kVal2 = static_cast<AtomicType>(-1); - - AtomicType value; - - base::subtle::NoBarrier_Store(&value, kVal1); - EXPECT_EQ(kVal1, value); - base::subtle::NoBarrier_Store(&value, kVal2); - EXPECT_EQ(kVal2, value); - - base::subtle::Acquire_Store(&value, kVal1); - EXPECT_EQ(kVal1, value); - base::subtle::Acquire_Store(&value, kVal2); - EXPECT_EQ(kVal2, value); - - base::subtle::Release_Store(&value, kVal1); - EXPECT_EQ(kVal1, value); - base::subtle::Release_Store(&value, kVal2); - EXPECT_EQ(kVal2, value); -} - -// This is a simple sanity check that values are correct. Not testing -// atomicity -template <class AtomicType> -static void TestLoad() { - const AtomicType kVal1 = static_cast<AtomicType>(0xa5a5a5a5a5a5a5a5LL); - const AtomicType kVal2 = static_cast<AtomicType>(-1); - - AtomicType value; - - value = kVal1; - EXPECT_EQ(kVal1, base::subtle::NoBarrier_Load(&value)); - value = kVal2; - EXPECT_EQ(kVal2, base::subtle::NoBarrier_Load(&value)); - - value = kVal1; - EXPECT_EQ(kVal1, base::subtle::Acquire_Load(&value)); - value = kVal2; - EXPECT_EQ(kVal2, base::subtle::Acquire_Load(&value)); - - value = kVal1; - EXPECT_EQ(kVal1, base::subtle::Release_Load(&value)); - value = kVal2; - EXPECT_EQ(kVal2, base::subtle::Release_Load(&value)); -} - -template <class AtomicType> -static void TestAtomicOps() { - TestCompareAndSwap<AtomicType>(); - TestAtomicExchange<AtomicType>(); - TestAtomicIncrementBounds<AtomicType>(); - TestStore<AtomicType>(); - TestLoad<AtomicType>(); -} - static void TestCalloc(size_t n, size_t s, bool ok) { char* p = reinterpret_cast<char*>(calloc(n, s)); if (!ok) { @@ -280,87 +85,17 @@ static void TestCalloc(size_t n, size_t s, bool ok) { } else { EXPECT_NE(reinterpret_cast<void*>(NULL), p) << "calloc(n, s) should succeed"; - for (int i = 0; i < n*s; i++) { + for (size_t i = 0; i < n*s; i++) { EXPECT_EQ('\0', p[i]); } free(p); } } -// MSVC C4530 complains about exception handler usage when exceptions are -// disabled. Temporarily disable that warning so we can test that they are, in -// fact, disabled. -#if defined(OS_WIN) -#pragma warning(push) -#pragma warning(disable: 4530) -#endif - -// A global test counter for number of times the NewHandler is called. -static int news_handled = 0; -static void TestNewHandler() { - ++news_handled; - throw std::bad_alloc(); -} - -// Because we compile without exceptions, we expect these will not throw. -static void TestOneNewWithoutExceptions(void* (*func)(size_t), - bool should_throw) { - // success test - try { - void* ptr = (*func)(kNotTooBig); - EXPECT_NE(reinterpret_cast<void*>(NULL), ptr) << - "allocation should not have failed."; - } catch(...) { - EXPECT_EQ(0, 1) << "allocation threw unexpected exception."; - } - - // failure test - try { - void* rv = (*func)(kTooBig); - EXPECT_EQ(NULL, rv); - EXPECT_FALSE(should_throw) << "allocation should have thrown."; - } catch(...) { - EXPECT_TRUE(should_throw) << "allocation threw unexpected exception."; - } -} - -static void TestNothrowNew(void* (*func)(size_t)) { - news_handled = 0; - - // test without new_handler: - std::new_handler saved_handler = std::set_new_handler(0); - TestOneNewWithoutExceptions(func, false); - - // test with new_handler: - std::set_new_handler(TestNewHandler); - TestOneNewWithoutExceptions(func, true); - EXPECT_EQ(news_handled, 1) << "nothrow new_handler was not called."; - std::set_new_handler(saved_handler); -} - -#if defined(OS_WIN) -#pragma warning(pop) -#endif - } // namespace //----------------------------------------------------------------------------- -TEST(Atomics, AtomicIncrementWord) { - TestAtomicIncrement<AtomicWord>(); -} - -TEST(Atomics, AtomicIncrement32) { - TestAtomicIncrement<Atomic32>(); -} - -TEST(Atomics, AtomicOpsWord) { - TestAtomicIncrement<AtomicWord>(); -} - -TEST(Atomics, AtomicOps32) { - TestAtomicIncrement<Atomic32>(); -} TEST(Allocators, Malloc) { // Try allocating data with a bunch of alignments and sizes @@ -394,11 +129,6 @@ TEST(Allocators, Calloc) { TestCalloc(kMaxSignedSize, kMaxSignedSize, false); } -TEST(Allocators, New) { - TestNothrowNew(&::operator new); - TestNothrowNew(&::operator new[]); -} - // This makes sure that reallocing a small number of bytes in either // direction doesn't cause us to allocate new memory. TEST(Allocators, Realloc1) { @@ -458,19 +188,6 @@ TEST(Allocators, Realloc2) { free(p); } -TEST(Allocators, ReallocZero) { - // Test that realloc to zero does not return NULL. - for (int size = 0; size >= 0; size = NextSize(size)) { - char* ptr = reinterpret_cast<char*>(malloc(size)); - EXPECT_NE(static_cast<char*>(NULL), ptr); - ptr = reinterpret_cast<char*>(realloc(ptr, 0)); - EXPECT_NE(static_cast<char*>(NULL), ptr); - if (ptr) - free(ptr); - } -} - -#ifdef WIN32 // Test recalloc TEST(Allocators, Recalloc) { for (int src_size = 0; src_size >= 0; src_size = NextSize(src_size)) { @@ -495,7 +212,7 @@ TEST(Allocators, AlignedMalloc) { // Try allocating data with a bunch of alignments and sizes static const int kTestAlignments[] = {8, 16, 256, 4096, 8192, 16384}; for (int size = 1; size > 0; size = NextSize(size)) { - for (int i = 0; i < ARRAYSIZE(kTestAlignments); ++i) { + for (int i = 0; i < arraysize(kTestAlignments); ++i) { unsigned char* ptr = static_cast<unsigned char*>( _aligned_malloc(size, kTestAlignments[i])); CheckAlignment(ptr, kTestAlignments[i]); @@ -522,9 +239,6 @@ TEST(Allocators, AlignedMalloc) { } } -#endif - - int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); diff --git a/chromium/base/allocator/generic_allocators.cc b/chromium/base/allocator/generic_allocators.cc deleted file mode 100644 index d12f3b976ac..00000000000 --- a/chromium/base/allocator/generic_allocators.cc +++ /dev/null @@ -1,176 +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. - -// When possible, we implement allocator functions on top of the basic -// low-level functions malloc() and free(). This way, including a new -// allocator is as simple as providing just a small interface. -// -// As such, this file should not contain any allocator-specific code. - -// Implement a C++ style allocation, which always calls the new_handler -// on failure. -inline void* generic_cpp_alloc(size_t size, bool nothrow) { - void* ptr; - for (;;) { - ptr = malloc(size); - if (ptr) - return ptr; - if (!call_new_handler(nothrow)) - break; - } - return ptr; -} - -extern "C++" { - -void* operator new(size_t size) { - return generic_cpp_alloc(size, false); -} - -void operator delete(void* p) { - free(p); -} - -void* operator new[](size_t size) { - return generic_cpp_alloc(size, false); -} - -void operator delete[](void* p) { - free(p); -} - -void* operator new(size_t size, const std::nothrow_t& nt) { - return generic_cpp_alloc(size, true); -} - -void operator delete(void* p, const std::nothrow_t& nt) { - free(p); -} - -void* operator new[](size_t size, const std::nothrow_t& nt) { - return generic_cpp_alloc(size, true); -} - -void operator delete[](void* p, const std::nothrow_t& nt) { - free(p); -} - -// This function behaves similarly to MSVC's _set_new_mode. -// If flag is 0 (default), calls to malloc will behave normally. -// If flag is 1, calls to malloc will behave like calls to new, -// and the std_new_handler will be invoked on failure. -// Returns the previous mode. -int _set_new_mode(int flag) throw() { - int old_mode = new_mode; - new_mode = flag; - return old_mode; -} - -} // extern "C++" - -extern "C" { - -void* calloc(size_t n, size_t elem_size) { - // Overflow check - const size_t size = n * elem_size; - if (elem_size != 0 && size / elem_size != n) return NULL; - - void* result = malloc(size); - if (result != NULL) { - memset(result, 0, size); - } - return result; -} - -void cfree(void* p) __THROW { - free(p); -} - -#ifdef WIN32 - -void* _recalloc(void* p, size_t n, size_t elem_size) { - if (!p) - return calloc(n, elem_size); - - // This API is a bit odd. - // Note: recalloc only guarantees zeroed memory when p is NULL. - // Generally, calls to malloc() have padding. So a request - // to malloc N bytes actually malloc's N+x bytes. Later, if - // that buffer is passed to recalloc, we don't know what N - // was anymore. We only know what N+x is. As such, there is - // no way to know what to zero out. - const size_t size = n * elem_size; - if (elem_size != 0 && size / elem_size != n) return NULL; - return realloc(p, size); -} - -void* _calloc_impl(size_t n, size_t size) { - return calloc(n, size); -} - -#ifndef NDEBUG -#undef malloc -#undef free -#undef calloc - -static int error_handler(int reportType) { - switch (reportType) { - case 0: // _CRT_WARN - __debugbreak(); - return 0; - - case 1: // _CRT_ERROR - __debugbreak(); - return 0; - - case 2: // _CRT_ASSERT - __debugbreak(); - return 0; - } - char* p = NULL; - *p = '\0'; - return 0; -} - -int _CrtDbgReport(int reportType, - const char*, - int, const char*, - const char*, - ...) { - return error_handler(reportType); -} - -int _CrtDbgReportW(int reportType, - const wchar_t*, - int, const wchar_t*, - const wchar_t*, - ...) { - return error_handler(reportType); -} - -int _CrtSetReportMode(int, int) { - return 0; -} - -void* _malloc_dbg(size_t size, int , const char*, int) { - return malloc(size); -} - -void* _realloc_dbg(void* ptr, size_t size, int, const char*, int) { - return realloc(ptr, size); -} - -void _free_dbg(void* ptr, int) { - free(ptr); -} - -void* _calloc_dbg(size_t n, size_t size, int, const char*, int) { - return calloc(n, size); -} -#endif // NDEBUG - -#endif // WIN32 - -} // extern C - diff --git a/chromium/base/allocator/prep_libc.py b/chromium/base/allocator/prep_libc.py index 471140cb548..079297b4629 100755 --- a/chromium/base/allocator/prep_libc.py +++ b/chromium/base/allocator/prep_libc.py @@ -4,8 +4,8 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # -# This script takes libcmt.lib for VS2005/08/10/12/13 and removes the allocation -# related functions from it. +# This script takes libcmt.lib for VS2013 and removes the allocation related +# functions from it. # # Usage: prep_libc.py <VCLibDir> <OutputDir> <arch> # @@ -19,16 +19,18 @@ import shutil import subprocess import sys -def run(command, filter=None): - """Run |command|, removing any lines that match |filter|. The filter is - to remove the echoing of input filename that 'lib' does.""" +def run(command): + """Run |command|. If any lines that match an error condition then + terminate.""" + error = 'cannot find member object' popen = subprocess.Popen( command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) out, _ = popen.communicate() for line in out.splitlines(): - if filter and line.strip() != filter: - print line - return popen.returncode + print line + if error and line.find(error) != -1: + print 'prep_libc.py: Error stripping object from C runtime.' + sys.exit(1) def main(): bindir = 'SELF_X86' @@ -43,28 +45,23 @@ def main(): shutil.copyfile(os.path.join(vs_install_dir, 'libcmt.lib'), output_lib) shutil.copyfile(os.path.join(vs_install_dir, 'libcmt.pdb'), os.path.join(outdir, 'libcmt.pdb')) + cvspath = 'f:\\binaries\\Intermediate\\vctools\\crt_bld\\' + bindir + \ + '\\crt\\prebuild\\build\\' + objdir + '\\mt_obj\\nativec\\\\'; + cppvspath = 'f:\\binaries\\Intermediate\\vctools\\crt_bld\\' + bindir + \ + '\\crt\\prebuild\\build\\' + objdir + '\\mt_obj\\nativecpp\\\\'; - vspaths = [ - 'build\\intel\\mt_obj\\', - 'f:\\dd\\vctools\\crt_bld\\' + bindir + \ - '\\crt\\src\\build\\' + objdir + '\\mt_obj\\', - 'F:\\dd\\vctools\\crt_bld\\' + bindir + \ - '\\crt\\src\\build\\' + objdir + '\\mt_obj\\nativec\\\\', - 'F:\\dd\\vctools\\crt_bld\\' + bindir + \ - '\\crt\\src\\build\\' + objdir + '\\mt_obj\\nativecpp\\\\', - 'f:\\binaries\\Intermediate\\vctools\\crt_bld\\' + bindir + \ - '\\crt\\prebuild\\build\\INTEL\\mt_obj\\cpp_obj\\\\', - ] - - objfiles = ['malloc', 'free', 'realloc', 'new', 'delete', 'new2', 'delete2', - 'align', 'msize', 'heapinit', 'expand', 'heapchk', 'heapwalk', - 'heapmin', 'sbheap', 'calloc', 'recalloc', 'calloc_impl', - 'new_mode', 'newopnt', 'newaopnt'] - for obj in objfiles: - for vspath in vspaths: - cmd = ('lib /nologo /ignore:4006,4014,4221 /remove:%s%s.obj %s' % - (vspath, obj, output_lib)) - run(cmd, obj + '.obj') + cobjfiles = ['malloc', 'free', 'realloc', 'heapinit', 'calloc', 'recalloc', + 'calloc_impl'] + cppobjfiles = ['new', 'new2', 'delete', 'delete2', 'new_mode', 'newopnt', + 'newaopnt'] + for obj in cobjfiles: + cmd = ('lib /nologo /ignore:4006,4221 /remove:%s%s.obj %s' % + (cvspath, obj, output_lib)) + run(cmd) + for obj in cppobjfiles: + cmd = ('lib /nologo /ignore:4006,4221 /remove:%s%s.obj %s' % + (cppvspath, obj, output_lib)) + run(cmd) if __name__ == "__main__": sys.exit(main()) diff --git a/chromium/base/allocator/tcmalloc_unittest.cc b/chromium/base/allocator/tcmalloc_unittest.cc index 053a9d50d79..0f7082eb026 100644 --- a/chromium/base/allocator/tcmalloc_unittest.cc +++ b/chromium/base/allocator/tcmalloc_unittest.cc @@ -1,17 +1,32 @@ // 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 <stddef.h> #include <stdio.h> -#include "base/allocator/allocator_shim.h" #include "testing/gtest/include/gtest/gtest.h" -// TCMalloc header files +// TCMalloc header files. #include "common.h" // For TCMalloc constants like page size, etc. -using base::allocator::TCMallocDoMallocForTest; -using base::allocator::TCMallocDoFreeForTest; -using base::allocator::ExcludeSpaceForMarkForTest; +// TCMalloc implementation. +#include "debugallocation_shim.cc" + +namespace { + +void* TCMallocDoMallocForTest(size_t size) { + return do_malloc(size); +} + +void TCMallocDoFreeForTest(void* ptr) { + do_free(ptr); +} + +size_t ExcludeSpaceForMarkForTest(size_t size) { + return ExcludeSpaceForMark(size); +} + +} // namespace TEST(TCMallocFreeCheck, BadPointerInFirstPageOfTheLargeObject) { char* p = reinterpret_cast<char*>( diff --git a/chromium/base/allocator/win_allocator.cc b/chromium/base/allocator/win_allocator.cc deleted file mode 100644 index ee451f54610..00000000000 --- a/chromium/base/allocator/win_allocator.cc +++ /dev/null @@ -1,78 +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 is a simple allocator based on the windows heap. - -extern "C" { - -HANDLE win_heap; - -bool win_heap_init(bool use_lfh) { - win_heap = HeapCreate(0, 0, 0); - if (win_heap == NULL) - return false; - - if (use_lfh) { - ULONG enable_lfh = 2; - HeapSetInformation(win_heap, HeapCompatibilityInformation, - &enable_lfh, sizeof(enable_lfh)); - // NOTE: Setting LFH may fail. Vista already has it enabled. - // And under the debugger, it won't use LFH. So we - // ignore any errors. - } - - return true; -} - -void* win_heap_malloc(size_t size) { - return HeapAlloc(win_heap, 0, size); -} - -void win_heap_free(void* size) { - HeapFree(win_heap, 0, size); -} - -void* win_heap_realloc(void* ptr, size_t size) { - if (!ptr) - return win_heap_malloc(size); - if (!size) { - win_heap_free(ptr); - return NULL; - } - return HeapReAlloc(win_heap, 0, ptr, size); -} - -size_t win_heap_msize(void* ptr) { - return HeapSize(win_heap, 0, ptr); -} - -void* win_heap_memalign(size_t alignment, size_t size) { - // Reserve enough space to ensure we can align and set aligned_ptr[-1] to the - // original allocation for use with win_heap_memalign_free() later. - size_t allocation_size = size + (alignment - 1) + sizeof(void*); - - // Check for overflow. Alignment and size are checked in allocator_shim. - DCHECK_LT(size, allocation_size); - DCHECK_LT(alignment, allocation_size); - - // Since we're directly calling the allocator function, before OOM handling, - // we need to NULL check to ensure the allocation succeeded. - void* ptr = win_heap_malloc(allocation_size); - if (!ptr) - return ptr; - - char* aligned_ptr = static_cast<char*>(ptr) + sizeof(void*); - aligned_ptr += - alignment - reinterpret_cast<uintptr_t>(aligned_ptr) & (alignment - 1); - - reinterpret_cast<void**>(aligned_ptr)[-1] = ptr; - return aligned_ptr; -} - -void win_heap_memalign_free(void* ptr) { - if (ptr) - win_heap_free(static_cast<void**>(ptr)[-1]); -} - -} // extern "C" diff --git a/chromium/base/android/animation_frame_time_histogram.cc b/chromium/base/android/animation_frame_time_histogram.cc new file mode 100644 index 00000000000..0d796193829 --- /dev/null +++ b/chromium/base/android/animation_frame_time_histogram.cc @@ -0,0 +1,36 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/android/animation_frame_time_histogram.h" + +#include "base/android/jni_string.h" +#include "base/metrics/histogram_macros.h" +#include "jni/AnimationFrameTimeHistogram_jni.h" + +// static +void SaveHistogram(JNIEnv* env, + jobject jcaller, + jstring j_histogram_name, + jlongArray j_frame_times_ms, + jint j_count) { + jlong *frame_times_ms = env->GetLongArrayElements(j_frame_times_ms, NULL); + std::string histogram_name = base::android::ConvertJavaStringToUTF8( + env, j_histogram_name); + + for (int i = 0; i < j_count; ++i) { + UMA_HISTOGRAM_TIMES(histogram_name.c_str(), + base::TimeDelta::FromMilliseconds(frame_times_ms[i])); + } +} + +namespace base { +namespace android { + +// static +bool RegisterAnimationFrameTimeHistogram(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +} // namespace android +} // namespace base diff --git a/chromium/base/android/animation_frame_time_histogram.h b/chromium/base/android/animation_frame_time_histogram.h new file mode 100644 index 00000000000..63f938b922d --- /dev/null +++ b/chromium/base/android/animation_frame_time_histogram.h @@ -0,0 +1,18 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_ANDROID_ANIMATION_FRAME_TIME_HISTOGRAM_H_ +#define BASE_ANDROID_ANIMATION_FRAME_TIME_HISTOGRAM_H_ + +#include <jni.h> + +namespace base { +namespace android { + +bool RegisterAnimationFrameTimeHistogram(JNIEnv* env); + +} // namespace android +} // namespace base + +#endif // BASE_ANDROID_ANIMATION_FRAME_TIME_HISTOGRAM_H_ diff --git a/chromium/base/android/application_status_listener.cc b/chromium/base/android/application_status_listener.cc index f48707282f5..02178c47bf3 100644 --- a/chromium/base/android/application_status_listener.cc +++ b/chromium/base/android/application_status_listener.cc @@ -60,7 +60,8 @@ bool ApplicationStatusListener::RegisterBindings(JNIEnv* env) { // static void ApplicationStatusListener::NotifyApplicationStateChange( ApplicationState state) { - g_observers.Get().Notify(&ApplicationStatusListener::Notify, state); + g_observers.Get().Notify(FROM_HERE, &ApplicationStatusListener::Notify, + state); } static void OnApplicationStateChange(JNIEnv* env, diff --git a/chromium/base/android/application_status_listener_unittest.cc b/chromium/base/android/application_status_listener_unittest.cc index 1049628f5a3..ce78bf9c862 100644 --- a/chromium/base/android/application_status_listener_unittest.cc +++ b/chromium/base/android/application_status_listener_unittest.cc @@ -7,7 +7,7 @@ #include "base/callback_forward.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" -#include "base/message_loop/message_loop_proxy.h" +#include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/synchronization/waitable_event.h" #include "base/threading/thread.h" @@ -49,9 +49,8 @@ class MultiThreadedTest { void Run() { // Start the thread and tell it to register for events. thread_.Start(); - thread_.message_loop() - ->PostTask(FROM_HERE, - base::Bind(&MultiThreadedTest::RegisterThreadForEvents, + thread_.task_runner()->PostTask( + FROM_HERE, base::Bind(&MultiThreadedTest::RegisterThreadForEvents, base::Unretained(this))); // Wait for its completion. diff --git a/chromium/base/android/build_info.cc b/chromium/base/android/build_info.cc index 11202a0627e..4d3cd559ede 100644 --- a/chromium/base/android/build_info.cc +++ b/chromium/base/android/build_info.cc @@ -68,11 +68,16 @@ BuildInfo* BuildInfo::GetInstance() { return Singleton<BuildInfo, BuildInfoSingletonTraits >::get(); } -void BuildInfo::set_java_exception_info(const std::string& info) { +void BuildInfo::SetJavaExceptionInfo(const std::string& info) { DCHECK(!java_exception_info_) << "info should be set only once."; java_exception_info_ = strndup(info.c_str(), 4096); } +void BuildInfo::ClearJavaExceptionInfo() { + delete java_exception_info_; + java_exception_info_ = nullptr; +} + // static bool BuildInfo::RegisterBindings(JNIEnv* env) { return RegisterNativesImpl(env); diff --git a/chromium/base/android/build_info.h b/chromium/base/android/build_info.h index cef8145fa6a..d6155b9eed9 100644 --- a/chromium/base/android/build_info.h +++ b/chromium/base/android/build_info.h @@ -15,6 +15,17 @@ namespace base { namespace android { +// This enumeration maps to the values returned by BuildInfo::sdk_int(), +// indicating the Android release associated with a given SDK version. +enum SdkVersion { + SDK_VERSION_JELLY_BEAN = 16, + SDK_VERSION_JELLY_BEAN_MR1 = 17, + SDK_VERSION_JELLY_BEAN_MR2 = 18, + SDK_VERSION_KITKAT = 19, + SDK_VERSION_KITKAT_WEAR = 20, + SDK_VERSION_LOLLIPOP = 21 +}; + // BuildInfo is a singleton class that stores android build and device // information. It will be called from Android specific code and gets used // primarily in crash reporting. @@ -88,7 +99,9 @@ class BASE_EXPORT BuildInfo { return java_exception_info_; } - void set_java_exception_info(const std::string& info); + void SetJavaExceptionInfo(const std::string& info); + + void ClearJavaExceptionInfo(); static bool RegisterBindings(JNIEnv* env); diff --git a/chromium/base/android/command_line_android.cc b/chromium/base/android/command_line_android.cc index 895ffab4f95..064450dd725 100644 --- a/chromium/base/android/command_line_android.cc +++ b/chromium/base/android/command_line_android.cc @@ -12,6 +12,7 @@ using base::android::ConvertUTF8ToJavaString; using base::android::ConvertJavaStringToUTF8; +using base::CommandLine; namespace { diff --git a/chromium/base/android/content_uri_utils.cc b/chromium/base/android/content_uri_utils.cc index 0e0c0ea6bb1..0482feef891 100644 --- a/chromium/base/android/content_uri_utils.cc +++ b/chromium/base/android/content_uri_utils.cc @@ -35,4 +35,14 @@ File OpenContentUriForRead(const FilePath& content_uri) { return File(fd); } +std::string GetContentUriMimeType(const FilePath& content_uri) { + JNIEnv* env = base::android::AttachCurrentThread(); + ScopedJavaLocalRef<jstring> j_uri = + ConvertUTF8ToJavaString(env, content_uri.value()); + ScopedJavaLocalRef<jstring> j_mime = + Java_ContentUriUtils_getMimeType( + env, base::android::GetApplicationContext(), j_uri.obj()); + return base::android::ConvertJavaStringToUTF8(env, j_mime.obj()); +} + } // namespace base diff --git a/chromium/base/android/content_uri_utils.h b/chromium/base/android/content_uri_utils.h index 827ec92fa39..e66b77077e1 100644 --- a/chromium/base/android/content_uri_utils.h +++ b/chromium/base/android/content_uri_utils.h @@ -16,13 +16,17 @@ namespace base { bool RegisterContentUriUtils(JNIEnv* env); -// Opens a content uri for read and returns the file descriptor to the caller. -// Returns -1 if the uri is invalid. +// Opens a content URI for read and returns the file descriptor to the caller. +// Returns -1 if the URI is invalid. BASE_EXPORT File OpenContentUriForRead(const FilePath& content_uri); -// Check whether a content uri exists. +// Check whether a content URI exists. BASE_EXPORT bool ContentUriExists(const FilePath& content_uri); +// Gets MIME type from a content URI. Returns an empty string if the URI is +// invalid. +BASE_EXPORT std::string GetContentUriMimeType(const FilePath& content_uri); + } // namespace base #endif // BASE_ANDROID_CONTENT_URI_UTILS_H_ diff --git a/chromium/base/android/content_uri_utils_unittest.cc b/chromium/base/android/content_uri_utils_unittest.cc new file mode 100644 index 00000000000..c762035afb2 --- /dev/null +++ b/chromium/base/android/content_uri_utils_unittest.cc @@ -0,0 +1,37 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/android/content_uri_utils.h" +#include "base/files/file_util.h" +#include "base/path_service.h" +#include "base/test/test_file_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { +namespace android { + +TEST(ContentUriUtilsTest, ContentUriMimeTest) { + // Get the test image path. + FilePath data_dir; + ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &data_dir)); + data_dir = data_dir.AppendASCII("file_util"); + ASSERT_TRUE(PathExists(data_dir)); + FilePath image_file = data_dir.Append(FILE_PATH_LITERAL("red.png")); + + // Insert the image into MediaStore. MediaStore will do some conversions, and + // return the content URI. + FilePath path = base::InsertImageIntoMediaStore(image_file); + EXPECT_TRUE(path.IsContentUri()); + EXPECT_TRUE(PathExists(path)); + + std::string mime = GetContentUriMimeType(path); + EXPECT_EQ(mime, std::string("image/png")); + + FilePath invalid_path("content://foo.bar"); + mime = GetContentUriMimeType(invalid_path); + EXPECT_TRUE(mime.empty()); +} + +} // namespace android +} // namespace base diff --git a/chromium/base/android/important_file_writer_android.h b/chromium/base/android/important_file_writer_android.h index 20956babcea..88e44418a29 100644 --- a/chromium/base/android/important_file_writer_android.h +++ b/chromium/base/android/important_file_writer_android.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef NATIVE_FRAMEWORK_CHROME_IMPORTANT_FILE_WRITE_ANDROID_H_ -#define NATIVE_FRAMEWORK_CHROME_IMPORTANT_FILE_WRITE_ANDROID_H_ +#ifndef BASE_ANDROID_IMPORTANT_FILE_WRITER_ANDROID_H_ +#define BASE_ANDROID_IMPORTANT_FILE_WRITER_ANDROID_H_ #include <jni.h> @@ -15,4 +15,4 @@ bool RegisterImportantFileWriterAndroid(JNIEnv* env); } // namespace android } // namespace base -#endif // NATIVE_FRAMEWORK_CHROME_IMPORTANT_FILE_WRITE_ANDROID_H_ +#endif // BASE_ANDROID_IMPORTANT_FILE_WRITER_ANDROID_H_ diff --git a/chromium/base/android/jni_generator/jni_generator.gyp b/chromium/base/android/jni_generator/jni_generator.gyp index 2ea36b0b389..4a17f3e57c1 100644 --- a/chromium/base/android/jni_generator/jni_generator.gyp +++ b/chromium/base/android/jni_generator/jni_generator.gyp @@ -7,6 +7,9 @@ { 'target_name': 'jni_generator_py_tests', 'type': 'none', + 'variables': { + 'stamp': '<(INTERMEDIATE_DIR)/jni_generator_py_tests.stamp', + }, 'actions': [ { 'action_name': 'run_jni_generator_py_tests', @@ -17,10 +20,11 @@ 'golden_sample_for_tests_jni.h', ], 'outputs': [ - '', + '<(stamp)', ], 'action': [ 'python', 'jni_generator_tests.py', + '--stamp=<(stamp)', ], }, ], diff --git a/chromium/base/android/library_loader/library_load_from_apk_status_codes.h b/chromium/base/android/library_loader/library_load_from_apk_status_codes.h index f99eebc8989..9591d3f6a37 100644 --- a/chromium/base/android/library_loader/library_load_from_apk_status_codes.h +++ b/chromium/base/android/library_loader/library_load_from_apk_status_codes.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_ANDROID_LIBRARY_LOAD_FROM_APK_STATUS_CODES_H_ -#define BASE_ANDROID_LIBRARY_LOAD_FROM_APK_STATUS_CODES_H_ +#ifndef BASE_ANDROID_LIBRARY_LOADER_LIBRARY_LOAD_FROM_APK_STATUS_CODES_H_ +#define BASE_ANDROID_LIBRARY_LOADER_LIBRARY_LOAD_FROM_APK_STATUS_CODES_H_ namespace base { namespace android { @@ -43,4 +43,4 @@ enum LibraryLoadFromApkStatusCodes { } // namespace android } // namespace base -#endif // BASE_ANDROID_LIBRARY_LOAD_FROM_APK_STATUS_CODES_H_ +#endif // BASE_ANDROID_LIBRARY_LOADER_LIBRARY_LOAD_FROM_APK_STATUS_CODES_H_ diff --git a/chromium/base/android/library_loader/library_loader_hooks.cc b/chromium/base/android/library_loader/library_loader_hooks.cc index 809275dc431..0b59a304296 100644 --- a/chromium/base/android/library_loader/library_loader_hooks.cc +++ b/chromium/base/android/library_loader/library_loader_hooks.cc @@ -7,6 +7,7 @@ #include "base/android/command_line_android.h" #include "base/android/jni_string.h" #include "base/android/library_loader/library_load_from_apk_status_codes.h" +#include "base/android/library_loader/library_prefetcher.h" #include "base/at_exit.h" #include "base/metrics/histogram.h" #include "jni/LibraryLoader_jni.h" @@ -51,7 +52,7 @@ RendererHistogramCode g_renderer_histogram_code = NO_PENDING_HISTOGRAM_CODE; static void RegisterChromiumAndroidLinkerRendererHistogram( JNIEnv* env, - jclass clazz, + jobject jcaller, jboolean requested_shared_relro, jboolean load_at_fixed_address_failed) { // Note a pending histogram value for later recording. @@ -75,7 +76,7 @@ void RecordChromiumAndroidLinkerRendererHistogram() { static void RecordChromiumAndroidLinkerBrowserHistogram( JNIEnv* env, - jclass clazz, + jobject jcaller, jboolean is_using_browser_shared_relros, jboolean load_at_fixed_address_failed, jint library_load_from_apk_status) { @@ -102,16 +103,17 @@ void SetLibraryLoadedHook(LibraryLoadedHook* func) { g_registration_callback = func; } -static void InitCommandLine(JNIEnv* env, jclass clazz, +static void InitCommandLine(JNIEnv* env, + jobject jcaller, jobjectArray init_command_line) { InitNativeCommandLineFromJavaArray(env, init_command_line); } -static jboolean LibraryLoaded(JNIEnv* env, jclass clazz) { +static jboolean LibraryLoaded(JNIEnv* env, jobject jcaller) { if (g_registration_callback == NULL) { return true; } - return g_registration_callback(env, clazz); + return g_registration_callback(env, NULL); } void LibraryLoaderExitHook() { @@ -121,10 +123,11 @@ void LibraryLoaderExitHook() { } } -bool RegisterLibraryLoaderEntryHook(JNIEnv* env) { - // We need the AtExitManager to be created at the very beginning. - g_at_exit_manager = new base::AtExitManager(); +static jboolean ForkAndPrefetchNativeLibrary(JNIEnv* env, jclass clazz) { + return NativeLibraryPrefetcher::ForkAndPrefetchNativeLibrary(); +} +bool RegisterLibraryLoaderEntryHook(JNIEnv* env) { return RegisterNativesImpl(env); } @@ -132,12 +135,17 @@ void SetVersionNumber(const char* version_number) { g_library_version_number = strdup(version_number); } -jstring GetVersionNumber(JNIEnv* env, jclass clazz) { +jstring GetVersionNumber(JNIEnv* env, jobject jcaller) { return ConvertUTF8ToJavaString(env, g_library_version_number).Release(); } -static void RecordNativeLibraryHack(JNIEnv*, jclass, jboolean usedHack) { - UMA_HISTOGRAM_BOOLEAN("LibraryLoader.NativeLibraryHack", usedHack); +LibraryProcessType GetLibraryProcessType(JNIEnv* env) { + return static_cast<LibraryProcessType>( + Java_LibraryLoader_getLibraryProcessType(env)); +} + +void InitAtExitManager() { + g_at_exit_manager = new base::AtExitManager(); } } // namespace android diff --git a/chromium/base/android/library_loader/library_loader_hooks.h b/chromium/base/android/library_loader/library_loader_hooks.h index 78dc5359f3e..ca3c5a20ae8 100644 --- a/chromium/base/android/library_loader/library_loader_hooks.h +++ b/chromium/base/android/library_loader/library_loader_hooks.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_ANDROID_LIBRARY_LOADER_HOOKS_H_ -#define BASE_ANDROID_LIBRARY_LOADER_HOOKS_H_ +#ifndef BASE_ANDROID_LIBRARY_LOADER_LIBRARY_LOADER_HOOKS_H_ +#define BASE_ANDROID_LIBRARY_LOADER_LIBRARY_LOADER_HOOKS_H_ #include <jni.h> @@ -12,6 +12,19 @@ namespace base { namespace android { +// The process the shared library is loaded in. +// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.base.library_loader +enum LibraryProcessType { + // The LibraryLoad has not been initialized. + PROCESS_UNINITIALIZED = 0, + // Shared library is running in browser process. + PROCESS_BROWSER = 1, + // Shared library is running in child process. + PROCESS_CHILD = 2, + // Shared library is running in webview process. + PROCESS_WEBVIEW = 3, +}; + // Record any pending renderer histogram value as a histogram. Pending values // are set by RegisterChromiumAndroidLinkerRendererHistogram. BASE_EXPORT void RecordChromiumAndroidLinkerRendererHistogram(); @@ -48,7 +61,14 @@ BASE_EXPORT void SetVersionNumber(const char* version_number); // created. BASE_EXPORT void LibraryLoaderExitHook(); +// Return the process type the shared library is loaded in. +BASE_EXPORT LibraryProcessType GetLibraryProcessType(JNIEnv* env); + +// Initialize AtExitManager, this must be done at the begining of loading +// shared library. +void InitAtExitManager(); + } // namespace android } // namespace base -#endif // BASE_ANDROID_LIBRARY_LOADER_HOOKS_H_ +#endif // BASE_ANDROID_LIBRARY_LOADER_LIBRARY_LOADER_HOOKS_H_ diff --git a/chromium/base/android/library_loader/library_prefetcher.cc b/chromium/base/android/library_loader/library_prefetcher.cc new file mode 100644 index 00000000000..798a283d71b --- /dev/null +++ b/chromium/base/android/library_loader/library_prefetcher.cc @@ -0,0 +1,150 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/android/library_loader/library_prefetcher.h" + +#include <sys/resource.h> +#include <sys/wait.h> +#include <unistd.h> +#include <utility> +#include <vector> + +#include "base/macros.h" +#include "base/posix/eintr_wrapper.h" +#include "base/strings/string_util.h" + +namespace base { +namespace android { + +namespace { + +// Android defines the background priority to this value since at least 2009 +// (see Process.java). +const int kBackgroundPriority = 10; +// Valid for all the Android architectures. +const size_t kPageSize = 4096; +const char* kLibchromeSuffix = "libchrome.so"; +// "base.apk" is a suffix because the library may be loaded directly from the +// APK. +const char* kSuffixesToMatch[] = {kLibchromeSuffix, "base.apk"}; + +bool IsReadableAndPrivate(const base::debug::MappedMemoryRegion& region) { + return region.permissions & base::debug::MappedMemoryRegion::READ && + region.permissions & base::debug::MappedMemoryRegion::PRIVATE; +} + +bool PathMatchesSuffix(const std::string& path) { + for (size_t i = 0; i < arraysize(kSuffixesToMatch); i++) { + if (EndsWith(path, kSuffixesToMatch[i], true)) { + return true; + } + } + return false; +} + +// For each range, reads a byte per page to force it into the page cache. +// Heap allocations, syscalls and library functions are not allowed in this +// function. +// Returns true for success. +bool Prefetch(const std::vector<std::pair<uintptr_t, uintptr_t>>& ranges) { + for (const auto& range : ranges) { + const uintptr_t page_mask = kPageSize - 1; + // If start or end is not page-aligned, parsing went wrong. It is better to + // exit with an error. + if ((range.first & page_mask) || (range.second & page_mask)) { + return false; // CHECK() is not allowed here. + } + unsigned char* start_ptr = reinterpret_cast<unsigned char*>(range.first); + unsigned char* end_ptr = reinterpret_cast<unsigned char*>(range.second); + unsigned char dummy = 0; + for (unsigned char* ptr = start_ptr; ptr < end_ptr; ptr += kPageSize) { + // Volatile is required to prevent the compiler from eliminating this + // loop. + dummy ^= *static_cast<volatile unsigned char*>(ptr); + } + } + return true; +} + +} // namespace + +// static +bool NativeLibraryPrefetcher::IsGoodToPrefetch( + const base::debug::MappedMemoryRegion& region) { + return PathMatchesSuffix(region.path) && + IsReadableAndPrivate(region); // .text and .data mappings are private. +} + +// static +void NativeLibraryPrefetcher::FilterLibchromeRangesOnlyIfPossible( + const std::vector<base::debug::MappedMemoryRegion>& regions, + std::vector<AddressRange>* ranges) { + bool has_libchrome_region = false; + for (const base::debug::MappedMemoryRegion& region : regions) { + if (EndsWith(region.path, kLibchromeSuffix, true)) { + has_libchrome_region = true; + break; + } + } + for (const base::debug::MappedMemoryRegion& region : regions) { + if (has_libchrome_region && + !EndsWith(region.path, kLibchromeSuffix, true)) { + continue; + } + ranges->push_back(std::make_pair(region.start, region.end)); + } +} + +// static +bool NativeLibraryPrefetcher::FindRanges(std::vector<AddressRange>* ranges) { + std::string proc_maps; + if (!base::debug::ReadProcMaps(&proc_maps)) + return false; + std::vector<base::debug::MappedMemoryRegion> regions; + if (!base::debug::ParseProcMaps(proc_maps, ®ions)) + return false; + + std::vector<base::debug::MappedMemoryRegion> regions_to_prefetch; + for (const auto& region : regions) { + if (IsGoodToPrefetch(region)) { + regions_to_prefetch.push_back(region); + } + } + + FilterLibchromeRangesOnlyIfPossible(regions_to_prefetch, ranges); + return true; +} + +// static +bool NativeLibraryPrefetcher::ForkAndPrefetchNativeLibrary() { + // Looking for ranges is done before the fork, to avoid syscalls and/or memory + // allocations in the forked process. The child process inherits the lock + // state of its parent thread. It cannot rely on being able to acquire any + // lock (unless special care is taken in a pre-fork handler), including being + // able to call malloc(). + std::vector<AddressRange> ranges; + if (!FindRanges(&ranges)) + return false; + pid_t pid = fork(); + if (pid == 0) { + setpriority(PRIO_PROCESS, 0, kBackgroundPriority); + // _exit() doesn't call the atexit() handlers. + _exit(Prefetch(ranges) ? 0 : 1); + } else { + if (pid < 0) { + return false; + } + int status; + const pid_t result = HANDLE_EINTR(waitpid(pid, &status, 0)); + if (result == pid) { + if (WIFEXITED(status)) { + return WEXITSTATUS(status) == 0; + } + } + return false; + } +} + +} // namespace android +} // namespace base diff --git a/chromium/base/android/library_loader/library_prefetcher.h b/chromium/base/android/library_loader/library_prefetcher.h new file mode 100644 index 00000000000..64e5e1ecb4b --- /dev/null +++ b/chromium/base/android/library_loader/library_prefetcher.h @@ -0,0 +1,67 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_ANDROID_LIBRARY_LOADER_LIBRARY_PREFETCHER_H_ +#define BASE_ANDROID_LIBRARY_LOADER_LIBRARY_PREFETCHER_H_ + +#include <jni.h> + +#include <stdint.h> +#include <string> + +#include "base/debug/proc_maps_linux.h" +#include "base/gtest_prod_util.h" + +namespace base { +namespace android { + +// Forks and waits for a process prefetching the native library. This is done in +// a forked process for the following reasons: +// - Isolating the main process from mistakes in the parsing. If the parsing +// returns an incorrect address, only the forked process will crash. +// - Not inflating the memory used by the main process uselessly, which could +// increase its likelihood to be killed. +// The forked process has background priority and, since it is not declared to +// the Android runtime, can be killed at any time, which is not an issue here. +class BASE_EXPORT NativeLibraryPrefetcher { + public: + // Finds the ranges matching the native library, forks a low priority + // process pre-fetching these ranges and wait()s for it. + // Returns true for success. + static bool ForkAndPrefetchNativeLibrary(); + + private: + using AddressRange = std::pair<uintptr_t, uintptr_t>; + // Returns true if the region matches native code or data. + static bool IsGoodToPrefetch(const base::debug::MappedMemoryRegion& region); + // Filters the regions to keep only libchrome ranges if possible. + static void FilterLibchromeRangesOnlyIfPossible( + const std::vector<base::debug::MappedMemoryRegion>& regions, + std::vector<AddressRange>* ranges); + // Finds the ranges matching the native library in /proc/self/maps. + // Returns true for success. + static bool FindRanges(std::vector<AddressRange>* ranges); + + FRIEND_TEST_ALL_PREFIXES(NativeLibraryPrefetcherTest, + TestIsGoodToPrefetchNoRange); + FRIEND_TEST_ALL_PREFIXES(NativeLibraryPrefetcherTest, + TestIsGoodToPrefetchUnreadableRange); + FRIEND_TEST_ALL_PREFIXES(NativeLibraryPrefetcherTest, + TestIsGoodToPrefetchSkipSharedRange); + FRIEND_TEST_ALL_PREFIXES(NativeLibraryPrefetcherTest, + TestIsGoodToPrefetchLibchromeRange); + FRIEND_TEST_ALL_PREFIXES(NativeLibraryPrefetcherTest, + TestIsGoodToPrefetchBaseApkRange); + FRIEND_TEST_ALL_PREFIXES(NativeLibraryPrefetcherTest, + TestFilterLibchromeRangesOnlyIfPossibleNoLibchrome); + FRIEND_TEST_ALL_PREFIXES(NativeLibraryPrefetcherTest, + TestFilterLibchromeRangesOnlyIfPossibleHasLibchrome); + + DISALLOW_IMPLICIT_CONSTRUCTORS(NativeLibraryPrefetcher); +}; + +} // namespace android +} // namespace base + +#endif // BASE_ANDROID_LIBRARY_LOADER_LIBRARY_PREFETCHER_H_ diff --git a/chromium/base/android/library_loader/library_prefetcher_unittest.cc b/chromium/base/android/library_loader/library_prefetcher_unittest.cc new file mode 100644 index 00000000000..7b7296f5f61 --- /dev/null +++ b/chromium/base/android/library_loader/library_prefetcher_unittest.cc @@ -0,0 +1,95 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/android/library_loader/library_prefetcher.h" + +#include <string> +#include <vector> +#include "base/debug/proc_maps_linux.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { +namespace android { + +namespace { +const uint8 kRead = base::debug::MappedMemoryRegion::READ; +const uint8 kReadPrivate = base::debug::MappedMemoryRegion::READ | + base::debug::MappedMemoryRegion::PRIVATE; +const uint8 kExecutePrivate = base::debug::MappedMemoryRegion::EXECUTE | + base::debug::MappedMemoryRegion::PRIVATE; +} // namespace + +TEST(NativeLibraryPrefetcherTest, TestIsGoodToPrefetchNoRange) { + const base::debug::MappedMemoryRegion regions[4] = { + base::debug::MappedMemoryRegion{0x4000, 0x5000, 10, kReadPrivate, ""}, + base::debug::MappedMemoryRegion{0x4000, 0x5000, 10, kReadPrivate, "foo"}, + base::debug::MappedMemoryRegion{ + 0x4000, 0x5000, 10, kReadPrivate, "foobar.apk"}, + base::debug::MappedMemoryRegion{ + 0x4000, 0x5000, 10, kReadPrivate, "libchromium.so"}}; + for (int i = 0; i < 4; ++i) { + ASSERT_FALSE(NativeLibraryPrefetcher::IsGoodToPrefetch(regions[i])); + } +} + +TEST(NativeLibraryPrefetcherTest, TestIsGoodToPrefetchUnreadableRange) { + const base::debug::MappedMemoryRegion region = { + 0x4000, 0x5000, 10, kExecutePrivate, "base.apk"}; + ASSERT_FALSE(NativeLibraryPrefetcher::IsGoodToPrefetch(region)); +} + +TEST(NativeLibraryPrefetcherTest, TestIsGoodToPrefetchSkipSharedRange) { + const base::debug::MappedMemoryRegion region = { + 0x4000, 0x5000, 10, kRead, "base.apk"}; + ASSERT_FALSE(NativeLibraryPrefetcher::IsGoodToPrefetch(region)); +} + +TEST(NativeLibraryPrefetcherTest, TestIsGoodToPrefetchLibchromeRange) { + const base::debug::MappedMemoryRegion region = { + 0x4000, 0x5000, 10, kReadPrivate, "libchrome.so"}; + ASSERT_TRUE(NativeLibraryPrefetcher::IsGoodToPrefetch(region)); +} + +TEST(NativeLibraryPrefetcherTest, TestIsGoodToPrefetchBaseApkRange) { + const base::debug::MappedMemoryRegion region = { + 0x4000, 0x5000, 10, kReadPrivate, "base.apk"}; + ASSERT_TRUE(NativeLibraryPrefetcher::IsGoodToPrefetch(region)); +} + +TEST(NativeLibraryPrefetcherTest, + TestFilterLibchromeRangesOnlyIfPossibleNoLibchrome) { + std::vector<base::debug::MappedMemoryRegion> regions; + regions.push_back( + base::debug::MappedMemoryRegion{0x1, 0x2, 0, kReadPrivate, "base.apk"}); + regions.push_back( + base::debug::MappedMemoryRegion{0x3, 0x4, 0, kReadPrivate, "base.apk"}); + std::vector<NativeLibraryPrefetcher::AddressRange> ranges; + NativeLibraryPrefetcher::FilterLibchromeRangesOnlyIfPossible(regions, + &ranges); + EXPECT_EQ(ranges.size(), 2U); + EXPECT_EQ(ranges[0].first, 0x1U); + EXPECT_EQ(ranges[0].second, 0x2U); + EXPECT_EQ(ranges[1].first, 0x3U); + EXPECT_EQ(ranges[1].second, 0x4U); +} + +TEST(NativeLibraryPrefetcherTest, + TestFilterLibchromeRangesOnlyIfPossibleHasLibchrome) { + std::vector<base::debug::MappedMemoryRegion> regions; + regions.push_back( + base::debug::MappedMemoryRegion{0x1, 0x2, 0, kReadPrivate, "base.apk"}); + regions.push_back(base::debug::MappedMemoryRegion{ + 0x6, 0x7, 0, kReadPrivate, "libchrome.so"}); + regions.push_back( + base::debug::MappedMemoryRegion{0x3, 0x4, 0, kReadPrivate, "base.apk"}); + std::vector<NativeLibraryPrefetcher::AddressRange> ranges; + NativeLibraryPrefetcher::FilterLibchromeRangesOnlyIfPossible(regions, + &ranges); + EXPECT_EQ(ranges.size(), 1U); + EXPECT_EQ(ranges[0].first, 0x6U); + EXPECT_EQ(ranges[0].second, 0x7U); +} + +} // namespace android +} // namespace base diff --git a/chromium/base/android/linker/BUILD.gn b/chromium/base/android/linker/BUILD.gn index b26e3b041f6..190ea4776d4 100644 --- a/chromium/base/android/linker/BUILD.gn +++ b/chromium/base/android/linker/BUILD.gn @@ -5,15 +5,19 @@ import("//build/config/android/config.gni") assert(is_android) -assert(!is_android_webview_build) # GYP: //base/base.gyp:chromium_android_linker shared_library("chromium_android_linker") { - sources = [ "linker_jni.cc" ] + sources = [ + "linker_jni.cc", + ] + # The NDK contains the crazy_linker here: # '<(android_ndk_root)/crazy_linker.gyp:crazy_linker' # However, we use our own fork. See bug 384700. - deps = [ "//third_party/android_crazy_linker" ] + deps = [ + "//third_party/android_crazy_linker", + ] # TODO(GYP): # The crazy linker is never instrumented. diff --git a/chromium/base/android/record_histogram.cc b/chromium/base/android/record_histogram.cc new file mode 100644 index 00000000000..9a68deca54f --- /dev/null +++ b/chromium/base/android/record_histogram.cc @@ -0,0 +1,245 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/android/record_histogram.h" + +#include <map> + +#include "base/android/jni_android.h" +#include "base/android/jni_string.h" +#include "base/lazy_instance.h" +#include "base/metrics/histogram.h" +#include "base/metrics/sparse_histogram.h" +#include "base/metrics/statistics_recorder.h" +#include "base/synchronization/lock.h" +#include "base/time/time.h" +#include "jni/RecordHistogram_jni.h" + +namespace base { +namespace android { +namespace { + +// Simple thread-safe wrapper for caching histograms. This avoids +// relatively expensive JNI string translation for each recording. +class HistogramCache { + public: + HistogramCache() {} + + HistogramBase* BooleanHistogram(JNIEnv* env, + jstring j_histogram_name, + jint j_histogram_key) { + DCHECK(j_histogram_name); + DCHECK(j_histogram_key); + HistogramBase* histogram = FindLocked(j_histogram_key); + if (histogram) + return histogram; + + std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name); + histogram = BooleanHistogram::FactoryGet( + histogram_name, HistogramBase::kUmaTargetedHistogramFlag); + return InsertLocked(j_histogram_key, histogram); + } + + HistogramBase* EnumeratedHistogram(JNIEnv* env, + jstring j_histogram_name, + jint j_histogram_key, + jint j_boundary) { + DCHECK(j_histogram_name); + DCHECK(j_histogram_key); + HistogramBase* histogram = FindLocked(j_histogram_key); + int boundary = static_cast<int>(j_boundary); + if (histogram) { + DCHECK(histogram->HasConstructionArguments(1, boundary, boundary + 1)); + return histogram; + } + + std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name); + histogram = + LinearHistogram::FactoryGet(histogram_name, 1, boundary, boundary + 1, + HistogramBase::kUmaTargetedHistogramFlag); + return InsertLocked(j_histogram_key, histogram); + } + + HistogramBase* CustomCountHistogram(JNIEnv* env, + jstring j_histogram_name, + jint j_histogram_key, + jint j_min, + jint j_max, + jint j_num_buckets) { + DCHECK(j_histogram_name); + DCHECK(j_histogram_key); + int64 min = static_cast<int64>(j_min); + int64 max = static_cast<int64>(j_max); + int num_buckets = static_cast<int>(j_num_buckets); + HistogramBase* histogram = FindLocked(j_histogram_key); + if (histogram) { + DCHECK(histogram->HasConstructionArguments(min, max, num_buckets)); + return histogram; + } + + std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name); + histogram = + Histogram::FactoryGet(histogram_name, min, max, num_buckets, + HistogramBase::kUmaTargetedHistogramFlag); + return InsertLocked(j_histogram_key, histogram); + } + + HistogramBase* SparseHistogram(JNIEnv* env, + jstring j_histogram_name, + jint j_histogram_key) { + DCHECK(j_histogram_name); + DCHECK(j_histogram_key); + HistogramBase* histogram = FindLocked(j_histogram_key); + if (histogram) + return histogram; + + std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name); + histogram = SparseHistogram::FactoryGet( + histogram_name, HistogramBase::kUmaTargetedHistogramFlag); + return InsertLocked(j_histogram_key, histogram); + } + + HistogramBase* CustomTimesHistogram(JNIEnv* env, + jstring j_histogram_name, + jint j_histogram_key, + jlong j_min, + jlong j_max, + jint j_bucket_count) { + DCHECK(j_histogram_name); + DCHECK(j_histogram_key); + HistogramBase* histogram = FindLocked(j_histogram_key); + int64 min = static_cast<int64>(j_min); + int64 max = static_cast<int64>(j_max); + int bucket_count = static_cast<int>(j_bucket_count); + if (histogram) { + DCHECK(histogram->HasConstructionArguments(min, max, bucket_count)); + return histogram; + } + + std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name); + // This intentionally uses FactoryGet and not FactoryTimeGet. FactoryTimeGet + // is just a convenience for constructing the underlying Histogram with + // TimeDelta arguments. + histogram = Histogram::FactoryGet(histogram_name, min, max, bucket_count, + HistogramBase::kUmaTargetedHistogramFlag); + return InsertLocked(j_histogram_key, histogram); + } + + private: + HistogramBase* FindLocked(jint j_histogram_key) { + base::AutoLock locked(lock_); + auto histogram_it = histograms_.find(j_histogram_key); + return histogram_it != histograms_.end() ? histogram_it->second : nullptr; + } + + HistogramBase* InsertLocked(jint j_histogram_key, HistogramBase* histogram) { + base::AutoLock locked(lock_); + histograms_.insert(std::make_pair(j_histogram_key, histogram)); + return histogram; + } + + base::Lock lock_; + std::map<jint, HistogramBase*> histograms_; + + DISALLOW_COPY_AND_ASSIGN(HistogramCache); +}; + +base::LazyInstance<HistogramCache>::Leaky g_histograms; + +} // namespace + +void RecordBooleanHistogram(JNIEnv* env, + jclass clazz, + jstring j_histogram_name, + jint j_histogram_key, + jboolean j_sample) { + bool sample = static_cast<bool>(j_sample); + g_histograms.Get() + .BooleanHistogram(env, j_histogram_name, j_histogram_key) + ->AddBoolean(sample); +} + +void RecordEnumeratedHistogram(JNIEnv* env, + jclass clazz, + jstring j_histogram_name, + jint j_histogram_key, + jint j_sample, + jint j_boundary) { + int sample = static_cast<int>(j_sample); + + g_histograms.Get() + .EnumeratedHistogram(env, j_histogram_name, j_histogram_key, j_boundary) + ->Add(sample); +} + +void RecordCustomCountHistogram(JNIEnv* env, + jclass clazz, + jstring j_histogram_name, + jint j_histogram_key, + jint j_sample, + jint j_min, + jint j_max, + jint j_num_buckets) { + int sample = static_cast<int>(j_sample); + + g_histograms.Get() + .CustomCountHistogram(env, j_histogram_name, j_histogram_key, j_min, + j_max, j_num_buckets) + ->Add(sample); +} + +void RecordSparseHistogram(JNIEnv* env, + jclass clazz, + jstring j_histogram_name, + jint j_histogram_key, + jint j_sample) { + int sample = static_cast<int>(j_sample); + g_histograms.Get() + .SparseHistogram(env, j_histogram_name, j_histogram_key) + ->Add(sample); +} + +void RecordCustomTimesHistogramMilliseconds(JNIEnv* env, + jclass clazz, + jstring j_histogram_name, + jint j_histogram_key, + jlong j_duration, + jlong j_min, + jlong j_max, + jint j_num_buckets) { + g_histograms.Get() + .CustomTimesHistogram(env, j_histogram_name, j_histogram_key, j_min, + j_max, j_num_buckets) + ->AddTime(TimeDelta::FromMilliseconds(static_cast<int64>(j_duration))); +} + +void Initialize(JNIEnv* env, jclass) { + StatisticsRecorder::Initialize(); +} + +// This backs a Java test util for testing histograms - +// MetricsUtils.HistogramDelta. It should live in a test-specific file, but we +// currently can't have test-specific native code packaged in test-specific Java +// targets - see http://crbug.com/415945. +jint GetHistogramValueCountForTesting(JNIEnv* env, + jclass clazz, + jstring histogram_name, + jint sample) { + HistogramBase* histogram = StatisticsRecorder::FindHistogram( + android::ConvertJavaStringToUTF8(env, histogram_name)); + if (histogram == nullptr) { + // No samples have been recorded for this histogram (yet?). + return 0; + } + + scoped_ptr<HistogramSamples> samples = histogram->SnapshotSamples(); + return samples->GetCount(static_cast<int>(sample)); +} + +bool RegisterRecordHistogram(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +} // namespace android +} // namespace base diff --git a/chromium/base/android/record_histogram.h b/chromium/base/android/record_histogram.h new file mode 100644 index 00000000000..caa10f06c42 --- /dev/null +++ b/chromium/base/android/record_histogram.h @@ -0,0 +1,18 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_ANDROID_RECORD_HISTOGRAM_H_ +#define BASE_ANDROID_RECORD_HISTOGRAM_H_ + +#include <jni.h> + +namespace base { +namespace android { + +bool RegisterRecordHistogram(JNIEnv* env); + +} // namespace android +} // namespace base + +#endif // BASE_ANDROID_RECORD_HISTOGRAM_H_ diff --git a/chromium/base/android/record_user_action.cc b/chromium/base/android/record_user_action.cc new file mode 100644 index 00000000000..6172f2e5667 --- /dev/null +++ b/chromium/base/android/record_user_action.cc @@ -0,0 +1,24 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/android/record_user_action.h" + +#include "base/android/jni_string.h" +#include "base/metrics/user_metrics.h" +#include "jni/RecordUserAction_jni.h" + +namespace base { +namespace android { + +static void RecordUserAction(JNIEnv* env, jclass clazz, jstring j_action) { + RecordComputedAction(ConvertJavaStringToUTF8(env, j_action)); +} + +// Register native methods +bool RegisterRecordUserAction(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +} // namespace android +} // namespace base diff --git a/chromium/base/android/record_user_action.h b/chromium/base/android/record_user_action.h new file mode 100644 index 00000000000..2c2b854a490 --- /dev/null +++ b/chromium/base/android/record_user_action.h @@ -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. + +#ifndef BASE_ANDROID_RECORD_USER_ACTION_H_ +#define BASE_ANDROID_RECORD_USER_ACTION_H_ + +#include <jni.h> + +namespace base { +namespace android { + +// Registers the native methods through jni +bool RegisterRecordUserAction(JNIEnv* env); + +} // namespace android +} // namespace base + +#endif // BASE_ANDROID_RECORD_USER_ACTION_H_ diff --git a/chromium/base/android/scoped_java_ref.h b/chromium/base/android/scoped_java_ref.h index 7863c0bef3a..8047ee8c167 100644 --- a/chromium/base/android/scoped_java_ref.h +++ b/chromium/base/android/scoped_java_ref.h @@ -178,6 +178,8 @@ class ScopedJavaGlobalRef : public JavaRef<T> { this->Reset(other); } + ScopedJavaGlobalRef(JNIEnv* env, T obj) { this->Reset(env, obj); } + template<typename U> explicit ScopedJavaGlobalRef(const U& other) { this->Reset(other); diff --git a/chromium/base/android/scoped_java_ref_unittest.cc b/chromium/base/android/scoped_java_ref_unittest.cc index 36f253c4e99..3f4419af6d2 100644 --- a/chromium/base/android/scoped_java_ref_unittest.cc +++ b/chromium/base/android/scoped_java_ref_unittest.cc @@ -40,7 +40,7 @@ void DeleteLocalRef(JNIEnv* env, jobject obj) { class ScopedJavaRefTest : public testing::Test { protected: - virtual void SetUp() { + void SetUp() override { g_local_refs = 0; g_global_refs = 0; JNIEnv* env = AttachCurrentThread(); @@ -55,7 +55,7 @@ class ScopedJavaRefTest : public testing::Test { hooked_functions.DeleteLocalRef = &DeleteLocalRef; } - virtual void TearDown() { + void TearDown() override { JNIEnv* env = AttachCurrentThread(); env->functions = g_previous_functions; } diff --git a/chromium/base/android/trace_event_binding.cc b/chromium/base/android/trace_event_binding.cc index 216ba7b096d..791b67fcf23 100644 --- a/chromium/base/android/trace_event_binding.cc +++ b/chromium/base/android/trace_event_binding.cc @@ -8,9 +8,9 @@ #include <set> -#include "base/debug/trace_event.h" -#include "base/debug/trace_event_impl.h" #include "base/lazy_instance.h" +#include "base/trace_event/trace_event.h" +#include "base/trace_event/trace_event_impl.h" #include "jni/TraceEvent_jni.h" namespace base { @@ -55,13 +55,14 @@ class TraceEventDataConverter { DISALLOW_COPY_AND_ASSIGN(TraceEventDataConverter); }; -class TraceEnabledObserver : public debug::TraceLog::EnabledStateObserver { +class TraceEnabledObserver + : public trace_event::TraceLog::EnabledStateObserver { public: - virtual void OnTraceLogEnabled() override { + void OnTraceLogEnabled() override { JNIEnv* env = base::android::AttachCurrentThread(); base::android::Java_TraceEvent_setEnabled(env, true); } - virtual void OnTraceLogDisabled() override { + void OnTraceLogDisabled() override { JNIEnv* env = base::android::AttachCurrentThread(); base::android::Java_TraceEvent_setEnabled(env, false); } @@ -72,18 +73,18 @@ base::LazyInstance<TraceEnabledObserver>::Leaky g_trace_enabled_state_observer_; } // namespace static void RegisterEnabledObserver(JNIEnv* env, jclass clazz) { - bool enabled = debug::TraceLog::GetInstance()->IsEnabled(); + bool enabled = trace_event::TraceLog::GetInstance()->IsEnabled(); base::android::Java_TraceEvent_setEnabled(env, enabled); - debug::TraceLog::GetInstance()->AddEnabledStateObserver( + trace_event::TraceLog::GetInstance()->AddEnabledStateObserver( g_trace_enabled_state_observer_.Pointer()); } static void StartATrace(JNIEnv* env, jclass clazz) { - base::debug::TraceLog::GetInstance()->StartATrace(); + base::trace_event::TraceLog::GetInstance()->StartATrace(); } static void StopATrace(JNIEnv* env, jclass clazz) { - base::debug::TraceLog::GetInstance()->StopATrace(); + base::trace_event::TraceLog::GetInstance()->StopATrace(); } static void Instant(JNIEnv* env, jclass clazz, @@ -129,36 +130,14 @@ static void EndToplevel(JNIEnv* env, jclass clazz) { TRACE_EVENT_END0(kToplevelCategory, kLooperDispatchMessage); } -static void StartAsync(JNIEnv* env, jclass clazz, - jstring jname, jlong jid, jstring jarg) { - TraceEventDataConverter converter(env, jname, jarg); - if (converter.arg()) { - TRACE_EVENT_COPY_ASYNC_BEGIN1(kJavaCategory, - converter.name(), - jid, - converter.arg_name(), - converter.arg()); - } else { - TRACE_EVENT_COPY_ASYNC_BEGIN0(kJavaCategory, - converter.name(), - jid); - } +static void StartAsync(JNIEnv* env, jclass clazz, jstring jname, jlong jid) { + TraceEventDataConverter converter(env, jname, nullptr); + TRACE_EVENT_COPY_ASYNC_BEGIN0(kJavaCategory, converter.name(), jid); } -static void FinishAsync(JNIEnv* env, jclass clazz, - jstring jname, jlong jid, jstring jarg) { - TraceEventDataConverter converter(env, jname, jarg); - if (converter.arg()) { - TRACE_EVENT_COPY_ASYNC_END1(kJavaCategory, - converter.name(), - jid, - converter.arg_name(), - converter.arg()); - } else { - TRACE_EVENT_COPY_ASYNC_END0(kJavaCategory, - converter.name(), - jid); - } +static void FinishAsync(JNIEnv* env, jclass clazz, jstring jname, jlong jid) { + TraceEventDataConverter converter(env, jname, nullptr); + TRACE_EVENT_COPY_ASYNC_END0(kJavaCategory, converter.name(), jid); } bool RegisterTraceEvent(JNIEnv* env) { diff --git a/chromium/base/android/trace_event_binding.h b/chromium/base/android/trace_event_binding.h index ed0626620aa..1c1a60b2ce9 100644 --- a/chromium/base/android/trace_event_binding.h +++ b/chromium/base/android/trace_event_binding.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_ANDROID_TRACE_EVENT_H_ -#define BASE_ANDROID_TRACE_EVENT_H_ +#ifndef BASE_ANDROID_TRACE_EVENT_BINDING_H_ +#define BASE_ANDROID_TRACE_EVENT_BINDING_H_ #include <jni.h> @@ -15,4 +15,4 @@ extern bool RegisterTraceEvent(JNIEnv* env); } // namespace android } // namespace base -#endif // CONTENT_COMMON_ANDROID_TRACE_EVENT_H_ +#endif // BASE_ANDROID_TRACE_EVENT_BINDING_H_ diff --git a/chromium/base/async_socket_io_handler.h b/chromium/base/async_socket_io_handler.h index bedb00f215b..a22c29d907d 100644 --- a/chromium/base/async_socket_io_handler.h +++ b/chromium/base/async_socket_io_handler.h @@ -76,9 +76,9 @@ class BASE_EXPORT AsyncSocketIoHandler private: #if defined(OS_WIN) // Implementation of IOHandler on Windows. - virtual void OnIOCompleted(base::MessageLoopForIO::IOContext* context, - DWORD bytes_transfered, - DWORD error) override; + void OnIOCompleted(base::MessageLoopForIO::IOContext* context, + DWORD bytes_transfered, + DWORD error) override; #elif defined(OS_POSIX) // Implementation of base::MessageLoopForIO::Watcher. void OnFileCanWriteWithoutBlocking(int socket) override {} diff --git a/chromium/base/async_socket_io_handler_unittest.cc b/chromium/base/async_socket_io_handler_unittest.cc index 2b0e3c25c30..721de9cd72d 100644 --- a/chromium/base/async_socket_io_handler_unittest.cc +++ b/chromium/base/async_socket_io_handler_unittest.cc @@ -5,6 +5,9 @@ #include "base/async_socket_io_handler.h" #include "base/bind.h" +#include "base/location.h" +#include "base/single_thread_task_runner.h" +#include "base/thread_task_runner_handle.h" #include "testing/gtest/include/gtest/gtest.h" namespace { @@ -104,8 +107,8 @@ TEST(AsyncSocketIoHandlerTest, SynchronousReadWithMessageLoop) { TestSocketReader reader(&pair[0], -1, false, false); pair[1].Send(kAsyncSocketIoTestString, kAsyncSocketIoTestStringLength); - base::MessageLoop::current()->PostDelayedTask(FROM_HERE, - base::MessageLoop::QuitClosure(), + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, base::MessageLoop::QuitClosure(), base::TimeDelta::FromMilliseconds(100)); base::MessageLoop::current()->Run(); @@ -135,15 +138,15 @@ TEST(AsyncSocketIoHandlerTest, ReadFromCallback) { // Issue sends on an interval to satisfy the Read() requirements. int64 milliseconds = 0; for (int i = 0; i < kReadOperationCount; ++i) { - base::MessageLoop::current()->PostDelayedTask(FROM_HERE, - base::Bind(&SendData, &pair[1], kAsyncSocketIoTestString, - kAsyncSocketIoTestStringLength), + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, base::Bind(&SendData, &pair[1], kAsyncSocketIoTestString, + kAsyncSocketIoTestStringLength), base::TimeDelta::FromMilliseconds(milliseconds)); milliseconds += 10; } - base::MessageLoop::current()->PostDelayedTask(FROM_HERE, - base::MessageLoop::QuitClosure(), + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, base::MessageLoop::QuitClosure(), base::TimeDelta::FromMilliseconds(100 + milliseconds)); base::MessageLoop::current()->Run(); diff --git a/chromium/base/atomic_ref_count.h b/chromium/base/atomic_ref_count.h index 553fab6a8c6..2ab72420020 100644 --- a/chromium/base/atomic_ref_count.h +++ b/chromium/base/atomic_ref_count.h @@ -4,9 +4,6 @@ // This is a low level implementation of atomic semantics for reference // counting. Please use base/memory/ref_counted.h directly instead. -// -// The implementation includes annotations to avoid some false positives -// when using data race detection tools. #ifndef BASE_ATOMIC_REF_COUNT_H_ #define BASE_ATOMIC_REF_COUNT_H_ diff --git a/chromium/base/atomicops.h b/chromium/base/atomicops.h index 833e1704291..6a5371c75c2 100644 --- a/chromium/base/atomicops.h +++ b/chromium/base/atomicops.h @@ -28,10 +28,14 @@ #ifndef BASE_ATOMICOPS_H_ #define BASE_ATOMICOPS_H_ -#include <cassert> // Small C++ header which defines implementation specific - // macros used to identify the STL implementation. #include <stdint.h> +// Small C++ header which defines implementation specific macros used to +// identify the STL implementation. +// - libc++: captures __config for _LIBCPP_VERSION +// - libstdc++: captures bits/c++config.h for __GLIBCXX__ +#include <cstddef> + #include "base/base_export.h" #include "build/build_config.h" diff --git a/chromium/base/atomicops_internals_arm64_gcc.h b/chromium/base/atomicops_internals_arm64_gcc.h index bb6a346eccb..ddcfec901f9 100644 --- a/chromium/base/atomicops_internals_arm64_gcc.h +++ b/chromium/base/atomicops_internals_arm64_gcc.h @@ -301,7 +301,7 @@ inline Atomic64 Release_Load(volatile const Atomic64* ptr) { return *ptr; } -} // namespace base::subtle +} // namespace subtle } // namespace base #endif // BASE_ATOMICOPS_INTERNALS_ARM64_GCC_H_ diff --git a/chromium/base/atomicops_internals_arm_gcc.h b/chromium/base/atomicops_internals_arm_gcc.h index e654afa7d27..44c91c87a25 100644 --- a/chromium/base/atomicops_internals_arm_gcc.h +++ b/chromium/base/atomicops_internals_arm_gcc.h @@ -288,7 +288,7 @@ inline Atomic32 Release_Load(volatile const Atomic32* ptr) { return *ptr; } -} // namespace base::subtle +} // namespace subtle } // namespace base #endif // BASE_ATOMICOPS_INTERNALS_ARM_GCC_H_ diff --git a/chromium/base/atomicops_internals_atomicword_compat.h b/chromium/base/atomicops_internals_atomicword_compat.h index e02d11d29bf..342a6e4592c 100644 --- a/chromium/base/atomicops_internals_atomicword_compat.h +++ b/chromium/base/atomicops_internals_atomicword_compat.h @@ -92,8 +92,8 @@ inline AtomicWord Release_Load(volatile const AtomicWord* ptr) { reinterpret_cast<volatile const Atomic32*>(ptr)); } -} // namespace base::subtle -} // namespace base +} // namespace subtle +} // namespace base #endif // !defined(ARCH_CPU_64_BITS) diff --git a/chromium/base/atomicops_internals_gcc.h b/chromium/base/atomicops_internals_gcc.h index ed1b2d79134..35c95fee56d 100644 --- a/chromium/base/atomicops_internals_gcc.h +++ b/chromium/base/atomicops_internals_gcc.h @@ -99,7 +99,7 @@ inline Atomic32 Release_Load(volatile const Atomic32* ptr) { return *ptr; } -} // namespace base::subtle +} // namespace subtle } // namespace base #endif // BASE_ATOMICOPS_INTERNALS_GCC_H_ diff --git a/chromium/base/atomicops_internals_mac.h b/chromium/base/atomicops_internals_mac.h index ccbb896e4cb..98864a77da2 100644 --- a/chromium/base/atomicops_internals_mac.h +++ b/chromium/base/atomicops_internals_mac.h @@ -191,7 +191,7 @@ inline Atomic64 Release_Load(volatile const Atomic64* ptr) { #endif // defined(__LP64__) -} // namespace base::subtle -} // namespace base +} // namespace subtle +} // namespace base #endif // BASE_ATOMICOPS_INTERNALS_MAC_H_ diff --git a/chromium/base/atomicops_internals_mips_gcc.h b/chromium/base/atomicops_internals_mips_gcc.h index 9111a535b26..b4551b8351e 100644 --- a/chromium/base/atomicops_internals_mips_gcc.h +++ b/chromium/base/atomicops_internals_mips_gcc.h @@ -38,7 +38,7 @@ inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, "2:\n" ".set pop\n" : "=&r" (prev), "=m" (*ptr), "=&r" (tmp) - : "Ir" (old_value), "r" (new_value), "m" (*ptr) + : "r" (old_value), "r" (new_value), "m" (*ptr) : "memory"); return prev; } @@ -167,7 +167,7 @@ inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr, "2:\n" ".set pop\n" : "=&r" (prev), "=m" (*ptr), "=&r" (tmp) - : "Ir" (old_value), "r" (new_value), "m" (*ptr) + : "r" (old_value), "r" (new_value), "m" (*ptr) : "memory"); return prev; } @@ -274,7 +274,7 @@ inline Atomic64 Release_Load(volatile const Atomic64* ptr) { } #endif -} // namespace base::subtle -} // namespace base +} // namespace subtle +} // namespace base #endif // BASE_ATOMICOPS_INTERNALS_MIPS_GCC_H_ diff --git a/chromium/base/atomicops_internals_portable.h b/chromium/base/atomicops_internals_portable.h index b25099fd220..d28561076dd 100644 --- a/chromium/base/atomicops_internals_portable.h +++ b/chromium/base/atomicops_internals_portable.h @@ -221,7 +221,7 @@ inline Atomic64 Release_Load(volatile const Atomic64* ptr) { } #endif // defined(ARCH_CPU_64_BITS) -} -} // namespace base::subtle +} // namespace subtle +} // namespace base #endif // BASE_ATOMICOPS_INTERNALS_PORTABLE_H_ diff --git a/chromium/base/atomicops_internals_x86_gcc.h b/chromium/base/atomicops_internals_x86_gcc.h index 69eacdb0873..f0d224264b9 100644 --- a/chromium/base/atomicops_internals_x86_gcc.h +++ b/chromium/base/atomicops_internals_x86_gcc.h @@ -220,8 +220,8 @@ inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr, #endif // defined(__x86_64__) -} // namespace base::subtle -} // namespace base +} // namespace subtle +} // namespace base #undef ATOMICOPS_COMPILER_BARRIER diff --git a/chromium/base/atomicops_internals_x86_msvc.h b/chromium/base/atomicops_internals_x86_msvc.h index 0269d894a1f..71ddca2ba38 100644 --- a/chromium/base/atomicops_internals_x86_msvc.h +++ b/chromium/base/atomicops_internals_x86_msvc.h @@ -55,9 +55,6 @@ inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, return Barrier_AtomicIncrement(ptr, increment); } -#if !(defined(_MSC_VER) && _MSC_VER >= 1400) -#error "We require at least vs2005 for MemoryBarrier" -#endif inline void MemoryBarrier() { #if defined(ARCH_CPU_64_BITS) // See #undef and note at the top of this file. @@ -192,7 +189,7 @@ inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr, #endif // defined(_WIN64) -} // namespace base::subtle +} // namespace subtle } // namespace base #endif // BASE_ATOMICOPS_INTERNALS_X86_MSVC_H_ diff --git a/chromium/base/auto_reset.h b/chromium/base/auto_reset.h index 32f138e2440..a5bcfaae8a8 100644 --- a/chromium/base/auto_reset.h +++ b/chromium/base/auto_reset.h @@ -36,6 +36,6 @@ class AutoReset { DISALLOW_COPY_AND_ASSIGN(AutoReset); }; -} +} // namespace base #endif // BASE_AUTO_RESET_H_ diff --git a/chromium/base/barrier_closure.cc b/chromium/base/barrier_closure.cc index 5abe2bfaec2..1dcc50238fb 100644 --- a/chromium/base/barrier_closure.cc +++ b/chromium/base/barrier_closure.cc @@ -28,8 +28,9 @@ BarrierInfo::BarrierInfo(int num_callbacks, const base::Closure& done_closure) void BarrierInfo::Run() { DCHECK(!base::AtomicRefCountIsZero(&num_callbacks_left_)); if (!base::AtomicRefCountDec(&num_callbacks_left_)) { - done_closure_.Run(); + base::Closure done_closure = done_closure_; done_closure_.Reset(); + done_closure.Run(); } } @@ -39,7 +40,7 @@ namespace base { base::Closure BarrierClosure(int num_callbacks_left, const base::Closure& done_closure) { - DCHECK(num_callbacks_left >= 0); + DCHECK_GE(num_callbacks_left, 0); if (num_callbacks_left == 0) done_closure.Run(); diff --git a/chromium/base/barrier_closure_unittest.cc b/chromium/base/barrier_closure_unittest.cc index ab05cb8af54..dcea09feffa 100644 --- a/chromium/base/barrier_closure_unittest.cc +++ b/chromium/base/barrier_closure_unittest.cc @@ -13,24 +13,68 @@ void Increment(int* count) { (*count)++; } TEST(BarrierClosureTest, RunImmediatelyForZeroClosures) { int count = 0; - base::Closure doneClosure(base::Bind(&Increment, base::Unretained(&count))); + base::Closure done_closure(base::Bind(&Increment, base::Unretained(&count))); - base::Closure barrierClosure = base::BarrierClosure(0, doneClosure); + base::Closure barrier_closure = base::BarrierClosure(0, done_closure); EXPECT_EQ(1, count); } TEST(BarrierClosureTest, RunAfterNumClosures) { int count = 0; - base::Closure doneClosure(base::Bind(&Increment, base::Unretained(&count))); + base::Closure done_closure(base::Bind(&Increment, base::Unretained(&count))); - base::Closure barrierClosure = base::BarrierClosure(2, doneClosure); + base::Closure barrier_closure = base::BarrierClosure(2, done_closure); EXPECT_EQ(0, count); - barrierClosure.Run(); + barrier_closure.Run(); EXPECT_EQ(0, count); - barrierClosure.Run(); + barrier_closure.Run(); EXPECT_EQ(1, count); } +class DestructionIndicator { + public: + // Sets |*destructed| to true in destructor. + DestructionIndicator(bool* destructed) : destructed_(destructed) { + *destructed_ = false; + } + + ~DestructionIndicator() { *destructed_ = true; } + + void DoNothing() {} + + private: + bool* destructed_; +}; + +TEST(BarrierClosureTest, ReleasesDoneClosureWhenDone) { + bool done_destructed = false; + base::Closure barrier_closure = base::BarrierClosure( + 1, base::Bind(&DestructionIndicator::DoNothing, + base::Owned(new DestructionIndicator(&done_destructed)))); + EXPECT_FALSE(done_destructed); + barrier_closure.Run(); + EXPECT_TRUE(done_destructed); +} + +void ResetBarrierClosure(base::Closure* closure) { + *closure = base::Closure(); +} + +// Tests a case when |done_closure| resets a |barrier_closure|. +// |barrier_closure| is a Closure holding the |done_closure|. |done_closure| +// holds a pointer back to the |barrier_closure|. When |barrier_closure| is +// Run() it calls ResetBarrierClosure() which erases the |barrier_closure| while +// still inside of its Run(). The Run() implementation (in base::BarrierClosure) +// must not try use itself after executing ResetBarrierClosure() or this test +// would crash inside Run(). +TEST(BarrierClosureTest, KeepingClosureAliveUntilDone) { + base::Closure barrier_closure; + base::Closure done_closure = + base::Bind(ResetBarrierClosure, &barrier_closure); + barrier_closure = base::BarrierClosure(1, done_closure); + barrier_closure.Run(); +} + } // namespace diff --git a/chromium/base/base.gyp b/chromium/base/base.gyp index ad4dd226792..ff6ce00e246 100644 --- a/chromium/base/base.gyp +++ b/chromium/base/base.gyp @@ -103,7 +103,9 @@ }], ], 'dependencies': [ + 'base_java', 'base_jni_headers', + '../build/android/ndk.gyp:cpu_features', '../third_party/ashmem/ashmem.gyp:ashmem', ], 'link_settings': { @@ -114,14 +116,6 @@ 'sources!': [ 'debug/stack_trace_posix.cc', ], - 'includes': [ - '../build/android/cpufeatures.gypi', - ], - }], - ['OS == "android" and _toolset == "target" and android_webview_build == 0', { - 'dependencies': [ - 'base_java', - ], }], ['os_bsd==1', { 'include_dirs': [ @@ -188,6 +182,17 @@ }, }, }, + 'copies': [ + { + 'destination': '<(PRODUCT_DIR)/', + 'files': [ + '../build/win/dbghelp_xp/dbghelp.dll', + ], + }, + ], + 'dependencies': [ + 'trace_event/etw_manifest/etw_manifest.gyp:etw_manifest', + ], }], ['OS == "mac" or (OS == "ios" and _toolset == "host")', { 'link_settings': { @@ -201,9 +206,6 @@ '$(SDKROOT)/System/Library/Frameworks/Security.framework', ], }, - 'dependencies': [ - '../third_party/mach_override/mach_override.gyp:mach_override', - ], }], ['OS == "ios" and _toolset != "host"', { 'link_settings': { @@ -230,15 +232,10 @@ }], ], 'sources': [ - 'third_party/xdg_user_dirs/xdg_user_dir_lookup.cc', - 'third_party/xdg_user_dirs/xdg_user_dir_lookup.h', 'async_socket_io_handler.h', 'async_socket_io_handler_posix.cc', 'async_socket_io_handler_win.cc', 'auto_reset.h', - 'event_recorder.h', - 'event_recorder_stubs.cc', - 'event_recorder_win.cc', 'linux_util.cc', 'linux_util.h', 'message_loop/message_pump_android.cc', @@ -256,8 +253,10 @@ 'posix/file_descriptor_shuffle.cc', 'posix/file_descriptor_shuffle.h', 'sync_socket.h', - 'sync_socket_win.cc', 'sync_socket_posix.cc', + 'sync_socket_win.cc', + 'third_party/xdg_user_dirs/xdg_user_dir_lookup.cc', + 'third_party/xdg_user_dirs/xdg_user_dir_lookup.h', ], 'includes': [ '../build/android/increase_size_for_speed.gypi', @@ -435,9 +434,11 @@ 'type': '<(gtest_target_type)', 'sources': [ 'android/application_status_listener_unittest.cc', + 'android/content_uri_utils_unittest.cc', 'android/jni_android_unittest.cc', 'android/jni_array_unittest.cc', 'android/jni_string_unittest.cc', + 'android/library_loader/library_prefetcher_unittest.cc', 'android/path_utils_unittest.cc', 'android/scoped_java_ref_unittest.cc', 'android/sys_utils_unittest.cc', @@ -457,31 +458,28 @@ 'callback_unittest.cc', 'callback_unittest.nc', 'cancelable_callback_unittest.cc', + 'chromeos/memory_pressure_monitor_unittest.cc', 'command_line_unittest.cc', 'containers/adapters_unittest.cc', 'containers/hash_tables_unittest.cc', 'containers/linked_list_unittest.cc', 'containers/mru_cache_unittest.cc', + 'containers/scoped_ptr_hash_map_unittest.cc', 'containers/small_map_unittest.cc', 'containers/stack_container_unittest.cc', 'cpu_unittest.cc', 'debug/crash_logging_unittest.cc', + 'debug/debugger_unittest.cc', 'debug/leak_tracker_unittest.cc', 'debug/proc_maps_linux_unittest.cc', 'debug/stack_trace_unittest.cc', 'debug/task_annotator_unittest.cc', - 'debug/trace_event_argument_unittest.cc', - 'debug/trace_event_memory_unittest.cc', - 'debug/trace_event_synthetic_delay_unittest.cc', - 'debug/trace_event_system_stats_monitor_unittest.cc', - 'debug/trace_event_unittest.cc', - 'debug/trace_event_unittest.h', - 'debug/trace_event_win_unittest.cc', 'deferred_sequenced_task_runner_unittest.cc', 'environment_unittest.cc', 'file_version_info_unittest.cc', 'files/dir_reader_posix_unittest.cc', 'files/file_path_unittest.cc', + 'files/file_path_watcher_unittest.cc', 'files/file_proxy_unittest.cc', 'files/file_unittest.cc', 'files/file_util_proxy_unittest.cc', @@ -492,10 +490,9 @@ 'gmock_unittest.cc', 'guid_unittest.cc', 'hash_unittest.cc', - 'id_map_unittest.cc', 'i18n/break_iterator_unittest.cc', - 'i18n/char_iterator_unittest.cc', 'i18n/case_conversion_unittest.cc', + 'i18n/char_iterator_unittest.cc', 'i18n/file_util_icu_unittest.cc', 'i18n/icu_string_conversions_unittest.cc', 'i18n/number_formatting_unittest.cc', @@ -504,7 +501,10 @@ 'i18n/string_search_unittest.cc', 'i18n/time_formatting_unittest.cc', 'i18n/timezone_unittest.cc', + 'id_map_unittest.cc', + 'ios/crb_protocol_observers_unittest.mm', 'ios/device_util_unittest.mm', + 'ios/weak_nsobject_unittest.mm', 'json/json_parser_unittest.cc', 'json/json_reader_unittest.cc', 'json/json_value_converter_unittest.cc', @@ -514,17 +514,17 @@ 'lazy_instance_unittest.cc', 'logging_unittest.cc', 'mac/bind_objc_block_unittest.mm', + 'mac/dispatch_source_mach_unittest.cc', 'mac/foundation_util_unittest.mm', 'mac/libdispatch_task_runner_unittest.cc', 'mac/mac_util_unittest.mm', + 'mac/memory_pressure_monitor_unittest.cc', 'mac/objc_property_releaser_unittest.mm', 'mac/scoped_nsobject_unittest.mm', 'mac/scoped_objc_class_swizzler_unittest.mm', 'mac/scoped_sending_event_unittest.mm', 'md5_unittest.cc', 'memory/aligned_memory_unittest.cc', - 'memory/discardable_memory_manager_unittest.cc', - 'memory/discardable_memory_unittest.cc', 'memory/discardable_shared_memory_unittest.cc', 'memory/linked_ptr_unittest.cc', 'memory/ref_counted_memory_unittest.cc', @@ -542,17 +542,19 @@ 'message_loop/message_pump_glib_unittest.cc', 'message_loop/message_pump_io_ios_unittest.cc', 'message_loop/message_pump_libevent_unittest.cc', - 'metrics/sample_map_unittest.cc', - 'metrics/sample_vector_unittest.cc', 'metrics/bucket_ranges_unittest.cc', 'metrics/field_trial_unittest.cc', 'metrics/histogram_base_unittest.cc', 'metrics/histogram_delta_serialization_unittest.cc', + 'metrics/histogram_macros_unittest.cc', 'metrics/histogram_snapshot_manager_unittest.cc', 'metrics/histogram_unittest.cc', + 'metrics/sample_map_unittest.cc', + 'metrics/sample_vector_unittest.cc', 'metrics/sparse_histogram_unittest.cc', - 'metrics/stats_table_unittest.cc', 'metrics/statistics_recorder_unittest.cc', + 'move_unittest.cc', + 'numerics/safe_numerics_unittest.cc', 'observer_list_unittest.cc', 'os_compat_android_unittest.cc', 'path_service_unittest.cc', @@ -578,9 +580,9 @@ 'process/process_metrics_unittest_ios.cc', 'process/process_unittest.cc', 'process/process_util_unittest.cc', + 'profiler/stack_sampling_profiler_unittest.cc', 'profiler/tracked_time_unittest.cc', 'rand_util_unittest.cc', - 'numerics/safe_numerics_unittest.cc', 'scoped_clear_errno_unittest.cc', 'scoped_generic_unittest.cc', 'scoped_native_library_unittest.cc', @@ -591,13 +593,13 @@ 'strings/nullable_string16_unittest.cc', 'strings/safe_sprintf_unittest.cc', 'strings/string16_unittest.cc', - 'strings/stringprintf_unittest.cc', 'strings/string_number_conversions_unittest.cc', 'strings/string_piece_unittest.cc', 'strings/string_split_unittest.cc', 'strings/string_tokenizer_unittest.cc', 'strings/string_util_unittest.cc', 'strings/stringize_macros_unittest.cc', + 'strings/stringprintf_unittest.cc', 'strings/sys_string_conversions_mac_unittest.mm', 'strings/sys_string_conversions_unittest.cc', 'strings/utf_offset_string_conversions_unittest.cc', @@ -620,6 +622,7 @@ 'test/test_pending_task_unittest.cc', 'test/test_reg_util_win_unittest.cc', 'test/trace_event_analyzer_unittest.cc', + 'test/user_action_tester_unittest.cc', 'threading/non_thread_safe_unittest.cc', 'threading/platform_thread_unittest.cc', 'threading/sequenced_worker_pool_unittest.cc', @@ -652,6 +655,7 @@ 'win/event_trace_provider_unittest.cc', 'win/i18n_unittest.cc', 'win/iunknown_impl_unittest.cc', + 'win/memory_pressure_monitor_unittest.cc', 'win/message_window_unittest.cc', 'win/object_watcher_unittest.cc', 'win/pe_image_unittest.cc', @@ -664,6 +668,7 @@ 'win/startup_information_unittest.cc', 'win/win_util_unittest.cc', 'win/wrapped_window_proc_unittest.cc', + '<@(trace_event_test_sources)', ], 'dependencies': [ 'base', @@ -699,8 +704,6 @@ ['exclude', '^process/process_unittest\\.cc$'], ['exclude', '^process/process_util_unittest\\.cc$'], ['include', '^process/process_util_unittest_ios\\.cc$'], - # Requires spawning processes. - ['exclude', '^metrics/stats_table_unittest\\.cc$'], # iOS does not use message_pump_libevent. ['exclude', '^message_loop/message_pump_libevent_unittest\\.cc$'], ], @@ -746,18 +749,24 @@ 'message_loop/message_pump_glib_unittest.cc', ] }], - ['OS == "linux" and use_allocator!="none"', { - 'dependencies': [ - 'allocator/allocator.gyp:allocator', - ], - }, + ['OS == "linux"', { + 'dependencies': [ + 'malloc_wrapper', + ], + 'conditions': [ + ['use_allocator!="none"', { + 'dependencies': [ + 'allocator/allocator.gyp:allocator', + ], + }], + ]}, ], ['OS == "win"', { 'sources!': [ 'file_descriptor_shuffle_unittest.cc', 'files/dir_reader_posix_unittest.cc', - 'threading/worker_pool_posix_unittest.cc', 'message_loop/message_pump_libevent_unittest.cc', + 'threading/worker_pool_posix_unittest.cc', ], # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. 'msvs_disabled_warnings': [ @@ -781,14 +790,6 @@ '../third_party/icu/icu.gyp:icudata', ], }], - ['incremental_chrome_dll', { - 'defines': [ - # Used only to workaround a linker bug, do not use this - # otherwise, and don't make it broader scope. See - # http://crbug.com/251251. - 'INCREMENTAL_LINKING', - ], - }], ], }, { # OS != "win" 'dependencies': [ @@ -808,19 +809,20 @@ ['include', '^sys_string_conversions_mac_unittest\\.mm$'], ], }], - ['OS == "android" and _toolset == "target"', { - 'sources': [ - 'memory/discardable_memory_ashmem_allocator_unittest.cc', - ], - }], ['OS == "android"', { 'sources/': [ ['include', '^debug/proc_maps_linux_unittest\\.cc$'], ], }], + # Enable more direct string conversions on platforms with native utf8 + # strings + ['OS=="mac" or OS=="ios" or <(chromeos)==1 or <(chromecast)==1', { + 'defines': ['SYSTEM_NATIVE_UTF8'], + }], ], # target_conditions }, { + # GN: //base:base_perftests 'target_name': 'base_perftests', 'type': '<(gtest_target_type)', 'dependencies': [ @@ -829,9 +831,9 @@ '../testing/gtest.gyp:gtest', ], 'sources': [ - 'threading/thread_perftest.cc', 'message_loop/message_pump_perftest.cc', 'test/run_all_unittests.cc', + 'threading/thread_perftest.cc', '../testing/perf/perf_test.cc' ], 'conditions': [ @@ -843,6 +845,7 @@ ], }, { + # GN: //base:base_i18n_perftests 'target_name': 'base_i18n_perftests', 'type': '<(gtest_target_type)', 'dependencies': [ @@ -866,6 +869,7 @@ 'base_i18n', '../testing/gmock.gyp:gmock', '../testing/gtest.gyp:gtest', + '../third_party/icu/icu.gyp:icuuc', '../third_party/libxml/libxml.gyp:libxml', 'third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', ], @@ -899,8 +903,14 @@ 'test/expectations/expectation.h', 'test/expectations/parser.cc', 'test/expectations/parser.h', + 'test/gtest_util.cc', + 'test/gtest_util.h', 'test/gtest_xml_util.cc', 'test/gtest_xml_util.h', + 'test/histogram_tester.cc', + 'test/histogram_tester.h', + 'test/ios/wait_util.h', + 'test/ios/wait_util.mm', 'test/launcher/test_launcher.cc', 'test/launcher/test_launcher.h', 'test/launcher/test_result.cc', @@ -914,8 +924,10 @@ 'test/mock_chrome_application_mac.mm', 'test/mock_devices_changed_observer.cc', 'test/mock_devices_changed_observer.h', - 'test/mock_time_provider.cc', - 'test/mock_time_provider.h', + 'test/mock_entropy_provider.cc', + 'test/mock_entropy_provider.h', + 'test/mock_log.cc', + 'test/mock_log.h', 'test/multiprocess_test.cc', 'test/multiprocess_test.h', 'test/multiprocess_test_android.cc', @@ -943,10 +955,10 @@ 'test/simple_test_clock.h', 'test/simple_test_tick_clock.cc', 'test/simple_test_tick_clock.h', - 'test/histogram_tester.cc', - 'test/histogram_tester.h', 'test/task_runner_test_template.cc', 'test/task_runner_test_template.h', + 'test/test_discardable_memory_allocator.cc', + 'test/test_discardable_memory_allocator.h', 'test/test_file_util.cc', 'test/test_file_util.h', 'test/test_file_util_android.cc', @@ -958,6 +970,8 @@ 'test/test_io_thread.h', 'test/test_listener_ios.h', 'test/test_listener_ios.mm', + 'test/test_mock_time_task_runner.cc', + 'test/test_mock_time_task_runner.h', 'test/test_pending_task.cc', 'test/test_pending_task.h', 'test/test_reg_util_win.cc', @@ -982,6 +996,8 @@ 'test/trace_event_analyzer.h', 'test/trace_to_file.cc', 'test/trace_to_file.h', + 'test/user_action_tester.cc', + 'test/user_action_tester.h', 'test/values_test_util.cc', 'test/values_test_util.h', ], @@ -1045,6 +1061,7 @@ ['OS!="ios"', { 'targets': [ { + # GN: //base:check_example 'target_name': 'check_example', 'type': 'executable', 'sources': [ @@ -1083,6 +1100,7 @@ 'allocator/allocator.gyp:allocator_extension_thunks_win64', '../third_party/modp_b64/modp_b64.gyp:modp_b64_win64', 'third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations_win64', + 'trace_event/etw_manifest/etw_manifest.gyp:etw_manifest', ], # TODO(gregoryd): direct_dependent_settings should be shared with the # 32-bit target, but it doesn't work due to a bug in gyp @@ -1146,15 +1164,10 @@ 4267, ], 'sources': [ - 'third_party/xdg_user_dirs/xdg_user_dir_lookup.cc', - 'third_party/xdg_user_dirs/xdg_user_dir_lookup.h', 'async_socket_io_handler.h', 'async_socket_io_handler_posix.cc', 'async_socket_io_handler_win.cc', 'auto_reset.h', - 'event_recorder.h', - 'event_recorder_stubs.cc', - 'event_recorder_win.cc', 'linux_util.cc', 'linux_util.h', 'md5.cc', @@ -1166,8 +1179,10 @@ 'posix/file_descriptor_shuffle.cc', 'posix/file_descriptor_shuffle.h', 'sync_socket.h', - 'sync_socket_win.cc', 'sync_socket_posix.cc', + 'sync_socket_win.cc', + 'third_party/xdg_user_dirs/xdg_user_dir_lookup.cc', + 'third_party/xdg_user_dirs/xdg_user_dir_lookup.h', ], }, { @@ -1306,6 +1321,20 @@ }, ], }], + ['OS == "linux"', { + 'targets': [ + { + 'target_name': 'malloc_wrapper', + 'type': 'shared_library', + 'dependencies': [ + 'base', + ], + 'sources': [ + 'test/malloc_wrapper.cc', + ], + } + ], + }], ['OS == "android"', { 'targets': [ { @@ -1314,6 +1343,7 @@ 'type': 'none', 'sources': [ 'android/java/src/org/chromium/base/ApplicationStatus.java', + 'android/java/src/org/chromium/base/AnimationFrameTimeHistogram.java', 'android/java/src/org/chromium/base/BuildInfo.java', 'android/java/src/org/chromium/base/CommandLine.java', 'android/java/src/org/chromium/base/ContentUriUtils.java', @@ -1322,24 +1352,39 @@ 'android/java/src/org/chromium/base/FieldTrialList.java', 'android/java/src/org/chromium/base/ImportantFileWriterAndroid.java', 'android/java/src/org/chromium/base/JNIUtils.java', - 'android/java/src/org/chromium/base/library_loader/LibraryLoader.java', + 'android/java/src/org/chromium/base/JavaHandlerThread.java', 'android/java/src/org/chromium/base/LocaleUtils.java', 'android/java/src/org/chromium/base/MemoryPressureListener.java', - 'android/java/src/org/chromium/base/JavaHandlerThread.java', 'android/java/src/org/chromium/base/PathService.java', 'android/java/src/org/chromium/base/PathUtils.java', 'android/java/src/org/chromium/base/PowerMonitor.java', - 'android/java/src/org/chromium/base/SystemMessageHandler.java', 'android/java/src/org/chromium/base/SysUtils.java', + 'android/java/src/org/chromium/base/SystemMessageHandler.java', 'android/java/src/org/chromium/base/ThreadUtils.java', 'android/java/src/org/chromium/base/TraceEvent.java', + 'android/java/src/org/chromium/base/library_loader/LibraryLoader.java', + 'android/java/src/org/chromium/base/metrics/RecordHistogram.java', + 'android/java/src/org/chromium/base/metrics/RecordUserAction.java', ], 'variables': { 'jni_gen_package': 'base', }, + 'dependencies': [ + 'android_runtime_jni_headers', + ], 'includes': [ '../build/jni_generator.gypi' ], }, { + # GN: //base:android_runtime_jni_headers + 'target_name': 'android_runtime_jni_headers', + 'type': 'none', + 'variables': { + 'jni_gen_package': 'base', + 'input_java_class': 'java/lang/Runtime.class', + }, + 'includes': [ '../build/jar_file_jni_generator.gypi' ], + }, + { # TODO(GN) 'target_name': 'base_unittests_jni_headers', 'type': 'none', @@ -1365,6 +1410,15 @@ 'includes': [ '../build/android/java_cpp_template.gypi' ], }, { + # GN: //base:base_android_java_enums_srcjar + 'target_name': 'base_java_library_process_type', + 'type': 'none', + 'variables': { + 'source_file': 'android/library_loader/library_loader_hooks.h', + }, + 'includes': [ '../build/android/java_cpp_enum.gypi' ], + }, + { # GN: //base:base_java 'target_name': 'base_java', 'type': 'none', @@ -1375,17 +1429,12 @@ 'dependencies': [ 'base_java_application_state', 'base_java_library_load_from_apk_status_codes', + 'base_java_library_process_type', 'base_java_memory_pressure_level', 'base_native_libraries_gen', + '../third_party/jsr-305/jsr-305.gyp:jsr_305_javalib', ], 'includes': [ '../build/java.gypi' ], - 'conditions': [ - ['android_webview_build==0', { - 'dependencies': [ - '../third_party/jsr-305/jsr-305.gyp:jsr_305_javalib', - ], - }] - ], }, { # GN: //base:base_java_unittest_support @@ -1432,6 +1481,7 @@ 'type': 'none', 'dependencies': [ 'base_java', + '../testing/android/on_device_instrumentation.gyp:reporter_java', ], 'variables': { 'java_in_dir': '../base/test/android/javatests', @@ -1439,6 +1489,23 @@ 'includes': [ '../build/java.gypi' ], }, { + # GN: //base:base_junit_tests + 'target_name': 'base_junit_tests', + 'type': 'none', + 'dependencies': [ + 'base_java', + 'base_java_test_support', + '../testing/android/junit/junit_test.gyp:junit_test_support', + ], + 'variables': { + 'main_class': 'org.chromium.testing.local.JunitTestMain', + 'src_paths': [ + '../base/android/junit/', + ], + }, + 'includes': [ '../build/host_jar.gypi' ], + }, + { # GN: //base:base_javatests 'target_name': 'base_javatests', 'type': 'none', @@ -1455,25 +1522,18 @@ # GN: //base/android/linker:chromium_android_linker 'target_name': 'chromium_android_linker', 'type': 'shared_library', - 'conditions': [ - # Avoid breaking the webview build because it - # does not have <(android_ndk_root)/crazy_linker.gyp. - # Note that webview never uses the linker anyway. - ['android_webview_build == 0', { - 'sources': [ - 'android/linker/linker_jni.cc', - ], - # The crazy linker is never instrumented. - 'cflags!': [ - '-finstrument-functions', - ], - 'dependencies': [ - # The NDK contains the crazy_linker here: - # '<(android_ndk_root)/crazy_linker.gyp:crazy_linker' - # However, we use our own fork. See bug 384700. - '../third_party/android_crazy_linker/crazy_linker.gyp:crazy_linker', - ], - }], + 'sources': [ + 'android/linker/linker_jni.cc', + ], + # The crazy linker is never instrumented. + 'cflags!': [ + '-finstrument-functions', + ], + 'dependencies': [ + # The NDK contains the crazy_linker here: + # '<(android_ndk_root)/crazy_linker.gyp:crazy_linker' + # However, we use our own fork. See bug 384700. + '../third_party/android_crazy_linker/crazy_linker.gyp:crazy_linker', ], }, { @@ -1517,6 +1577,28 @@ }, }, }, + { + # Target to manually rebuild pe_image_test.dll which is checked into + # base/test/data/pe_image. + 'target_name': 'pe_image_test', + 'type': 'shared_library', + 'sources': [ + 'win/pe_image_test.cc', + ], + 'msvs_settings': { + 'VCLinkerTool': { + 'SubSystem': '2', # Set /SUBSYSTEM:WINDOWS + 'DelayLoadDLLs': [ + 'cfgmgr32.dll', + 'shell32.dll', + ], + 'AdditionalDependencies': [ + 'cfgmgr32.lib', + 'shell32.lib', + ], + }, + }, + }, ], }], ['test_isolation_mode != "noop"', { diff --git a/chromium/base/base.gypi b/chromium/base/base.gypi index 416a7e5aa6e..a39952d5c76 100644 --- a/chromium/base/base.gypi +++ b/chromium/base/base.gypi @@ -3,6 +3,9 @@ # found in the LICENSE file. { + 'includes': [ + 'trace_event/trace_event.gypi', + ], 'target_defaults': { 'variables': { 'base_target': 0, @@ -13,21 +16,16 @@ ['base_target==1', { 'sources': [ '../build/build_config.h', - 'third_party/dmg_fp/dmg_fp.h', - 'third_party/dmg_fp/g_fmt.cc', - 'third_party/dmg_fp/dtoa_wrapper.cc', - 'third_party/icu/icu_utf.cc', - 'third_party/icu/icu_utf.h', - 'third_party/nspr/prtime.cc', - 'third_party/nspr/prtime.h', - 'third_party/superfasthash/superfasthash.c', - 'third_party/xdg_mime/xdgmime.h', 'allocator/allocator_extension.cc', 'allocator/allocator_extension.h', 'allocator/type_profiler_control.cc', 'allocator/type_profiler_control.h', + 'android/animation_frame_time_histogram.cc', + 'android/animation_frame_time_histogram.h', 'android/application_status_listener.cc', 'android/application_status_listener.h', + 'android/base_jni_onload.cc', + 'android/base_jni_onload.h', 'android/base_jni_registrar.cc', 'android/base_jni_registrar.h', 'android/build_info.cc', @@ -45,10 +43,10 @@ 'android/fifo_utils.h', 'android/important_file_writer_android.cc', 'android/important_file_writer_android.h', - 'android/locale_utils.h', - 'android/locale_utils.cc', - 'android/scoped_java_ref.cc', - 'android/scoped_java_ref.h', + 'android/java_handler_thread.cc', + 'android/java_handler_thread.h', + 'android/java_runtime.cc', + 'android/java_runtime.h', 'android/jni_android.cc', 'android/jni_android.h', 'android/jni_array.cc', @@ -61,17 +59,25 @@ 'android/jni_utils.h', 'android/jni_weak_ref.cc', 'android/jni_weak_ref.h', + 'android/library_loader/library_load_from_apk_status_codes.h', 'android/library_loader/library_loader_hooks.cc', 'android/library_loader/library_loader_hooks.h', - 'android/library_loader/library_load_from_apk_status_codes.h', + 'android/library_loader/library_prefetcher.cc', + 'android/library_loader/library_prefetcher.h', + 'android/locale_utils.cc', + 'android/locale_utils.h', 'android/memory_pressure_listener_android.cc', 'android/memory_pressure_listener_android.h', - 'android/java_handler_thread.cc', - 'android/java_handler_thread.h', 'android/path_service_android.cc', 'android/path_service_android.h', 'android/path_utils.cc', 'android/path_utils.h', + 'android/record_histogram.cc', + 'android/record_histogram.h', + 'android/record_user_action.cc', + 'android/record_user_action.h', + 'android/scoped_java_ref.cc', + 'android/scoped_java_ref.h', 'android/sys_utils.cc', 'android/sys_utils.h', 'android/thread_utils.h', @@ -122,6 +128,8 @@ 'callback_internal.h', 'callback_list.h', 'cancelable_callback.h', + 'chromeos/memory_pressure_monitor.cc', + 'chromeos/memory_pressure_monitor.h', 'command_line.cc', 'command_line.h', 'compiler_specific.h', @@ -166,19 +174,6 @@ 'debug/stack_trace_win.cc', 'debug/task_annotator.cc', 'debug/task_annotator.h', - 'debug/trace_event.h', - 'debug/trace_event_android.cc', - 'debug/trace_event_argument.cc', - 'debug/trace_event_argument.h', - 'debug/trace_event_impl.cc', - 'debug/trace_event_impl.h', - 'debug/trace_event_impl_constants.cc', - 'debug/trace_event_synthetic_delay.cc', - 'debug/trace_event_synthetic_delay.h', - 'debug/trace_event_system_stats_monitor.cc', - 'debug/trace_event_memory.cc', - 'debug/trace_event_memory.h', - 'debug/trace_event_win.cc', 'deferred_sequenced_task_runner.cc', 'deferred_sequenced_task_runner.h', 'environment.cc', @@ -214,6 +209,8 @@ 'files/file_posix.cc', 'files/file_proxy.cc', 'files/file_proxy.h', + 'files/file_tracing.cc', + 'files/file_tracing.h', 'files/file_util.cc', 'files/file_util.h', 'files/file_util_android.cc', @@ -224,8 +221,8 @@ 'files/file_util_proxy.h', 'files/file_util_win.cc', 'files/file_win.cc', - 'files/important_file_writer.h', 'files/important_file_writer.cc', + 'files/important_file_writer.h', 'files/memory_mapped_file.cc', 'files/memory_mapped_file.h', 'files/memory_mapped_file_posix.cc', @@ -234,7 +231,6 @@ 'files/scoped_file.h', 'files/scoped_temp_dir.cc', 'files/scoped_temp_dir.h', - 'float_util.h', 'format_macros.h', 'gtest_prod_util.h', 'guid.cc', @@ -244,12 +240,17 @@ 'hash.cc', 'hash.h', 'id_map.h', + 'ios/block_types.h', + 'ios/crb_protocol_observers.h', + 'ios/crb_protocol_observers.mm', 'ios/device_util.h', 'ios/device_util.mm', 'ios/ios_util.h', 'ios/ios_util.mm', 'ios/scoped_critical_action.h', 'ios/scoped_critical_action.mm', + 'ios/weak_nsobject.h', + 'ios/weak_nsobject.mm', 'json/json_file_value_serializer.cc', 'json/json_file_value_serializer.h', 'json/json_parser.cc', @@ -258,6 +259,7 @@ 'json/json_reader.h', 'json/json_string_value_serializer.cc', 'json/json_string_value_serializer.h', + 'json/json_value_converter.cc', 'json/json_value_converter.h', 'json/json_writer.cc', 'json/json_writer.h', @@ -278,6 +280,8 @@ 'mac/bundle_locations.mm', 'mac/close_nocancel.cc', 'mac/cocoa_protocols.h', + 'mac/dispatch_source_mach.cc', + 'mac/dispatch_source_mach.h', 'mac/foundation_util.h', 'mac/foundation_util.mm', 'mac/launch_services_util.cc', @@ -286,12 +290,14 @@ 'mac/launchd.h', 'mac/libdispatch_task_runner.cc', 'mac/libdispatch_task_runner.h', - 'mac/mac_logging.h', 'mac/mac_logging.cc', + 'mac/mac_logging.h', 'mac/mac_util.h', 'mac/mac_util.mm', 'mac/mach_logging.cc', 'mac/mach_logging.h', + 'mac/memory_pressure_monitor.cc', + 'mac/memory_pressure_monitor.h', 'mac/objc_property_releaser.h', 'mac/objc_property_releaser.mm', 'mac/os_crash_dumps.cc', @@ -326,31 +332,22 @@ 'memory/aligned_memory.h', 'memory/discardable_memory.cc', 'memory/discardable_memory.h', - 'memory/discardable_memory_android.cc', - 'memory/discardable_memory_emulated.cc', - 'memory/discardable_memory_emulated.h', - 'memory/discardable_memory_linux.cc', - 'memory/discardable_memory_mac.cc', - 'memory/discardable_memory_manager.cc', - 'memory/discardable_memory_manager.h', - 'memory/discardable_memory_shmem.cc', - 'memory/discardable_memory_shmem.h', - 'memory/discardable_memory_shmem_allocator.cc', - 'memory/discardable_memory_shmem_allocator.h', - 'memory/discardable_memory_win.cc', + 'memory/discardable_memory_allocator.cc', + 'memory/discardable_memory_allocator.h', 'memory/discardable_shared_memory.cc', 'memory/discardable_shared_memory.h', 'memory/linked_ptr.h', 'memory/manual_constructor.h', 'memory/memory_pressure_listener.cc', 'memory/memory_pressure_listener.h', + 'memory/memory_pressure_monitor.cc', + 'memory/memory_pressure_monitor.h', 'memory/raw_scoped_refptr_mismatch_checker.h', 'memory/ref_counted.cc', 'memory/ref_counted.h', 'memory/ref_counted_delete_on_message_loop.h', 'memory/ref_counted_memory.cc', 'memory/ref_counted_memory.h', - 'memory/scoped_open_process.h', 'memory/scoped_policy.h', 'memory/scoped_ptr.h', 'memory/scoped_vector.h', @@ -380,10 +377,6 @@ 'message_loop/message_pump_win.cc', 'message_loop/message_pump_win.h', 'message_loop/timer_slack.h', - 'metrics/sample_map.cc', - 'metrics/sample_map.h', - 'metrics/sample_vector.cc', - 'metrics/sample_vector.h', 'metrics/bucket_ranges.cc', 'metrics/bucket_ranges.h', 'metrics/histogram.cc', @@ -393,23 +386,25 @@ 'metrics/histogram_delta_serialization.cc', 'metrics/histogram_delta_serialization.h', 'metrics/histogram_flattener.h', + 'metrics/histogram_macros.h', 'metrics/histogram_samples.cc', 'metrics/histogram_samples.h', 'metrics/histogram_snapshot_manager.cc', 'metrics/histogram_snapshot_manager.h', + 'metrics/sample_map.cc', + 'metrics/sample_map.h', + 'metrics/sample_vector.cc', + 'metrics/sample_vector.h', 'metrics/sparse_histogram.cc', 'metrics/sparse_histogram.h', 'metrics/statistics_recorder.cc', 'metrics/statistics_recorder.h', - 'metrics/stats_counters.cc', - 'metrics/stats_counters.h', - 'metrics/stats_table.cc', - 'metrics/stats_table.h', 'metrics/user_metrics.cc', 'metrics/user_metrics.h', 'metrics/user_metrics_action.h', 'move.h', 'native_library.h', + 'native_library_ios.mm', 'native_library_mac.mm', 'native_library_posix.cc', 'native_library_win.cc', @@ -417,6 +412,10 @@ 'nix/mime_util_xdg.h', 'nix/xdg_util.cc', 'nix/xdg_util.h', + 'numerics/safe_conversions.h', + 'numerics/safe_conversions_impl.h', + 'numerics/safe_math.h', + 'numerics/safe_math_impl.h', 'observer_list.h', 'observer_list_threadsafe.h', 'os_compat_android.cc', @@ -437,11 +436,11 @@ 'posix/unix_domain_socket_linux.h', 'power_monitor/power_monitor.cc', 'power_monitor/power_monitor.h', + 'power_monitor/power_monitor_device_source.cc', + 'power_monitor/power_monitor_device_source.h', 'power_monitor/power_monitor_device_source_android.cc', 'power_monitor/power_monitor_device_source_android.h', 'power_monitor/power_monitor_device_source_chromeos.cc', - 'power_monitor/power_monitor_device_source.cc', - 'power_monitor/power_monitor_device_source.h', 'power_monitor/power_monitor_device_source_ios.mm', 'power_monitor/power_monitor_device_source_mac.mm', 'power_monitor/power_monitor_device_source_posix.cc', @@ -462,8 +461,8 @@ 'process/launch_mac.cc', 'process/launch_posix.cc', 'process/launch_win.cc', - 'process/memory.h', 'process/memory.cc', + 'process/memory.h', 'process/memory_linux.cc', 'process/memory_mac.mm', 'process/memory_win.cc', @@ -486,8 +485,9 @@ 'process/process_iterator_openbsd.cc', 'process/process_iterator_win.cc', 'process/process_linux.cc', - 'process/process_metrics.h', + 'process/process_mac.cc', 'process/process_metrics.cc', + 'process/process_metrics.h', 'process/process_metrics_freebsd.cc', 'process/process_metrics_ios.cc', 'process/process_metrics_linux.cc', @@ -499,10 +499,16 @@ 'process/process_win.cc', 'profiler/alternate_timer.cc', 'profiler/alternate_timer.h', + 'profiler/native_stack_sampler.cc', + 'profiler/native_stack_sampler.h', 'profiler/scoped_profile.cc', 'profiler/scoped_profile.h', 'profiler/scoped_tracker.cc', 'profiler/scoped_tracker.h', + 'profiler/stack_sampling_profiler.cc', + 'profiler/stack_sampling_profiler.h', + 'profiler/stack_sampling_profiler_posix.cc', + 'profiler/stack_sampling_profiler_win.cc', 'profiler/tracked_time.cc', 'profiler/tracked_time.h', 'rand_util.cc', @@ -512,10 +518,6 @@ 'rand_util_win.cc', 'run_loop.cc', 'run_loop.h', - 'numerics/safe_conversions.h', - 'numerics/safe_conversions_impl.h', - 'numerics/safe_math.h', - 'numerics/safe_math_impl.h', 'safe_strerror_posix.cc', 'safe_strerror_posix.h', 'scoped_generic.h', @@ -542,11 +544,11 @@ 'strings/string16.cc', 'strings/string16.h', 'strings/string_number_conversions.cc', - 'strings/string_split.cc', - 'strings/string_split.h', 'strings/string_number_conversions.h', 'strings/string_piece.cc', 'strings/string_piece.h', + 'strings/string_split.cc', + 'strings/string_split.h', 'strings/string_tokenizer.h', 'strings/string_util.cc', 'strings/string_util.h', @@ -585,8 +587,6 @@ 'synchronization/waitable_event_watcher_posix.cc', 'synchronization/waitable_event_watcher_win.cc', 'synchronization/waitable_event_win.cc', - 'system_monitor/system_monitor.cc', - 'system_monitor/system_monitor.h', 'sys_byteorder.h', 'sys_info.cc', 'sys_info.h', @@ -600,12 +600,23 @@ 'sys_info_openbsd.cc', 'sys_info_posix.cc', 'sys_info_win.cc', + 'system_monitor/system_monitor.cc', + 'system_monitor/system_monitor.h', 'task/cancelable_task_tracker.cc', 'task/cancelable_task_tracker.h', 'task_runner.cc', 'task_runner.h', 'task_runner_util.h', 'template_util.h', + 'third_party/dmg_fp/dmg_fp.h', + 'third_party/dmg_fp/dtoa_wrapper.cc', + 'third_party/dmg_fp/g_fmt.cc', + 'third_party/icu/icu_utf.cc', + 'third_party/icu/icu_utf.h', + 'third_party/nspr/prtime.cc', + 'third_party/nspr/prtime.h', + 'third_party/superfasthash/superfasthash.c', + 'third_party/xdg_mime/xdgmime.h', 'thread_task_runner_handle.cc', 'thread_task_runner_handle.h', 'threading/non_thread_safe.h', @@ -613,6 +624,8 @@ 'threading/non_thread_safe_impl.h', 'threading/platform_thread.h', 'threading/platform_thread_android.cc', + 'threading/platform_thread_internal_posix.cc', + 'threading/platform_thread_internal_posix.h', 'threading/platform_thread_linux.cc', 'threading/platform_thread_mac.mm', 'threading/platform_thread_posix.cc', @@ -640,12 +653,12 @@ 'threading/thread_local_storage_posix.cc', 'threading/thread_local_storage_win.cc', 'threading/thread_local_win.cc', - 'threading/thread_restrictions.h', 'threading/thread_restrictions.cc', + 'threading/thread_restrictions.h', 'threading/watchdog.cc', 'threading/watchdog.h', - 'threading/worker_pool.h', 'threading/worker_pool.cc', + 'threading/worker_pool.h', 'threading/worker_pool_posix.cc', 'threading/worker_pool_posix.h', 'threading/worker_pool_win.cc', @@ -676,10 +689,10 @@ 'tracking_info.cc', 'tracking_info.h', 'tuple.h', - 'values.cc', - 'values.h', 'value_conversions.cc', 'value_conversions.h', + 'values.cc', + 'values.h', 'version.cc', 'version.h', 'vlog.cc', @@ -697,6 +710,8 @@ 'win/iat_patch_function.h', 'win/iunknown_impl.cc', 'win/iunknown_impl.h', + 'win/memory_pressure_monitor.cc', + 'win/memory_pressure_monitor.h', 'win/message_window.cc', 'win/message_window.h', 'win/metro.cc', @@ -733,6 +748,7 @@ 'win/windows_version.h', 'win/wrapped_window_proc.cc', 'win/wrapped_window_proc.h', + '<@(trace_event_sources)', ], 'defines': [ 'BASE_IMPLEMENTATION', @@ -770,6 +786,7 @@ 'allocator/type_profiler_control.h', 'base_paths.cc', 'cpu.cc', + 'debug/stack_trace.cc', 'debug/stack_trace_posix.cc', 'files/file_enumerator_posix.cc', 'files/file_path_watcher_fsevents.cc', @@ -780,17 +797,22 @@ 'files/file_util.cc', 'files/file_util_posix.cc', 'files/file_util_proxy.cc', + 'files/important_file_writer.cc', + 'files/scoped_temp_dir.cc', 'memory/shared_memory_posix.cc', 'native_library_posix.cc', 'path_service.cc', 'posix/unix_domain_socket_linux.cc', + 'process/kill.cc', 'process/kill_posix.cc', + 'process/launch.cc', 'process/launch_posix.cc', + 'process/process_metrics.cc', 'process/process_metrics_posix.cc', 'process/process_posix.cc', 'rand_util_posix.cc', 'scoped_native_library.cc', - 'files/scoped_temp_dir.cc', + 'sys_info.cc', 'sys_info_posix.cc', 'third_party/dynamic_annotations/dynamic_annotations.c', ], @@ -798,14 +820,6 @@ ['include', '^threading/platform_thread_linux\\.cc$'], ], }], - ['OS == "android" and _toolset == "target" and >(nacl_untrusted_build)==0', { - 'sources': [ - 'memory/discardable_memory_ashmem_allocator.cc', - 'memory/discardable_memory_ashmem_allocator.h', - 'memory/discardable_memory_ashmem.cc', - 'memory/discardable_memory_ashmem.h', - ], - }], ['OS == "android" and >(nacl_untrusted_build)==0', { 'sources!': [ 'base_paths_posix.cc', @@ -841,13 +855,6 @@ ['include', '^threading/platform_thread_linux\\.cc$'], ], }], - ['OS == "android" and <(android_webview_build)==1', { - 'defines': [ - # WebView builds as part of the system which already has sincos; - # avoid defining it again as it causes a linker warning. - 'ANDROID_SINCOS_PROVIDED', - ], - }], ['<(chromeos) == 1', { 'sources!': [ 'power_monitor/power_monitor_device_source_posix.cc', @@ -870,7 +877,7 @@ ['include', '^mac/scoped_mach_vm\\.'], ['include', '^mac/scoped_nsautorelease_pool\\.'], ['include', '^mac/scoped_nsobject\\.'], - ['include', '^memory/discardable_memory_mac\\.'], + ['include', '^mac/scoped_objc_class_swizzler\\.'], ['include', '^message_loop/message_pump_mac\\.'], ['include', '^strings/sys_string_conversions_mac\\.'], ['include', '^threading/platform_thread_mac\\.'], @@ -882,6 +889,8 @@ ['include', '^process/.*_ios\.(cc|mm)$'], ['include', '^process/memory_stubs\.cc$'], ['include', '^process/process_handle_posix\.cc$'], + ['include', '^process/process_metrics\\.cc$'], + ['exclude', '^threading/platform_thread_internal_posix\\.(h|cc)'], ['exclude', 'files/file_path_watcher_fsevents.cc'], ['exclude', 'files/file_path_watcher_fsevents.h'], ['include', 'files/file_path_watcher_mac.cc'], @@ -920,7 +929,6 @@ '<(DEPTH)/third_party/wtl/include', ], 'sources!': [ - 'event_recorder_stubs.cc', 'files/file_path_watcher_fsevents.cc', 'files/file_path_watcher_fsevents.h', 'files/file_path_watcher_kqueue.cc', @@ -949,15 +957,12 @@ ], }], ['(OS == "mac" or OS == "ios") and >(nacl_untrusted_build)==0', { - 'sources': [ - 'memory/discardable_memory_mach.cc', - 'memory/discardable_memory_mach.h', - ], 'sources/': [ - ['exclude', '^files/file_path_watcher_stub\\.cc$'], ['exclude', '^base_paths_posix\\.cc$'], + ['exclude', '^files/file_path_watcher_stub\\.cc$'], ['exclude', '^native_library_posix\\.cc$'], ['exclude', '^strings/sys_string_conversions_posix\\.cc$'], + ['exclude', '^threading/platform_thread_internal_posix\\.cc$'], ], }], ['<(os_bsd)==1 and >(nacl_untrusted_build)==0', { @@ -977,6 +982,11 @@ ['OS == "win" and >(nacl_untrusted_build)==1', { 'sources/': [ ['exclude', '\\.h$'] ], }], + # Enable more direct string conversions on platforms with native utf8 + # strings + ['OS=="mac" or OS=="ios" or <(chromeos)==1 or <(chromecast)==1', { + 'defines': ['SYSTEM_NATIVE_UTF8'], + }], ], }], ['base_i18n_target==1', { diff --git a/chromium/base/base.isolate b/chromium/base/base.isolate index 0416935e373..c7ba651ac71 100644 --- a/chromium/base/base.isolate +++ b/chromium/base/base.isolate @@ -7,9 +7,11 @@ # itself, virtually all targets using it has to include icu. The only # exception is the Windows sandbox (?). '../third_party/icu/icu.isolate', + # Sanitizer-instrumented third-party libraries (if enabled). + '../third_party/instrumented_libraries/instrumented_libraries.isolate', ], 'conditions': [ - ['OS=="linux" and asan==1 and chromeos==0', { + ['use_custom_libcxx==1', { 'variables': { 'files': [ '<(PRODUCT_DIR)/lib/libc++.so', @@ -23,35 +25,42 @@ ], }, }], - ['OS=="linux" and asan==1', { + ['OS=="win"', { + # Required for base/stack_trace_win.cc to symbolize correctly. 'variables': { 'files': [ - '../third_party/llvm-build/Release+Asserts/lib/libstdc++.so.6', + '<(PRODUCT_DIR)/dbghelp.dll', ], }, }], - ['asan==1', { + ['OS=="win" and asan==1 and component=="shared_library"', { 'variables': { 'files': [ - '../tools/valgrind/asan/', - '../third_party/llvm-build/Release+Asserts/bin/llvm-symbolizer', + '../third_party/llvm-build/Release+Asserts/lib/clang/3.7.0/lib/windows/clang_rt.asan_dynamic-i386.dll', + ], + }, + }], + ['OS=="linux" and (asan==1 or lsan==1 or msan==1 or tsan==1)', { + 'variables': { + 'files': [ + # For llvm-symbolizer. + '../third_party/llvm-build/Release+Asserts/lib/libstdc++.so.6', ], }, }], - ['lsan==1', { + ['asan==1 or lsan==1 or msan==1 or tsan==1', { 'variables': { 'files': [ - '../tools/lsan/suppressions.txt', + '../tools/valgrind/asan/', + '../third_party/llvm-build/Release+Asserts/bin/llvm-symbolizer<(EXECUTABLE_SUFFIX)', ], }, }], + # Copy the VS runtime DLLs into the isolate so that they + # don't have to be preinstalled on the target machine. ['OS=="win" and component=="shared_library" and CONFIGURATION_NAME=="Debug"', { 'variables': { 'files': [ - # Copy the VS runtime DLLs into the isolate so that they - # don't have to be preinstalled on the target machine. - '<(PRODUCT_DIR)/msvcp120d.dll', - '<(PRODUCT_DIR)/msvcr120d.dll', '<(PRODUCT_DIR)/x64/msvcp120d.dll', '<(PRODUCT_DIR)/x64/msvcr120d.dll', ], @@ -60,12 +69,30 @@ ['OS=="win" and component=="shared_library" and CONFIGURATION_NAME=="Release"', { 'variables': { 'files': [ - # Copy the VS runtime DLLs into the isolate so that they - # don't have to be preinstalled on the target machine. + '<(PRODUCT_DIR)/x64/msvcp120.dll', + '<(PRODUCT_DIR)/x64/msvcr120.dll', + ], + }, + }], + ['OS=="win" and component=="shared_library" and (CONFIGURATION_NAME=="Debug" or CONFIGURATION_NAME=="Debug_x64")', { + 'variables': { + 'files': [ + '<(PRODUCT_DIR)/msvcp120d.dll', + '<(PRODUCT_DIR)/msvcr120d.dll', + ], + }, + }], + ['OS=="win" and component=="shared_library" and (CONFIGURATION_NAME=="Release" or CONFIGURATION_NAME=="Release_x64")', { + 'variables': { + 'files': [ '<(PRODUCT_DIR)/msvcp120.dll', '<(PRODUCT_DIR)/msvcr120.dll', ], }, }], + # Workaround for https://code.google.com/p/swarming/issues/detail?id=211 + ['asan==0 or lsan==0 or msan==0 or tsan==0', { + 'variables': {}, + }], ], } diff --git a/chromium/base/base64.h b/chromium/base/base64.h index 43d8f76eb76..dd72c39a239 100644 --- a/chromium/base/base64.h +++ b/chromium/base/base64.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_BASE64_H__ -#define BASE_BASE64_H__ +#ifndef BASE_BASE64_H_ +#define BASE_BASE64_H_ #include <string> @@ -12,13 +12,14 @@ namespace base { -// Encodes the input string in base64. +// Encodes the input string in base64. The encoding can be done in-place. BASE_EXPORT void Base64Encode(const StringPiece& input, std::string* output); // Decodes the base64 input string. Returns true if successful and false -// otherwise. The output string is only modified if successful. +// otherwise. The output string is only modified if successful. The decoding can +// be done in-place. BASE_EXPORT bool Base64Decode(const StringPiece& input, std::string* output); } // namespace base -#endif // BASE_BASE64_H__ +#endif // BASE_BASE64_H_ diff --git a/chromium/base/base64_unittest.cc b/chromium/base/base64_unittest.cc index 9b23194ccf6..91651f4d39b 100644 --- a/chromium/base/base64_unittest.cc +++ b/chromium/base/base64_unittest.cc @@ -24,4 +24,17 @@ TEST(Base64Test, Basic) { EXPECT_EQ(kText, decoded); } +TEST(Base64Test, InPlace) { + const std::string kText = "hello world"; + const std::string kBase64Text = "aGVsbG8gd29ybGQ="; + std::string text(kText); + + Base64Encode(text, &text); + EXPECT_EQ(kBase64Text, text); + + bool ok = Base64Decode(text, &text); + EXPECT_TRUE(ok); + EXPECT_EQ(text, kText); +} + } // namespace base diff --git a/chromium/base/base_nacl.gyp b/chromium/base/base_nacl.gyp index 63e1ed422bd..40005d2cafb 100644 --- a/chromium/base/base_nacl.gyp +++ b/chromium/base/base_nacl.gyp @@ -7,8 +7,12 @@ 'chromium_code': 1, }, 'includes': [ - '../build/common_untrusted.gypi', + # base.gypi must be included before common_untrusted.gypi. + # + # TODO(sergeyu): Replace the target_defaults magic in base.gypi with a + # sources variables lists. That way order of includes will not matter. 'base.gypi', + '../build/common_untrusted.gypi', ], 'conditions': [ ['disable_nacl==0 and disable_nacl_untrusted==0', { @@ -35,9 +39,6 @@ '-fno-strict-aliasing', ], }, - 'dependencies': [ - '../native_client/tools.gyp:prep_toolchain', - ], }, { 'target_name': 'base_i18n_nacl', @@ -59,7 +60,6 @@ ], }, 'dependencies': [ - '../native_client/tools.gyp:prep_toolchain', '../third_party/icu/icu_nacl.gyp:icudata_nacl', '../third_party/icu/icu_nacl.gyp:icui18n_nacl', '../third_party/icu/icu_nacl.gyp:icuuc_nacl', @@ -109,7 +109,6 @@ 'rand_util_nacl.cc', ], 'dependencies': [ - '../native_client/tools.gyp:prep_toolchain', '../third_party/libevent/libevent_nacl_nonsfi.gyp:event_nacl_nonsfi', ], }, diff --git a/chromium/base/base_paths_win.cc b/chromium/base/base_paths_win.cc index 5bef3106aaf..4ecb59d4dec 100644 --- a/chromium/base/base_paths_win.cc +++ b/chromium/base/base_paths_win.cc @@ -6,8 +6,10 @@ #include <shlobj.h> #include "base/base_paths.h" +#include "base/environment.h" #include "base/files/file_path.h" #include "base/path_service.h" +#include "base/strings/utf_string_conversions.h" #include "base/win/scoped_co_mem.h" #include "base/win/windows_version.h" @@ -65,6 +67,27 @@ bool PathProviderWin(int key, FilePath* result) { return false; cur = FilePath(system_buffer); break; + case base::DIR_PROGRAM_FILES6432: +#if !defined(_WIN64) + if (base::win::OSInfo::GetInstance()->wow64_status() == + base::win::OSInfo::WOW64_ENABLED) { + scoped_ptr<base::Environment> env(base::Environment::Create()); + std::string programfiles_w6432; + // 32-bit process running in WOW64 sets ProgramW6432 environment + // variable. See + // https://msdn.microsoft.com/library/windows/desktop/aa384274.aspx. + if (!env->GetVar("ProgramW6432", &programfiles_w6432)) + return false; + // GetVar returns UTF8 - convert back to Wide. + cur = FilePath(UTF8ToWide(programfiles_w6432)); + break; + } +#endif + if (FAILED(SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES, NULL, + SHGFP_TYPE_CURRENT, system_buffer))) + return false; + cur = FilePath(system_buffer); + break; case base::DIR_IE_INTERNET_CACHE: if (FAILED(SHGetFolderPath(NULL, CSIDL_INTERNET_CACHE, NULL, SHGFP_TYPE_CURRENT, system_buffer))) diff --git a/chromium/base/base_paths_win.h b/chromium/base/base_paths_win.h index 032de348c5c..9ac9e45e0a7 100644 --- a/chromium/base/base_paths_win.h +++ b/chromium/base/base_paths_win.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_BASE_PATHS_WIN_H__ -#define BASE_BASE_PATHS_WIN_H__ +#ifndef BASE_BASE_PATHS_WIN_H_ +#define BASE_BASE_PATHS_WIN_H_ // This file declares windows-specific path keys for the base module. // These can be used with the PathService to access various special @@ -16,8 +16,14 @@ enum { DIR_WINDOWS, // Windows directory, usually "c:\windows" DIR_SYSTEM, // Usually c:\windows\system32" - DIR_PROGRAM_FILES, // Usually c:\program files - DIR_PROGRAM_FILESX86, // Usually c:\program files or c:\program files (x86) + // 32-bit 32-bit on 64-bit 64-bit on 64-bit + // DIR_PROGRAM_FILES 1 2 1 + // DIR_PROGRAM_FILESX86 1 2 2 + // DIR_PROGRAM_FILES6432 1 1 1 + // 1 - C:\Program Files 2 - C:\Program Files (x86) + DIR_PROGRAM_FILES, // See table above. + DIR_PROGRAM_FILESX86, // See table above. + DIR_PROGRAM_FILES6432, // See table above. DIR_IE_INTERNET_CACHE, // Temporary Internet Files directory. DIR_COMMON_START_MENU, // Usually "C:\Documents and Settings\All Users\ @@ -45,4 +51,4 @@ enum { } // namespace base -#endif // BASE_BASE_PATHS_WIN_H__ +#endif // BASE_BASE_PATHS_WIN_H_ diff --git a/chromium/base/base_switches.cc b/chromium/base/base_switches.cc index 27f52cdb330..30765405de1 100644 --- a/chromium/base/base_switches.cc +++ b/chromium/base/base_switches.cc @@ -17,10 +17,11 @@ const char kEnableCrashReporter[] = "enable-crash-reporter"; // Generates full memory crash dump. const char kFullMemoryCrashReport[] = "full-memory-crash-report"; -// Force low-end device when set to 1; -// Auto-detect low-end device when set to 2; -// Force non-low-end device when set to other values or empty; -const char kLowEndDeviceMode[] = "low-end-device-mode"; +// Force low-end device mode when set. +const char kEnableLowEndDeviceMode[] = "enable-low-end-device-mode"; + +// Force disabling of low-end device mode when set. +const char kDisableLowEndDeviceMode[] = "disable-low-end-device-mode"; // Suppresses all error dialogs when present. const char kNoErrorDialogs[] = "noerrdialogs"; diff --git a/chromium/base/base_switches.h b/chromium/base/base_switches.h index 96cbd5bbb56..c579f6a240d 100644 --- a/chromium/base/base_switches.h +++ b/chromium/base/base_switches.h @@ -14,7 +14,8 @@ namespace switches { extern const char kDisableBreakpad[]; extern const char kEnableCrashReporter[]; extern const char kFullMemoryCrashReport[]; -extern const char kLowEndDeviceMode[]; +extern const char kEnableLowEndDeviceMode[]; +extern const char kDisableLowEndDeviceMode[]; extern const char kNoErrorDialogs[]; extern const char kProfilerTiming[]; extern const char kProfilerTimingDisabledValue[]; diff --git a/chromium/base/base_unittests.isolate b/chromium/base/base_unittests.isolate index f561d209cf5..b1c270ca89d 100644 --- a/chromium/base/base_unittests.isolate +++ b/chromium/base/base_unittests.isolate @@ -3,14 +3,20 @@ # found in the LICENSE file. { 'conditions': [ - ['OS=="android" or OS=="linux" or OS=="mac" or OS=="win"', { + ['use_x11==0', { 'variables': { - 'files': [ - 'test/data/', + 'command': [ + '../testing/test_env.py', + '<(PRODUCT_DIR)/base_unittests<(EXECUTABLE_SUFFIX)', + '--brave-new-test-launcher', + '--test-launcher-bot-mode', + '--asan=<(asan)', + '--msan=<(msan)', + '--tsan=<(tsan)', ], }, }], - ['OS=="linux"', { + ['use_x11==1', { 'variables': { 'command': [ '../testing/xvfb.py', @@ -19,17 +25,19 @@ '--brave-new-test-launcher', '--test-launcher-bot-mode', '--asan=<(asan)', - '--lsan=<(lsan)', + '--msan=<(msan)', + '--tsan=<(tsan)', ], 'files': [ '../testing/xvfb.py', + '<(PRODUCT_DIR)/xdisplaycheck<(EXECUTABLE_SUFFIX)', ], }, }], - ['OS=="linux" and use_ozone==0', { + ['OS=="android" or OS=="linux" or OS=="mac" or OS=="win"', { 'variables': { 'files': [ - '<(PRODUCT_DIR)/xdisplaycheck<(EXECUTABLE_SUFFIX)', + 'test/data/', ], }, }], @@ -42,15 +50,17 @@ 'read_only': 1, }, }], - ['OS=="mac" or OS=="win"', { + ['OS=="linux"', { 'variables': { - 'command': [ - '../testing/test_env.py', - '<(PRODUCT_DIR)/base_unittests<(EXECUTABLE_SUFFIX)', - '--brave-new-test-launcher', - '--test-launcher-bot-mode', - '--asan=<(asan)', - '--lsan=<(lsan)', + 'files': [ + '<(PRODUCT_DIR)/lib/libmalloc_wrapper.so', + ], + }, + }], + ['OS=="mac" and asan==1 and fastbuild==0', { + 'variables': { + 'files': [ + '<(PRODUCT_DIR)/base_unittests.dSYM/', ], }, }], diff --git a/chromium/base/bind.h b/chromium/base/bind.h index b14f70c109f..51be10dd7e6 100644 --- a/chromium/base/bind.h +++ b/chromium/base/bind.h @@ -1,8 +1,3 @@ -// This file was GENERATED by command: -// pump.py bind.h.pump -// DO NOT EDIT BY HAND!!! - - // 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. @@ -46,10 +41,9 @@ // // TODO(ajwong): We might be able to avoid this now, but need to test. // -// It is possible to move most of the COMPILE_ASSERT asserts into BindState<>, -// but it feels a little nicer to have the asserts here so people do not -// need to crack open bind_internal.h. On the other hand, it makes Bind() -// harder to read. +// It is possible to move most of the static_assert into BindState<>, but it +// feels a little nicer to have the asserts here so people do not need to crack +// open bind_internal.h. On the other hand, it makes Bind() harder to read. namespace base { @@ -58,317 +52,28 @@ base::Callback< typename internal::BindState< typename internal::FunctorTraits<Functor>::RunnableType, typename internal::FunctorTraits<Functor>::RunType, - void()> - ::UnboundRunType> + internal::TypeList<>>::UnboundRunType> Bind(Functor functor) { // Typedefs for how to store and run the functor. typedef typename internal::FunctorTraits<Functor>::RunnableType RunnableType; typedef typename internal::FunctorTraits<Functor>::RunType RunType; - typedef internal::BindState<RunnableType, RunType, void()> BindState; - - - return Callback<typename BindState::UnboundRunType>( - new BindState(internal::MakeRunnable(functor))); -} - -template <typename Functor, typename P1> -base::Callback< - typename internal::BindState< - typename internal::FunctorTraits<Functor>::RunnableType, - typename internal::FunctorTraits<Functor>::RunType, - void(typename internal::CallbackParamTraits<P1>::StorageType)> - ::UnboundRunType> -Bind(Functor functor, const P1& p1) { - // Typedefs for how to store and run the functor. - typedef typename internal::FunctorTraits<Functor>::RunnableType RunnableType; - typedef typename internal::FunctorTraits<Functor>::RunType RunType; - - // Use RunnableType::RunType instead of RunType above because our - // checks should below for bound references need to know what the actual - // functor is going to interpret the argument as. - typedef internal::FunctionTraits<typename RunnableType::RunType> - BoundFunctorTraits; - - // Do not allow binding a non-const reference parameter. Non-const reference - // parameters are disallowed by the Google style guide. Also, binding a - // non-const reference parameter can make for subtle bugs because the - // invoked function will receive a reference to the stored copy of the - // argument and not the original. - COMPILE_ASSERT( - !(is_non_const_reference<typename BoundFunctorTraits::A1Type>::value ), - do_not_bind_functions_with_nonconst_ref); - - // For methods, we need to be careful for parameter 1. We do not require - // a scoped_refptr because BindState<> itself takes care of AddRef() for - // methods. We also disallow binding of an array as the method's target - // object. - COMPILE_ASSERT( - internal::HasIsMethodTag<RunnableType>::value || - !internal::NeedsScopedRefptrButGetsRawPtr<P1>::value, - p1_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::HasIsMethodTag<RunnableType>::value || - !is_array<P1>::value, - first_bound_argument_to_method_cannot_be_array); - typedef internal::BindState<RunnableType, RunType, - void(typename internal::CallbackParamTraits<P1>::StorageType)> BindState; - - - return Callback<typename BindState::UnboundRunType>( - new BindState(internal::MakeRunnable(functor), p1)); -} - -template <typename Functor, typename P1, typename P2> -base::Callback< - typename internal::BindState< - typename internal::FunctorTraits<Functor>::RunnableType, - typename internal::FunctorTraits<Functor>::RunType, - void(typename internal::CallbackParamTraits<P1>::StorageType, - typename internal::CallbackParamTraits<P2>::StorageType)> - ::UnboundRunType> -Bind(Functor functor, const P1& p1, const P2& p2) { - // Typedefs for how to store and run the functor. - typedef typename internal::FunctorTraits<Functor>::RunnableType RunnableType; - typedef typename internal::FunctorTraits<Functor>::RunType RunType; - - // Use RunnableType::RunType instead of RunType above because our - // checks should below for bound references need to know what the actual - // functor is going to interpret the argument as. - typedef internal::FunctionTraits<typename RunnableType::RunType> - BoundFunctorTraits; - - // Do not allow binding a non-const reference parameter. Non-const reference - // parameters are disallowed by the Google style guide. Also, binding a - // non-const reference parameter can make for subtle bugs because the - // invoked function will receive a reference to the stored copy of the - // argument and not the original. - COMPILE_ASSERT( - !(is_non_const_reference<typename BoundFunctorTraits::A1Type>::value || - is_non_const_reference<typename BoundFunctorTraits::A2Type>::value ), - do_not_bind_functions_with_nonconst_ref); - - // For methods, we need to be careful for parameter 1. We do not require - // a scoped_refptr because BindState<> itself takes care of AddRef() for - // methods. We also disallow binding of an array as the method's target - // object. - COMPILE_ASSERT( - internal::HasIsMethodTag<RunnableType>::value || - !internal::NeedsScopedRefptrButGetsRawPtr<P1>::value, - p1_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::HasIsMethodTag<RunnableType>::value || - !is_array<P1>::value, - first_bound_argument_to_method_cannot_be_array); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P2>::value, - p2_is_refcounted_type_and_needs_scoped_refptr); - typedef internal::BindState<RunnableType, RunType, - void(typename internal::CallbackParamTraits<P1>::StorageType, - typename internal::CallbackParamTraits<P2>::StorageType)> BindState; - - - return Callback<typename BindState::UnboundRunType>( - new BindState(internal::MakeRunnable(functor), p1, p2)); -} - -template <typename Functor, typename P1, typename P2, typename P3> -base::Callback< - typename internal::BindState< - typename internal::FunctorTraits<Functor>::RunnableType, - typename internal::FunctorTraits<Functor>::RunType, - void(typename internal::CallbackParamTraits<P1>::StorageType, - typename internal::CallbackParamTraits<P2>::StorageType, - typename internal::CallbackParamTraits<P3>::StorageType)> - ::UnboundRunType> -Bind(Functor functor, const P1& p1, const P2& p2, const P3& p3) { - // Typedefs for how to store and run the functor. - typedef typename internal::FunctorTraits<Functor>::RunnableType RunnableType; - typedef typename internal::FunctorTraits<Functor>::RunType RunType; - - // Use RunnableType::RunType instead of RunType above because our - // checks should below for bound references need to know what the actual - // functor is going to interpret the argument as. - typedef internal::FunctionTraits<typename RunnableType::RunType> - BoundFunctorTraits; - - // Do not allow binding a non-const reference parameter. Non-const reference - // parameters are disallowed by the Google style guide. Also, binding a - // non-const reference parameter can make for subtle bugs because the - // invoked function will receive a reference to the stored copy of the - // argument and not the original. - COMPILE_ASSERT( - !(is_non_const_reference<typename BoundFunctorTraits::A1Type>::value || - is_non_const_reference<typename BoundFunctorTraits::A2Type>::value || - is_non_const_reference<typename BoundFunctorTraits::A3Type>::value ), - do_not_bind_functions_with_nonconst_ref); - - // For methods, we need to be careful for parameter 1. We do not require - // a scoped_refptr because BindState<> itself takes care of AddRef() for - // methods. We also disallow binding of an array as the method's target - // object. - COMPILE_ASSERT( - internal::HasIsMethodTag<RunnableType>::value || - !internal::NeedsScopedRefptrButGetsRawPtr<P1>::value, - p1_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::HasIsMethodTag<RunnableType>::value || - !is_array<P1>::value, - first_bound_argument_to_method_cannot_be_array); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P2>::value, - p2_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P3>::value, - p3_is_refcounted_type_and_needs_scoped_refptr); - typedef internal::BindState<RunnableType, RunType, - void(typename internal::CallbackParamTraits<P1>::StorageType, - typename internal::CallbackParamTraits<P2>::StorageType, - typename internal::CallbackParamTraits<P3>::StorageType)> BindState; - - - return Callback<typename BindState::UnboundRunType>( - new BindState(internal::MakeRunnable(functor), p1, p2, p3)); -} - -template <typename Functor, typename P1, typename P2, typename P3, typename P4> -base::Callback< - typename internal::BindState< - typename internal::FunctorTraits<Functor>::RunnableType, - typename internal::FunctorTraits<Functor>::RunType, - void(typename internal::CallbackParamTraits<P1>::StorageType, - typename internal::CallbackParamTraits<P2>::StorageType, - typename internal::CallbackParamTraits<P3>::StorageType, - typename internal::CallbackParamTraits<P4>::StorageType)> - ::UnboundRunType> -Bind(Functor functor, const P1& p1, const P2& p2, const P3& p3, const P4& p4) { - // Typedefs for how to store and run the functor. - typedef typename internal::FunctorTraits<Functor>::RunnableType RunnableType; - typedef typename internal::FunctorTraits<Functor>::RunType RunType; - - // Use RunnableType::RunType instead of RunType above because our - // checks should below for bound references need to know what the actual - // functor is going to interpret the argument as. - typedef internal::FunctionTraits<typename RunnableType::RunType> - BoundFunctorTraits; - - // Do not allow binding a non-const reference parameter. Non-const reference - // parameters are disallowed by the Google style guide. Also, binding a - // non-const reference parameter can make for subtle bugs because the - // invoked function will receive a reference to the stored copy of the - // argument and not the original. - COMPILE_ASSERT( - !(is_non_const_reference<typename BoundFunctorTraits::A1Type>::value || - is_non_const_reference<typename BoundFunctorTraits::A2Type>::value || - is_non_const_reference<typename BoundFunctorTraits::A3Type>::value || - is_non_const_reference<typename BoundFunctorTraits::A4Type>::value ), - do_not_bind_functions_with_nonconst_ref); - - // For methods, we need to be careful for parameter 1. We do not require - // a scoped_refptr because BindState<> itself takes care of AddRef() for - // methods. We also disallow binding of an array as the method's target - // object. - COMPILE_ASSERT( - internal::HasIsMethodTag<RunnableType>::value || - !internal::NeedsScopedRefptrButGetsRawPtr<P1>::value, - p1_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::HasIsMethodTag<RunnableType>::value || - !is_array<P1>::value, - first_bound_argument_to_method_cannot_be_array); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P2>::value, - p2_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P3>::value, - p3_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P4>::value, - p4_is_refcounted_type_and_needs_scoped_refptr); - typedef internal::BindState<RunnableType, RunType, - void(typename internal::CallbackParamTraits<P1>::StorageType, - typename internal::CallbackParamTraits<P2>::StorageType, - typename internal::CallbackParamTraits<P3>::StorageType, - typename internal::CallbackParamTraits<P4>::StorageType)> BindState; - - - return Callback<typename BindState::UnboundRunType>( - new BindState(internal::MakeRunnable(functor), p1, p2, p3, p4)); -} - -template <typename Functor, typename P1, typename P2, typename P3, typename P4, - typename P5> -base::Callback< - typename internal::BindState< - typename internal::FunctorTraits<Functor>::RunnableType, - typename internal::FunctorTraits<Functor>::RunType, - void(typename internal::CallbackParamTraits<P1>::StorageType, - typename internal::CallbackParamTraits<P2>::StorageType, - typename internal::CallbackParamTraits<P3>::StorageType, - typename internal::CallbackParamTraits<P4>::StorageType, - typename internal::CallbackParamTraits<P5>::StorageType)> - ::UnboundRunType> -Bind(Functor functor, const P1& p1, const P2& p2, const P3& p3, const P4& p4, - const P5& p5) { - // Typedefs for how to store and run the functor. - typedef typename internal::FunctorTraits<Functor>::RunnableType RunnableType; - typedef typename internal::FunctorTraits<Functor>::RunType RunType; - - // Use RunnableType::RunType instead of RunType above because our - // checks should below for bound references need to know what the actual - // functor is going to interpret the argument as. - typedef internal::FunctionTraits<typename RunnableType::RunType> - BoundFunctorTraits; - - // Do not allow binding a non-const reference parameter. Non-const reference - // parameters are disallowed by the Google style guide. Also, binding a - // non-const reference parameter can make for subtle bugs because the - // invoked function will receive a reference to the stored copy of the - // argument and not the original. - COMPILE_ASSERT( - !(is_non_const_reference<typename BoundFunctorTraits::A1Type>::value || - is_non_const_reference<typename BoundFunctorTraits::A2Type>::value || - is_non_const_reference<typename BoundFunctorTraits::A3Type>::value || - is_non_const_reference<typename BoundFunctorTraits::A4Type>::value || - is_non_const_reference<typename BoundFunctorTraits::A5Type>::value ), - do_not_bind_functions_with_nonconst_ref); - - // For methods, we need to be careful for parameter 1. We do not require - // a scoped_refptr because BindState<> itself takes care of AddRef() for - // methods. We also disallow binding of an array as the method's target - // object. - COMPILE_ASSERT( - internal::HasIsMethodTag<RunnableType>::value || - !internal::NeedsScopedRefptrButGetsRawPtr<P1>::value, - p1_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::HasIsMethodTag<RunnableType>::value || - !is_array<P1>::value, - first_bound_argument_to_method_cannot_be_array); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P2>::value, - p2_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P3>::value, - p3_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P4>::value, - p4_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P5>::value, - p5_is_refcounted_type_and_needs_scoped_refptr); typedef internal::BindState<RunnableType, RunType, - void(typename internal::CallbackParamTraits<P1>::StorageType, - typename internal::CallbackParamTraits<P2>::StorageType, - typename internal::CallbackParamTraits<P3>::StorageType, - typename internal::CallbackParamTraits<P4>::StorageType, - typename internal::CallbackParamTraits<P5>::StorageType)> BindState; - + internal::TypeList<>> BindState; return Callback<typename BindState::UnboundRunType>( - new BindState(internal::MakeRunnable(functor), p1, p2, p3, p4, p5)); + new BindState(internal::MakeRunnable(functor))); } -template <typename Functor, typename P1, typename P2, typename P3, typename P4, - typename P5, typename P6> +template <typename Functor, typename... Args> base::Callback< typename internal::BindState< typename internal::FunctorTraits<Functor>::RunnableType, typename internal::FunctorTraits<Functor>::RunType, - void(typename internal::CallbackParamTraits<P1>::StorageType, - typename internal::CallbackParamTraits<P2>::StorageType, - typename internal::CallbackParamTraits<P3>::StorageType, - typename internal::CallbackParamTraits<P4>::StorageType, - typename internal::CallbackParamTraits<P5>::StorageType, - typename internal::CallbackParamTraits<P6>::StorageType)> + internal::TypeList< + typename internal::CallbackParamTraits<Args>::StorageType...>> ::UnboundRunType> -Bind(Functor functor, const P1& p1, const P2& p2, const P3& p3, const P4& p4, - const P5& p5, const P6& p6) { +Bind(Functor functor, const Args&... args) { // Typedefs for how to store and run the functor. typedef typename internal::FunctorTraits<Functor>::RunnableType RunnableType; typedef typename internal::FunctorTraits<Functor>::RunType RunType; @@ -376,134 +81,36 @@ Bind(Functor functor, const P1& p1, const P2& p2, const P3& p3, const P4& p4, // Use RunnableType::RunType instead of RunType above because our // checks should below for bound references need to know what the actual // functor is going to interpret the argument as. - typedef internal::FunctionTraits<typename RunnableType::RunType> - BoundFunctorTraits; + typedef typename RunnableType::RunType BoundRunType; // Do not allow binding a non-const reference parameter. Non-const reference // parameters are disallowed by the Google style guide. Also, binding a // non-const reference parameter can make for subtle bugs because the // invoked function will receive a reference to the stored copy of the // argument and not the original. - COMPILE_ASSERT( - !(is_non_const_reference<typename BoundFunctorTraits::A1Type>::value || - is_non_const_reference<typename BoundFunctorTraits::A2Type>::value || - is_non_const_reference<typename BoundFunctorTraits::A3Type>::value || - is_non_const_reference<typename BoundFunctorTraits::A4Type>::value || - is_non_const_reference<typename BoundFunctorTraits::A5Type>::value || - is_non_const_reference<typename BoundFunctorTraits::A6Type>::value ), - do_not_bind_functions_with_nonconst_ref); - - // For methods, we need to be careful for parameter 1. We do not require - // a scoped_refptr because BindState<> itself takes care of AddRef() for - // methods. We also disallow binding of an array as the method's target - // object. - COMPILE_ASSERT( - internal::HasIsMethodTag<RunnableType>::value || - !internal::NeedsScopedRefptrButGetsRawPtr<P1>::value, - p1_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::HasIsMethodTag<RunnableType>::value || - !is_array<P1>::value, - first_bound_argument_to_method_cannot_be_array); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P2>::value, - p2_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P3>::value, - p3_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P4>::value, - p4_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P5>::value, - p5_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P6>::value, - p6_is_refcounted_type_and_needs_scoped_refptr); - typedef internal::BindState<RunnableType, RunType, - void(typename internal::CallbackParamTraits<P1>::StorageType, - typename internal::CallbackParamTraits<P2>::StorageType, - typename internal::CallbackParamTraits<P3>::StorageType, - typename internal::CallbackParamTraits<P4>::StorageType, - typename internal::CallbackParamTraits<P5>::StorageType, - typename internal::CallbackParamTraits<P6>::StorageType)> BindState; + static_assert(!internal::HasNonConstReferenceParam<BoundRunType>::value, + "do_not_bind_functions_with_nonconst_ref"); - - return Callback<typename BindState::UnboundRunType>( - new BindState(internal::MakeRunnable(functor), p1, p2, p3, p4, p5, p6)); -} - -template <typename Functor, typename P1, typename P2, typename P3, typename P4, - typename P5, typename P6, typename P7> -base::Callback< - typename internal::BindState< - typename internal::FunctorTraits<Functor>::RunnableType, - typename internal::FunctorTraits<Functor>::RunType, - void(typename internal::CallbackParamTraits<P1>::StorageType, - typename internal::CallbackParamTraits<P2>::StorageType, - typename internal::CallbackParamTraits<P3>::StorageType, - typename internal::CallbackParamTraits<P4>::StorageType, - typename internal::CallbackParamTraits<P5>::StorageType, - typename internal::CallbackParamTraits<P6>::StorageType, - typename internal::CallbackParamTraits<P7>::StorageType)> - ::UnboundRunType> -Bind(Functor functor, const P1& p1, const P2& p2, const P3& p3, const P4& p4, - const P5& p5, const P6& p6, const P7& p7) { - // Typedefs for how to store and run the functor. - typedef typename internal::FunctorTraits<Functor>::RunnableType RunnableType; - typedef typename internal::FunctorTraits<Functor>::RunType RunType; - - // Use RunnableType::RunType instead of RunType above because our - // checks should below for bound references need to know what the actual - // functor is going to interpret the argument as. - typedef internal::FunctionTraits<typename RunnableType::RunType> - BoundFunctorTraits; - - // Do not allow binding a non-const reference parameter. Non-const reference - // parameters are disallowed by the Google style guide. Also, binding a - // non-const reference parameter can make for subtle bugs because the - // invoked function will receive a reference to the stored copy of the - // argument and not the original. - COMPILE_ASSERT( - !(is_non_const_reference<typename BoundFunctorTraits::A1Type>::value || - is_non_const_reference<typename BoundFunctorTraits::A2Type>::value || - is_non_const_reference<typename BoundFunctorTraits::A3Type>::value || - is_non_const_reference<typename BoundFunctorTraits::A4Type>::value || - is_non_const_reference<typename BoundFunctorTraits::A5Type>::value || - is_non_const_reference<typename BoundFunctorTraits::A6Type>::value || - is_non_const_reference<typename BoundFunctorTraits::A7Type>::value ), - do_not_bind_functions_with_nonconst_ref); + const bool is_method = internal::HasIsMethodTag<RunnableType>::value; // For methods, we need to be careful for parameter 1. We do not require // a scoped_refptr because BindState<> itself takes care of AddRef() for // methods. We also disallow binding of an array as the method's target // object. - COMPILE_ASSERT( - internal::HasIsMethodTag<RunnableType>::value || - !internal::NeedsScopedRefptrButGetsRawPtr<P1>::value, - p1_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::HasIsMethodTag<RunnableType>::value || - !is_array<P1>::value, - first_bound_argument_to_method_cannot_be_array); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P2>::value, - p2_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P3>::value, - p3_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P4>::value, - p4_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P5>::value, - p5_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P6>::value, - p6_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P7>::value, - p7_is_refcounted_type_and_needs_scoped_refptr); - typedef internal::BindState<RunnableType, RunType, - void(typename internal::CallbackParamTraits<P1>::StorageType, - typename internal::CallbackParamTraits<P2>::StorageType, - typename internal::CallbackParamTraits<P3>::StorageType, - typename internal::CallbackParamTraits<P4>::StorageType, - typename internal::CallbackParamTraits<P5>::StorageType, - typename internal::CallbackParamTraits<P6>::StorageType, - typename internal::CallbackParamTraits<P7>::StorageType)> BindState; - + static_assert(!internal::BindsArrayToFirstArg<is_method, Args...>::value, + "first_bound_argument_to_method_cannot_be_array"); + static_assert( + !internal::HasRefCountedParamAsRawPtr<is_method, Args...>::value, + "a_parameter_is_refcounted_type_and_needs_scoped_refptr"); + + typedef internal::BindState< + RunnableType, RunType, + internal::TypeList< + typename internal::CallbackParamTraits<Args>::StorageType...>> + BindState; return Callback<typename BindState::UnboundRunType>( - new BindState(internal::MakeRunnable(functor), p1, p2, p3, p4, p5, p6, - p7)); + new BindState(internal::MakeRunnable(functor), args...)); } } // namespace base diff --git a/chromium/base/bind.h.pump b/chromium/base/bind.h.pump deleted file mode 100644 index 2cdd7d5e498..00000000000 --- a/chromium/base/bind.h.pump +++ /dev/null @@ -1,153 +0,0 @@ -$$ This is a pump file for generating file templates. Pump is a python -$$ script that is part of the Google Test suite of utilities. Description -$$ can be found here: -$$ -$$ http://code.google.com/p/googletest/wiki/PumpManual -$$ - -$$ -$$ MAX_ARITY controls the number of arguments that Bind() supports. -$$ The amount of code, and more importantly, the number of template types -$$ generated by pump grows at O(MAX_ARITY^2). -$$ -$$ We tried going to 11 and found it imposed an extra 10 penalty on windows -$$ cycle times compared to our original baseline of 6. -$$ -$$ Currently 7 is chosen as a compromise between supporting a convenient -$$ number of arguments and keeping compile times low. At 7, we have 115 -$$ templates being generated by pump. -$$ -$$ Be careful when adjusting this number. If people find a need to bind -$$ a larger number of arguments, consider refactoring the function to use -$$ a param struct instead of raising the MAX_ARITY. -$$ -$$ See http://crbug.com/98542 for more context. -$$ -$var MAX_ARITY = 7 - -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_BIND_H_ -#define BASE_BIND_H_ - -#include "base/bind_internal.h" -#include "base/callback_internal.h" - -// ----------------------------------------------------------------------------- -// Usage documentation -// ----------------------------------------------------------------------------- -// -// See base/callback.h for documentation. -// -// -// ----------------------------------------------------------------------------- -// Implementation notes -// ----------------------------------------------------------------------------- -// -// If you're reading the implementation, before proceeding further, you should -// read the top comment of base/bind_internal.h for a definition of common -// terms and concepts. -// -// RETURN TYPES -// -// Though Bind()'s result is meant to be stored in a Callback<> type, it -// cannot actually return the exact type without requiring a large amount -// of extra template specializations. The problem is that in order to -// discern the correct specialization of Callback<>, Bind would need to -// unwrap the function signature to determine the signature's arity, and -// whether or not it is a method. -// -// Each unique combination of (arity, function_type, num_prebound) where -// function_type is one of {function, method, const_method} would require -// one specialization. We eventually have to do a similar number of -// specializations anyways in the implementation (see the Invoker<>, -// classes). However, it is avoidable in Bind if we return the result -// via an indirection like we do below. -// -// TODO(ajwong): We might be able to avoid this now, but need to test. -// -// It is possible to move most of the COMPILE_ASSERT asserts into BindState<>, -// but it feels a little nicer to have the asserts here so people do not -// need to crack open bind_internal.h. On the other hand, it makes Bind() -// harder to read. - -namespace base { - -$range ARITY 0..MAX_ARITY -$for ARITY [[ -$range ARG 1..ARITY - -template <typename Functor[[]] -$if ARITY > 0 [[, ]] $for ARG , [[typename P$(ARG)]]> -base::Callback< - typename internal::BindState< - typename internal::FunctorTraits<Functor>::RunnableType, - typename internal::FunctorTraits<Functor>::RunType, - void($for ARG , [[typename internal::CallbackParamTraits<P$(ARG)>::StorageType]])> - ::UnboundRunType> -Bind(Functor functor -$if ARITY > 0 [[, ]] $for ARG , [[const P$(ARG)& p$(ARG)]]) { - // Typedefs for how to store and run the functor. - typedef typename internal::FunctorTraits<Functor>::RunnableType RunnableType; - typedef typename internal::FunctorTraits<Functor>::RunType RunType; - -$if ARITY > 0 [[ - - // Use RunnableType::RunType instead of RunType above because our - // checks should below for bound references need to know what the actual - // functor is going to interpret the argument as. - typedef internal::FunctionTraits<typename RunnableType::RunType> - BoundFunctorTraits; - - // Do not allow binding a non-const reference parameter. Non-const reference - // parameters are disallowed by the Google style guide. Also, binding a - // non-const reference parameter can make for subtle bugs because the - // invoked function will receive a reference to the stored copy of the - // argument and not the original. - COMPILE_ASSERT( - !($for ARG || [[ -is_non_const_reference<typename BoundFunctorTraits::A$(ARG)Type>::value ]]), - do_not_bind_functions_with_nonconst_ref); - -]] - - -$for ARG [[ - - -$if ARG == 1 [[ - // For methods, we need to be careful for parameter 1. We do not require - // a scoped_refptr because BindState<> itself takes care of AddRef() for - // methods. We also disallow binding of an array as the method's target - // object. - COMPILE_ASSERT( - internal::HasIsMethodTag<RunnableType>::value || - !internal::NeedsScopedRefptrButGetsRawPtr<P$(ARG)>::value, - p$(ARG)_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::HasIsMethodTag<RunnableType>::value || - !is_array<P$(ARG)>::value, - first_bound_argument_to_method_cannot_be_array); -]] $else [[ - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P$(ARG)>::value, - p$(ARG)_is_refcounted_type_and_needs_scoped_refptr); -]] $$ $if ARG - -]] $$ $for ARG - - typedef internal::BindState<RunnableType, RunType, [[]] -void($for ARG , [[typename internal::CallbackParamTraits<P$(ARG)>::StorageType]])> [[]] -BindState; - - - return Callback<typename BindState::UnboundRunType>( - new BindState(internal::MakeRunnable(functor)[[]] -$if ARITY > 0 [[, ]] $for ARG , [[p$(ARG)]])); -} - -]] $$ for ARITY - -} // namespace base - -#endif // BASE_BIND_H_ diff --git a/chromium/base/bind_helpers.h b/chromium/base/bind_helpers.h index f3efc31953c..24063ad1ce5 100644 --- a/chromium/base/bind_helpers.h +++ b/chromium/base/bind_helpers.h @@ -129,7 +129,7 @@ // Passed() is particularly useful with PostTask() when you are transferring // ownership of an argument into a task, but don't necessarily know if the // task will always be executed. This can happen if the task is cancellable -// or if it is posted to a MessageLoopProxy. +// or if it is posted to a TaskRunner. // // // SIMPLE FUNCTIONS AND UTILITIES. @@ -435,45 +435,46 @@ struct UnwrapTraits<PassedWrapper<T> > { // Utility for handling different refcounting semantics in the Bind() // function. -template <bool is_method, typename T> -struct MaybeRefcount; +template <bool is_method, typename... T> +struct MaybeScopedRefPtr; -template <typename T> -struct MaybeRefcount<false, T> { - static void AddRef(const T&) {} - static void Release(const T&) {} +template <bool is_method> +struct MaybeScopedRefPtr<is_method> { + MaybeScopedRefPtr() {} }; -template <typename T, size_t n> -struct MaybeRefcount<false, T[n]> { - static void AddRef(const T*) {} - static void Release(const T*) {} +template <typename T, typename... Rest> +struct MaybeScopedRefPtr<false, T, Rest...> { + MaybeScopedRefPtr(const T&, const Rest&...) {} }; -template <typename T> -struct MaybeRefcount<true, T> { - static void AddRef(const T&) {} - static void Release(const T&) {} +template <typename T, size_t n, typename... Rest> +struct MaybeScopedRefPtr<false, T[n], Rest...> { + MaybeScopedRefPtr(const T*, const Rest&...) {} }; -template <typename T> -struct MaybeRefcount<true, T*> { - static void AddRef(T* o) { o->AddRef(); } - static void Release(T* o) { o->Release(); } +template <typename T, typename... Rest> +struct MaybeScopedRefPtr<true, T, Rest...> { + MaybeScopedRefPtr(const T& o, const Rest&...) {} +}; + +template <typename T, typename... Rest> +struct MaybeScopedRefPtr<true, T*, Rest...> { + MaybeScopedRefPtr(T* o, const Rest&...) : ref_(o) {} + scoped_refptr<T> ref_; }; // No need to additionally AddRef() and Release() since we are storing a // scoped_refptr<> inside the storage object already. -template <typename T> -struct MaybeRefcount<true, scoped_refptr<T> > { - static void AddRef(const scoped_refptr<T>& o) {} - static void Release(const scoped_refptr<T>& o) {} +template <typename T, typename... Rest> +struct MaybeScopedRefPtr<true, scoped_refptr<T>, Rest...> { + MaybeScopedRefPtr(const scoped_refptr<T>&, const Rest&...) {} }; -template <typename T> -struct MaybeRefcount<true, const T*> { - static void AddRef(const T* o) { o->AddRef(); } - static void Release(const T* o) { o->Release(); } +template <typename T, typename... Rest> +struct MaybeScopedRefPtr<true, const T*, Rest...> { + MaybeScopedRefPtr(const T* o, const Rest&...) : ref_(o) {} + scoped_refptr<const T> ref_; }; // IsWeakMethod is a helper that determine if we are binding a WeakPtr<> to a @@ -481,15 +482,72 @@ struct MaybeRefcount<true, const T*> { // InvokeHelper that will no-op itself in the event the WeakPtr<> for // the target object is invalidated. // -// P1 should be the type of the object that will be received of the method. -template <bool IsMethod, typename P1> +// The first argument should be the type of the object that will be received by +// the method. +template <bool IsMethod, typename... Args> struct IsWeakMethod : public false_type {}; -template <typename T> -struct IsWeakMethod<true, WeakPtr<T> > : public true_type {}; +template <typename T, typename... Args> +struct IsWeakMethod<true, WeakPtr<T>, Args...> : public true_type {}; -template <typename T> -struct IsWeakMethod<true, ConstRefWrapper<WeakPtr<T> > > : public true_type {}; +template <typename T, typename... Args> +struct IsWeakMethod<true, ConstRefWrapper<WeakPtr<T>>, Args...> + : public true_type {}; + + +// Packs a list of types to hold them in a single type. +template <typename... Types> +struct TypeList {}; + +// Used for DropTypeListItem implementation. +template <size_t n, typename List> +struct DropTypeListItemImpl; + +// Do not use enable_if and SFINAE here to avoid MSVC2013 compile failure. +template <size_t n, typename T, typename... List> +struct DropTypeListItemImpl<n, TypeList<T, List...>> + : DropTypeListItemImpl<n - 1, TypeList<List...>> {}; + +template <typename T, typename... List> +struct DropTypeListItemImpl<0, TypeList<T, List...>> { + typedef TypeList<T, List...> Type; +}; + +template <> +struct DropTypeListItemImpl<0, TypeList<>> { + typedef TypeList<> Type; +}; + +// A type-level function that drops |n| list item from given TypeList. +template <size_t n, typename List> +using DropTypeListItem = typename DropTypeListItemImpl<n, List>::Type; + +// Used for ConcatTypeLists implementation. +template <typename List1, typename List2> +struct ConcatTypeListsImpl; + +template <typename... Types1, typename... Types2> +struct ConcatTypeListsImpl<TypeList<Types1...>, TypeList<Types2...>> { + typedef TypeList<Types1..., Types2...> Type; +}; + +// A type-level function that concats two TypeLists. +template <typename List1, typename List2> +using ConcatTypeLists = typename ConcatTypeListsImpl<List1, List2>::Type; + +// Used for MakeFunctionType implementation. +template <typename R, typename ArgList> +struct MakeFunctionTypeImpl; + +template <typename R, typename... Args> +struct MakeFunctionTypeImpl<R, TypeList<Args...>> { + typedef R(Type)(Args...); +}; + +// A type-level function that constructs a function type that has |R| as its +// return type and has TypeLists items as its arguments. +template <typename R, typename ArgList> +using MakeFunctionType = typename MakeFunctionTypeImpl<R, ArgList>::Type; } // namespace internal diff --git a/chromium/base/bind_internal.h b/chromium/base/bind_internal.h index ae17ebf86c1..e0532188622 100644 --- a/chromium/base/bind_internal.h +++ b/chromium/base/bind_internal.h @@ -1,8 +1,3 @@ -// This file was GENERATED by command: -// pump.py bind_internal.h.pump -// DO NOT EDIT BY HAND!!! - - // 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. @@ -15,6 +10,7 @@ #include "base/memory/raw_scoped_refptr_mismatch_checker.h" #include "base/memory/weak_ptr.h" #include "base/template_util.h" +#include "base/tuple.h" #include "build/build_config.h" #if defined(OS_WIN) @@ -51,33 +47,79 @@ namespace internal { // Types: // RunnableAdapter<> -- Wraps the various "function" pointer types into an // object that adheres to the Runnable interface. -// There are |3*ARITY| RunnableAdapter types. -// FunctionTraits<> -- Type traits that unwrap a function signature into a -// a set of easier to use typedefs. Used mainly for -// compile time asserts. -// There are |ARITY| FunctionTraits types. // ForceVoidReturn<> -- Helper class for translating function signatures to // equivalent forms with a "void" return type. -// There are |ARITY| ForceVoidReturn types. // FunctorTraits<> -- Type traits used determine the correct RunType and // RunnableType for a Functor. This is where function // signature adapters are applied. -// There are |ARITY| ForceVoidReturn types. // MakeRunnable<> -- Takes a Functor and returns an object in the Runnable // type class that represents the underlying Functor. // There are |O(1)| MakeRunnable types. // InvokeHelper<> -- Take a Runnable + arguments and actully invokes it. -// Handle the differing syntaxes needed for WeakPtr<> support, -// and for ignoring return values. This is separate from -// Invoker to avoid creating multiple version of Invoker<> -// which grows at O(n^2) with the arity. -// There are |k*ARITY| InvokeHelper types. +// Handle the differing syntaxes needed for WeakPtr<> +// support, and for ignoring return values. This is separate +// from Invoker to avoid creating multiple version of +// Invoker<>. // Invoker<> -- Unwraps the curried parameters and executes the Runnable. -// There are |(ARITY^2 + ARITY)/2| Invoketypes. // BindState<> -- Stores the curried parameters, and is the main entry point // into the Bind() system, doing most of the type resolution. // There are ARITY BindState types. +// HasNonConstReferenceParam selects true_type when any of the parameters in +// |Sig| is a non-const reference. +// Implementation note: This non-specialized case handles zero-arity case only. +// Non-zero-arity cases should be handled by the specialization below. +template <typename Sig> +struct HasNonConstReferenceParam : false_type {}; + +// Implementation note: Select true_type if the first parameter is a non-const +// reference. Otherwise, skip the first parameter and check rest of parameters +// recursively. +template <typename R, typename T, typename... Args> +struct HasNonConstReferenceParam<R(T, Args...)> + : SelectType<is_non_const_reference<T>::value, + true_type, + HasNonConstReferenceParam<R(Args...)>>::Type {}; + +// HasRefCountedTypeAsRawPtr selects true_type when any of the |Args| is a raw +// pointer to a RefCounted type. +// Implementation note: This non-specialized case handles zero-arity case only. +// Non-zero-arity cases should be handled by the specialization below. +template <typename... Args> +struct HasRefCountedTypeAsRawPtr : false_type {}; + +// Implementation note: Select true_type if the first parameter is a raw pointer +// to a RefCounted type. Otherwise, skip the first parameter and check rest of +// parameters recursively. +template <typename T, typename... Args> +struct HasRefCountedTypeAsRawPtr<T, Args...> + : SelectType<NeedsScopedRefptrButGetsRawPtr<T>::value, + true_type, + HasRefCountedTypeAsRawPtr<Args...>>::Type {}; + +// BindsArrayToFirstArg selects true_type when |is_method| is true and the first +// item of |Args| is an array type. +// Implementation note: This non-specialized case handles !is_method case and +// zero-arity case only. Other cases should be handled by the specialization +// below. +template <bool is_method, typename... Args> +struct BindsArrayToFirstArg : false_type {}; + +template <typename T, typename... Args> +struct BindsArrayToFirstArg<true, T, Args...> : is_array<T> {}; + +// HasRefCountedParamAsRawPtr is the same to HasRefCountedTypeAsRawPtr except +// when |is_method| is true HasRefCountedParamAsRawPtr skips the first argument. +// Implementation note: This non-specialized case handles !is_method case and +// zero-arity case only. Other cases should be handled by the specialization +// below. +template <bool is_method, typename... Args> +struct HasRefCountedParamAsRawPtr : HasRefCountedTypeAsRawPtr<Args...> {}; + +template <typename T, typename... Args> +struct HasRefCountedParamAsRawPtr<true, T, Args...> + : HasRefCountedTypeAsRawPtr<Args...> {}; + // RunnableAdapter<> // // The RunnableAdapter<> templates provide a uniform interface for invoking @@ -101,625 +143,61 @@ namespace internal { template <typename Functor> class RunnableAdapter; -// Function: Arity 0. -template <typename R> -class RunnableAdapter<R(*)()> { - public: - typedef R (RunType)(); - - explicit RunnableAdapter(R(*function)()) - : function_(function) { - } - - R Run() { - return function_(); - } - - private: - R (*function_)(); -}; - -// Method: Arity 0. -template <typename R, typename T> -class RunnableAdapter<R(T::*)()> { - public: - typedef R (RunType)(T*); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)()) - : method_(method) { - } - - R Run(T* object) { - return (object->*method_)(); - } - - private: - R (T::*method_)(); -}; - -// Const Method: Arity 0. -template <typename R, typename T> -class RunnableAdapter<R(T::*)() const> { - public: - typedef R (RunType)(const T*); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)() const) - : method_(method) { - } - - R Run(const T* object) { - return (object->*method_)(); - } - - private: - R (T::*method_)() const; -}; - -// Function: Arity 1. -template <typename R, typename A1> -class RunnableAdapter<R(*)(A1)> { - public: - typedef R (RunType)(A1); - - explicit RunnableAdapter(R(*function)(A1)) - : function_(function) { - } - - R Run(typename CallbackParamTraits<A1>::ForwardType a1) { - return function_(CallbackForward(a1)); - } - - private: - R (*function_)(A1); -}; - -// Method: Arity 1. -template <typename R, typename T, typename A1> -class RunnableAdapter<R(T::*)(A1)> { - public: - typedef R (RunType)(T*, A1); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1)) - : method_(method) { - } - - R Run(T* object, typename CallbackParamTraits<A1>::ForwardType a1) { - return (object->*method_)(CallbackForward(a1)); - } - - private: - R (T::*method_)(A1); -}; - -// Const Method: Arity 1. -template <typename R, typename T, typename A1> -class RunnableAdapter<R(T::*)(A1) const> { - public: - typedef R (RunType)(const T*, A1); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1) const) - : method_(method) { - } - - R Run(const T* object, typename CallbackParamTraits<A1>::ForwardType a1) { - return (object->*method_)(CallbackForward(a1)); - } - - private: - R (T::*method_)(A1) const; -}; - -// Function: Arity 2. -template <typename R, typename A1, typename A2> -class RunnableAdapter<R(*)(A1, A2)> { - public: - typedef R (RunType)(A1, A2); - - explicit RunnableAdapter(R(*function)(A1, A2)) - : function_(function) { - } - - R Run(typename CallbackParamTraits<A1>::ForwardType a1, - typename CallbackParamTraits<A2>::ForwardType a2) { - return function_(CallbackForward(a1), CallbackForward(a2)); - } - - private: - R (*function_)(A1, A2); -}; - -// Method: Arity 2. -template <typename R, typename T, typename A1, typename A2> -class RunnableAdapter<R(T::*)(A1, A2)> { - public: - typedef R (RunType)(T*, A1, A2); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1, A2)) - : method_(method) { - } - - R Run(T* object, typename CallbackParamTraits<A1>::ForwardType a1, - typename CallbackParamTraits<A2>::ForwardType a2) { - return (object->*method_)(CallbackForward(a1), CallbackForward(a2)); - } - - private: - R (T::*method_)(A1, A2); -}; - -// Const Method: Arity 2. -template <typename R, typename T, typename A1, typename A2> -class RunnableAdapter<R(T::*)(A1, A2) const> { - public: - typedef R (RunType)(const T*, A1, A2); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1, A2) const) - : method_(method) { - } - - R Run(const T* object, typename CallbackParamTraits<A1>::ForwardType a1, - typename CallbackParamTraits<A2>::ForwardType a2) { - return (object->*method_)(CallbackForward(a1), CallbackForward(a2)); - } - - private: - R (T::*method_)(A1, A2) const; -}; - -// Function: Arity 3. -template <typename R, typename A1, typename A2, typename A3> -class RunnableAdapter<R(*)(A1, A2, A3)> { - public: - typedef R (RunType)(A1, A2, A3); - - explicit RunnableAdapter(R(*function)(A1, A2, A3)) - : function_(function) { - } - - R Run(typename CallbackParamTraits<A1>::ForwardType a1, - typename CallbackParamTraits<A2>::ForwardType a2, - typename CallbackParamTraits<A3>::ForwardType a3) { - return function_(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3)); - } - - private: - R (*function_)(A1, A2, A3); -}; - -// Method: Arity 3. -template <typename R, typename T, typename A1, typename A2, typename A3> -class RunnableAdapter<R(T::*)(A1, A2, A3)> { - public: - typedef R (RunType)(T*, A1, A2, A3); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1, A2, A3)) - : method_(method) { - } - - R Run(T* object, typename CallbackParamTraits<A1>::ForwardType a1, - typename CallbackParamTraits<A2>::ForwardType a2, - typename CallbackParamTraits<A3>::ForwardType a3) { - return (object->*method_)(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3)); - } - - private: - R (T::*method_)(A1, A2, A3); -}; - -// Const Method: Arity 3. -template <typename R, typename T, typename A1, typename A2, typename A3> -class RunnableAdapter<R(T::*)(A1, A2, A3) const> { - public: - typedef R (RunType)(const T*, A1, A2, A3); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1, A2, A3) const) - : method_(method) { - } - - R Run(const T* object, typename CallbackParamTraits<A1>::ForwardType a1, - typename CallbackParamTraits<A2>::ForwardType a2, - typename CallbackParamTraits<A3>::ForwardType a3) { - return (object->*method_)(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3)); - } - - private: - R (T::*method_)(A1, A2, A3) const; -}; - -// Function: Arity 4. -template <typename R, typename A1, typename A2, typename A3, typename A4> -class RunnableAdapter<R(*)(A1, A2, A3, A4)> { - public: - typedef R (RunType)(A1, A2, A3, A4); - - explicit RunnableAdapter(R(*function)(A1, A2, A3, A4)) - : function_(function) { - } - - R Run(typename CallbackParamTraits<A1>::ForwardType a1, - typename CallbackParamTraits<A2>::ForwardType a2, - typename CallbackParamTraits<A3>::ForwardType a3, - typename CallbackParamTraits<A4>::ForwardType a4) { - return function_(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4)); - } - - private: - R (*function_)(A1, A2, A3, A4); -}; - -// Method: Arity 4. -template <typename R, typename T, typename A1, typename A2, typename A3, - typename A4> -class RunnableAdapter<R(T::*)(A1, A2, A3, A4)> { - public: - typedef R (RunType)(T*, A1, A2, A3, A4); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1, A2, A3, A4)) - : method_(method) { - } - - R Run(T* object, typename CallbackParamTraits<A1>::ForwardType a1, - typename CallbackParamTraits<A2>::ForwardType a2, - typename CallbackParamTraits<A3>::ForwardType a3, - typename CallbackParamTraits<A4>::ForwardType a4) { - return (object->*method_)(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4)); - } - - private: - R (T::*method_)(A1, A2, A3, A4); -}; - -// Const Method: Arity 4. -template <typename R, typename T, typename A1, typename A2, typename A3, - typename A4> -class RunnableAdapter<R(T::*)(A1, A2, A3, A4) const> { - public: - typedef R (RunType)(const T*, A1, A2, A3, A4); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1, A2, A3, A4) const) - : method_(method) { - } - - R Run(const T* object, typename CallbackParamTraits<A1>::ForwardType a1, - typename CallbackParamTraits<A2>::ForwardType a2, - typename CallbackParamTraits<A3>::ForwardType a3, - typename CallbackParamTraits<A4>::ForwardType a4) { - return (object->*method_)(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4)); - } - - private: - R (T::*method_)(A1, A2, A3, A4) const; -}; - -// Function: Arity 5. -template <typename R, typename A1, typename A2, typename A3, typename A4, - typename A5> -class RunnableAdapter<R(*)(A1, A2, A3, A4, A5)> { - public: - typedef R (RunType)(A1, A2, A3, A4, A5); - - explicit RunnableAdapter(R(*function)(A1, A2, A3, A4, A5)) - : function_(function) { - } - - R Run(typename CallbackParamTraits<A1>::ForwardType a1, - typename CallbackParamTraits<A2>::ForwardType a2, - typename CallbackParamTraits<A3>::ForwardType a3, - typename CallbackParamTraits<A4>::ForwardType a4, - typename CallbackParamTraits<A5>::ForwardType a5) { - return function_(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4), CallbackForward(a5)); - } - - private: - R (*function_)(A1, A2, A3, A4, A5); -}; - -// Method: Arity 5. -template <typename R, typename T, typename A1, typename A2, typename A3, - typename A4, typename A5> -class RunnableAdapter<R(T::*)(A1, A2, A3, A4, A5)> { - public: - typedef R (RunType)(T*, A1, A2, A3, A4, A5); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1, A2, A3, A4, A5)) - : method_(method) { - } - - R Run(T* object, typename CallbackParamTraits<A1>::ForwardType a1, - typename CallbackParamTraits<A2>::ForwardType a2, - typename CallbackParamTraits<A3>::ForwardType a3, - typename CallbackParamTraits<A4>::ForwardType a4, - typename CallbackParamTraits<A5>::ForwardType a5) { - return (object->*method_)(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4), CallbackForward(a5)); - } - - private: - R (T::*method_)(A1, A2, A3, A4, A5); -}; - -// Const Method: Arity 5. -template <typename R, typename T, typename A1, typename A2, typename A3, - typename A4, typename A5> -class RunnableAdapter<R(T::*)(A1, A2, A3, A4, A5) const> { - public: - typedef R (RunType)(const T*, A1, A2, A3, A4, A5); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1, A2, A3, A4, A5) const) - : method_(method) { - } - - R Run(const T* object, typename CallbackParamTraits<A1>::ForwardType a1, - typename CallbackParamTraits<A2>::ForwardType a2, - typename CallbackParamTraits<A3>::ForwardType a3, - typename CallbackParamTraits<A4>::ForwardType a4, - typename CallbackParamTraits<A5>::ForwardType a5) { - return (object->*method_)(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4), CallbackForward(a5)); - } - - private: - R (T::*method_)(A1, A2, A3, A4, A5) const; -}; - -// Function: Arity 6. -template <typename R, typename A1, typename A2, typename A3, typename A4, - typename A5, typename A6> -class RunnableAdapter<R(*)(A1, A2, A3, A4, A5, A6)> { - public: - typedef R (RunType)(A1, A2, A3, A4, A5, A6); - - explicit RunnableAdapter(R(*function)(A1, A2, A3, A4, A5, A6)) - : function_(function) { - } - - R Run(typename CallbackParamTraits<A1>::ForwardType a1, - typename CallbackParamTraits<A2>::ForwardType a2, - typename CallbackParamTraits<A3>::ForwardType a3, - typename CallbackParamTraits<A4>::ForwardType a4, - typename CallbackParamTraits<A5>::ForwardType a5, - typename CallbackParamTraits<A6>::ForwardType a6) { - return function_(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4), CallbackForward(a5), - CallbackForward(a6)); - } - - private: - R (*function_)(A1, A2, A3, A4, A5, A6); -}; - -// Method: Arity 6. -template <typename R, typename T, typename A1, typename A2, typename A3, - typename A4, typename A5, typename A6> -class RunnableAdapter<R(T::*)(A1, A2, A3, A4, A5, A6)> { - public: - typedef R (RunType)(T*, A1, A2, A3, A4, A5, A6); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1, A2, A3, A4, A5, A6)) - : method_(method) { - } - - R Run(T* object, typename CallbackParamTraits<A1>::ForwardType a1, - typename CallbackParamTraits<A2>::ForwardType a2, - typename CallbackParamTraits<A3>::ForwardType a3, - typename CallbackParamTraits<A4>::ForwardType a4, - typename CallbackParamTraits<A5>::ForwardType a5, - typename CallbackParamTraits<A6>::ForwardType a6) { - return (object->*method_)(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4), CallbackForward(a5), - CallbackForward(a6)); - } - - private: - R (T::*method_)(A1, A2, A3, A4, A5, A6); -}; - -// Const Method: Arity 6. -template <typename R, typename T, typename A1, typename A2, typename A3, - typename A4, typename A5, typename A6> -class RunnableAdapter<R(T::*)(A1, A2, A3, A4, A5, A6) const> { - public: - typedef R (RunType)(const T*, A1, A2, A3, A4, A5, A6); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1, A2, A3, A4, A5, A6) const) - : method_(method) { - } - - R Run(const T* object, typename CallbackParamTraits<A1>::ForwardType a1, - typename CallbackParamTraits<A2>::ForwardType a2, - typename CallbackParamTraits<A3>::ForwardType a3, - typename CallbackParamTraits<A4>::ForwardType a4, - typename CallbackParamTraits<A5>::ForwardType a5, - typename CallbackParamTraits<A6>::ForwardType a6) { - return (object->*method_)(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4), CallbackForward(a5), - CallbackForward(a6)); - } - - private: - R (T::*method_)(A1, A2, A3, A4, A5, A6) const; -}; - -// Function: Arity 7. -template <typename R, typename A1, typename A2, typename A3, typename A4, - typename A5, typename A6, typename A7> -class RunnableAdapter<R(*)(A1, A2, A3, A4, A5, A6, A7)> { +// Function. +template <typename R, typename... Args> +class RunnableAdapter<R(*)(Args...)> { public: - typedef R (RunType)(A1, A2, A3, A4, A5, A6, A7); + typedef R (RunType)(Args...); - explicit RunnableAdapter(R(*function)(A1, A2, A3, A4, A5, A6, A7)) + explicit RunnableAdapter(R(*function)(Args...)) : function_(function) { } - R Run(typename CallbackParamTraits<A1>::ForwardType a1, - typename CallbackParamTraits<A2>::ForwardType a2, - typename CallbackParamTraits<A3>::ForwardType a3, - typename CallbackParamTraits<A4>::ForwardType a4, - typename CallbackParamTraits<A5>::ForwardType a5, - typename CallbackParamTraits<A6>::ForwardType a6, - typename CallbackParamTraits<A7>::ForwardType a7) { - return function_(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4), CallbackForward(a5), - CallbackForward(a6), CallbackForward(a7)); + R Run(typename CallbackParamTraits<Args>::ForwardType... args) { + return function_(CallbackForward(args)...); } private: - R (*function_)(A1, A2, A3, A4, A5, A6, A7); + R (*function_)(Args...); }; -// Method: Arity 7. -template <typename R, typename T, typename A1, typename A2, typename A3, - typename A4, typename A5, typename A6, typename A7> -class RunnableAdapter<R(T::*)(A1, A2, A3, A4, A5, A6, A7)> { +// Method. +template <typename R, typename T, typename... Args> +class RunnableAdapter<R(T::*)(Args...)> { public: - typedef R (RunType)(T*, A1, A2, A3, A4, A5, A6, A7); + typedef R (RunType)(T*, Args...); typedef true_type IsMethod; - explicit RunnableAdapter(R(T::*method)(A1, A2, A3, A4, A5, A6, A7)) + explicit RunnableAdapter(R(T::*method)(Args...)) : method_(method) { } - R Run(T* object, typename CallbackParamTraits<A1>::ForwardType a1, - typename CallbackParamTraits<A2>::ForwardType a2, - typename CallbackParamTraits<A3>::ForwardType a3, - typename CallbackParamTraits<A4>::ForwardType a4, - typename CallbackParamTraits<A5>::ForwardType a5, - typename CallbackParamTraits<A6>::ForwardType a6, - typename CallbackParamTraits<A7>::ForwardType a7) { - return (object->*method_)(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4), CallbackForward(a5), - CallbackForward(a6), CallbackForward(a7)); + R Run(T* object, typename CallbackParamTraits<Args>::ForwardType... args) { + return (object->*method_)(CallbackForward(args)...); } private: - R (T::*method_)(A1, A2, A3, A4, A5, A6, A7); + R (T::*method_)(Args...); }; -// Const Method: Arity 7. -template <typename R, typename T, typename A1, typename A2, typename A3, - typename A4, typename A5, typename A6, typename A7> -class RunnableAdapter<R(T::*)(A1, A2, A3, A4, A5, A6, A7) const> { +// Const Method. +template <typename R, typename T, typename... Args> +class RunnableAdapter<R(T::*)(Args...) const> { public: - typedef R (RunType)(const T*, A1, A2, A3, A4, A5, A6, A7); + typedef R (RunType)(const T*, Args...); typedef true_type IsMethod; - explicit RunnableAdapter(R(T::*method)(A1, A2, A3, A4, A5, A6, A7) const) + explicit RunnableAdapter(R(T::*method)(Args...) const) : method_(method) { } - R Run(const T* object, typename CallbackParamTraits<A1>::ForwardType a1, - typename CallbackParamTraits<A2>::ForwardType a2, - typename CallbackParamTraits<A3>::ForwardType a3, - typename CallbackParamTraits<A4>::ForwardType a4, - typename CallbackParamTraits<A5>::ForwardType a5, - typename CallbackParamTraits<A6>::ForwardType a6, - typename CallbackParamTraits<A7>::ForwardType a7) { - return (object->*method_)(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4), CallbackForward(a5), - CallbackForward(a6), CallbackForward(a7)); + R Run(const T* object, + typename CallbackParamTraits<Args>::ForwardType... args) { + return (object->*method_)(CallbackForward(args)...); } private: - R (T::*method_)(A1, A2, A3, A4, A5, A6, A7) const; -}; - - -// FunctionTraits<> -// -// Breaks a function signature apart into typedefs for easier introspection. -template <typename Sig> -struct FunctionTraits; - -template <typename R> -struct FunctionTraits<R()> { - typedef R ReturnType; -}; - -template <typename R, typename A1> -struct FunctionTraits<R(A1)> { - typedef R ReturnType; - typedef A1 A1Type; -}; - -template <typename R, typename A1, typename A2> -struct FunctionTraits<R(A1, A2)> { - typedef R ReturnType; - typedef A1 A1Type; - typedef A2 A2Type; -}; - -template <typename R, typename A1, typename A2, typename A3> -struct FunctionTraits<R(A1, A2, A3)> { - typedef R ReturnType; - typedef A1 A1Type; - typedef A2 A2Type; - typedef A3 A3Type; -}; - -template <typename R, typename A1, typename A2, typename A3, typename A4> -struct FunctionTraits<R(A1, A2, A3, A4)> { - typedef R ReturnType; - typedef A1 A1Type; - typedef A2 A2Type; - typedef A3 A3Type; - typedef A4 A4Type; -}; - -template <typename R, typename A1, typename A2, typename A3, typename A4, - typename A5> -struct FunctionTraits<R(A1, A2, A3, A4, A5)> { - typedef R ReturnType; - typedef A1 A1Type; - typedef A2 A2Type; - typedef A3 A3Type; - typedef A4 A4Type; - typedef A5 A5Type; -}; - -template <typename R, typename A1, typename A2, typename A3, typename A4, - typename A5, typename A6> -struct FunctionTraits<R(A1, A2, A3, A4, A5, A6)> { - typedef R ReturnType; - typedef A1 A1Type; - typedef A2 A2Type; - typedef A3 A3Type; - typedef A4 A4Type; - typedef A5 A5Type; - typedef A6 A6Type; -}; - -template <typename R, typename A1, typename A2, typename A3, typename A4, - typename A5, typename A6, typename A7> -struct FunctionTraits<R(A1, A2, A3, A4, A5, A6, A7)> { - typedef R ReturnType; - typedef A1 A1Type; - typedef A2 A2Type; - typedef A3 A3Type; - typedef A4 A4Type; - typedef A5 A5Type; - typedef A6 A6Type; - typedef A7 A7Type; + R (T::*method_)(Args...) const; }; @@ -729,47 +207,9 @@ struct FunctionTraits<R(A1, A2, A3, A4, A5, A6, A7)> { template <typename Sig> struct ForceVoidReturn; -template <typename R> -struct ForceVoidReturn<R()> { - typedef void(RunType)(); -}; - -template <typename R, typename A1> -struct ForceVoidReturn<R(A1)> { - typedef void(RunType)(A1); -}; - -template <typename R, typename A1, typename A2> -struct ForceVoidReturn<R(A1, A2)> { - typedef void(RunType)(A1, A2); -}; - -template <typename R, typename A1, typename A2, typename A3> -struct ForceVoidReturn<R(A1, A2, A3)> { - typedef void(RunType)(A1, A2, A3); -}; - -template <typename R, typename A1, typename A2, typename A3, typename A4> -struct ForceVoidReturn<R(A1, A2, A3, A4)> { - typedef void(RunType)(A1, A2, A3, A4); -}; - -template <typename R, typename A1, typename A2, typename A3, typename A4, - typename A5> -struct ForceVoidReturn<R(A1, A2, A3, A4, A5)> { - typedef void(RunType)(A1, A2, A3, A4, A5); -}; - -template <typename R, typename A1, typename A2, typename A3, typename A4, - typename A5, typename A6> -struct ForceVoidReturn<R(A1, A2, A3, A4, A5, A6)> { - typedef void(RunType)(A1, A2, A3, A4, A5, A6); -}; - -template <typename R, typename A1, typename A2, typename A3, typename A4, - typename A5, typename A6, typename A7> -struct ForceVoidReturn<R(A1, A2, A3, A4, A5, A6, A7)> { - typedef void(RunType)(A1, A2, A3, A4, A5, A6, A7); +template <typename R, typename... Args> +struct ForceVoidReturn<R(Args...)> { + typedef void(RunType)(Args...); }; @@ -783,14 +223,14 @@ struct FunctorTraits { }; template <typename T> -struct FunctorTraits<IgnoreResultHelper<T> > { +struct FunctorTraits<IgnoreResultHelper<T>> { typedef typename FunctorTraits<T>::RunnableType RunnableType; typedef typename ForceVoidReturn< typename RunnableType::RunType>::RunType RunType; }; template <typename T> -struct FunctorTraits<Callback<T> > { +struct FunctorTraits<Callback<T>> { typedef Callback<T> RunnableType; typedef typename Callback<T>::RunType RunType; }; @@ -812,7 +252,7 @@ MakeRunnable(const IgnoreResultHelper<T>& t) { } template <typename T> -const typename FunctorTraits<Callback<T> >::RunnableType& +const typename FunctorTraits<Callback<T>>::RunnableType& MakeRunnable(const Callback<T>& t) { DCHECK(!t.is_null()); return t; @@ -840,246 +280,27 @@ template <bool IsWeakCall, typename ReturnType, typename Runnable, typename ArgsType> struct InvokeHelper; -template <typename ReturnType, typename Runnable> -struct InvokeHelper<false, ReturnType, Runnable, - void()> { - static ReturnType MakeItSo(Runnable runnable) { - return runnable.Run(); - } -}; - -template <typename Runnable> -struct InvokeHelper<false, void, Runnable, - void()> { - static void MakeItSo(Runnable runnable) { - runnable.Run(); - } -}; - -template <typename ReturnType, typename Runnable,typename A1> -struct InvokeHelper<false, ReturnType, Runnable, - void(A1)> { - static ReturnType MakeItSo(Runnable runnable, A1 a1) { - return runnable.Run(CallbackForward(a1)); - } -}; - -template <typename Runnable,typename A1> -struct InvokeHelper<false, void, Runnable, - void(A1)> { - static void MakeItSo(Runnable runnable, A1 a1) { - runnable.Run(CallbackForward(a1)); - } -}; - -template <typename Runnable, typename BoundWeakPtr> -struct InvokeHelper<true, void, Runnable, - void(BoundWeakPtr)> { - static void MakeItSo(Runnable runnable, BoundWeakPtr weak_ptr) { - if (!weak_ptr.get()) { - return; - } - runnable.Run(weak_ptr.get()); - } -}; - -template <typename ReturnType, typename Runnable,typename A1, typename A2> -struct InvokeHelper<false, ReturnType, Runnable, - void(A1, A2)> { - static ReturnType MakeItSo(Runnable runnable, A1 a1, A2 a2) { - return runnable.Run(CallbackForward(a1), CallbackForward(a2)); - } -}; - -template <typename Runnable,typename A1, typename A2> -struct InvokeHelper<false, void, Runnable, - void(A1, A2)> { - static void MakeItSo(Runnable runnable, A1 a1, A2 a2) { - runnable.Run(CallbackForward(a1), CallbackForward(a2)); - } -}; - -template <typename Runnable, typename BoundWeakPtr, typename A2> -struct InvokeHelper<true, void, Runnable, - void(BoundWeakPtr, A2)> { - static void MakeItSo(Runnable runnable, BoundWeakPtr weak_ptr, A2 a2) { - if (!weak_ptr.get()) { - return; - } - runnable.Run(weak_ptr.get(), CallbackForward(a2)); - } -}; - -template <typename ReturnType, typename Runnable,typename A1, typename A2, - typename A3> -struct InvokeHelper<false, ReturnType, Runnable, - void(A1, A2, A3)> { - static ReturnType MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3) { - return runnable.Run(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3)); - } -}; - -template <typename Runnable,typename A1, typename A2, typename A3> -struct InvokeHelper<false, void, Runnable, - void(A1, A2, A3)> { - static void MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3) { - runnable.Run(CallbackForward(a1), CallbackForward(a2), CallbackForward(a3)); - } -}; - -template <typename Runnable, typename BoundWeakPtr, typename A2, typename A3> -struct InvokeHelper<true, void, Runnable, - void(BoundWeakPtr, A2, A3)> { - static void MakeItSo(Runnable runnable, BoundWeakPtr weak_ptr, A2 a2, A3 a3) { - if (!weak_ptr.get()) { - return; - } - runnable.Run(weak_ptr.get(), CallbackForward(a2), CallbackForward(a3)); - } -}; - -template <typename ReturnType, typename Runnable,typename A1, typename A2, - typename A3, typename A4> -struct InvokeHelper<false, ReturnType, Runnable, - void(A1, A2, A3, A4)> { - static ReturnType MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4) { - return runnable.Run(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4)); - } -}; - -template <typename Runnable,typename A1, typename A2, typename A3, typename A4> -struct InvokeHelper<false, void, Runnable, - void(A1, A2, A3, A4)> { - static void MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4) { - runnable.Run(CallbackForward(a1), CallbackForward(a2), CallbackForward(a3), - CallbackForward(a4)); - } -}; - -template <typename Runnable, typename BoundWeakPtr, typename A2, typename A3, - typename A4> -struct InvokeHelper<true, void, Runnable, - void(BoundWeakPtr, A2, A3, A4)> { - static void MakeItSo(Runnable runnable, BoundWeakPtr weak_ptr, A2 a2, A3 a3, - A4 a4) { - if (!weak_ptr.get()) { - return; - } - runnable.Run(weak_ptr.get(), CallbackForward(a2), CallbackForward(a3), - CallbackForward(a4)); - } -}; - -template <typename ReturnType, typename Runnable,typename A1, typename A2, - typename A3, typename A4, typename A5> -struct InvokeHelper<false, ReturnType, Runnable, - void(A1, A2, A3, A4, A5)> { - static ReturnType MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4, - A5 a5) { - return runnable.Run(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4), CallbackForward(a5)); +template <typename ReturnType, typename Runnable, typename... Args> +struct InvokeHelper<false, ReturnType, Runnable, TypeList<Args...>> { + static ReturnType MakeItSo(Runnable runnable, Args... args) { + return runnable.Run(CallbackForward(args)...); } }; -template <typename Runnable,typename A1, typename A2, typename A3, typename A4, - typename A5> -struct InvokeHelper<false, void, Runnable, - void(A1, A2, A3, A4, A5)> { - static void MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { - runnable.Run(CallbackForward(a1), CallbackForward(a2), CallbackForward(a3), - CallbackForward(a4), CallbackForward(a5)); +template <typename Runnable, typename... Args> +struct InvokeHelper<false, void, Runnable, TypeList<Args...>> { + static void MakeItSo(Runnable runnable, Args... args) { + runnable.Run(CallbackForward(args)...); } }; -template <typename Runnable, typename BoundWeakPtr, typename A2, typename A3, - typename A4, typename A5> -struct InvokeHelper<true, void, Runnable, - void(BoundWeakPtr, A2, A3, A4, A5)> { - static void MakeItSo(Runnable runnable, BoundWeakPtr weak_ptr, A2 a2, A3 a3, - A4 a4, A5 a5) { +template <typename Runnable, typename BoundWeakPtr, typename... Args> +struct InvokeHelper<true, void, Runnable, TypeList<BoundWeakPtr, Args...>> { + static void MakeItSo(Runnable runnable, BoundWeakPtr weak_ptr, Args... args) { if (!weak_ptr.get()) { return; } - runnable.Run(weak_ptr.get(), CallbackForward(a2), CallbackForward(a3), - CallbackForward(a4), CallbackForward(a5)); - } -}; - -template <typename ReturnType, typename Runnable,typename A1, typename A2, - typename A3, typename A4, typename A5, typename A6> -struct InvokeHelper<false, ReturnType, Runnable, - void(A1, A2, A3, A4, A5, A6)> { - static ReturnType MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4, - A5 a5, A6 a6) { - return runnable.Run(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4), CallbackForward(a5), - CallbackForward(a6)); - } -}; - -template <typename Runnable,typename A1, typename A2, typename A3, typename A4, - typename A5, typename A6> -struct InvokeHelper<false, void, Runnable, - void(A1, A2, A3, A4, A5, A6)> { - static void MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, - A6 a6) { - runnable.Run(CallbackForward(a1), CallbackForward(a2), CallbackForward(a3), - CallbackForward(a4), CallbackForward(a5), CallbackForward(a6)); - } -}; - -template <typename Runnable, typename BoundWeakPtr, typename A2, typename A3, - typename A4, typename A5, typename A6> -struct InvokeHelper<true, void, Runnable, - void(BoundWeakPtr, A2, A3, A4, A5, A6)> { - static void MakeItSo(Runnable runnable, BoundWeakPtr weak_ptr, A2 a2, A3 a3, - A4 a4, A5 a5, A6 a6) { - if (!weak_ptr.get()) { - return; - } - runnable.Run(weak_ptr.get(), CallbackForward(a2), CallbackForward(a3), - CallbackForward(a4), CallbackForward(a5), CallbackForward(a6)); - } -}; - -template <typename ReturnType, typename Runnable,typename A1, typename A2, - typename A3, typename A4, typename A5, typename A6, typename A7> -struct InvokeHelper<false, ReturnType, Runnable, - void(A1, A2, A3, A4, A5, A6, A7)> { - static ReturnType MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4, - A5 a5, A6 a6, A7 a7) { - return runnable.Run(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4), CallbackForward(a5), - CallbackForward(a6), CallbackForward(a7)); - } -}; - -template <typename Runnable,typename A1, typename A2, typename A3, typename A4, - typename A5, typename A6, typename A7> -struct InvokeHelper<false, void, Runnable, - void(A1, A2, A3, A4, A5, A6, A7)> { - static void MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, - A6 a6, A7 a7) { - runnable.Run(CallbackForward(a1), CallbackForward(a2), CallbackForward(a3), - CallbackForward(a4), CallbackForward(a5), CallbackForward(a6), - CallbackForward(a7)); - } -}; - -template <typename Runnable, typename BoundWeakPtr, typename A2, typename A3, - typename A4, typename A5, typename A6, typename A7> -struct InvokeHelper<true, void, Runnable, - void(BoundWeakPtr, A2, A3, A4, A5, A6, A7)> { - static void MakeItSo(Runnable runnable, BoundWeakPtr weak_ptr, A2 a2, A3 a3, - A4 a4, A5 a5, A6 a6, A7 a7) { - if (!weak_ptr.get()) { - return; - } - runnable.Run(weak_ptr.get(), CallbackForward(a2), CallbackForward(a3), - CallbackForward(a4), CallbackForward(a5), CallbackForward(a6), - CallbackForward(a7)); + runnable.Run(weak_ptr.get(), CallbackForward(args)...); } }; @@ -1099,1419 +320,30 @@ struct InvokeHelper<true, ReturnType, Runnable, ArgsType> { // Invoker<> // // See description at the top of the file. -template <int NumBound, typename Storage, typename RunType> +template <typename BoundIndices, + typename StorageType, typename Unwrappers, + typename InvokeHelperType, typename UnboundForwardRunType> struct Invoker; -// Arity 0 -> 0. -template <typename StorageType, typename R> -struct Invoker<0, StorageType, R()> { - typedef R(RunType)(BindStateBase*); - - typedef R(UnboundRunType)(); - - static R Run(BindStateBase* base) { - StorageType* storage = static_cast<StorageType*>(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void()> - ::MakeItSo(storage->runnable_); - } -}; - -// Arity 1 -> 1. -template <typename StorageType, typename R,typename X1> -struct Invoker<0, StorageType, R(X1)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits<X1>::ForwardType); - - typedef R(UnboundRunType)(X1); - - static R Run(BindStateBase* base, - typename CallbackParamTraits<X1>::ForwardType x1) { - StorageType* storage = static_cast<StorageType*>(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void(typename CallbackParamTraits<X1>::ForwardType x1)> - ::MakeItSo(storage->runnable_, CallbackForward(x1)); - } -}; - -// Arity 1 -> 0. -template <typename StorageType, typename R,typename X1> -struct Invoker<1, StorageType, R(X1)> { - typedef R(RunType)(BindStateBase*); - - typedef R(UnboundRunType)(); - - static R Run(BindStateBase* base) { - StorageType* storage = static_cast<StorageType*>(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void(typename Bound1UnwrapTraits::ForwardType)> - ::MakeItSo(storage->runnable_, CallbackForward(x1)); - } -}; - -// Arity 2 -> 2. -template <typename StorageType, typename R,typename X1, typename X2> -struct Invoker<0, StorageType, R(X1, X2)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits<X1>::ForwardType, - typename CallbackParamTraits<X2>::ForwardType); - - typedef R(UnboundRunType)(X1, X2); - - static R Run(BindStateBase* base, - typename CallbackParamTraits<X1>::ForwardType x1, - typename CallbackParamTraits<X2>::ForwardType x2) { - StorageType* storage = static_cast<StorageType*>(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void(typename CallbackParamTraits<X1>::ForwardType x1, - typename CallbackParamTraits<X2>::ForwardType x2)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2)); - } -}; - -// Arity 2 -> 1. -template <typename StorageType, typename R,typename X1, typename X2> -struct Invoker<1, StorageType, R(X1, X2)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits<X2>::ForwardType); - - typedef R(UnboundRunType)(X2); - - static R Run(BindStateBase* base, - typename CallbackParamTraits<X2>::ForwardType x2) { - StorageType* storage = static_cast<StorageType*>(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void(typename Bound1UnwrapTraits::ForwardType, - typename CallbackParamTraits<X2>::ForwardType x2)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2)); - } -}; - -// Arity 2 -> 0. -template <typename StorageType, typename R,typename X1, typename X2> -struct Invoker<2, StorageType, R(X1, X2)> { - typedef R(RunType)(BindStateBase*); - - typedef R(UnboundRunType)(); - - static R Run(BindStateBase* base) { - StorageType* storage = static_cast<StorageType*>(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void(typename Bound1UnwrapTraits::ForwardType, - typename Bound2UnwrapTraits::ForwardType)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2)); - } -}; - -// Arity 3 -> 3. -template <typename StorageType, typename R,typename X1, typename X2, - typename X3> -struct Invoker<0, StorageType, R(X1, X2, X3)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits<X1>::ForwardType, - typename CallbackParamTraits<X2>::ForwardType, - typename CallbackParamTraits<X3>::ForwardType); - - typedef R(UnboundRunType)(X1, X2, X3); - - static R Run(BindStateBase* base, - typename CallbackParamTraits<X1>::ForwardType x1, - typename CallbackParamTraits<X2>::ForwardType x2, - typename CallbackParamTraits<X3>::ForwardType x3) { - StorageType* storage = static_cast<StorageType*>(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void(typename CallbackParamTraits<X1>::ForwardType x1, - typename CallbackParamTraits<X2>::ForwardType x2, - typename CallbackParamTraits<X3>::ForwardType x3)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3)); - } -}; - -// Arity 3 -> 2. -template <typename StorageType, typename R,typename X1, typename X2, - typename X3> -struct Invoker<1, StorageType, R(X1, X2, X3)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits<X2>::ForwardType, - typename CallbackParamTraits<X3>::ForwardType); - - typedef R(UnboundRunType)(X2, X3); - - static R Run(BindStateBase* base, - typename CallbackParamTraits<X2>::ForwardType x2, - typename CallbackParamTraits<X3>::ForwardType x3) { - StorageType* storage = static_cast<StorageType*>(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void(typename Bound1UnwrapTraits::ForwardType, - typename CallbackParamTraits<X2>::ForwardType x2, - typename CallbackParamTraits<X3>::ForwardType x3)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3)); - } -}; - -// Arity 3 -> 1. -template <typename StorageType, typename R,typename X1, typename X2, - typename X3> -struct Invoker<2, StorageType, R(X1, X2, X3)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits<X3>::ForwardType); - - typedef R(UnboundRunType)(X3); - - static R Run(BindStateBase* base, - typename CallbackParamTraits<X3>::ForwardType x3) { - StorageType* storage = static_cast<StorageType*>(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void(typename Bound1UnwrapTraits::ForwardType, - typename Bound2UnwrapTraits::ForwardType, - typename CallbackParamTraits<X3>::ForwardType x3)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3)); - } -}; - -// Arity 3 -> 0. -template <typename StorageType, typename R,typename X1, typename X2, - typename X3> -struct Invoker<3, StorageType, R(X1, X2, X3)> { - typedef R(RunType)(BindStateBase*); - - typedef R(UnboundRunType)(); - - static R Run(BindStateBase* base) { - StorageType* storage = static_cast<StorageType*>(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void(typename Bound1UnwrapTraits::ForwardType, - typename Bound2UnwrapTraits::ForwardType, - typename Bound3UnwrapTraits::ForwardType)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3)); - } -}; - -// Arity 4 -> 4. -template <typename StorageType, typename R,typename X1, typename X2, - typename X3, typename X4> -struct Invoker<0, StorageType, R(X1, X2, X3, X4)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits<X1>::ForwardType, - typename CallbackParamTraits<X2>::ForwardType, - typename CallbackParamTraits<X3>::ForwardType, - typename CallbackParamTraits<X4>::ForwardType); - - typedef R(UnboundRunType)(X1, X2, X3, X4); - - static R Run(BindStateBase* base, - typename CallbackParamTraits<X1>::ForwardType x1, - typename CallbackParamTraits<X2>::ForwardType x2, - typename CallbackParamTraits<X3>::ForwardType x3, - typename CallbackParamTraits<X4>::ForwardType x4) { - StorageType* storage = static_cast<StorageType*>(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void(typename CallbackParamTraits<X1>::ForwardType x1, - typename CallbackParamTraits<X2>::ForwardType x2, - typename CallbackParamTraits<X3>::ForwardType x3, - typename CallbackParamTraits<X4>::ForwardType x4)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4)); - } -}; - -// Arity 4 -> 3. -template <typename StorageType, typename R,typename X1, typename X2, - typename X3, typename X4> -struct Invoker<1, StorageType, R(X1, X2, X3, X4)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits<X2>::ForwardType, - typename CallbackParamTraits<X3>::ForwardType, - typename CallbackParamTraits<X4>::ForwardType); - - typedef R(UnboundRunType)(X2, X3, X4); - - static R Run(BindStateBase* base, - typename CallbackParamTraits<X2>::ForwardType x2, - typename CallbackParamTraits<X3>::ForwardType x3, - typename CallbackParamTraits<X4>::ForwardType x4) { - StorageType* storage = static_cast<StorageType*>(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void(typename Bound1UnwrapTraits::ForwardType, - typename CallbackParamTraits<X2>::ForwardType x2, - typename CallbackParamTraits<X3>::ForwardType x3, - typename CallbackParamTraits<X4>::ForwardType x4)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4)); - } -}; - -// Arity 4 -> 2. -template <typename StorageType, typename R,typename X1, typename X2, - typename X3, typename X4> -struct Invoker<2, StorageType, R(X1, X2, X3, X4)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits<X3>::ForwardType, - typename CallbackParamTraits<X4>::ForwardType); - - typedef R(UnboundRunType)(X3, X4); - - static R Run(BindStateBase* base, - typename CallbackParamTraits<X3>::ForwardType x3, - typename CallbackParamTraits<X4>::ForwardType x4) { - StorageType* storage = static_cast<StorageType*>(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void(typename Bound1UnwrapTraits::ForwardType, - typename Bound2UnwrapTraits::ForwardType, - typename CallbackParamTraits<X3>::ForwardType x3, - typename CallbackParamTraits<X4>::ForwardType x4)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4)); - } -}; - -// Arity 4 -> 1. -template <typename StorageType, typename R,typename X1, typename X2, - typename X3, typename X4> -struct Invoker<3, StorageType, R(X1, X2, X3, X4)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits<X4>::ForwardType); - - typedef R(UnboundRunType)(X4); - - static R Run(BindStateBase* base, - typename CallbackParamTraits<X4>::ForwardType x4) { - StorageType* storage = static_cast<StorageType*>(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void(typename Bound1UnwrapTraits::ForwardType, - typename Bound2UnwrapTraits::ForwardType, - typename Bound3UnwrapTraits::ForwardType, - typename CallbackParamTraits<X4>::ForwardType x4)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4)); - } -}; - -// Arity 4 -> 0. -template <typename StorageType, typename R,typename X1, typename X2, - typename X3, typename X4> -struct Invoker<4, StorageType, R(X1, X2, X3, X4)> { - typedef R(RunType)(BindStateBase*); - - typedef R(UnboundRunType)(); - - static R Run(BindStateBase* base) { - StorageType* storage = static_cast<StorageType*>(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - typename Bound4UnwrapTraits::ForwardType x4 = - Bound4UnwrapTraits::Unwrap(storage->p4_); - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void(typename Bound1UnwrapTraits::ForwardType, - typename Bound2UnwrapTraits::ForwardType, - typename Bound3UnwrapTraits::ForwardType, - typename Bound4UnwrapTraits::ForwardType)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4)); - } -}; - -// Arity 5 -> 5. -template <typename StorageType, typename R,typename X1, typename X2, - typename X3, typename X4, typename X5> -struct Invoker<0, StorageType, R(X1, X2, X3, X4, X5)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits<X1>::ForwardType, - typename CallbackParamTraits<X2>::ForwardType, - typename CallbackParamTraits<X3>::ForwardType, - typename CallbackParamTraits<X4>::ForwardType, - typename CallbackParamTraits<X5>::ForwardType); - - typedef R(UnboundRunType)(X1, X2, X3, X4, X5); - - static R Run(BindStateBase* base, - typename CallbackParamTraits<X1>::ForwardType x1, - typename CallbackParamTraits<X2>::ForwardType x2, - typename CallbackParamTraits<X3>::ForwardType x3, - typename CallbackParamTraits<X4>::ForwardType x4, - typename CallbackParamTraits<X5>::ForwardType x5) { - StorageType* storage = static_cast<StorageType*>(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void(typename CallbackParamTraits<X1>::ForwardType x1, - typename CallbackParamTraits<X2>::ForwardType x2, - typename CallbackParamTraits<X3>::ForwardType x3, - typename CallbackParamTraits<X4>::ForwardType x4, - typename CallbackParamTraits<X5>::ForwardType x5)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5)); - } -}; - -// Arity 5 -> 4. -template <typename StorageType, typename R,typename X1, typename X2, - typename X3, typename X4, typename X5> -struct Invoker<1, StorageType, R(X1, X2, X3, X4, X5)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits<X2>::ForwardType, - typename CallbackParamTraits<X3>::ForwardType, - typename CallbackParamTraits<X4>::ForwardType, - typename CallbackParamTraits<X5>::ForwardType); - - typedef R(UnboundRunType)(X2, X3, X4, X5); - - static R Run(BindStateBase* base, - typename CallbackParamTraits<X2>::ForwardType x2, - typename CallbackParamTraits<X3>::ForwardType x3, - typename CallbackParamTraits<X4>::ForwardType x4, - typename CallbackParamTraits<X5>::ForwardType x5) { - StorageType* storage = static_cast<StorageType*>(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void(typename Bound1UnwrapTraits::ForwardType, - typename CallbackParamTraits<X2>::ForwardType x2, - typename CallbackParamTraits<X3>::ForwardType x3, - typename CallbackParamTraits<X4>::ForwardType x4, - typename CallbackParamTraits<X5>::ForwardType x5)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5)); - } -}; - -// Arity 5 -> 3. -template <typename StorageType, typename R,typename X1, typename X2, - typename X3, typename X4, typename X5> -struct Invoker<2, StorageType, R(X1, X2, X3, X4, X5)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits<X3>::ForwardType, - typename CallbackParamTraits<X4>::ForwardType, - typename CallbackParamTraits<X5>::ForwardType); - - typedef R(UnboundRunType)(X3, X4, X5); - - static R Run(BindStateBase* base, - typename CallbackParamTraits<X3>::ForwardType x3, - typename CallbackParamTraits<X4>::ForwardType x4, - typename CallbackParamTraits<X5>::ForwardType x5) { - StorageType* storage = static_cast<StorageType*>(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void(typename Bound1UnwrapTraits::ForwardType, - typename Bound2UnwrapTraits::ForwardType, - typename CallbackParamTraits<X3>::ForwardType x3, - typename CallbackParamTraits<X4>::ForwardType x4, - typename CallbackParamTraits<X5>::ForwardType x5)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5)); - } -}; - -// Arity 5 -> 2. -template <typename StorageType, typename R,typename X1, typename X2, - typename X3, typename X4, typename X5> -struct Invoker<3, StorageType, R(X1, X2, X3, X4, X5)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits<X4>::ForwardType, - typename CallbackParamTraits<X5>::ForwardType); - - typedef R(UnboundRunType)(X4, X5); - - static R Run(BindStateBase* base, - typename CallbackParamTraits<X4>::ForwardType x4, - typename CallbackParamTraits<X5>::ForwardType x5) { - StorageType* storage = static_cast<StorageType*>(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void(typename Bound1UnwrapTraits::ForwardType, - typename Bound2UnwrapTraits::ForwardType, - typename Bound3UnwrapTraits::ForwardType, - typename CallbackParamTraits<X4>::ForwardType x4, - typename CallbackParamTraits<X5>::ForwardType x5)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5)); - } -}; - -// Arity 5 -> 1. -template <typename StorageType, typename R,typename X1, typename X2, - typename X3, typename X4, typename X5> -struct Invoker<4, StorageType, R(X1, X2, X3, X4, X5)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits<X5>::ForwardType); - - typedef R(UnboundRunType)(X5); - - static R Run(BindStateBase* base, - typename CallbackParamTraits<X5>::ForwardType x5) { - StorageType* storage = static_cast<StorageType*>(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - typename Bound4UnwrapTraits::ForwardType x4 = - Bound4UnwrapTraits::Unwrap(storage->p4_); - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void(typename Bound1UnwrapTraits::ForwardType, - typename Bound2UnwrapTraits::ForwardType, - typename Bound3UnwrapTraits::ForwardType, - typename Bound4UnwrapTraits::ForwardType, - typename CallbackParamTraits<X5>::ForwardType x5)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5)); - } -}; - -// Arity 5 -> 0. -template <typename StorageType, typename R,typename X1, typename X2, - typename X3, typename X4, typename X5> -struct Invoker<5, StorageType, R(X1, X2, X3, X4, X5)> { - typedef R(RunType)(BindStateBase*); - - typedef R(UnboundRunType)(); - - static R Run(BindStateBase* base) { - StorageType* storage = static_cast<StorageType*>(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits; - typedef typename StorageType::Bound5UnwrapTraits Bound5UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - typename Bound4UnwrapTraits::ForwardType x4 = - Bound4UnwrapTraits::Unwrap(storage->p4_); - typename Bound5UnwrapTraits::ForwardType x5 = - Bound5UnwrapTraits::Unwrap(storage->p5_); - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void(typename Bound1UnwrapTraits::ForwardType, - typename Bound2UnwrapTraits::ForwardType, - typename Bound3UnwrapTraits::ForwardType, - typename Bound4UnwrapTraits::ForwardType, - typename Bound5UnwrapTraits::ForwardType)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5)); - } -}; - -// Arity 6 -> 6. -template <typename StorageType, typename R,typename X1, typename X2, - typename X3, typename X4, typename X5, typename X6> -struct Invoker<0, StorageType, R(X1, X2, X3, X4, X5, X6)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits<X1>::ForwardType, - typename CallbackParamTraits<X2>::ForwardType, - typename CallbackParamTraits<X3>::ForwardType, - typename CallbackParamTraits<X4>::ForwardType, - typename CallbackParamTraits<X5>::ForwardType, - typename CallbackParamTraits<X6>::ForwardType); - - typedef R(UnboundRunType)(X1, X2, X3, X4, X5, X6); - - static R Run(BindStateBase* base, - typename CallbackParamTraits<X1>::ForwardType x1, - typename CallbackParamTraits<X2>::ForwardType x2, - typename CallbackParamTraits<X3>::ForwardType x3, - typename CallbackParamTraits<X4>::ForwardType x4, - typename CallbackParamTraits<X5>::ForwardType x5, - typename CallbackParamTraits<X6>::ForwardType x6) { - StorageType* storage = static_cast<StorageType*>(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void(typename CallbackParamTraits<X1>::ForwardType x1, - typename CallbackParamTraits<X2>::ForwardType x2, - typename CallbackParamTraits<X3>::ForwardType x3, - typename CallbackParamTraits<X4>::ForwardType x4, - typename CallbackParamTraits<X5>::ForwardType x5, - typename CallbackParamTraits<X6>::ForwardType x6)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6)); - } -}; - -// Arity 6 -> 5. -template <typename StorageType, typename R,typename X1, typename X2, - typename X3, typename X4, typename X5, typename X6> -struct Invoker<1, StorageType, R(X1, X2, X3, X4, X5, X6)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits<X2>::ForwardType, - typename CallbackParamTraits<X3>::ForwardType, - typename CallbackParamTraits<X4>::ForwardType, - typename CallbackParamTraits<X5>::ForwardType, - typename CallbackParamTraits<X6>::ForwardType); - - typedef R(UnboundRunType)(X2, X3, X4, X5, X6); - - static R Run(BindStateBase* base, - typename CallbackParamTraits<X2>::ForwardType x2, - typename CallbackParamTraits<X3>::ForwardType x3, - typename CallbackParamTraits<X4>::ForwardType x4, - typename CallbackParamTraits<X5>::ForwardType x5, - typename CallbackParamTraits<X6>::ForwardType x6) { - StorageType* storage = static_cast<StorageType*>(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void(typename Bound1UnwrapTraits::ForwardType, - typename CallbackParamTraits<X2>::ForwardType x2, - typename CallbackParamTraits<X3>::ForwardType x3, - typename CallbackParamTraits<X4>::ForwardType x4, - typename CallbackParamTraits<X5>::ForwardType x5, - typename CallbackParamTraits<X6>::ForwardType x6)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6)); - } -}; - -// Arity 6 -> 4. -template <typename StorageType, typename R,typename X1, typename X2, - typename X3, typename X4, typename X5, typename X6> -struct Invoker<2, StorageType, R(X1, X2, X3, X4, X5, X6)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits<X3>::ForwardType, - typename CallbackParamTraits<X4>::ForwardType, - typename CallbackParamTraits<X5>::ForwardType, - typename CallbackParamTraits<X6>::ForwardType); - - typedef R(UnboundRunType)(X3, X4, X5, X6); - - static R Run(BindStateBase* base, - typename CallbackParamTraits<X3>::ForwardType x3, - typename CallbackParamTraits<X4>::ForwardType x4, - typename CallbackParamTraits<X5>::ForwardType x5, - typename CallbackParamTraits<X6>::ForwardType x6) { - StorageType* storage = static_cast<StorageType*>(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void(typename Bound1UnwrapTraits::ForwardType, - typename Bound2UnwrapTraits::ForwardType, - typename CallbackParamTraits<X3>::ForwardType x3, - typename CallbackParamTraits<X4>::ForwardType x4, - typename CallbackParamTraits<X5>::ForwardType x5, - typename CallbackParamTraits<X6>::ForwardType x6)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6)); - } -}; - -// Arity 6 -> 3. -template <typename StorageType, typename R,typename X1, typename X2, - typename X3, typename X4, typename X5, typename X6> -struct Invoker<3, StorageType, R(X1, X2, X3, X4, X5, X6)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits<X4>::ForwardType, - typename CallbackParamTraits<X5>::ForwardType, - typename CallbackParamTraits<X6>::ForwardType); - - typedef R(UnboundRunType)(X4, X5, X6); - - static R Run(BindStateBase* base, - typename CallbackParamTraits<X4>::ForwardType x4, - typename CallbackParamTraits<X5>::ForwardType x5, - typename CallbackParamTraits<X6>::ForwardType x6) { - StorageType* storage = static_cast<StorageType*>(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void(typename Bound1UnwrapTraits::ForwardType, - typename Bound2UnwrapTraits::ForwardType, - typename Bound3UnwrapTraits::ForwardType, - typename CallbackParamTraits<X4>::ForwardType x4, - typename CallbackParamTraits<X5>::ForwardType x5, - typename CallbackParamTraits<X6>::ForwardType x6)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6)); - } -}; - -// Arity 6 -> 2. -template <typename StorageType, typename R,typename X1, typename X2, - typename X3, typename X4, typename X5, typename X6> -struct Invoker<4, StorageType, R(X1, X2, X3, X4, X5, X6)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits<X5>::ForwardType, - typename CallbackParamTraits<X6>::ForwardType); - - typedef R(UnboundRunType)(X5, X6); - - static R Run(BindStateBase* base, - typename CallbackParamTraits<X5>::ForwardType x5, - typename CallbackParamTraits<X6>::ForwardType x6) { - StorageType* storage = static_cast<StorageType*>(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - typename Bound4UnwrapTraits::ForwardType x4 = - Bound4UnwrapTraits::Unwrap(storage->p4_); - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void(typename Bound1UnwrapTraits::ForwardType, - typename Bound2UnwrapTraits::ForwardType, - typename Bound3UnwrapTraits::ForwardType, - typename Bound4UnwrapTraits::ForwardType, - typename CallbackParamTraits<X5>::ForwardType x5, - typename CallbackParamTraits<X6>::ForwardType x6)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6)); - } -}; - -// Arity 6 -> 1. -template <typename StorageType, typename R,typename X1, typename X2, - typename X3, typename X4, typename X5, typename X6> -struct Invoker<5, StorageType, R(X1, X2, X3, X4, X5, X6)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits<X6>::ForwardType); - - typedef R(UnboundRunType)(X6); - - static R Run(BindStateBase* base, - typename CallbackParamTraits<X6>::ForwardType x6) { - StorageType* storage = static_cast<StorageType*>(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits; - typedef typename StorageType::Bound5UnwrapTraits Bound5UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - typename Bound4UnwrapTraits::ForwardType x4 = - Bound4UnwrapTraits::Unwrap(storage->p4_); - typename Bound5UnwrapTraits::ForwardType x5 = - Bound5UnwrapTraits::Unwrap(storage->p5_); - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void(typename Bound1UnwrapTraits::ForwardType, - typename Bound2UnwrapTraits::ForwardType, - typename Bound3UnwrapTraits::ForwardType, - typename Bound4UnwrapTraits::ForwardType, - typename Bound5UnwrapTraits::ForwardType, - typename CallbackParamTraits<X6>::ForwardType x6)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6)); - } -}; - -// Arity 6 -> 0. -template <typename StorageType, typename R,typename X1, typename X2, - typename X3, typename X4, typename X5, typename X6> -struct Invoker<6, StorageType, R(X1, X2, X3, X4, X5, X6)> { - typedef R(RunType)(BindStateBase*); - - typedef R(UnboundRunType)(); - - static R Run(BindStateBase* base) { - StorageType* storage = static_cast<StorageType*>(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits; - typedef typename StorageType::Bound5UnwrapTraits Bound5UnwrapTraits; - typedef typename StorageType::Bound6UnwrapTraits Bound6UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - typename Bound4UnwrapTraits::ForwardType x4 = - Bound4UnwrapTraits::Unwrap(storage->p4_); - typename Bound5UnwrapTraits::ForwardType x5 = - Bound5UnwrapTraits::Unwrap(storage->p5_); - typename Bound6UnwrapTraits::ForwardType x6 = - Bound6UnwrapTraits::Unwrap(storage->p6_); - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void(typename Bound1UnwrapTraits::ForwardType, - typename Bound2UnwrapTraits::ForwardType, - typename Bound3UnwrapTraits::ForwardType, - typename Bound4UnwrapTraits::ForwardType, - typename Bound5UnwrapTraits::ForwardType, - typename Bound6UnwrapTraits::ForwardType)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6)); - } -}; - -// Arity 7 -> 7. -template <typename StorageType, typename R,typename X1, typename X2, - typename X3, typename X4, typename X5, typename X6, typename X7> -struct Invoker<0, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits<X1>::ForwardType, - typename CallbackParamTraits<X2>::ForwardType, - typename CallbackParamTraits<X3>::ForwardType, - typename CallbackParamTraits<X4>::ForwardType, - typename CallbackParamTraits<X5>::ForwardType, - typename CallbackParamTraits<X6>::ForwardType, - typename CallbackParamTraits<X7>::ForwardType); - - typedef R(UnboundRunType)(X1, X2, X3, X4, X5, X6, X7); - - static R Run(BindStateBase* base, - typename CallbackParamTraits<X1>::ForwardType x1, - typename CallbackParamTraits<X2>::ForwardType x2, - typename CallbackParamTraits<X3>::ForwardType x3, - typename CallbackParamTraits<X4>::ForwardType x4, - typename CallbackParamTraits<X5>::ForwardType x5, - typename CallbackParamTraits<X6>::ForwardType x6, - typename CallbackParamTraits<X7>::ForwardType x7) { - StorageType* storage = static_cast<StorageType*>(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void(typename CallbackParamTraits<X1>::ForwardType x1, - typename CallbackParamTraits<X2>::ForwardType x2, - typename CallbackParamTraits<X3>::ForwardType x3, - typename CallbackParamTraits<X4>::ForwardType x4, - typename CallbackParamTraits<X5>::ForwardType x5, - typename CallbackParamTraits<X6>::ForwardType x6, - typename CallbackParamTraits<X7>::ForwardType x7)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6), CallbackForward(x7)); - } -}; - -// Arity 7 -> 6. -template <typename StorageType, typename R,typename X1, typename X2, - typename X3, typename X4, typename X5, typename X6, typename X7> -struct Invoker<1, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits<X2>::ForwardType, - typename CallbackParamTraits<X3>::ForwardType, - typename CallbackParamTraits<X4>::ForwardType, - typename CallbackParamTraits<X5>::ForwardType, - typename CallbackParamTraits<X6>::ForwardType, - typename CallbackParamTraits<X7>::ForwardType); - - typedef R(UnboundRunType)(X2, X3, X4, X5, X6, X7); - - static R Run(BindStateBase* base, - typename CallbackParamTraits<X2>::ForwardType x2, - typename CallbackParamTraits<X3>::ForwardType x3, - typename CallbackParamTraits<X4>::ForwardType x4, - typename CallbackParamTraits<X5>::ForwardType x5, - typename CallbackParamTraits<X6>::ForwardType x6, - typename CallbackParamTraits<X7>::ForwardType x7) { - StorageType* storage = static_cast<StorageType*>(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void(typename Bound1UnwrapTraits::ForwardType, - typename CallbackParamTraits<X2>::ForwardType x2, - typename CallbackParamTraits<X3>::ForwardType x3, - typename CallbackParamTraits<X4>::ForwardType x4, - typename CallbackParamTraits<X5>::ForwardType x5, - typename CallbackParamTraits<X6>::ForwardType x6, - typename CallbackParamTraits<X7>::ForwardType x7)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6), CallbackForward(x7)); - } -}; - -// Arity 7 -> 5. -template <typename StorageType, typename R,typename X1, typename X2, - typename X3, typename X4, typename X5, typename X6, typename X7> -struct Invoker<2, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits<X3>::ForwardType, - typename CallbackParamTraits<X4>::ForwardType, - typename CallbackParamTraits<X5>::ForwardType, - typename CallbackParamTraits<X6>::ForwardType, - typename CallbackParamTraits<X7>::ForwardType); - - typedef R(UnboundRunType)(X3, X4, X5, X6, X7); - - static R Run(BindStateBase* base, - typename CallbackParamTraits<X3>::ForwardType x3, - typename CallbackParamTraits<X4>::ForwardType x4, - typename CallbackParamTraits<X5>::ForwardType x5, - typename CallbackParamTraits<X6>::ForwardType x6, - typename CallbackParamTraits<X7>::ForwardType x7) { - StorageType* storage = static_cast<StorageType*>(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void(typename Bound1UnwrapTraits::ForwardType, - typename Bound2UnwrapTraits::ForwardType, - typename CallbackParamTraits<X3>::ForwardType x3, - typename CallbackParamTraits<X4>::ForwardType x4, - typename CallbackParamTraits<X5>::ForwardType x5, - typename CallbackParamTraits<X6>::ForwardType x6, - typename CallbackParamTraits<X7>::ForwardType x7)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6), CallbackForward(x7)); - } -}; - -// Arity 7 -> 4. -template <typename StorageType, typename R,typename X1, typename X2, - typename X3, typename X4, typename X5, typename X6, typename X7> -struct Invoker<3, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits<X4>::ForwardType, - typename CallbackParamTraits<X5>::ForwardType, - typename CallbackParamTraits<X6>::ForwardType, - typename CallbackParamTraits<X7>::ForwardType); - - typedef R(UnboundRunType)(X4, X5, X6, X7); - +template <size_t... bound_indices, + typename StorageType, + typename... Unwrappers, + typename InvokeHelperType, + typename R, + typename... UnboundForwardArgs> +struct Invoker<IndexSequence<bound_indices...>, + StorageType, TypeList<Unwrappers...>, + InvokeHelperType, R(UnboundForwardArgs...)> { static R Run(BindStateBase* base, - typename CallbackParamTraits<X4>::ForwardType x4, - typename CallbackParamTraits<X5>::ForwardType x5, - typename CallbackParamTraits<X6>::ForwardType x6, - typename CallbackParamTraits<X7>::ForwardType x7) { + UnboundForwardArgs... unbound_args) { StorageType* storage = static_cast<StorageType*>(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void(typename Bound1UnwrapTraits::ForwardType, - typename Bound2UnwrapTraits::ForwardType, - typename Bound3UnwrapTraits::ForwardType, - typename CallbackParamTraits<X4>::ForwardType x4, - typename CallbackParamTraits<X5>::ForwardType x5, - typename CallbackParamTraits<X6>::ForwardType x6, - typename CallbackParamTraits<X7>::ForwardType x7)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6), CallbackForward(x7)); - } -}; - -// Arity 7 -> 3. -template <typename StorageType, typename R,typename X1, typename X2, - typename X3, typename X4, typename X5, typename X6, typename X7> -struct Invoker<4, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits<X5>::ForwardType, - typename CallbackParamTraits<X6>::ForwardType, - typename CallbackParamTraits<X7>::ForwardType); - - typedef R(UnboundRunType)(X5, X6, X7); - - static R Run(BindStateBase* base, - typename CallbackParamTraits<X5>::ForwardType x5, - typename CallbackParamTraits<X6>::ForwardType x6, - typename CallbackParamTraits<X7>::ForwardType x7) { - StorageType* storage = static_cast<StorageType*>(base); - // Local references to make debugger stepping easier. If in a debugger, // you really want to warp ahead and step through the // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - typename Bound4UnwrapTraits::ForwardType x4 = - Bound4UnwrapTraits::Unwrap(storage->p4_); - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void(typename Bound1UnwrapTraits::ForwardType, - typename Bound2UnwrapTraits::ForwardType, - typename Bound3UnwrapTraits::ForwardType, - typename Bound4UnwrapTraits::ForwardType, - typename CallbackParamTraits<X5>::ForwardType x5, - typename CallbackParamTraits<X6>::ForwardType x6, - typename CallbackParamTraits<X7>::ForwardType x7)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6), CallbackForward(x7)); - } -}; - -// Arity 7 -> 2. -template <typename StorageType, typename R,typename X1, typename X2, - typename X3, typename X4, typename X5, typename X6, typename X7> -struct Invoker<5, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits<X6>::ForwardType, - typename CallbackParamTraits<X7>::ForwardType); - - typedef R(UnboundRunType)(X6, X7); - - static R Run(BindStateBase* base, - typename CallbackParamTraits<X6>::ForwardType x6, - typename CallbackParamTraits<X7>::ForwardType x7) { - StorageType* storage = static_cast<StorageType*>(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits; - typedef typename StorageType::Bound5UnwrapTraits Bound5UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - typename Bound4UnwrapTraits::ForwardType x4 = - Bound4UnwrapTraits::Unwrap(storage->p4_); - typename Bound5UnwrapTraits::ForwardType x5 = - Bound5UnwrapTraits::Unwrap(storage->p5_); - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void(typename Bound1UnwrapTraits::ForwardType, - typename Bound2UnwrapTraits::ForwardType, - typename Bound3UnwrapTraits::ForwardType, - typename Bound4UnwrapTraits::ForwardType, - typename Bound5UnwrapTraits::ForwardType, - typename CallbackParamTraits<X6>::ForwardType x6, - typename CallbackParamTraits<X7>::ForwardType x7)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6), CallbackForward(x7)); - } -}; - -// Arity 7 -> 1. -template <typename StorageType, typename R,typename X1, typename X2, - typename X3, typename X4, typename X5, typename X6, typename X7> -struct Invoker<6, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits<X7>::ForwardType); - - typedef R(UnboundRunType)(X7); - - static R Run(BindStateBase* base, - typename CallbackParamTraits<X7>::ForwardType x7) { - StorageType* storage = static_cast<StorageType*>(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits; - typedef typename StorageType::Bound5UnwrapTraits Bound5UnwrapTraits; - typedef typename StorageType::Bound6UnwrapTraits Bound6UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - typename Bound4UnwrapTraits::ForwardType x4 = - Bound4UnwrapTraits::Unwrap(storage->p4_); - typename Bound5UnwrapTraits::ForwardType x5 = - Bound5UnwrapTraits::Unwrap(storage->p5_); - typename Bound6UnwrapTraits::ForwardType x6 = - Bound6UnwrapTraits::Unwrap(storage->p6_); - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void(typename Bound1UnwrapTraits::ForwardType, - typename Bound2UnwrapTraits::ForwardType, - typename Bound3UnwrapTraits::ForwardType, - typename Bound4UnwrapTraits::ForwardType, - typename Bound5UnwrapTraits::ForwardType, - typename Bound6UnwrapTraits::ForwardType, - typename CallbackParamTraits<X7>::ForwardType x7)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6), CallbackForward(x7)); - } -}; - -// Arity 7 -> 0. -template <typename StorageType, typename R,typename X1, typename X2, - typename X3, typename X4, typename X5, typename X6, typename X7> -struct Invoker<7, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> { - typedef R(RunType)(BindStateBase*); - - typedef R(UnboundRunType)(); - - static R Run(BindStateBase* base) { - StorageType* storage = static_cast<StorageType*>(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits; - typedef typename StorageType::Bound5UnwrapTraits Bound5UnwrapTraits; - typedef typename StorageType::Bound6UnwrapTraits Bound6UnwrapTraits; - typedef typename StorageType::Bound7UnwrapTraits Bound7UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - typename Bound4UnwrapTraits::ForwardType x4 = - Bound4UnwrapTraits::Unwrap(storage->p4_); - typename Bound5UnwrapTraits::ForwardType x5 = - Bound5UnwrapTraits::Unwrap(storage->p5_); - typename Bound6UnwrapTraits::ForwardType x6 = - Bound6UnwrapTraits::Unwrap(storage->p6_); - typename Bound7UnwrapTraits::ForwardType x7 = - Bound7UnwrapTraits::Unwrap(storage->p7_); - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void(typename Bound1UnwrapTraits::ForwardType, - typename Bound2UnwrapTraits::ForwardType, - typename Bound3UnwrapTraits::ForwardType, - typename Bound4UnwrapTraits::ForwardType, - typename Bound5UnwrapTraits::ForwardType, - typename Bound6UnwrapTraits::ForwardType, - typename Bound7UnwrapTraits::ForwardType)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6), CallbackForward(x7)); + return InvokeHelperType::MakeItSo( + storage->runnable_, + Unwrappers::Unwrap(get<bound_indices>(storage->bound_args_))..., + CallbackForward(unbound_args)...); } }; @@ -2528,259 +360,60 @@ struct Invoker<7, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> { // // BoundArgsType contains the storage type for all the bound arguments by // (ab)using a function type. -template <typename Runnable, typename RunType, typename BoundArgsType> +template <typename Runnable, typename RunType, typename BoundArgList> struct BindState; -template <typename Runnable, typename RunType> -struct BindState<Runnable, RunType, void()> : public BindStateBase { - typedef Runnable RunnableType; - typedef false_type IsWeakCall; - typedef Invoker<0, BindState, RunType> InvokerType; - typedef typename InvokerType::UnboundRunType UnboundRunType; - explicit BindState(const Runnable& runnable) - : runnable_(runnable) { - } - - virtual ~BindState() { } - - RunnableType runnable_; -}; - -template <typename Runnable, typename RunType, typename P1> -struct BindState<Runnable, RunType, void(P1)> : public BindStateBase { - typedef Runnable RunnableType; - typedef IsWeakMethod<HasIsMethodTag<Runnable>::value, P1> IsWeakCall; - typedef Invoker<1, BindState, RunType> InvokerType; - typedef typename InvokerType::UnboundRunType UnboundRunType; - - // Convenience typedefs for bound argument types. - typedef UnwrapTraits<P1> Bound1UnwrapTraits; - - BindState(const Runnable& runnable, const P1& p1) - : runnable_(runnable), - p1_(p1) { - MaybeRefcount<HasIsMethodTag<Runnable>::value, P1>::AddRef(p1_); - } - - virtual ~BindState() { MaybeRefcount<HasIsMethodTag<Runnable>::value, - P1>::Release(p1_); } - - RunnableType runnable_; - P1 p1_; -}; - -template <typename Runnable, typename RunType, typename P1, typename P2> -struct BindState<Runnable, RunType, void(P1, P2)> : public BindStateBase { - typedef Runnable RunnableType; - typedef IsWeakMethod<HasIsMethodTag<Runnable>::value, P1> IsWeakCall; - typedef Invoker<2, BindState, RunType> InvokerType; - typedef typename InvokerType::UnboundRunType UnboundRunType; - - // Convenience typedefs for bound argument types. - typedef UnwrapTraits<P1> Bound1UnwrapTraits; - typedef UnwrapTraits<P2> Bound2UnwrapTraits; - - BindState(const Runnable& runnable, const P1& p1, const P2& p2) - : runnable_(runnable), - p1_(p1), - p2_(p2) { - MaybeRefcount<HasIsMethodTag<Runnable>::value, P1>::AddRef(p1_); - } - - virtual ~BindState() { MaybeRefcount<HasIsMethodTag<Runnable>::value, - P1>::Release(p1_); } - - RunnableType runnable_; - P1 p1_; - P2 p2_; -}; - -template <typename Runnable, typename RunType, typename P1, typename P2, - typename P3> -struct BindState<Runnable, RunType, void(P1, P2, P3)> : public BindStateBase { - typedef Runnable RunnableType; - typedef IsWeakMethod<HasIsMethodTag<Runnable>::value, P1> IsWeakCall; - typedef Invoker<3, BindState, RunType> InvokerType; - typedef typename InvokerType::UnboundRunType UnboundRunType; - - // Convenience typedefs for bound argument types. - typedef UnwrapTraits<P1> Bound1UnwrapTraits; - typedef UnwrapTraits<P2> Bound2UnwrapTraits; - typedef UnwrapTraits<P3> Bound3UnwrapTraits; - - BindState(const Runnable& runnable, const P1& p1, const P2& p2, const P3& p3) - : runnable_(runnable), - p1_(p1), - p2_(p2), - p3_(p3) { - MaybeRefcount<HasIsMethodTag<Runnable>::value, P1>::AddRef(p1_); - } - - virtual ~BindState() { MaybeRefcount<HasIsMethodTag<Runnable>::value, - P1>::Release(p1_); } - - RunnableType runnable_; - P1 p1_; - P2 p2_; - P3 p3_; -}; - -template <typename Runnable, typename RunType, typename P1, typename P2, - typename P3, typename P4> -struct BindState<Runnable, RunType, void(P1, P2, P3, - P4)> : public BindStateBase { - typedef Runnable RunnableType; - typedef IsWeakMethod<HasIsMethodTag<Runnable>::value, P1> IsWeakCall; - typedef Invoker<4, BindState, RunType> InvokerType; - typedef typename InvokerType::UnboundRunType UnboundRunType; - - // Convenience typedefs for bound argument types. - typedef UnwrapTraits<P1> Bound1UnwrapTraits; - typedef UnwrapTraits<P2> Bound2UnwrapTraits; - typedef UnwrapTraits<P3> Bound3UnwrapTraits; - typedef UnwrapTraits<P4> Bound4UnwrapTraits; - - BindState(const Runnable& runnable, const P1& p1, const P2& p2, const P3& p3, - const P4& p4) - : runnable_(runnable), - p1_(p1), - p2_(p2), - p3_(p3), - p4_(p4) { - MaybeRefcount<HasIsMethodTag<Runnable>::value, P1>::AddRef(p1_); - } - - virtual ~BindState() { MaybeRefcount<HasIsMethodTag<Runnable>::value, - P1>::Release(p1_); } - - RunnableType runnable_; - P1 p1_; - P2 p2_; - P3 p3_; - P4 p4_; -}; - -template <typename Runnable, typename RunType, typename P1, typename P2, - typename P3, typename P4, typename P5> -struct BindState<Runnable, RunType, void(P1, P2, P3, P4, - P5)> : public BindStateBase { - typedef Runnable RunnableType; - typedef IsWeakMethod<HasIsMethodTag<Runnable>::value, P1> IsWeakCall; - typedef Invoker<5, BindState, RunType> InvokerType; - typedef typename InvokerType::UnboundRunType UnboundRunType; - - // Convenience typedefs for bound argument types. - typedef UnwrapTraits<P1> Bound1UnwrapTraits; - typedef UnwrapTraits<P2> Bound2UnwrapTraits; - typedef UnwrapTraits<P3> Bound3UnwrapTraits; - typedef UnwrapTraits<P4> Bound4UnwrapTraits; - typedef UnwrapTraits<P5> Bound5UnwrapTraits; - - BindState(const Runnable& runnable, const P1& p1, const P2& p2, const P3& p3, - const P4& p4, const P5& p5) - : runnable_(runnable), - p1_(p1), - p2_(p2), - p3_(p3), - p4_(p4), - p5_(p5) { - MaybeRefcount<HasIsMethodTag<Runnable>::value, P1>::AddRef(p1_); - } +template <typename Runnable, + typename R, + typename... Args, + typename... BoundArgs> +struct BindState<Runnable, R(Args...), TypeList<BoundArgs...>> final + : public BindStateBase { + private: + using StorageType = BindState<Runnable, R(Args...), TypeList<BoundArgs...>>; + using RunnableType = Runnable; - virtual ~BindState() { MaybeRefcount<HasIsMethodTag<Runnable>::value, - P1>::Release(p1_); } + // true_type if Runnable is a method invocation and the first bound argument + // is a WeakPtr. + using IsWeakCall = + IsWeakMethod<HasIsMethodTag<Runnable>::value, BoundArgs...>; - RunnableType runnable_; - P1 p1_; - P2 p2_; - P3 p3_; - P4 p4_; - P5 p5_; -}; + using BoundIndices = MakeIndexSequence<sizeof...(BoundArgs)>; + using Unwrappers = TypeList<UnwrapTraits<BoundArgs>...>; + using UnboundForwardArgs = DropTypeListItem< + sizeof...(BoundArgs), + TypeList<typename CallbackParamTraits<Args>::ForwardType...>>; + using UnboundForwardRunType = MakeFunctionType<R, UnboundForwardArgs>; -template <typename Runnable, typename RunType, typename P1, typename P2, - typename P3, typename P4, typename P5, typename P6> -struct BindState<Runnable, RunType, void(P1, P2, P3, P4, P5, - P6)> : public BindStateBase { - typedef Runnable RunnableType; - typedef IsWeakMethod<HasIsMethodTag<Runnable>::value, P1> IsWeakCall; - typedef Invoker<6, BindState, RunType> InvokerType; - typedef typename InvokerType::UnboundRunType UnboundRunType; + using InvokeHelperArgs = ConcatTypeLists< + TypeList<typename UnwrapTraits<BoundArgs>::ForwardType...>, + UnboundForwardArgs>; + using InvokeHelperType = + InvokeHelper<IsWeakCall::value, R, Runnable, InvokeHelperArgs>; - // Convenience typedefs for bound argument types. - typedef UnwrapTraits<P1> Bound1UnwrapTraits; - typedef UnwrapTraits<P2> Bound2UnwrapTraits; - typedef UnwrapTraits<P3> Bound3UnwrapTraits; - typedef UnwrapTraits<P4> Bound4UnwrapTraits; - typedef UnwrapTraits<P5> Bound5UnwrapTraits; - typedef UnwrapTraits<P6> Bound6UnwrapTraits; + using UnboundArgs = DropTypeListItem<sizeof...(BoundArgs), TypeList<Args...>>; - BindState(const Runnable& runnable, const P1& p1, const P2& p2, const P3& p3, - const P4& p4, const P5& p5, const P6& p6) - : runnable_(runnable), - p1_(p1), - p2_(p2), - p3_(p3), - p4_(p4), - p5_(p5), - p6_(p6) { - MaybeRefcount<HasIsMethodTag<Runnable>::value, P1>::AddRef(p1_); - } + public: + using InvokerType = Invoker<BoundIndices, StorageType, Unwrappers, + InvokeHelperType, UnboundForwardRunType>; + using UnboundRunType = MakeFunctionType<R, UnboundArgs>; - virtual ~BindState() { MaybeRefcount<HasIsMethodTag<Runnable>::value, - P1>::Release(p1_); } + BindState(const Runnable& runnable, const BoundArgs&... bound_args) + : BindStateBase(&Destroy), + runnable_(runnable), + ref_(bound_args...), + bound_args_(bound_args...) {} RunnableType runnable_; - P1 p1_; - P2 p2_; - P3 p3_; - P4 p4_; - P5 p5_; - P6 p6_; -}; - -template <typename Runnable, typename RunType, typename P1, typename P2, - typename P3, typename P4, typename P5, typename P6, typename P7> -struct BindState<Runnable, RunType, void(P1, P2, P3, P4, P5, P6, - P7)> : public BindStateBase { - typedef Runnable RunnableType; - typedef IsWeakMethod<HasIsMethodTag<Runnable>::value, P1> IsWeakCall; - typedef Invoker<7, BindState, RunType> InvokerType; - typedef typename InvokerType::UnboundRunType UnboundRunType; + MaybeScopedRefPtr<HasIsMethodTag<Runnable>::value, BoundArgs...> ref_; + Tuple<BoundArgs...> bound_args_; - // Convenience typedefs for bound argument types. - typedef UnwrapTraits<P1> Bound1UnwrapTraits; - typedef UnwrapTraits<P2> Bound2UnwrapTraits; - typedef UnwrapTraits<P3> Bound3UnwrapTraits; - typedef UnwrapTraits<P4> Bound4UnwrapTraits; - typedef UnwrapTraits<P5> Bound5UnwrapTraits; - typedef UnwrapTraits<P6> Bound6UnwrapTraits; - typedef UnwrapTraits<P7> Bound7UnwrapTraits; + private: + ~BindState() {} - BindState(const Runnable& runnable, const P1& p1, const P2& p2, const P3& p3, - const P4& p4, const P5& p5, const P6& p6, const P7& p7) - : runnable_(runnable), - p1_(p1), - p2_(p2), - p3_(p3), - p4_(p4), - p5_(p5), - p6_(p6), - p7_(p7) { - MaybeRefcount<HasIsMethodTag<Runnable>::value, P1>::AddRef(p1_); + static void Destroy(BindStateBase* self) { + delete static_cast<BindState*>(self); } - - virtual ~BindState() { MaybeRefcount<HasIsMethodTag<Runnable>::value, - P1>::Release(p1_); } - - RunnableType runnable_; - P1 p1_; - P2 p2_; - P3 p3_; - P4 p4_; - P5 p5_; - P6 p6_; - P7 p7_; }; } // namespace internal diff --git a/chromium/base/bind_internal.h.pump b/chromium/base/bind_internal.h.pump deleted file mode 100644 index f632b99e556..00000000000 --- a/chromium/base/bind_internal.h.pump +++ /dev/null @@ -1,500 +0,0 @@ -$$ This is a pump file for generating file templates. Pump is a python -$$ script that is part of the Google Test suite of utilities. Description -$$ can be found here: -$$ -$$ http://code.google.com/p/googletest/wiki/PumpManual -$$ - -$$ See comment for MAX_ARITY in base/bind.h.pump. -$var MAX_ARITY = 7 -$range ARITY 0..MAX_ARITY - -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_BIND_INTERNAL_H_ -#define BASE_BIND_INTERNAL_H_ - -#include "base/bind_helpers.h" -#include "base/callback_internal.h" -#include "base/memory/raw_scoped_refptr_mismatch_checker.h" -#include "base/memory/weak_ptr.h" -#include "base/template_util.h" -#include "build/build_config.h" - -#if defined(OS_WIN) -#include "base/bind_internal_win.h" -#endif - -namespace base { -namespace internal { - -// See base/callback.h for user documentation. -// -// -// CONCEPTS: -// Runnable -- A type (really a type class) that has a single Run() method -// and a RunType typedef that corresponds to the type of Run(). -// A Runnable can declare that it should treated like a method -// call by including a typedef named IsMethod. The value of -// this typedef is NOT inspected, only the existence. When a -// Runnable declares itself a method, Bind() will enforce special -// refcounting + WeakPtr handling semantics for the first -// parameter which is expected to be an object. -// Functor -- A copyable type representing something that should be called. -// All function pointers, Callback<>, and Runnables are functors -// even if the invocation syntax differs. -// RunType -- A function type (as opposed to function _pointer_ type) for -// a Run() function. Usually just a convenience typedef. -// (Bound)ArgsType -- A function type that is being (ab)used to store the -// types of set of arguments. The "return" type is always -// void here. We use this hack so that we do not need -// a new type name for each arity of type. (eg., -// BindState1, BindState2). This makes forward -// declarations and friending much much easier. -// -// Types: -// RunnableAdapter<> -- Wraps the various "function" pointer types into an -// object that adheres to the Runnable interface. -// There are |3*ARITY| RunnableAdapter types. -// FunctionTraits<> -- Type traits that unwrap a function signature into a -// a set of easier to use typedefs. Used mainly for -// compile time asserts. -// There are |ARITY| FunctionTraits types. -// ForceVoidReturn<> -- Helper class for translating function signatures to -// equivalent forms with a "void" return type. -// There are |ARITY| ForceVoidReturn types. -// FunctorTraits<> -- Type traits used determine the correct RunType and -// RunnableType for a Functor. This is where function -// signature adapters are applied. -// There are |ARITY| ForceVoidReturn types. -// MakeRunnable<> -- Takes a Functor and returns an object in the Runnable -// type class that represents the underlying Functor. -// There are |O(1)| MakeRunnable types. -// InvokeHelper<> -- Take a Runnable + arguments and actully invokes it. -// Handle the differing syntaxes needed for WeakPtr<> support, -// and for ignoring return values. This is separate from -// Invoker to avoid creating multiple version of Invoker<> -// which grows at O(n^2) with the arity. -// There are |k*ARITY| InvokeHelper types. -// Invoker<> -- Unwraps the curried parameters and executes the Runnable. -// There are |(ARITY^2 + ARITY)/2| Invoketypes. -// BindState<> -- Stores the curried parameters, and is the main entry point -// into the Bind() system, doing most of the type resolution. -// There are ARITY BindState types. - -// RunnableAdapter<> -// -// The RunnableAdapter<> templates provide a uniform interface for invoking -// a function pointer, method pointer, or const method pointer. The adapter -// exposes a Run() method with an appropriate signature. Using this wrapper -// allows for writing code that supports all three pointer types without -// undue repetition. Without it, a lot of code would need to be repeated 3 -// times. -// -// For method pointers and const method pointers the first argument to Run() -// is considered to be the received of the method. This is similar to STL's -// mem_fun(). -// -// This class also exposes a RunType typedef that is the function type of the -// Run() function. -// -// If and only if the wrapper contains a method or const method pointer, an -// IsMethod typedef is exposed. The existence of this typedef (NOT the value) -// marks that the wrapper should be considered a method wrapper. - -template <typename Functor> -class RunnableAdapter; - -$for ARITY [[ -$range ARG 1..ARITY - -// Function: Arity $(ARITY). -template <typename R[[]] -$if ARITY > 0[[, ]] $for ARG , [[typename A$(ARG)]]> -class RunnableAdapter<R(*)($for ARG , [[A$(ARG)]])> { - public: - typedef R (RunType)($for ARG , [[A$(ARG)]]); - - explicit RunnableAdapter(R(*function)($for ARG , [[A$(ARG)]])) - : function_(function) { - } - - R Run($for ARG , [[typename CallbackParamTraits<A$(ARG)>::ForwardType a$(ARG)]]) { - return function_($for ARG , [[CallbackForward(a$(ARG))]]); - } - - private: - R (*function_)($for ARG , [[A$(ARG)]]); -}; - -// Method: Arity $(ARITY). -template <typename R, typename T[[]] -$if ARITY > 0[[, ]] $for ARG , [[typename A$(ARG)]]> -class RunnableAdapter<R(T::*)($for ARG , [[A$(ARG)]])> { - public: - typedef R (RunType)(T*[[]] -$if ARITY > 0[[, ]] $for ARG , [[A$(ARG)]]); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)($for ARG , [[A$(ARG)]])) - : method_(method) { - } - - R Run(T* object[[]] -$if ARITY > 0[[, ]] $for ARG, [[typename CallbackParamTraits<A$(ARG)>::ForwardType a$(ARG)]]) { - return (object->*method_)($for ARG , [[CallbackForward(a$(ARG))]]); - } - - private: - R (T::*method_)($for ARG , [[A$(ARG)]]); -}; - -// Const Method: Arity $(ARITY). -template <typename R, typename T[[]] -$if ARITY > 0[[, ]] $for ARG , [[typename A$(ARG)]]> -class RunnableAdapter<R(T::*)($for ARG , [[A$(ARG)]]) const> { - public: - typedef R (RunType)(const T*[[]] -$if ARITY > 0[[, ]] $for ARG , [[A$(ARG)]]); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)($for ARG , [[A$(ARG)]]) const) - : method_(method) { - } - - R Run(const T* object[[]] -$if ARITY > 0[[, ]] $for ARG, [[typename CallbackParamTraits<A$(ARG)>::ForwardType a$(ARG)]]) { - return (object->*method_)($for ARG , [[CallbackForward(a$(ARG))]]); - } - - private: - R (T::*method_)($for ARG , [[A$(ARG)]]) const; -}; - -]] $$ for ARITY - - -// FunctionTraits<> -// -// Breaks a function signature apart into typedefs for easier introspection. -template <typename Sig> -struct FunctionTraits; - -$for ARITY [[ -$range ARG 1..ARITY - -template <typename R[[]] -$if ARITY > 0[[, ]] $for ARG , [[typename A$(ARG)]]> -struct FunctionTraits<R($for ARG , [[A$(ARG)]])> { - typedef R ReturnType; -$for ARG [[ - - typedef A$(ARG) A$(ARG)Type; -]] - -}; - -]] - - -// ForceVoidReturn<> -// -// Set of templates that support forcing the function return type to void. -template <typename Sig> -struct ForceVoidReturn; - -$for ARITY [[ -$range ARG 1..ARITY - -template <typename R[[]] -$if ARITY > 0[[, ]] $for ARG , [[typename A$(ARG)]]> -struct ForceVoidReturn<R($for ARG , [[A$(ARG)]])> { - typedef void(RunType)($for ARG , [[A$(ARG)]]); -}; - -]] $$ for ARITY - - -// FunctorTraits<> -// -// See description at top of file. -template <typename T> -struct FunctorTraits { - typedef RunnableAdapter<T> RunnableType; - typedef typename RunnableType::RunType RunType; -}; - -template <typename T> -struct FunctorTraits<IgnoreResultHelper<T> > { - typedef typename FunctorTraits<T>::RunnableType RunnableType; - typedef typename ForceVoidReturn< - typename RunnableType::RunType>::RunType RunType; -}; - -template <typename T> -struct FunctorTraits<Callback<T> > { - typedef Callback<T> RunnableType; - typedef typename Callback<T>::RunType RunType; -}; - - -// MakeRunnable<> -// -// Converts a passed in functor to a RunnableType using type inference. - -template <typename T> -typename FunctorTraits<T>::RunnableType MakeRunnable(const T& t) { - return RunnableAdapter<T>(t); -} - -template <typename T> -typename FunctorTraits<T>::RunnableType -MakeRunnable(const IgnoreResultHelper<T>& t) { - return MakeRunnable(t.functor_); -} - -template <typename T> -const typename FunctorTraits<Callback<T> >::RunnableType& -MakeRunnable(const Callback<T>& t) { - DCHECK(!t.is_null()); - return t; -} - - -// InvokeHelper<> -// -// There are 3 logical InvokeHelper<> specializations: normal, void-return, -// WeakCalls. -// -// The normal type just calls the underlying runnable. -// -// We need a InvokeHelper to handle void return types in order to support -// IgnoreResult(). Normally, if the Runnable's RunType had a void return, -// the template system would just accept "return functor.Run()" ignoring -// the fact that a void function is being used with return. This piece of -// sugar breaks though when the Runnable's RunType is not void. Thus, we -// need a partial specialization to change the syntax to drop the "return" -// from the invocation call. -// -// WeakCalls similarly need special syntax that is applied to the first -// argument to check if they should no-op themselves. -template <bool IsWeakCall, typename ReturnType, typename Runnable, - typename ArgsType> -struct InvokeHelper; - -$for ARITY [[ -$range ARG 1..ARITY -$range WEAKCALL_ARG 2..ARITY - -template <typename ReturnType, typename Runnable[[]] -$if ARITY > 0 [[,]] $for ARG , [[typename A$(ARG)]]> -struct InvokeHelper<false, ReturnType, Runnable, - void($for ARG , [[A$(ARG)]])> { - static ReturnType MakeItSo(Runnable runnable[[]] -$if ARITY > 0[[, ]] $for ARG , [[A$(ARG) a$(ARG)]]) { - return runnable.Run($for ARG , [[CallbackForward(a$(ARG))]]); - } -}; - -template <typename Runnable[[]] -$if ARITY > 0 [[,]] $for ARG , [[typename A$(ARG)]]> -struct InvokeHelper<false, void, Runnable, - void($for ARG , [[A$(ARG)]])> { - static void MakeItSo(Runnable runnable[[]] -$if ARITY > 0[[, ]] $for ARG , [[A$(ARG) a$(ARG)]]) { - runnable.Run($for ARG , [[CallbackForward(a$(ARG))]]); - } -}; - -$if ARITY > 0 [[ - -template <typename Runnable[[]], typename BoundWeakPtr -$if ARITY > 1[[, ]] $for WEAKCALL_ARG , [[typename A$(WEAKCALL_ARG)]]> -struct InvokeHelper<true, void, Runnable, - void(BoundWeakPtr -$if ARITY > 1[[, ]] $for WEAKCALL_ARG , [[A$(WEAKCALL_ARG)]])> { - static void MakeItSo(Runnable runnable, BoundWeakPtr weak_ptr -$if ARITY > 1[[, ]] $for WEAKCALL_ARG , [[A$(WEAKCALL_ARG) a$(WEAKCALL_ARG)]]) { - if (!weak_ptr.get()) { - return; - } - runnable.Run(weak_ptr.get() -$if ARITY > 1[[, ]] $for WEAKCALL_ARG , [[CallbackForward(a$(WEAKCALL_ARG))]]); - } -}; - -]] - -]] $$ for ARITY - -#if !defined(_MSC_VER) - -template <typename ReturnType, typename Runnable, typename ArgsType> -struct InvokeHelper<true, ReturnType, Runnable, ArgsType> { - // WeakCalls are only supported for functions with a void return type. - // Otherwise, the function result would be undefined if the the WeakPtr<> - // is invalidated. - COMPILE_ASSERT(is_void<ReturnType>::value, - weak_ptrs_can_only_bind_to_methods_without_return_values); -}; - -#endif - -// Invoker<> -// -// See description at the top of the file. -template <int NumBound, typename Storage, typename RunType> -struct Invoker; - -$for ARITY [[ - -$$ Number of bound arguments. -$range BOUND 0..ARITY -$for BOUND [[ - -$var UNBOUND = ARITY - BOUND -$range ARG 1..ARITY -$range BOUND_ARG 1..BOUND -$range UNBOUND_ARG (ARITY - UNBOUND + 1)..ARITY - -// Arity $(ARITY) -> $(UNBOUND). -template <typename StorageType, typename R[[]] -$if ARITY > 0 [[,]][[]] -$for ARG , [[typename X$(ARG)]]> -struct Invoker<$(BOUND), StorageType, R($for ARG , [[X$(ARG)]])> { - typedef R(RunType)(BindStateBase*[[]] -$if UNBOUND != 0 [[, ]] -$for UNBOUND_ARG , [[typename CallbackParamTraits<X$(UNBOUND_ARG)>::ForwardType]]); - - typedef R(UnboundRunType)($for UNBOUND_ARG , [[X$(UNBOUND_ARG)]]); - - static R Run(BindStateBase* base[[]] -$if UNBOUND != 0 [[, ]][[]] -$for UNBOUND_ARG , [[ -typename CallbackParamTraits<X$(UNBOUND_ARG)>::ForwardType x$(UNBOUND_ARG) -]][[]] -) { - StorageType* storage = static_cast<StorageType*>(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. -$for BOUND_ARG -[[ - - typedef typename StorageType::Bound$(BOUND_ARG)UnwrapTraits Bound$(BOUND_ARG)UnwrapTraits; -]] - - -$for BOUND_ARG -[[ - - typename Bound$(BOUND_ARG)UnwrapTraits::ForwardType x$(BOUND_ARG) = - Bound$(BOUND_ARG)UnwrapTraits::Unwrap(storage->p$(BOUND_ARG)_); -]] - - return InvokeHelper<StorageType::IsWeakCall::value, R, - typename StorageType::RunnableType, - void( -$for BOUND_ARG , [[ -typename Bound$(BOUND_ARG)UnwrapTraits::ForwardType -]] - -$if UNBOUND > 0 [[$if BOUND > 0 [[, ]]]][[]] - -$for UNBOUND_ARG , [[ -typename CallbackParamTraits<X$(UNBOUND_ARG)>::ForwardType x$(UNBOUND_ARG) -]] -)> - ::MakeItSo(storage->runnable_ -$if ARITY > 0[[, ]] $for ARG , [[CallbackForward(x$(ARG))]]); - } -}; - -]] $$ for BOUND -]] $$ for ARITY - - -// BindState<> -// -// This stores all the state passed into Bind() and is also where most -// of the template resolution magic occurs. -// -// Runnable is the functor we are binding arguments to. -// RunType is type of the Run() function that the Invoker<> should use. -// Normally, this is the same as the RunType of the Runnable, but it can -// be different if an adapter like IgnoreResult() has been used. -// -// BoundArgsType contains the storage type for all the bound arguments by -// (ab)using a function type. -template <typename Runnable, typename RunType, typename BoundArgsType> -struct BindState; - -$for ARITY [[ -$range ARG 1..ARITY - -template <typename Runnable, typename RunType[[]] -$if ARITY > 0[[, ]] $for ARG , [[typename P$(ARG)]]> -struct BindState<Runnable, RunType, void($for ARG , [[P$(ARG)]])> : public BindStateBase { - typedef Runnable RunnableType; - -$if ARITY > 0 [[ - typedef IsWeakMethod<HasIsMethodTag<Runnable>::value, P1> IsWeakCall; -]] $else [[ - typedef false_type IsWeakCall; -]] - - typedef Invoker<$(ARITY), BindState, RunType> InvokerType; - typedef typename InvokerType::UnboundRunType UnboundRunType; - -$if ARITY > 0 [[ - - // Convenience typedefs for bound argument types. - -$for ARG [[ - typedef UnwrapTraits<P$(ARG)> Bound$(ARG)UnwrapTraits; - -]] $$ for ARG - - -]] $$ if ARITY > 0 - -$$ The extra [[ ]] is needed to massage spacing. Silly pump.py. -[[ ]]$if ARITY == 0 [[explicit ]]BindState(const Runnable& runnable -$if ARITY > 0 [[, ]] $for ARG , [[const P$(ARG)& p$(ARG)]]) - : runnable_(runnable)[[]] -$if ARITY == 0 [[ - { - -]] $else [[ -, $for ARG , [[ - - p$(ARG)_(p$(ARG)) -]] { - MaybeRefcount<HasIsMethodTag<Runnable>::value, P1>::AddRef(p1_); - -]] - } - - virtual ~BindState() { -$if ARITY > 0 [[ - MaybeRefcount<HasIsMethodTag<Runnable>::value, P1>::Release(p1_); -]] - } - - RunnableType runnable_; - -$for ARG [[ - P$(ARG) p$(ARG)_; - -]] -}; - -]] $$ for ARITY - -} // namespace internal -} // namespace base - -#endif // BASE_BIND_INTERNAL_H_ diff --git a/chromium/base/bind_unittest.cc b/chromium/base/bind_unittest.cc index a30b7756704..f885403c92d 100644 --- a/chromium/base/bind_unittest.cc +++ b/chromium/base/bind_unittest.cc @@ -778,10 +778,10 @@ TEST_F(BindTest, ArgumentCopies) { copies = 0; assigns = 0; - DerivedCopyCounter dervied(&copies, &assigns); + DerivedCopyCounter derived(&copies, &assigns); Callback<void(CopyCounter)> coerce_cb = Bind(&VoidPolymorphic1<CopyCounter>); - coerce_cb.Run(CopyCounter(dervied)); + coerce_cb.Run(CopyCounter(derived)); EXPECT_GE(2, copies); EXPECT_EQ(0, assigns); } diff --git a/chromium/base/bind_unittest.nc b/chromium/base/bind_unittest.nc index 4732097d776..259638673a9 100644 --- a/chromium/base/bind_unittest.nc +++ b/chromium/base/bind_unittest.nc @@ -167,7 +167,7 @@ void WontCompile() { method_bound_to_array_cb.Run(); } -#elif defined(NCTEST_NO_RAW_PTR_FOR_REFCOUNTED_TYPES) // [r"fatal error: static_assert failed \"p1_is_refcounted_type_and_needs_scoped_refptr\""] +#elif defined(NCTEST_NO_RAW_PTR_FOR_REFCOUNTED_TYPES) // [r"fatal error: static_assert failed \"a_parameter_is_refcounted_type_and_needs_scoped_refptr\""] // Refcounted types should not be bound as a raw pointer. void WontCompile() { @@ -190,7 +190,7 @@ void WontCompile() { weak_ptr_with_non_void_return_type.Run(); } -#elif defined(NCTEST_DISALLOW_ASSIGN_DIFFERENT_TYPES) // [r"fatal error: no viable conversion from 'Callback<typename internal::BindState<typename internal::FunctorTraits<void \(\*\)\(int\)>::RunnableType, typename internal::FunctorTraits<void \(\*\)\(int\)>::RunType, void \(\)>::UnboundRunType>' to 'Callback<void \(\)>'"] +#elif defined(NCTEST_DISALLOW_ASSIGN_DIFFERENT_TYPES) // [r"fatal error: no viable conversion from 'Callback<typename internal::BindState<typename internal::FunctorTraits<void \(\*\)\(int\)>::RunnableType, typename internal::FunctorTraits<void \(\*\)\(int\)>::RunType, internal::TypeList<> >::UnboundRunType>' to 'Callback<void \(\)>'"] // Bind result cannot be assigned to Callbacks with a mismatching type. void WontCompile() { diff --git a/chromium/base/build_time.h b/chromium/base/build_time.h index c7df479b622..4f0abc38133 100644 --- a/chromium/base/build_time.h +++ b/chromium/base/build_time.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_BUILD_TIME_ -#define BASE_BUILD_TIME_ +#ifndef BASE_BUILD_TIME_H_ +#define BASE_BUILD_TIME_H_ #include "base/base_export.h" #include "base/time/time.h" @@ -25,4 +25,4 @@ Time BASE_EXPORT GetBuildTime(); } // namespace base -#endif // BASE_BUILD_TIME_ +#endif // BASE_BUILD_TIME_H_ diff --git a/chromium/base/callback.h b/chromium/base/callback.h index 364f506139f..00669dd83d1 100644 --- a/chromium/base/callback.h +++ b/chromium/base/callback.h @@ -1,8 +1,3 @@ -// This file was GENERATED by command: -// pump.py callback.h.pump -// DO NOT EDIT BY HAND!!! - - // 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. @@ -367,10 +362,10 @@ template <typename Runnable, typename RunType, typename BoundArgsType> struct BindState; } // namespace internal -template <typename R> -class Callback<R(void)> : public internal::CallbackBase { +template <typename R, typename... Args> +class Callback<R(Args...)> : public internal::CallbackBase { public: - typedef R(RunType)(); + typedef R(RunType)(Args...); Callback() : CallbackBase(NULL) { } @@ -380,7 +375,6 @@ class Callback<R(void)> : public internal::CallbackBase { Callback(internal::BindState<Runnable, BindRunType, BoundArgsType>* bind_state) : CallbackBase(bind_state) { - // Force the assignment to a local variable of PolymorphicInvoke // so the compiler will typecheck that the passed in Run() method has // the correct type. @@ -394,377 +388,24 @@ class Callback<R(void)> : public internal::CallbackBase { return CallbackBase::Equals(other); } - R Run() const { + R Run(typename internal::CallbackParamTraits<Args>::ForwardType... args) + const { PolymorphicInvoke f = reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_); - return f(bind_state_.get()); - } - - private: - typedef R(*PolymorphicInvoke)( - internal::BindStateBase*); - -}; - -template <typename R, typename A1> -class Callback<R(A1)> : public internal::CallbackBase { - public: - typedef R(RunType)(A1); - - Callback() : CallbackBase(NULL) { } - - // Note that this constructor CANNOT be explicit, and that Bind() CANNOT - // return the exact Callback<> type. See base/bind.h for details. - template <typename Runnable, typename BindRunType, typename BoundArgsType> - Callback(internal::BindState<Runnable, BindRunType, - BoundArgsType>* bind_state) - : CallbackBase(bind_state) { - - // Force the assignment to a local variable of PolymorphicInvoke - // so the compiler will typecheck that the passed in Run() method has - // the correct type. - PolymorphicInvoke invoke_func = - &internal::BindState<Runnable, BindRunType, BoundArgsType> - ::InvokerType::Run; - polymorphic_invoke_ = reinterpret_cast<InvokeFuncStorage>(invoke_func); - } - - bool Equals(const Callback& other) const { - return CallbackBase::Equals(other); - } - - R Run(typename internal::CallbackParamTraits<A1>::ForwardType a1) const { - PolymorphicInvoke f = - reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_); - - return f(bind_state_.get(), internal::CallbackForward(a1)); + return f(bind_state_.get(), internal::CallbackForward(args)...); } private: typedef R(*PolymorphicInvoke)( internal::BindStateBase*, - typename internal::CallbackParamTraits<A1>::ForwardType); - + typename internal::CallbackParamTraits<Args>::ForwardType...); }; -template <typename R, typename A1, typename A2> -class Callback<R(A1, A2)> : public internal::CallbackBase { - public: - typedef R(RunType)(A1, A2); - - Callback() : CallbackBase(NULL) { } - - // Note that this constructor CANNOT be explicit, and that Bind() CANNOT - // return the exact Callback<> type. See base/bind.h for details. - template <typename Runnable, typename BindRunType, typename BoundArgsType> - Callback(internal::BindState<Runnable, BindRunType, - BoundArgsType>* bind_state) - : CallbackBase(bind_state) { - - // Force the assignment to a local variable of PolymorphicInvoke - // so the compiler will typecheck that the passed in Run() method has - // the correct type. - PolymorphicInvoke invoke_func = - &internal::BindState<Runnable, BindRunType, BoundArgsType> - ::InvokerType::Run; - polymorphic_invoke_ = reinterpret_cast<InvokeFuncStorage>(invoke_func); - } - - bool Equals(const Callback& other) const { - return CallbackBase::Equals(other); - } - - R Run(typename internal::CallbackParamTraits<A1>::ForwardType a1, - typename internal::CallbackParamTraits<A2>::ForwardType a2) const { - PolymorphicInvoke f = - reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_); - - return f(bind_state_.get(), internal::CallbackForward(a1), - internal::CallbackForward(a2)); - } - - private: - typedef R(*PolymorphicInvoke)( - internal::BindStateBase*, - typename internal::CallbackParamTraits<A1>::ForwardType, - typename internal::CallbackParamTraits<A2>::ForwardType); - -}; - -template <typename R, typename A1, typename A2, typename A3> -class Callback<R(A1, A2, A3)> : public internal::CallbackBase { - public: - typedef R(RunType)(A1, A2, A3); - - Callback() : CallbackBase(NULL) { } - - // Note that this constructor CANNOT be explicit, and that Bind() CANNOT - // return the exact Callback<> type. See base/bind.h for details. - template <typename Runnable, typename BindRunType, typename BoundArgsType> - Callback(internal::BindState<Runnable, BindRunType, - BoundArgsType>* bind_state) - : CallbackBase(bind_state) { - - // Force the assignment to a local variable of PolymorphicInvoke - // so the compiler will typecheck that the passed in Run() method has - // the correct type. - PolymorphicInvoke invoke_func = - &internal::BindState<Runnable, BindRunType, BoundArgsType> - ::InvokerType::Run; - polymorphic_invoke_ = reinterpret_cast<InvokeFuncStorage>(invoke_func); - } - - bool Equals(const Callback& other) const { - return CallbackBase::Equals(other); - } - - R Run(typename internal::CallbackParamTraits<A1>::ForwardType a1, - typename internal::CallbackParamTraits<A2>::ForwardType a2, - typename internal::CallbackParamTraits<A3>::ForwardType a3) const { - PolymorphicInvoke f = - reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_); - - return f(bind_state_.get(), internal::CallbackForward(a1), - internal::CallbackForward(a2), - internal::CallbackForward(a3)); - } - - private: - typedef R(*PolymorphicInvoke)( - internal::BindStateBase*, - typename internal::CallbackParamTraits<A1>::ForwardType, - typename internal::CallbackParamTraits<A2>::ForwardType, - typename internal::CallbackParamTraits<A3>::ForwardType); - -}; - -template <typename R, typename A1, typename A2, typename A3, typename A4> -class Callback<R(A1, A2, A3, A4)> : public internal::CallbackBase { - public: - typedef R(RunType)(A1, A2, A3, A4); - - Callback() : CallbackBase(NULL) { } - - // Note that this constructor CANNOT be explicit, and that Bind() CANNOT - // return the exact Callback<> type. See base/bind.h for details. - template <typename Runnable, typename BindRunType, typename BoundArgsType> - Callback(internal::BindState<Runnable, BindRunType, - BoundArgsType>* bind_state) - : CallbackBase(bind_state) { - - // Force the assignment to a local variable of PolymorphicInvoke - // so the compiler will typecheck that the passed in Run() method has - // the correct type. - PolymorphicInvoke invoke_func = - &internal::BindState<Runnable, BindRunType, BoundArgsType> - ::InvokerType::Run; - polymorphic_invoke_ = reinterpret_cast<InvokeFuncStorage>(invoke_func); - } - - bool Equals(const Callback& other) const { - return CallbackBase::Equals(other); - } - - R Run(typename internal::CallbackParamTraits<A1>::ForwardType a1, - typename internal::CallbackParamTraits<A2>::ForwardType a2, - typename internal::CallbackParamTraits<A3>::ForwardType a3, - typename internal::CallbackParamTraits<A4>::ForwardType a4) const { - PolymorphicInvoke f = - reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_); - - return f(bind_state_.get(), internal::CallbackForward(a1), - internal::CallbackForward(a2), - internal::CallbackForward(a3), - internal::CallbackForward(a4)); - } - - private: - typedef R(*PolymorphicInvoke)( - internal::BindStateBase*, - typename internal::CallbackParamTraits<A1>::ForwardType, - typename internal::CallbackParamTraits<A2>::ForwardType, - typename internal::CallbackParamTraits<A3>::ForwardType, - typename internal::CallbackParamTraits<A4>::ForwardType); - -}; - -template <typename R, typename A1, typename A2, typename A3, typename A4, - typename A5> -class Callback<R(A1, A2, A3, A4, A5)> : public internal::CallbackBase { - public: - typedef R(RunType)(A1, A2, A3, A4, A5); - - Callback() : CallbackBase(NULL) { } - - // Note that this constructor CANNOT be explicit, and that Bind() CANNOT - // return the exact Callback<> type. See base/bind.h for details. - template <typename Runnable, typename BindRunType, typename BoundArgsType> - Callback(internal::BindState<Runnable, BindRunType, - BoundArgsType>* bind_state) - : CallbackBase(bind_state) { - - // Force the assignment to a local variable of PolymorphicInvoke - // so the compiler will typecheck that the passed in Run() method has - // the correct type. - PolymorphicInvoke invoke_func = - &internal::BindState<Runnable, BindRunType, BoundArgsType> - ::InvokerType::Run; - polymorphic_invoke_ = reinterpret_cast<InvokeFuncStorage>(invoke_func); - } - - bool Equals(const Callback& other) const { - return CallbackBase::Equals(other); - } - - R Run(typename internal::CallbackParamTraits<A1>::ForwardType a1, - typename internal::CallbackParamTraits<A2>::ForwardType a2, - typename internal::CallbackParamTraits<A3>::ForwardType a3, - typename internal::CallbackParamTraits<A4>::ForwardType a4, - typename internal::CallbackParamTraits<A5>::ForwardType a5) const { - PolymorphicInvoke f = - reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_); - - return f(bind_state_.get(), internal::CallbackForward(a1), - internal::CallbackForward(a2), - internal::CallbackForward(a3), - internal::CallbackForward(a4), - internal::CallbackForward(a5)); - } - - private: - typedef R(*PolymorphicInvoke)( - internal::BindStateBase*, - typename internal::CallbackParamTraits<A1>::ForwardType, - typename internal::CallbackParamTraits<A2>::ForwardType, - typename internal::CallbackParamTraits<A3>::ForwardType, - typename internal::CallbackParamTraits<A4>::ForwardType, - typename internal::CallbackParamTraits<A5>::ForwardType); - -}; - -template <typename R, typename A1, typename A2, typename A3, typename A4, - typename A5, typename A6> -class Callback<R(A1, A2, A3, A4, A5, A6)> : public internal::CallbackBase { - public: - typedef R(RunType)(A1, A2, A3, A4, A5, A6); - - Callback() : CallbackBase(NULL) { } - - // Note that this constructor CANNOT be explicit, and that Bind() CANNOT - // return the exact Callback<> type. See base/bind.h for details. - template <typename Runnable, typename BindRunType, typename BoundArgsType> - Callback(internal::BindState<Runnable, BindRunType, - BoundArgsType>* bind_state) - : CallbackBase(bind_state) { - - // Force the assignment to a local variable of PolymorphicInvoke - // so the compiler will typecheck that the passed in Run() method has - // the correct type. - PolymorphicInvoke invoke_func = - &internal::BindState<Runnable, BindRunType, BoundArgsType> - ::InvokerType::Run; - polymorphic_invoke_ = reinterpret_cast<InvokeFuncStorage>(invoke_func); - } - - bool Equals(const Callback& other) const { - return CallbackBase::Equals(other); - } - - R Run(typename internal::CallbackParamTraits<A1>::ForwardType a1, - typename internal::CallbackParamTraits<A2>::ForwardType a2, - typename internal::CallbackParamTraits<A3>::ForwardType a3, - typename internal::CallbackParamTraits<A4>::ForwardType a4, - typename internal::CallbackParamTraits<A5>::ForwardType a5, - typename internal::CallbackParamTraits<A6>::ForwardType a6) const { - PolymorphicInvoke f = - reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_); - - return f(bind_state_.get(), internal::CallbackForward(a1), - internal::CallbackForward(a2), - internal::CallbackForward(a3), - internal::CallbackForward(a4), - internal::CallbackForward(a5), - internal::CallbackForward(a6)); - } - - private: - typedef R(*PolymorphicInvoke)( - internal::BindStateBase*, - typename internal::CallbackParamTraits<A1>::ForwardType, - typename internal::CallbackParamTraits<A2>::ForwardType, - typename internal::CallbackParamTraits<A3>::ForwardType, - typename internal::CallbackParamTraits<A4>::ForwardType, - typename internal::CallbackParamTraits<A5>::ForwardType, - typename internal::CallbackParamTraits<A6>::ForwardType); - -}; - -template <typename R, typename A1, typename A2, typename A3, typename A4, - typename A5, typename A6, typename A7> -class Callback<R(A1, A2, A3, A4, A5, A6, A7)> : public internal::CallbackBase { - public: - typedef R(RunType)(A1, A2, A3, A4, A5, A6, A7); - - Callback() : CallbackBase(NULL) { } - - // Note that this constructor CANNOT be explicit, and that Bind() CANNOT - // return the exact Callback<> type. See base/bind.h for details. - template <typename Runnable, typename BindRunType, typename BoundArgsType> - Callback(internal::BindState<Runnable, BindRunType, - BoundArgsType>* bind_state) - : CallbackBase(bind_state) { - - // Force the assignment to a local variable of PolymorphicInvoke - // so the compiler will typecheck that the passed in Run() method has - // the correct type. - PolymorphicInvoke invoke_func = - &internal::BindState<Runnable, BindRunType, BoundArgsType> - ::InvokerType::Run; - polymorphic_invoke_ = reinterpret_cast<InvokeFuncStorage>(invoke_func); - } - - bool Equals(const Callback& other) const { - return CallbackBase::Equals(other); - } - - R Run(typename internal::CallbackParamTraits<A1>::ForwardType a1, - typename internal::CallbackParamTraits<A2>::ForwardType a2, - typename internal::CallbackParamTraits<A3>::ForwardType a3, - typename internal::CallbackParamTraits<A4>::ForwardType a4, - typename internal::CallbackParamTraits<A5>::ForwardType a5, - typename internal::CallbackParamTraits<A6>::ForwardType a6, - typename internal::CallbackParamTraits<A7>::ForwardType a7) const { - PolymorphicInvoke f = - reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_); - - return f(bind_state_.get(), internal::CallbackForward(a1), - internal::CallbackForward(a2), - internal::CallbackForward(a3), - internal::CallbackForward(a4), - internal::CallbackForward(a5), - internal::CallbackForward(a6), - internal::CallbackForward(a7)); - } - - private: - typedef R(*PolymorphicInvoke)( - internal::BindStateBase*, - typename internal::CallbackParamTraits<A1>::ForwardType, - typename internal::CallbackParamTraits<A2>::ForwardType, - typename internal::CallbackParamTraits<A3>::ForwardType, - typename internal::CallbackParamTraits<A4>::ForwardType, - typename internal::CallbackParamTraits<A5>::ForwardType, - typename internal::CallbackParamTraits<A6>::ForwardType, - typename internal::CallbackParamTraits<A7>::ForwardType); - -}; - - // Syntactic sugar to make Callback<void(void)> easier to declare since it // will be used in a lot of APIs with delayed execution. typedef Callback<void(void)> Closure; } // namespace base -#endif // BASE_CALLBACK_H +#endif // BASE_CALLBACK_H_ diff --git a/chromium/base/callback.h.pump b/chromium/base/callback.h.pump deleted file mode 100644 index 686196dae6a..00000000000 --- a/chromium/base/callback.h.pump +++ /dev/null @@ -1,436 +0,0 @@ -$$ This is a pump file for generating file templates. Pump is a python -$$ script that is part of the Google Test suite of utilities. Description -$$ can be found here: -$$ -$$ http://code.google.com/p/googletest/wiki/PumpManual -$$ - -$$ See comment for MAX_ARITY in base/bind.h.pump. -$var MAX_ARITY = 7 - -// 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_CALLBACK_H_ -#define BASE_CALLBACK_H_ - -#include "base/callback_forward.h" -#include "base/callback_internal.h" -#include "base/template_util.h" - -// NOTE: Header files that do not require the full definition of Callback or -// Closure should #include "base/callback_forward.h" instead of this file. - -// ----------------------------------------------------------------------------- -// Introduction -// ----------------------------------------------------------------------------- -// -// The templated Callback class is a generalized function object. Together -// with the Bind() function in bind.h, they provide a type-safe method for -// performing partial application of functions. -// -// Partial application (or "currying") is the process of binding a subset of -// a function's arguments to produce another function that takes fewer -// arguments. This can be used to pass around a unit of delayed execution, -// much like lexical closures are used in other languages. For example, it -// is used in Chromium code to schedule tasks on different MessageLoops. -// -// A callback with no unbound input parameters (base::Callback<void(void)>) -// is called a base::Closure. Note that this is NOT the same as what other -// languages refer to as a closure -- it does not retain a reference to its -// enclosing environment. -// -// MEMORY MANAGEMENT AND PASSING -// -// The Callback objects themselves should be passed by const-reference, and -// stored by copy. They internally store their state via a refcounted class -// and thus do not need to be deleted. -// -// The reason to pass via a const-reference is to avoid unnecessary -// AddRef/Release pairs to the internal state. -// -// -// ----------------------------------------------------------------------------- -// Quick reference for basic stuff -// ----------------------------------------------------------------------------- -// -// BINDING A BARE FUNCTION -// -// int Return5() { return 5; } -// base::Callback<int(void)> func_cb = base::Bind(&Return5); -// LOG(INFO) << func_cb.Run(); // Prints 5. -// -// BINDING A CLASS METHOD -// -// The first argument to bind is the member function to call, the second is -// the object on which to call it. -// -// class Ref : public base::RefCountedThreadSafe<Ref> { -// public: -// int Foo() { return 3; } -// void PrintBye() { LOG(INFO) << "bye."; } -// }; -// scoped_refptr<Ref> ref = new Ref(); -// base::Callback<void(void)> ref_cb = base::Bind(&Ref::Foo, ref); -// LOG(INFO) << ref_cb.Run(); // Prints out 3. -// -// By default the object must support RefCounted or you will get a compiler -// error. If you're passing between threads, be sure it's -// RefCountedThreadSafe! See "Advanced binding of member functions" below if -// you don't want to use reference counting. -// -// RUNNING A CALLBACK -// -// Callbacks can be run with their "Run" method, which has the same -// signature as the template argument to the callback. -// -// void DoSomething(const base::Callback<void(int, std::string)>& callback) { -// callback.Run(5, "hello"); -// } -// -// Callbacks can be run more than once (they don't get deleted or marked when -// run). However, this precludes using base::Passed (see below). -// -// void DoSomething(const base::Callback<double(double)>& callback) { -// double myresult = callback.Run(3.14159); -// myresult += callback.Run(2.71828); -// } -// -// PASSING UNBOUND INPUT PARAMETERS -// -// Unbound parameters are specified at the time a callback is Run(). They are -// specified in the Callback template type: -// -// void MyFunc(int i, const std::string& str) {} -// base::Callback<void(int, const std::string&)> cb = base::Bind(&MyFunc); -// cb.Run(23, "hello, world"); -// -// PASSING BOUND INPUT PARAMETERS -// -// Bound parameters are specified when you create thee callback as arguments -// to Bind(). They will be passed to the function and the Run()ner of the -// callback doesn't see those values or even know that the function it's -// calling. -// -// void MyFunc(int i, const std::string& str) {} -// base::Callback<void(void)> cb = base::Bind(&MyFunc, 23, "hello world"); -// cb.Run(); -// -// A callback with no unbound input parameters (base::Callback<void(void)>) -// is called a base::Closure. So we could have also written: -// -// base::Closure cb = base::Bind(&MyFunc, 23, "hello world"); -// -// When calling member functions, bound parameters just go after the object -// pointer. -// -// base::Closure cb = base::Bind(&MyClass::MyFunc, this, 23, "hello world"); -// -// PARTIAL BINDING OF PARAMETERS -// -// You can specify some parameters when you create the callback, and specify -// the rest when you execute the callback. -// -// void MyFunc(int i, const std::string& str) {} -// base::Callback<void(const std::string&)> cb = base::Bind(&MyFunc, 23); -// cb.Run("hello world"); -// -// When calling a function bound parameters are first, followed by unbound -// parameters. -// -// -// ----------------------------------------------------------------------------- -// Quick reference for advanced binding -// ----------------------------------------------------------------------------- -// -// BINDING A CLASS METHOD WITH WEAK POINTERS -// -// base::Bind(&MyClass::Foo, GetWeakPtr()); -// -// The callback will not be run if the object has already been destroyed. -// DANGER: weak pointers are not threadsafe, so don't use this -// when passing between threads! -// -// BINDING A CLASS METHOD WITH MANUAL LIFETIME MANAGEMENT -// -// base::Bind(&MyClass::Foo, base::Unretained(this)); -// -// This disables all lifetime management on the object. You're responsible -// for making sure the object is alive at the time of the call. You break it, -// you own it! -// -// BINDING A CLASS METHOD AND HAVING THE CALLBACK OWN THE CLASS -// -// MyClass* myclass = new MyClass; -// base::Bind(&MyClass::Foo, base::Owned(myclass)); -// -// The object will be deleted when the callback is destroyed, even if it's -// not run (like if you post a task during shutdown). Potentially useful for -// "fire and forget" cases. -// -// IGNORING RETURN VALUES -// -// Sometimes you want to call a function that returns a value in a callback -// that doesn't expect a return value. -// -// int DoSomething(int arg) { cout << arg << endl; } -// base::Callback<void<int>) cb = -// base::Bind(base::IgnoreResult(&DoSomething)); -// -// -// ----------------------------------------------------------------------------- -// Quick reference for binding parameters to Bind() -// ----------------------------------------------------------------------------- -// -// Bound parameters are specified as arguments to Bind() and are passed to the -// function. A callback with no parameters or no unbound parameters is called a -// Closure (base::Callback<void(void)> and base::Closure are the same thing). -// -// PASSING PARAMETERS OWNED BY THE CALLBACK -// -// void Foo(int* arg) { cout << *arg << endl; } -// int* pn = new int(1); -// base::Closure foo_callback = base::Bind(&foo, base::Owned(pn)); -// -// The parameter will be deleted when the callback is destroyed, even if it's -// not run (like if you post a task during shutdown). -// -// PASSING PARAMETERS AS A scoped_ptr -// -// void TakesOwnership(scoped_ptr<Foo> arg) {} -// scoped_ptr<Foo> f(new Foo); -// // f becomes null during the following call. -// base::Closure cb = base::Bind(&TakesOwnership, base::Passed(&f)); -// -// Ownership of the parameter will be with the callback until the it is run, -// when ownership is passed to the callback function. This means the callback -// can only be run once. If the callback is never run, it will delete the -// object when it's destroyed. -// -// PASSING PARAMETERS AS A scoped_refptr -// -// void TakesOneRef(scoped_refptr<Foo> arg) {} -// scoped_refptr<Foo> f(new Foo) -// base::Closure cb = base::Bind(&TakesOneRef, f); -// -// This should "just work." The closure will take a reference as long as it -// is alive, and another reference will be taken for the called function. -// -// PASSING PARAMETERS BY REFERENCE -// -// void foo(int arg) { cout << arg << endl } -// int n = 1; -// base::Closure has_ref = base::Bind(&foo, base::ConstRef(n)); -// n = 2; -// has_ref.Run(); // Prints "2" -// -// Normally parameters are copied in the closure. DANGER: ConstRef stores a -// const reference instead, referencing the original parameter. This means -// that you must ensure the object outlives the callback! -// -// -// ----------------------------------------------------------------------------- -// Implementation notes -// ----------------------------------------------------------------------------- -// -// WHERE IS THIS DESIGN FROM: -// -// The design Callback and Bind is heavily influenced by C++'s -// tr1::function/tr1::bind, and by the "Google Callback" system used inside -// Google. -// -// -// HOW THE IMPLEMENTATION WORKS: -// -// There are three main components to the system: -// 1) The Callback classes. -// 2) The Bind() functions. -// 3) The arguments wrappers (e.g., Unretained() and ConstRef()). -// -// The Callback classes represent a generic function pointer. Internally, -// it stores a refcounted piece of state that represents the target function -// and all its bound parameters. Each Callback specialization has a templated -// constructor that takes an BindState<>*. In the context of the constructor, -// the static type of this BindState<> pointer uniquely identifies the -// function it is representing, all its bound parameters, and a Run() method -// that is capable of invoking the target. -// -// Callback's constructor takes the BindState<>* that has the full static type -// and erases the target function type as well as the types of the bound -// parameters. It does this by storing a pointer to the specific Run() -// function, and upcasting the state of BindState<>* to a -// BindStateBase*. This is safe as long as this BindStateBase pointer -// is only used with the stored Run() pointer. -// -// To BindState<> objects are created inside the Bind() functions. -// These functions, along with a set of internal templates, are responsible for -// -// - Unwrapping the function signature into return type, and parameters -// - Determining the number of parameters that are bound -// - Creating the BindState storing the bound parameters -// - Performing compile-time asserts to avoid error-prone behavior -// - Returning an Callback<> with an arity matching the number of unbound -// parameters and that knows the correct refcounting semantics for the -// target object if we are binding a method. -// -// The Bind functions do the above using type-inference, and template -// specializations. -// -// By default Bind() will store copies of all bound parameters, and attempt -// to refcount a target object if the function being bound is a class method. -// These copies are created even if the function takes parameters as const -// references. (Binding to non-const references is forbidden, see bind.h.) -// -// To change this behavior, we introduce a set of argument wrappers -// (e.g., Unretained(), and ConstRef()). These are simple container templates -// that are passed by value, and wrap a pointer to argument. See the -// file-level comment in base/bind_helpers.h for more info. -// -// These types are passed to the Unwrap() functions, and the MaybeRefcount() -// functions respectively to modify the behavior of Bind(). The Unwrap() -// and MaybeRefcount() functions change behavior by doing partial -// specialization based on whether or not a parameter is a wrapper type. -// -// ConstRef() is similar to tr1::cref. Unretained() is specific to Chromium. -// -// -// WHY NOT TR1 FUNCTION/BIND? -// -// Direct use of tr1::function and tr1::bind was considered, but ultimately -// rejected because of the number of copy constructors invocations involved -// in the binding of arguments during construction, and the forwarding of -// arguments during invocation. These copies will no longer be an issue in -// C++0x because C++0x will support rvalue reference allowing for the compiler -// to avoid these copies. However, waiting for C++0x is not an option. -// -// Measured with valgrind on gcc version 4.4.3 (Ubuntu 4.4.3-4ubuntu5), the -// tr1::bind call itself will invoke a non-trivial copy constructor three times -// for each bound parameter. Also, each when passing a tr1::function, each -// bound argument will be copied again. -// -// In addition to the copies taken at binding and invocation, copying a -// tr1::function causes a copy to be made of all the bound parameters and -// state. -// -// Furthermore, in Chromium, it is desirable for the Callback to take a -// reference on a target object when representing a class method call. This -// is not supported by tr1. -// -// Lastly, tr1::function and tr1::bind has a more general and flexible API. -// This includes things like argument reordering by use of -// tr1::bind::placeholder, support for non-const reference parameters, and some -// limited amount of subtyping of the tr1::function object (e.g., -// tr1::function<int(int)> is convertible to tr1::function<void(int)>). -// -// These are not features that are required in Chromium. Some of them, such as -// allowing for reference parameters, and subtyping of functions, may actually -// become a source of errors. Removing support for these features actually -// allows for a simpler implementation, and a terser Currying API. -// -// -// WHY NOT GOOGLE CALLBACKS? -// -// The Google callback system also does not support refcounting. Furthermore, -// its implementation has a number of strange edge cases with respect to type -// conversion of its arguments. In particular, the argument's constness must -// at times match exactly the function signature, or the type-inference might -// break. Given the above, writing a custom solution was easier. -// -// -// MISSING FUNCTIONALITY -// - Invoking the return of Bind. Bind(&foo).Run() does not work; -// - Binding arrays to functions that take a non-const pointer. -// Example: -// void Foo(const char* ptr); -// void Bar(char* ptr); -// Bind(&Foo, "test"); -// Bind(&Bar, "test"); // This fails because ptr is not const. - -namespace base { - -// First, we forward declare the Callback class template. This informs the -// compiler that the template only has 1 type parameter which is the function -// signature that the Callback is representing. -// -// After this, create template specializations for 0-$(MAX_ARITY) parameters. Note that -// even though the template typelist grows, the specialization still -// only has one type: the function signature. -// -// If you are thinking of forward declaring Callback in your own header file, -// please include "base/callback_forward.h" instead. -template <typename Sig> -class Callback; - -namespace internal { -template <typename Runnable, typename RunType, typename BoundArgsType> -struct BindState; -} // namespace internal - - -$range ARITY 0..MAX_ARITY -$for ARITY [[ -$range ARG 1..ARITY - -$if ARITY == 0 [[ -template <typename R> -class Callback<R(void)> : public internal::CallbackBase { -]] $else [[ -template <typename R, $for ARG , [[typename A$(ARG)]]> -class Callback<R($for ARG , [[A$(ARG)]])> : public internal::CallbackBase { -]] - - public: - typedef R(RunType)($for ARG , [[A$(ARG)]]); - - Callback() : CallbackBase(NULL) { } - - // Note that this constructor CANNOT be explicit, and that Bind() CANNOT - // return the exact Callback<> type. See base/bind.h for details. - template <typename Runnable, typename BindRunType, typename BoundArgsType> - Callback(internal::BindState<Runnable, BindRunType, - BoundArgsType>* bind_state) - : CallbackBase(bind_state) { - - // Force the assignment to a local variable of PolymorphicInvoke - // so the compiler will typecheck that the passed in Run() method has - // the correct type. - PolymorphicInvoke invoke_func = - &internal::BindState<Runnable, BindRunType, BoundArgsType> - ::InvokerType::Run; - polymorphic_invoke_ = reinterpret_cast<InvokeFuncStorage>(invoke_func); - } - - bool Equals(const Callback& other) const { - return CallbackBase::Equals(other); - } - - R Run($for ARG , - [[typename internal::CallbackParamTraits<A$(ARG)>::ForwardType a$(ARG)]]) const { - PolymorphicInvoke f = - reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_); - - return f(bind_state_.get()[[]] -$if ARITY != 0 [[, ]] -$for ARG , - [[internal::CallbackForward(a$(ARG))]]); - } - - private: - typedef R(*PolymorphicInvoke)( - internal::BindStateBase*[[]] -$if ARITY != 0 [[, ]] -$for ARG , [[typename internal::CallbackParamTraits<A$(ARG)>::ForwardType]]); - -}; - - -]] $$ for ARITY - -// Syntactic sugar to make Callback<void(void)> easier to declare since it -// will be used in a lot of APIs with delayed execution. -typedef Callback<void(void)> Closure; - -} // namespace base - -#endif // BASE_CALLBACK_H diff --git a/chromium/base/callback_forward.h b/chromium/base/callback_forward.h index 79832481af2..262c3065300 100644 --- a/chromium/base/callback_forward.h +++ b/chromium/base/callback_forward.h @@ -14,4 +14,4 @@ typedef Callback<void(void)> Closure; } // namespace base -#endif // BASE_CALLBACK_FORWARD_H +#endif // BASE_CALLBACK_FORWARD_H_ diff --git a/chromium/base/callback_internal.cc b/chromium/base/callback_internal.cc index ed0fb0dd0cc..2553fe7e1b6 100644 --- a/chromium/base/callback_internal.cc +++ b/chromium/base/callback_internal.cc @@ -9,6 +9,18 @@ namespace base { namespace internal { +void BindStateBase::AddRef() { + AtomicRefCountInc(&ref_count_); +} + +void BindStateBase::Release() { + if (!AtomicRefCountDec(&ref_count_)) + destructor_(this); +} + +CallbackBase::CallbackBase(const CallbackBase& c) = default; +CallbackBase& CallbackBase::operator=(const CallbackBase& c) = default; + void CallbackBase::Reset() { polymorphic_invoke_ = NULL; // NULL the bind_state_ last, since it may be holding the last ref to whatever @@ -24,7 +36,7 @@ bool CallbackBase::Equals(const CallbackBase& other) const { CallbackBase::CallbackBase(BindStateBase* bind_state) : bind_state_(bind_state), polymorphic_invoke_(NULL) { - DCHECK(!bind_state_.get() || bind_state_->HasOneRef()); + DCHECK(!bind_state_.get() || bind_state_->ref_count_ == 1); } CallbackBase::~CallbackBase() { diff --git a/chromium/base/callback_internal.h b/chromium/base/callback_internal.h index b85973d3815..fefd7a2b201 100644 --- a/chromium/base/callback_internal.h +++ b/chromium/base/callback_internal.h @@ -10,15 +10,19 @@ #include <stddef.h> +#include "base/atomic_ref_count.h" #include "base/base_export.h" +#include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "base/template_util.h" template <typename T> class ScopedVector; namespace base { namespace internal { +class CallbackBase; // BindStateBase is used to provide an opaque handle that the Callback // class can use to represent a function object with bound arguments. It @@ -26,16 +30,39 @@ namespace internal { // DoInvoke function to perform the function execution. This allows // us to shield the Callback class from the types of the bound argument via // "type erasure." -class BindStateBase : public RefCountedThreadSafe<BindStateBase> { +// At the base level, the only task is to add reference counting data. Don't use +// RefCountedThreadSafe since it requires the destructor to be a virtual method. +// Creating a vtable for every BindState template instantiation results in a lot +// of bloat. Its only task is to call the destructor which can be done with a +// function pointer. +class BindStateBase { protected: - friend class RefCountedThreadSafe<BindStateBase>; - virtual ~BindStateBase() {} + explicit BindStateBase(void (*destructor)(BindStateBase*)) + : ref_count_(0), destructor_(destructor) {} + ~BindStateBase() = default; + + private: + friend class scoped_refptr<BindStateBase>; + friend class CallbackBase; + + void AddRef(); + void Release(); + + AtomicRefCount ref_count_; + + // Pointer to a function that will properly destroy |this|. + void (*destructor_)(BindStateBase*); + + DISALLOW_COPY_AND_ASSIGN(BindStateBase); }; // Holds the Callback methods that don't require specialization to reduce // template bloat. class BASE_EXPORT CallbackBase { public: + CallbackBase(const CallbackBase& c); + CallbackBase& operator=(const CallbackBase& c); + // Returns true if Callback is null (doesn't refer to anything). bool is_null() const { return bind_state_.get() == NULL; } @@ -81,6 +108,28 @@ template <typename T> struct IsMoveOnlyType { !is_const<T>::value; }; +// Returns |Then| as SelectType::Type if |condition| is true. Otherwise returns +// |Else|. +template <bool condition, typename Then, typename Else> +struct SelectType { + typedef Then Type; +}; + +template <typename Then, typename Else> +struct SelectType<false, Then, Else> { + typedef Else Type; +}; + +template <typename> +struct CallbackParamTraitsForMoveOnlyType; + +template <typename> +struct CallbackParamTraitsForNonMoveOnlyType; + +// TODO(tzik): Use a default parameter once MSVS supports variadic templates +// with default values. +// http://connect.microsoft.com/VisualStudio/feedbackdetail/view/957801/compilation-error-with-variadic-templates +// // This is a typetraits object that's used to take an argument type, and // extract a suitable type for storing and forwarding arguments. // @@ -92,8 +141,15 @@ template <typename T> struct IsMoveOnlyType { // parameters by const reference. In this case, we end up passing an actual // array type in the initializer list which C++ does not allow. This will // break passing of C-string literals. -template <typename T, bool is_move_only = IsMoveOnlyType<T>::value> -struct CallbackParamTraits { +template <typename T> +struct CallbackParamTraits + : SelectType<IsMoveOnlyType<T>::value, + CallbackParamTraitsForMoveOnlyType<T>, + CallbackParamTraitsForNonMoveOnlyType<T> >::Type { +}; + +template <typename T> +struct CallbackParamTraitsForNonMoveOnlyType { typedef const T& ForwardType; typedef T StorageType; }; @@ -104,7 +160,7 @@ struct CallbackParamTraits { // // The ForwardType should only be used for unbound arguments. template <typename T> -struct CallbackParamTraits<T&, false> { +struct CallbackParamTraitsForNonMoveOnlyType<T&> { typedef T& ForwardType; typedef T StorageType; }; @@ -115,14 +171,14 @@ struct CallbackParamTraits<T&, false> { // T[n]" does not seem to match correctly, so we are stuck with this // restriction. template <typename T, size_t n> -struct CallbackParamTraits<T[n], false> { +struct CallbackParamTraitsForNonMoveOnlyType<T[n]> { typedef const T* ForwardType; typedef const T* StorageType; }; // See comment for CallbackParamTraits<T[n]>. template <typename T> -struct CallbackParamTraits<T[], false> { +struct CallbackParamTraitsForNonMoveOnlyType<T[]> { typedef const T* ForwardType; typedef const T* StorageType; }; @@ -141,7 +197,7 @@ struct CallbackParamTraits<T[], false> { // reference cannot be used with temporaries which means the result of a // function or a cast would not be usable with Callback<> or Bind(). template <typename T> -struct CallbackParamTraits<T, true> { +struct CallbackParamTraitsForMoveOnlyType { typedef T ForwardType; typedef T StorageType; }; diff --git a/chromium/base/callback_list.h b/chromium/base/callback_list.h index 5b911fd4867..aeed5f1e221 100644 --- a/chromium/base/callback_list.h +++ b/chromium/base/callback_list.h @@ -1,8 +1,3 @@ -// This file was GENERATED by command: -// pump.py callback_list.h.pump -// DO NOT EDIT BY HAND!!! - - // 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. @@ -208,192 +203,21 @@ class CallbackListBase { template <typename Sig> class CallbackList; -template <> -class CallbackList<void(void)> - : public internal::CallbackListBase<Callback<void(void)> > { - public: - typedef Callback<void(void)> CallbackType; - - CallbackList() {} - - void Notify() { - internal::CallbackListBase<CallbackType>::Iterator it = - this->GetIterator(); - CallbackType* cb; - while ((cb = it.GetNext()) != NULL) { - cb->Run(); - } - } - - private: - DISALLOW_COPY_AND_ASSIGN(CallbackList); -}; - -template <typename A1> -class CallbackList<void(A1)> - : public internal::CallbackListBase<Callback<void(A1)> > { - public: - typedef Callback<void(A1)> CallbackType; - - CallbackList() {} - - void Notify(typename internal::CallbackParamTraits<A1>::ForwardType a1) { - typename internal::CallbackListBase<CallbackType>::Iterator it = - this->GetIterator(); - CallbackType* cb; - while ((cb = it.GetNext()) != NULL) { - cb->Run(a1); - } - } - - private: - DISALLOW_COPY_AND_ASSIGN(CallbackList); -}; - -template <typename A1, typename A2> -class CallbackList<void(A1, A2)> - : public internal::CallbackListBase<Callback<void(A1, A2)> > { - public: - typedef Callback<void(A1, A2)> CallbackType; - - CallbackList() {} - - void Notify(typename internal::CallbackParamTraits<A1>::ForwardType a1, - typename internal::CallbackParamTraits<A2>::ForwardType a2) { - typename internal::CallbackListBase<CallbackType>::Iterator it = - this->GetIterator(); - CallbackType* cb; - while ((cb = it.GetNext()) != NULL) { - cb->Run(a1, a2); - } - } - - private: - DISALLOW_COPY_AND_ASSIGN(CallbackList); -}; - -template <typename A1, typename A2, typename A3> -class CallbackList<void(A1, A2, A3)> - : public internal::CallbackListBase<Callback<void(A1, A2, A3)> > { - public: - typedef Callback<void(A1, A2, A3)> CallbackType; - - CallbackList() {} - - void Notify(typename internal::CallbackParamTraits<A1>::ForwardType a1, - typename internal::CallbackParamTraits<A2>::ForwardType a2, - typename internal::CallbackParamTraits<A3>::ForwardType a3) { - typename internal::CallbackListBase<CallbackType>::Iterator it = - this->GetIterator(); - CallbackType* cb; - while ((cb = it.GetNext()) != NULL) { - cb->Run(a1, a2, a3); - } - } - - private: - DISALLOW_COPY_AND_ASSIGN(CallbackList); -}; - -template <typename A1, typename A2, typename A3, typename A4> -class CallbackList<void(A1, A2, A3, A4)> - : public internal::CallbackListBase<Callback<void(A1, A2, A3, A4)> > { - public: - typedef Callback<void(A1, A2, A3, A4)> CallbackType; - - CallbackList() {} - - void Notify(typename internal::CallbackParamTraits<A1>::ForwardType a1, - typename internal::CallbackParamTraits<A2>::ForwardType a2, - typename internal::CallbackParamTraits<A3>::ForwardType a3, - typename internal::CallbackParamTraits<A4>::ForwardType a4) { - typename internal::CallbackListBase<CallbackType>::Iterator it = - this->GetIterator(); - CallbackType* cb; - while ((cb = it.GetNext()) != NULL) { - cb->Run(a1, a2, a3, a4); - } - } - - private: - DISALLOW_COPY_AND_ASSIGN(CallbackList); -}; - -template <typename A1, typename A2, typename A3, typename A4, typename A5> -class CallbackList<void(A1, A2, A3, A4, A5)> - : public internal::CallbackListBase<Callback<void(A1, A2, A3, A4, A5)> > { - public: - typedef Callback<void(A1, A2, A3, A4, A5)> CallbackType; - - CallbackList() {} - - void Notify(typename internal::CallbackParamTraits<A1>::ForwardType a1, - typename internal::CallbackParamTraits<A2>::ForwardType a2, - typename internal::CallbackParamTraits<A3>::ForwardType a3, - typename internal::CallbackParamTraits<A4>::ForwardType a4, - typename internal::CallbackParamTraits<A5>::ForwardType a5) { - typename internal::CallbackListBase<CallbackType>::Iterator it = - this->GetIterator(); - CallbackType* cb; - while ((cb = it.GetNext()) != NULL) { - cb->Run(a1, a2, a3, a4, a5); - } - } - - private: - DISALLOW_COPY_AND_ASSIGN(CallbackList); -}; - -template <typename A1, typename A2, typename A3, typename A4, typename A5, - typename A6> -class CallbackList<void(A1, A2, A3, A4, A5, A6)> - : public internal::CallbackListBase<Callback<void(A1, A2, A3, A4, A5, - A6)> > { - public: - typedef Callback<void(A1, A2, A3, A4, A5, A6)> CallbackType; - - CallbackList() {} - - void Notify(typename internal::CallbackParamTraits<A1>::ForwardType a1, - typename internal::CallbackParamTraits<A2>::ForwardType a2, - typename internal::CallbackParamTraits<A3>::ForwardType a3, - typename internal::CallbackParamTraits<A4>::ForwardType a4, - typename internal::CallbackParamTraits<A5>::ForwardType a5, - typename internal::CallbackParamTraits<A6>::ForwardType a6) { - typename internal::CallbackListBase<CallbackType>::Iterator it = - this->GetIterator(); - CallbackType* cb; - while ((cb = it.GetNext()) != NULL) { - cb->Run(a1, a2, a3, a4, a5, a6); - } - } - - private: - DISALLOW_COPY_AND_ASSIGN(CallbackList); -}; - -template <typename A1, typename A2, typename A3, typename A4, typename A5, - typename A6, typename A7> -class CallbackList<void(A1, A2, A3, A4, A5, A6, A7)> - : public internal::CallbackListBase<Callback<void(A1, A2, A3, A4, A5, A6, - A7)> > { +template <typename... Args> +class CallbackList<void(Args...)> + : public internal::CallbackListBase<Callback<void(Args...)> > { public: - typedef Callback<void(A1, A2, A3, A4, A5, A6, A7)> CallbackType; + typedef Callback<void(Args...)> CallbackType; CallbackList() {} - void Notify(typename internal::CallbackParamTraits<A1>::ForwardType a1, - typename internal::CallbackParamTraits<A2>::ForwardType a2, - typename internal::CallbackParamTraits<A3>::ForwardType a3, - typename internal::CallbackParamTraits<A4>::ForwardType a4, - typename internal::CallbackParamTraits<A5>::ForwardType a5, - typename internal::CallbackParamTraits<A6>::ForwardType a6, - typename internal::CallbackParamTraits<A7>::ForwardType a7) { + void Notify( + typename internal::CallbackParamTraits<Args>::ForwardType... args) { typename internal::CallbackListBase<CallbackType>::Iterator it = this->GetIterator(); CallbackType* cb; while ((cb = it.GetNext()) != NULL) { - cb->Run(a1, a2, a3, a4, a5, a6, a7); + cb->Run(args...); } } diff --git a/chromium/base/callback_list.h.pump b/chromium/base/callback_list.h.pump deleted file mode 100644 index d7f84736c15..00000000000 --- a/chromium/base/callback_list.h.pump +++ /dev/null @@ -1,269 +0,0 @@ -$$ This is a pump file for generating file templates. Pump is a python -$$ script that is part of the Google Test suite of utilities. Description -$$ can be found here: -$$ -$$ http://code.google.com/p/googletest/wiki/PumpManual -$$ - -$$ See comment for MAX_ARITY in base/bind.h.pump. -$var MAX_ARITY = 7 - -// 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. - -#ifndef BASE_CALLBACK_LIST_H_ -#define BASE_CALLBACK_LIST_H_ - -#include <list> - -#include "base/basictypes.h" -#include "base/callback.h" -#include "base/callback_internal.h" -#include "base/compiler_specific.h" -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" - -// OVERVIEW: -// -// A container for a list of callbacks. Unlike a normal STL vector or list, -// this container can be modified during iteration without invalidating the -// iterator. It safely handles the case of a callback removing itself -// or another callback from the list while callbacks are being run. -// -// TYPICAL USAGE: -// -// class MyWidget { -// public: -// ... -// -// typedef base::Callback<void(const Foo&)> OnFooCallback; -// -// scoped_ptr<base::CallbackList<void(const Foo&)>::Subscription> -// RegisterCallback(const OnFooCallback& cb) { -// return callback_list_.Add(cb); -// } -// -// private: -// void NotifyFoo(const Foo& foo) { -// callback_list_.Notify(foo); -// } -// -// base::CallbackList<void(const Foo&)> callback_list_; -// -// DISALLOW_COPY_AND_ASSIGN(MyWidget); -// }; -// -// -// class MyWidgetListener { -// public: -// MyWidgetListener::MyWidgetListener() { -// foo_subscription_ = MyWidget::GetCurrent()->RegisterCallback( -// base::Bind(&MyWidgetListener::OnFoo, this))); -// } -// -// MyWidgetListener::~MyWidgetListener() { -// // Subscription gets deleted automatically and will deregister -// // the callback in the process. -// } -// -// private: -// void OnFoo(const Foo& foo) { -// // Do something. -// } -// -// scoped_ptr<base::CallbackList<void(const Foo&)>::Subscription> -// foo_subscription_; -// -// DISALLOW_COPY_AND_ASSIGN(MyWidgetListener); -// }; - -namespace base { - -namespace internal { - -template <typename CallbackType> -class CallbackListBase { - public: - class Subscription { - public: - Subscription(CallbackListBase<CallbackType>* list, - typename std::list<CallbackType>::iterator iter) - : list_(list), - iter_(iter) { - } - - ~Subscription() { - if (list_->active_iterator_count_) { - iter_->Reset(); - } else { - list_->callbacks_.erase(iter_); - if (!list_->removal_callback_.is_null()) - list_->removal_callback_.Run(); - } - } - - private: - CallbackListBase<CallbackType>* list_; - typename std::list<CallbackType>::iterator iter_; - - DISALLOW_COPY_AND_ASSIGN(Subscription); - }; - - // Add a callback to the list. The callback will remain registered until the - // returned Subscription is destroyed, which must occur before the - // CallbackList is destroyed. - scoped_ptr<Subscription> Add(const CallbackType& cb) WARN_UNUSED_RESULT { - DCHECK(!cb.is_null()); - return scoped_ptr<Subscription>( - new Subscription(this, callbacks_.insert(callbacks_.end(), cb))); - } - - // Sets a callback which will be run when a subscription list is changed. - void set_removal_callback(const Closure& callback) { - removal_callback_ = callback; - } - - // Returns true if there are no subscriptions. This is only valid to call when - // not looping through the list. - bool empty() { - DCHECK_EQ(0, active_iterator_count_); - return callbacks_.empty(); - } - - protected: - // An iterator class that can be used to access the list of callbacks. - class Iterator { - public: - explicit Iterator(CallbackListBase<CallbackType>* list) - : list_(list), - list_iter_(list_->callbacks_.begin()) { - ++list_->active_iterator_count_; - } - - Iterator(const Iterator& iter) - : list_(iter.list_), - list_iter_(iter.list_iter_) { - ++list_->active_iterator_count_; - } - - ~Iterator() { - if (list_ && --list_->active_iterator_count_ == 0) { - list_->Compact(); - } - } - - CallbackType* GetNext() { - while ((list_iter_ != list_->callbacks_.end()) && list_iter_->is_null()) - ++list_iter_; - - CallbackType* cb = NULL; - if (list_iter_ != list_->callbacks_.end()) { - cb = &(*list_iter_); - ++list_iter_; - } - return cb; - } - - private: - CallbackListBase<CallbackType>* list_; - typename std::list<CallbackType>::iterator list_iter_; - }; - - CallbackListBase() : active_iterator_count_(0) {} - - ~CallbackListBase() { - DCHECK_EQ(0, active_iterator_count_); - DCHECK_EQ(0U, callbacks_.size()); - } - - // Returns an instance of a CallbackListBase::Iterator which can be used - // to run callbacks. - Iterator GetIterator() { - return Iterator(this); - } - - // Compact the list: remove any entries which were NULLed out during - // iteration. - void Compact() { - typename std::list<CallbackType>::iterator it = callbacks_.begin(); - bool updated = false; - while (it != callbacks_.end()) { - if ((*it).is_null()) { - updated = true; - it = callbacks_.erase(it); - } else { - ++it; - } - - if (updated && !removal_callback_.is_null()) - removal_callback_.Run(); - } - } - - private: - std::list<CallbackType> callbacks_; - int active_iterator_count_; - Closure removal_callback_; - - DISALLOW_COPY_AND_ASSIGN(CallbackListBase); -}; - -} // namespace internal - -template <typename Sig> class CallbackList; - - -$range ARITY 0..MAX_ARITY -$for ARITY [[ -$range ARG 1..ARITY - -$if ARITY == 0 [[ -template <> -class CallbackList<void(void)> - : public internal::CallbackListBase<Callback<void(void)> > { -]] $else [[ -template <$for ARG , [[typename A$(ARG)]]> -class CallbackList<void($for ARG , [[A$(ARG)]])> - : public internal::CallbackListBase<Callback<void($for ARG , [[A$(ARG)]])> > { -]] - - public: -$if ARITY == 0 [[ - - typedef Callback<void(void)> CallbackType; -]] $else [[ - - typedef Callback<void($for ARG , [[A$(ARG)]])> CallbackType; -]] - - - CallbackList() {} - - void Notify($for ARG , - [[typename internal::CallbackParamTraits<A$(ARG)>::ForwardType a$(ARG)]]) { -$if ARITY == 0 [[ - - internal::CallbackListBase<CallbackType>::Iterator it = - this->GetIterator(); -]] $else [[ - - typename internal::CallbackListBase<CallbackType>::Iterator it = - this->GetIterator(); -]] - - CallbackType* cb; - while ((cb = it.GetNext()) != NULL) { - cb->Run($for ARG , [[a$(ARG)]]); - } - } - - private: - DISALLOW_COPY_AND_ASSIGN(CallbackList); -}; - - -]] $$ for ARITY -} // namespace base - -#endif // BASE_CALLBACK_LIST_H_ diff --git a/chromium/base/callback_unittest.cc b/chromium/base/callback_unittest.cc index 6103b2e9675..2844aa98a29 100644 --- a/chromium/base/callback_unittest.cc +++ b/chromium/base/callback_unittest.cc @@ -35,7 +35,13 @@ template <> struct BindState<void(void), void(void), void(FakeInvoker)> : public BindStateBase { public: + BindState() : BindStateBase(&Destroy) {} typedef FakeInvoker InvokerType; + private: + ~BindState() {} + static void Destroy(BindStateBase* self) { + delete static_cast<BindState*>(self); + } }; template <> @@ -43,7 +49,13 @@ struct BindState<void(void), void(void), void(FakeInvoker, FakeInvoker)> : public BindStateBase { public: + BindState() : BindStateBase(&Destroy) {} typedef FakeInvoker InvokerType; + private: + ~BindState() {} + static void Destroy(BindStateBase* self) { + delete static_cast<BindState*>(self); + } }; } // namespace internal @@ -62,8 +74,7 @@ class CallbackTest : public ::testing::Test { callback_b_(new FakeBindState2()) { } - virtual ~CallbackTest() { - } + ~CallbackTest() override {} protected: Callback<void(void)> callback_a_; diff --git a/chromium/base/cancelable_callback.h b/chromium/base/cancelable_callback.h index 159100f71bf..2b9d2609464 100644 --- a/chromium/base/cancelable_callback.h +++ b/chromium/base/cancelable_callback.h @@ -55,15 +55,14 @@ namespace base { template <typename Sig> class CancelableCallback; -template <> -class CancelableCallback<void(void)> { +template <typename... A> +class CancelableCallback<void(A...)> { public: CancelableCallback() : weak_factory_(this) {} // |callback| must not be null. - explicit CancelableCallback(const base::Callback<void(void)>& callback) - : weak_factory_(this), - callback_(callback) { + explicit CancelableCallback(const base::Callback<void(A...)>& callback) + : callback_(callback), weak_factory_(this) { DCHECK(!callback.is_null()); InitializeForwarder(); } @@ -84,7 +83,7 @@ class CancelableCallback<void(void)> { // Sets |callback| as the closure that may be cancelled. |callback| may not // be null. Outstanding and any previously wrapped callbacks are cancelled. - void Reset(const base::Callback<void(void)>& callback) { + void Reset(const base::Callback<void(A...)>& callback) { DCHECK(!callback.is_null()); // Outstanding tasks (e.g., posted to a message loop) must not be called. @@ -97,173 +96,34 @@ class CancelableCallback<void(void)> { } // Returns a callback that can be disabled by calling Cancel(). - const base::Callback<void(void)>& callback() const { + const base::Callback<void(A...)>& callback() const { return forwarder_; } private: - void Forward() { - callback_.Run(); + void Forward(A... args) const { + callback_.Run(args...); } // Helper method to bind |forwarder_| using a weak pointer from // |weak_factory_|. void InitializeForwarder() { - forwarder_ = base::Bind(&CancelableCallback<void(void)>::Forward, + forwarder_ = base::Bind(&CancelableCallback<void(A...)>::Forward, weak_factory_.GetWeakPtr()); } - // Used to ensure Forward() is not run when this object is destroyed. - base::WeakPtrFactory<CancelableCallback<void(void)> > weak_factory_; - // The wrapper closure. - base::Callback<void(void)> forwarder_; + base::Callback<void(A...)> forwarder_; // The stored closure that may be cancelled. - base::Callback<void(void)> callback_; - - DISALLOW_COPY_AND_ASSIGN(CancelableCallback); -}; - -template <typename A1> -class CancelableCallback<void(A1)> { - public: - CancelableCallback() : weak_factory_(this) {} - - // |callback| must not be null. - explicit CancelableCallback(const base::Callback<void(A1)>& callback) - : weak_factory_(this), - callback_(callback) { - DCHECK(!callback.is_null()); - InitializeForwarder(); - } - - ~CancelableCallback() {} - - // Cancels and drops the reference to the wrapped callback. - void Cancel() { - weak_factory_.InvalidateWeakPtrs(); - forwarder_.Reset(); - callback_.Reset(); - } - - // Returns true if the wrapped callback has been cancelled. - bool IsCancelled() const { - return callback_.is_null(); - } - - // Sets |callback| as the closure that may be cancelled. |callback| may not - // be null. Outstanding and any previously wrapped callbacks are cancelled. - void Reset(const base::Callback<void(A1)>& callback) { - DCHECK(!callback.is_null()); - - // Outstanding tasks (e.g., posted to a message loop) must not be called. - Cancel(); - - // |forwarder_| is no longer valid after Cancel(), so re-bind. - InitializeForwarder(); - - callback_ = callback; - } - - // Returns a callback that can be disabled by calling Cancel(). - const base::Callback<void(A1)>& callback() const { - return forwarder_; - } - - private: - void Forward(A1 a1) const { - callback_.Run(a1); - } - - // Helper method to bind |forwarder_| using a weak pointer from - // |weak_factory_|. - void InitializeForwarder() { - forwarder_ = base::Bind(&CancelableCallback<void(A1)>::Forward, - weak_factory_.GetWeakPtr()); - } + base::Callback<void(A...)> callback_; // Used to ensure Forward() is not run when this object is destroyed. - base::WeakPtrFactory<CancelableCallback<void(A1)> > weak_factory_; - - // The wrapper closure. - base::Callback<void(A1)> forwarder_; - - // The stored closure that may be cancelled. - base::Callback<void(A1)> callback_; + base::WeakPtrFactory<CancelableCallback<void(A...)>> weak_factory_; DISALLOW_COPY_AND_ASSIGN(CancelableCallback); }; -template <typename A1, typename A2> -class CancelableCallback<void(A1, A2)> { - public: - CancelableCallback() : weak_factory_(this) {} - - // |callback| must not be null. - explicit CancelableCallback(const base::Callback<void(A1, A2)>& callback) - : weak_factory_(this), - callback_(callback) { - DCHECK(!callback.is_null()); - InitializeForwarder(); - } - - ~CancelableCallback() {} - - // Cancels and drops the reference to the wrapped callback. - void Cancel() { - weak_factory_.InvalidateWeakPtrs(); - forwarder_.Reset(); - callback_.Reset(); - } - - // Returns true if the wrapped callback has been cancelled. - bool IsCancelled() const { - return callback_.is_null(); - } - - // Sets |callback| as the closure that may be cancelled. |callback| may not - // be null. Outstanding and any previously wrapped callbacks are cancelled. - void Reset(const base::Callback<void(A1, A2)>& callback) { - DCHECK(!callback.is_null()); - - // Outstanding tasks (e.g., posted to a message loop) must not be called. - Cancel(); - - // |forwarder_| is no longer valid after Cancel(), so re-bind. - InitializeForwarder(); - - callback_ = callback; - } - - // Returns a callback that can be disabled by calling Cancel(). - const base::Callback<void(A1, A2)>& callback() const { - return forwarder_; - } - - private: - void Forward(A1 a1, A2 a2) const { - callback_.Run(a1, a2); - } - - // Helper method to bind |forwarder_| using a weak pointer from - // |weak_factory_|. - void InitializeForwarder() { - forwarder_ = base::Bind(&CancelableCallback<void(A1, A2)>::Forward, - weak_factory_.GetWeakPtr()); - } - - // The wrapper closure. - base::Callback<void(A1, A2)> forwarder_; - - // The stored closure that may be cancelled. - base::Callback<void(A1, A2)> callback_; - - // Used to ensure Forward() is not run when this object is destroyed. - base::WeakPtrFactory<CancelableCallback<void(A1, A2)> > weak_factory_; - DISALLOW_COPY_AND_ASSIGN(CancelableCallback); -}; - typedef CancelableCallback<void(void)> CancelableClosure; } // namespace base diff --git a/chromium/base/cancelable_callback_unittest.cc b/chromium/base/cancelable_callback_unittest.cc index fcbe23c1de3..6d0a1144c42 100644 --- a/chromium/base/cancelable_callback_unittest.cc +++ b/chromium/base/cancelable_callback_unittest.cc @@ -6,9 +6,11 @@ #include "base/bind.h" #include "base/bind_helpers.h" +#include "base/location.h" #include "base/memory/ref_counted.h" -#include "base/message_loop/message_loop.h" #include "base/run_loop.h" +#include "base/single_thread_task_runner.h" +#include "base/thread_task_runner_handle.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { @@ -165,12 +167,12 @@ TEST(CancelableCallbackTest, PostTask) { CancelableClosure cancelable(base::Bind(&Increment, base::Unretained(&count))); - MessageLoop::current()->PostTask(FROM_HERE, cancelable.callback()); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, cancelable.callback()); RunLoop().RunUntilIdle(); EXPECT_EQ(1, count); - MessageLoop::current()->PostTask(FROM_HERE, cancelable.callback()); + ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, cancelable.callback()); // Cancel before running the message loop. cancelable.Cancel(); diff --git a/chromium/base/chromeos/OWNERS b/chromium/base/chromeos/OWNERS new file mode 100644 index 00000000000..8fda46a1f4e --- /dev/null +++ b/chromium/base/chromeos/OWNERS @@ -0,0 +1,3 @@ +skuhne@chromium.org +oshima@chromium.org + diff --git a/chromium/base/chromeos/memory_pressure_monitor.cc b/chromium/base/chromeos/memory_pressure_monitor.cc new file mode 100644 index 00000000000..5e8aadfbd81 --- /dev/null +++ b/chromium/base/chromeos/memory_pressure_monitor.cc @@ -0,0 +1,272 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/chromeos/memory_pressure_monitor.h" + +#include <fcntl.h> +#include <sys/select.h> + +#include "base/metrics/histogram_macros.h" +#include "base/posix/eintr_wrapper.h" +#include "base/process/process_metrics.h" +#include "base/single_thread_task_runner.h" +#include "base/thread_task_runner_handle.h" +#include "base/time/time.h" + +namespace base { +namespace chromeos { + +namespace { + +// The time between memory pressure checks. While under critical pressure, this +// is also the timer to repeat cleanup attempts. +const int kMemoryPressureIntervalMs = 1000; + +// The time which should pass between two moderate memory pressure calls. +const int kModerateMemoryPressureCooldownMs = 10000; + +// Number of event polls before the next moderate pressure event can be sent. +const int kModerateMemoryPressureCooldown = + kModerateMemoryPressureCooldownMs / kMemoryPressureIntervalMs; + +// Threshold constants to emit pressure events. +const int kNormalMemoryPressureModerateThresholdPercent = 60; +const int kNormalMemoryPressureCriticalThresholdPercent = 95; +const int kAggressiveMemoryPressureModerateThresholdPercent = 35; +const int kAggressiveMemoryPressureCriticalThresholdPercent = 70; + +// The possible state for memory pressure level. The values should be in line +// with values in MemoryPressureListener::MemoryPressureLevel and should be +// updated if more memory pressure levels are introduced. +enum MemoryPressureLevelUMA { + MEMORY_PRESSURE_LEVEL_NONE = 0, + MEMORY_PRESSURE_LEVEL_MODERATE, + MEMORY_PRESSURE_LEVEL_CRITICAL, + NUM_MEMORY_PRESSURE_LEVELS +}; + +// This is the file that will exist if low memory notification is available +// on the device. Whenever it becomes readable, it signals a low memory +// condition. +const char kLowMemFile[] = "/dev/chromeos-low-mem"; + +// Converts a |MemoryPressureThreshold| value into a used memory percentage for +// the moderate pressure event. +int GetModerateMemoryThresholdInPercent( + MemoryPressureMonitor::MemoryPressureThresholds thresholds) { + return thresholds == MemoryPressureMonitor:: + THRESHOLD_AGGRESSIVE_CACHE_DISCARD || + thresholds == MemoryPressureMonitor::THRESHOLD_AGGRESSIVE + ? kAggressiveMemoryPressureModerateThresholdPercent + : kNormalMemoryPressureModerateThresholdPercent; +} + +// Converts a |MemoryPressureThreshold| value into a used memory percentage for +// the critical pressure event. +int GetCriticalMemoryThresholdInPercent( + MemoryPressureMonitor::MemoryPressureThresholds thresholds) { + return thresholds == MemoryPressureMonitor:: + THRESHOLD_AGGRESSIVE_TAB_DISCARD || + thresholds == MemoryPressureMonitor::THRESHOLD_AGGRESSIVE + ? kAggressiveMemoryPressureCriticalThresholdPercent + : kNormalMemoryPressureCriticalThresholdPercent; +} + +// Converts free percent of memory into a memory pressure value. +MemoryPressureListener::MemoryPressureLevel GetMemoryPressureLevelFromFillLevel( + int actual_fill_level, + int moderate_threshold, + int critical_threshold) { + if (actual_fill_level < moderate_threshold) + return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE; + return actual_fill_level < critical_threshold + ? MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE + : MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL; +} + +// This function will be called less then once a second. It will check if +// the kernel has detected a low memory situation. +bool IsLowMemoryCondition(int file_descriptor) { + fd_set fds; + struct timeval tv; + + FD_ZERO(&fds); + FD_SET(file_descriptor, &fds); + + tv.tv_sec = 0; + tv.tv_usec = 0; + + return HANDLE_EINTR(select(file_descriptor + 1, &fds, NULL, NULL, &tv)) > 0; +} + +} // namespace + +MemoryPressureMonitor::MemoryPressureMonitor( + MemoryPressureThresholds thresholds) + : current_memory_pressure_level_( + MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE), + moderate_pressure_repeat_count_(0), + moderate_pressure_threshold_percent_( + GetModerateMemoryThresholdInPercent(thresholds)), + critical_pressure_threshold_percent_( + GetCriticalMemoryThresholdInPercent(thresholds)), + low_mem_file_(HANDLE_EINTR(::open(kLowMemFile, O_RDONLY))), + weak_ptr_factory_(this) { + StartObserving(); + LOG_IF(ERROR, !low_mem_file_.is_valid()) << "Cannot open kernel listener"; +} + +MemoryPressureMonitor::~MemoryPressureMonitor() { + StopObserving(); +} + +void MemoryPressureMonitor::ScheduleEarlyCheck() { + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, Bind(&MemoryPressureMonitor::CheckMemoryPressure, + weak_ptr_factory_.GetWeakPtr())); +} + +MemoryPressureListener::MemoryPressureLevel +MemoryPressureMonitor::GetCurrentPressureLevel() const { + return current_memory_pressure_level_; +} + +// static +MemoryPressureMonitor* MemoryPressureMonitor::Get() { + return static_cast<MemoryPressureMonitor*>( + base::MemoryPressureMonitor::Get()); +} + +void MemoryPressureMonitor::StartObserving() { + timer_.Start(FROM_HERE, + TimeDelta::FromMilliseconds(kMemoryPressureIntervalMs), + Bind(&MemoryPressureMonitor:: + CheckMemoryPressureAndRecordStatistics, + weak_ptr_factory_.GetWeakPtr())); +} + +void MemoryPressureMonitor::StopObserving() { + // If StartObserving failed, StopObserving will still get called. + timer_.Stop(); +} + +void MemoryPressureMonitor::CheckMemoryPressureAndRecordStatistics() { + CheckMemoryPressure(); + + // Record UMA histogram statistics for the current memory pressure level. + MemoryPressureLevelUMA memory_pressure_level_uma(MEMORY_PRESSURE_LEVEL_NONE); + switch (current_memory_pressure_level_) { + case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE: + memory_pressure_level_uma = MEMORY_PRESSURE_LEVEL_NONE; + break; + case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE: + memory_pressure_level_uma = MEMORY_PRESSURE_LEVEL_MODERATE; + break; + case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL: + memory_pressure_level_uma = MEMORY_PRESSURE_LEVEL_CRITICAL; + break; + } + + UMA_HISTOGRAM_ENUMERATION("ChromeOS.MemoryPressureLevel", + memory_pressure_level_uma, + NUM_MEMORY_PRESSURE_LEVELS); +} + +void MemoryPressureMonitor::CheckMemoryPressure() { + MemoryPressureListener::MemoryPressureLevel old_pressure = + current_memory_pressure_level_; + + // If we have the kernel low memory observer, we use it's flag instead of our + // own computation (for now). Note that in "simulation mode" it can be null. + // TODO(skuhne): We need to add code which makes sure that the kernel and this + // computation come to similar results and then remove this override again. + // TODO(skuhne): Add some testing framework here to see how close the kernel + // and the internal functions are. + if (low_mem_file_.is_valid() && IsLowMemoryCondition(low_mem_file_.get())) { + current_memory_pressure_level_ = + MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL; + } else { + current_memory_pressure_level_ = GetMemoryPressureLevelFromFillLevel( + GetUsedMemoryInPercent(), + moderate_pressure_threshold_percent_, + critical_pressure_threshold_percent_); + + // When listening to the kernel, we ignore the reported memory pressure + // level from our own computation and reduce critical to moderate. + if (low_mem_file_.is_valid() && + current_memory_pressure_level_ == + MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) { + current_memory_pressure_level_ = + MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE; + } + } + + // In case there is no memory pressure we do not notify. + if (current_memory_pressure_level_ == + MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE) { + return; + } + if (old_pressure == current_memory_pressure_level_) { + // If the memory pressure is still at the same level, we notify again for a + // critical level. In case of a moderate level repeat however, we only send + // a notification after a certain time has passed. + if (current_memory_pressure_level_ == + MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE && + ++moderate_pressure_repeat_count_ < + kModerateMemoryPressureCooldown) { + return; + } + } else if (current_memory_pressure_level_ == + MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE && + old_pressure == + MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) { + // When we reducing the pressure level from critical to moderate, we + // restart the timeout and do not send another notification. + moderate_pressure_repeat_count_ = 0; + return; + } + moderate_pressure_repeat_count_ = 0; + MemoryPressureListener::NotifyMemoryPressure(current_memory_pressure_level_); +} + +// Gets the used ChromeOS memory in percent. +int MemoryPressureMonitor::GetUsedMemoryInPercent() { + base::SystemMemoryInfoKB info; + if (!base::GetSystemMemoryInfo(&info)) { + VLOG(1) << "Cannot determine the free memory of the system."; + return 0; + } + // TODO(skuhne): Instead of adding the kernel memory pressure calculation + // logic here, we should have a kernel mechanism similar to the low memory + // notifier in ChromeOS which offers multiple pressure states. + // To track this, we have crbug.com/381196. + + // The available memory consists of "real" and virtual (z)ram memory. + // Since swappable memory uses a non pre-deterministic compression and + // the compression creates its own "dynamic" in the system, it gets + // de-emphasized by the |kSwapWeight| factor. + const int kSwapWeight = 4; + + // The total memory we have is the "real memory" plus the virtual (z)ram. + int total_memory = info.total + info.swap_total / kSwapWeight; + + // The kernel internally uses 50MB. + const int kMinFileMemory = 50 * 1024; + + // Most file memory can be easily reclaimed. + int file_memory = info.active_file + info.inactive_file; + // unless it is dirty or it's a minimal portion which is required. + file_memory -= info.dirty + kMinFileMemory; + + // Available memory is the sum of free, swap and easy reclaimable memory. + int available_memory = + info.free + info.swap_free / kSwapWeight + file_memory; + + DCHECK(available_memory < total_memory); + int percentage = ((total_memory - available_memory) * 100) / total_memory; + return percentage; +} + +} // namespace chromeos +} // namespace base diff --git a/chromium/base/chromeos/memory_pressure_monitor.h b/chromium/base/chromeos/memory_pressure_monitor.h new file mode 100644 index 00000000000..bed1ec39f6a --- /dev/null +++ b/chromium/base/chromeos/memory_pressure_monitor.h @@ -0,0 +1,119 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_CHROMEOS_MEMORY_PRESSURE_MONITOR_H_ +#define BASE_CHROMEOS_MEMORY_PRESSURE_MONITOR_H_ + +#include "base/base_export.h" +#include "base/files/scoped_file.h" +#include "base/gtest_prod_util.h" +#include "base/macros.h" +#include "base/memory/memory_pressure_listener.h" +#include "base/memory/memory_pressure_monitor.h" +#include "base/memory/weak_ptr.h" +#include "base/timer/timer.h" + +namespace base { +namespace chromeos { + +class TestMemoryPressureMonitor; + +//////////////////////////////////////////////////////////////////////////////// +// MemoryPressureMonitor +// +// A class to handle the observation of our free memory. It notifies the +// MemoryPressureListener of memory fill level changes, so that it can take +// action to reduce memory resources accordingly. +// +class BASE_EXPORT MemoryPressureMonitor : public base::MemoryPressureMonitor { + public: + using GetUsedMemoryInPercentCallback = int (*)(); + + // There are two memory pressure events: + // MODERATE - which will mainly release caches. + // CRITICAL - which will discard tabs. + // The |MemoryPressureThresholds| enum selects the strategy of firing these + // events: A conservative strategy will keep as much content in memory as + // possible (causing the system to swap to zram) and an aggressive strategy + // will release memory earlier to avoid swapping. + enum MemoryPressureThresholds { + // Use the system default. + THRESHOLD_DEFAULT = 0, + // Try to keep as much content in memory as possible. + THRESHOLD_CONSERVATIVE = 1, + // Discard caches earlier, allowing to keep more tabs in memory. + THRESHOLD_AGGRESSIVE_CACHE_DISCARD = 2, + // Discard tabs earlier, allowing the system to get faster. + THRESHOLD_AGGRESSIVE_TAB_DISCARD = 3, + // Discard caches and tabs earlier to allow the system to be faster. + THRESHOLD_AGGRESSIVE = 4 + }; + + explicit MemoryPressureMonitor(MemoryPressureThresholds thresholds); + ~MemoryPressureMonitor() override; + + // Redo the memory pressure calculation soon and call again if a critical + // memory pressure prevails. Note that this call will trigger an asynchronous + // action which gives the system time to release memory back into the pool. + void ScheduleEarlyCheck(); + + // Get the current memory pressure level. + MemoryPressureListener::MemoryPressureLevel GetCurrentPressureLevel() const + override; + + // Returns a type-casted version of the current memory pressure monitor. A + // simple wrapper to base::MemoryPressureMonitor::Get. + static MemoryPressureMonitor* Get(); + + private: + friend TestMemoryPressureMonitor; + // Starts observing the memory fill level. + // Calls to StartObserving should always be matched with calls to + // StopObserving. + void StartObserving(); + + // Stop observing the memory fill level. + // May be safely called if StartObserving has not been called. + void StopObserving(); + + // The function which gets periodically called to check any changes in the + // memory pressure. It will report pressure changes as well as continuous + // critical pressure levels. + void CheckMemoryPressure(); + + // The function periodically checks the memory pressure changes and records + // the UMA histogram statistics for the current memory pressure level. + void CheckMemoryPressureAndRecordStatistics(); + + // Get the memory pressure in percent (virtual for testing). + virtual int GetUsedMemoryInPercent(); + + // The current memory pressure. + base::MemoryPressureListener::MemoryPressureLevel + current_memory_pressure_level_; + + // A periodic timer to check for resource pressure changes. This will get + // replaced by a kernel triggered event system (see crbug.com/381196). + base::RepeatingTimer<MemoryPressureMonitor> timer_; + + // To slow down the amount of moderate pressure event calls, this counter + // gets used to count the number of events since the last event occured. + int moderate_pressure_repeat_count_; + + // The thresholds for moderate and critical pressure. + const int moderate_pressure_threshold_percent_; + const int critical_pressure_threshold_percent_; + + // File descriptor used to detect low memory condition. + ScopedFD low_mem_file_; + + base::WeakPtrFactory<MemoryPressureMonitor> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(MemoryPressureMonitor); +}; + +} // namespace chromeos +} // namespace base + +#endif // BASE_CHROMEOS_MEMORY_PRESSURE_MONITOR_H_ diff --git a/chromium/base/chromeos/memory_pressure_monitor_unittest.cc b/chromium/base/chromeos/memory_pressure_monitor_unittest.cc new file mode 100644 index 00000000000..4fb83327f4c --- /dev/null +++ b/chromium/base/chromeos/memory_pressure_monitor_unittest.cc @@ -0,0 +1,165 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/basictypes.h" +#include "base/chromeos/memory_pressure_monitor.h" +#include "base/memory/memory_pressure_listener.h" +#include "base/message_loop/message_loop.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { +namespace chromeos { + +namespace { + +// True if the memory notifier got called. +// Do not read/modify value directly. +bool on_memory_pressure_called = false; + +// If the memory notifier got called, this is the memory pressure reported. +MemoryPressureListener::MemoryPressureLevel on_memory_pressure_level = + MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE; + +// Processes OnMemoryPressure calls. +void OnMemoryPressure(MemoryPressureListener::MemoryPressureLevel level) { + on_memory_pressure_called = true; + on_memory_pressure_level = level; +} + +// Resets the indicator for memory pressure. +void ResetOnMemoryPressureCalled() { + on_memory_pressure_called = false; +} + +// Returns true when OnMemoryPressure was called (and resets it). +bool WasOnMemoryPressureCalled() { + bool b = on_memory_pressure_called; + ResetOnMemoryPressureCalled(); + return b; +} + +} // namespace + +class TestMemoryPressureMonitor : public MemoryPressureMonitor { + public: + TestMemoryPressureMonitor() + : MemoryPressureMonitor(THRESHOLD_DEFAULT), + memory_in_percent_override_(0) { + // Disable any timers which are going on and set a special memory reporting + // function. + StopObserving(); + } + ~TestMemoryPressureMonitor() override {} + + void SetMemoryInPercentOverride(int percent) { + memory_in_percent_override_ = percent; + } + + void CheckMemoryPressureForTest() { + CheckMemoryPressure(); + } + + private: + int GetUsedMemoryInPercent() override { + return memory_in_percent_override_; + } + + int memory_in_percent_override_; + DISALLOW_COPY_AND_ASSIGN(TestMemoryPressureMonitor); +}; + +// This test tests the various transition states from memory pressure, looking +// for the correct behavior on event reposting as well as state updates. +TEST(ChromeOSMemoryPressureMonitorTest, CheckMemoryPressure) { + base::MessageLoopForUI message_loop; + scoped_ptr<TestMemoryPressureMonitor> monitor( + new TestMemoryPressureMonitor); + scoped_ptr<MemoryPressureListener> listener( + new MemoryPressureListener(base::Bind(&OnMemoryPressure))); + // Checking the memory pressure while 0% are used should not produce any + // events. + monitor->SetMemoryInPercentOverride(0); + ResetOnMemoryPressureCalled(); + + monitor->CheckMemoryPressureForTest(); + message_loop.RunUntilIdle(); + EXPECT_FALSE(WasOnMemoryPressureCalled()); + EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, + monitor->GetCurrentPressureLevel()); + + // Setting the memory level to 80% should produce a moderate pressure level. + monitor->SetMemoryInPercentOverride(80); + monitor->CheckMemoryPressureForTest(); + message_loop.RunUntilIdle(); + EXPECT_TRUE(WasOnMemoryPressureCalled()); + EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, + monitor->GetCurrentPressureLevel()); + EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, + on_memory_pressure_level); + + // We need to check that the event gets reposted after a while. + int i = 0; + for (; i < 100; i++) { + monitor->CheckMemoryPressureForTest(); + message_loop.RunUntilIdle(); + EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, + monitor->GetCurrentPressureLevel()); + if (WasOnMemoryPressureCalled()) { + EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, + on_memory_pressure_level); + break; + } + } + // Should be more then 5 and less then 100. + EXPECT_LE(5, i); + EXPECT_GE(99, i); + + // Setting the memory usage to 99% should produce critical levels. + monitor->SetMemoryInPercentOverride(99); + monitor->CheckMemoryPressureForTest(); + message_loop.RunUntilIdle(); + EXPECT_TRUE(WasOnMemoryPressureCalled()); + EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL, + on_memory_pressure_level); + EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL, + monitor->GetCurrentPressureLevel()); + + // Calling it again should immediately produce a second call. + monitor->CheckMemoryPressureForTest(); + message_loop.RunUntilIdle(); + EXPECT_TRUE(WasOnMemoryPressureCalled()); + EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL, + on_memory_pressure_level); + EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL, + monitor->GetCurrentPressureLevel()); + + // When lowering the pressure again we should not get an event, but the + // pressure should go back to moderate. + monitor->SetMemoryInPercentOverride(80); + monitor->CheckMemoryPressureForTest(); + message_loop.RunUntilIdle(); + EXPECT_FALSE(WasOnMemoryPressureCalled()); + EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, + monitor->GetCurrentPressureLevel()); + + // We should need exactly the same amount of calls as before, before the next + // call comes in. + int j = 0; + for (; j < 100; j++) { + monitor->CheckMemoryPressureForTest(); + message_loop.RunUntilIdle(); + EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, + monitor->GetCurrentPressureLevel()); + if (WasOnMemoryPressureCalled()) { + EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, + on_memory_pressure_level); + break; + } + } + // We should have needed exactly the same amount of checks as before. + EXPECT_EQ(j, i); +} + +} // namespace chromeos +} // namespace base diff --git a/chromium/base/command_line.cc b/chromium/base/command_line.cc index 1f5edce0fff..61ff5c10c38 100644 --- a/chromium/base/command_line.cc +++ b/chromium/base/command_line.cc @@ -70,7 +70,7 @@ bool IsSwitch(const CommandLine::StringType& string, } // Append switches and arguments, keeping switches before arguments. -void AppendSwitchesAndArguments(CommandLine& command_line, +void AppendSwitchesAndArguments(CommandLine* command_line, const CommandLine::StringVector& argv) { bool parse_switches = true; for (size_t i = 1; i < argv.size(); ++i) { @@ -82,51 +82,52 @@ void AppendSwitchesAndArguments(CommandLine& command_line, parse_switches &= (arg != kSwitchTerminator); if (parse_switches && IsSwitch(arg, &switch_string, &switch_value)) { #if defined(OS_WIN) - command_line.AppendSwitchNative(UTF16ToASCII(switch_string), - switch_value); + command_line->AppendSwitchNative(UTF16ToASCII(switch_string), + switch_value); #elif defined(OS_POSIX) - command_line.AppendSwitchNative(switch_string, switch_value); + command_line->AppendSwitchNative(switch_string, switch_value); #endif } else { - command_line.AppendArgNative(arg); + command_line->AppendArgNative(arg); } } } // Lowercase switches for backwards compatiblity *on Windows*. -std::string LowerASCIIOnWindows(const std::string& string) { #if defined(OS_WIN) - return base::StringToLowerASCII(string); +std::string LowerASCIIOnWindows(const std::string& string) { + return StringToLowerASCII(string); +} #elif defined(OS_POSIX) +const std::string& LowerASCIIOnWindows(const std::string& string) { return string; -#endif } +#endif #if defined(OS_WIN) // Quote a string as necessary for CommandLineToArgvW compatiblity *on Windows*. -base::string16 QuoteForCommandLineToArgvW(const base::string16& arg, - bool quote_placeholders) { +string16 QuoteForCommandLineToArgvW(const string16& arg, + bool quote_placeholders) { // We follow the quoting rules of CommandLineToArgvW. // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx - base::string16 quotable_chars(L" \\\""); + string16 quotable_chars(L" \\\""); // We may also be required to quote '%', which is commonly used in a command // line as a placeholder. (It may be substituted for a string with spaces.) if (quote_placeholders) quotable_chars.push_back(L'%'); - if (arg.find_first_of(quotable_chars) == base::string16::npos) { + if (arg.find_first_of(quotable_chars) == string16::npos) { // No quoting necessary. return arg; } - base::string16 out; + string16 out; out.push_back(L'"'); for (size_t i = 0; i < arg.size(); ++i) { if (arg[i] == '\\') { // Find the extent of this run of backslashes. size_t start = i, end = start + 1; - for (; end < arg.size() && arg[end] == '\\'; ++end) - /* empty */; + for (; end < arg.size() && arg[end] == '\\'; ++end) {} size_t backslash_count = end - start; // Backslashes are escapes only if the run is followed by a double quote. @@ -186,7 +187,7 @@ CommandLine::~CommandLine() { // static void CommandLine::set_slash_is_not_a_switch() { // The last switch prefix should be slash, so adjust the size to skip it. - DCHECK(wcscmp(kSwitchPrefixes[arraysize(kSwitchPrefixes) - 1], L"/") == 0); + DCHECK_EQ(wcscmp(kSwitchPrefixes[arraysize(kSwitchPrefixes) - 1], L"/"), 0); switch_prefix_count = arraysize(kSwitchPrefixes) - 1; } #endif @@ -230,7 +231,7 @@ bool CommandLine::InitializedForCurrentProcess() { #if defined(OS_WIN) // static -CommandLine CommandLine::FromString(const base::string16& command_line) { +CommandLine CommandLine::FromString(const string16& command_line) { CommandLine cmd(NO_PROGRAM); cmd.ParseFromString(command_line); return cmd; @@ -250,7 +251,7 @@ void CommandLine::InitFromArgv(const StringVector& argv) { switches_.clear(); begin_args_ = 1; SetProgram(argv.empty() ? FilePath() : FilePath(argv[0])); - AppendSwitchesAndArguments(*this, argv); + AppendSwitchesAndArguments(this, argv); } FilePath CommandLine::GetProgram() const { @@ -262,7 +263,12 @@ void CommandLine::SetProgram(const FilePath& program) { } bool CommandLine::HasSwitch(const std::string& switch_string) const { - return switches_.find(LowerASCIIOnWindows(switch_string)) != switches_.end(); + DCHECK_EQ(StringToLowerASCII(switch_string), switch_string); + return switches_.find(switch_string) != switches_.end(); +} + +bool CommandLine::HasSwitch(const char string_constant[]) const { + return HasSwitch(std::string(string_constant)); } std::string CommandLine::GetSwitchValueASCII( @@ -304,7 +310,7 @@ void CommandLine::AppendSwitchNative(const std::string& switch_string, const CommandLine::StringType& value) { std::string switch_key(LowerASCIIOnWindows(switch_string)); #if defined(OS_WIN) - StringType combined_switch_string(ASCIIToWide(switch_key)); + StringType combined_switch_string(ASCIIToUTF16(switch_key)); #elif defined(OS_POSIX) StringType combined_switch_string(switch_string); #endif @@ -322,7 +328,7 @@ void CommandLine::AppendSwitchNative(const std::string& switch_string, void CommandLine::AppendSwitchASCII(const std::string& switch_string, const std::string& value_string) { #if defined(OS_WIN) - AppendSwitchNative(switch_string, ASCIIToWide(value_string)); + AppendSwitchNative(switch_string, ASCIIToUTF16(value_string)); #elif defined(OS_POSIX) AppendSwitchNative(switch_string, value_string); #endif @@ -369,7 +375,7 @@ void CommandLine::AppendArguments(const CommandLine& other, bool include_program) { if (include_program) SetProgram(other.GetProgram()); - AppendSwitchesAndArguments(*this, other.argv()); + AppendSwitchesAndArguments(this, other.argv()); } void CommandLine::PrependWrapper(const CommandLine::StringType& wrapper) { @@ -385,8 +391,8 @@ void CommandLine::PrependWrapper(const CommandLine::StringType& wrapper) { } #if defined(OS_WIN) -void CommandLine::ParseFromString(const base::string16& command_line) { - base::string16 command_line_string; +void CommandLine::ParseFromString(const string16& command_line) { + string16 command_line_string; TrimWhitespace(command_line, TRIM_ALL, &command_line_string); if (command_line_string.empty()) return; @@ -437,8 +443,7 @@ CommandLine::StringType CommandLine::GetArgumentsStringInternal( #endif params.append(kSwitchValueSeparator + switch_value); } - } - else { + } else { #if defined(OS_WIN) arg = QuoteForCommandLineToArgvW(arg, quote_placeholders); #endif diff --git a/chromium/base/command_line.h b/chromium/base/command_line.h index 1829e63e1b5..439921ed6e5 100644 --- a/chromium/base/command_line.h +++ b/chromium/base/command_line.h @@ -142,8 +142,11 @@ class BASE_EXPORT CommandLine { void SetProgram(const FilePath& program); // Returns true if this command line contains the given switch. - // (Switch names are case-insensitive). + // Switch names should only be lowercase. + // The second override provides an optimized version to avoid inlining the + // codegen for the string allocation. bool HasSwitch(const std::string& switch_string) const; + bool HasSwitch(const char switch_constant[]) const; // Returns the value associated with the given switch. If the switch has no // value or isn't present, this method returns the empty string. @@ -226,7 +229,4 @@ class BASE_EXPORT CommandLine { } // namespace base -// TODO(brettw) remove once all callers specify the namespace properly. -using base::CommandLine; - #endif // BASE_COMMAND_LINE_H_ diff --git a/chromium/base/command_line_unittest.cc b/chromium/base/command_line_unittest.cc index 8fdd605db79..db1a0b27cb1 100644 --- a/chromium/base/command_line_unittest.cc +++ b/chromium/base/command_line_unittest.cc @@ -11,7 +11,7 @@ #include "base/strings/utf_string_conversions.h" #include "testing/gtest/include/gtest/gtest.h" -using base::FilePath; +namespace base { // To test Windows quoting behavior, we use a string that has some backslashes // and quotes. @@ -60,12 +60,13 @@ TEST(CommandLineTest, CommandLineConstructor) { cl.GetProgram().value()); EXPECT_TRUE(cl.HasSwitch("foo")); - EXPECT_TRUE(cl.HasSwitch("bAr")); - EXPECT_TRUE(cl.HasSwitch("baz")); - EXPECT_TRUE(cl.HasSwitch("spaetzle")); #if defined(OS_WIN) - EXPECT_TRUE(cl.HasSwitch("SPAETZLE")); + EXPECT_TRUE(cl.HasSwitch("bar")); +#else + EXPECT_FALSE(cl.HasSwitch("bar")); #endif + EXPECT_TRUE(cl.HasSwitch("baz")); + EXPECT_TRUE(cl.HasSwitch("spaetzle")); EXPECT_TRUE(cl.HasSwitch("other-switches")); EXPECT_TRUE(cl.HasSwitch("input-translation")); @@ -128,7 +129,6 @@ TEST(CommandLineTest, CommandLineFromString) { EXPECT_TRUE(cl.HasSwitch("bar")); EXPECT_TRUE(cl.HasSwitch("baz")); EXPECT_TRUE(cl.HasSwitch("spaetzle")); - EXPECT_TRUE(cl.HasSwitch("SPAETZLE")); EXPECT_TRUE(cl.HasSwitch("other-switches")); EXPECT_TRUE(cl.HasSwitch("input-translation")); EXPECT_TRUE(cl.HasSwitch("quotes")); @@ -200,15 +200,11 @@ TEST(CommandLineTest, GetArgumentsString) { cl.AppendArg(kFifthArgName); #if defined(OS_WIN) - CommandLine::StringType expected_first_arg( - base::UTF8ToUTF16(kFirstArgName)); - CommandLine::StringType expected_second_arg( - base::UTF8ToUTF16(kSecondArgName)); - CommandLine::StringType expected_third_arg( - base::UTF8ToUTF16(kThirdArgName)); - CommandLine::StringType expected_fourth_arg( - base::UTF8ToUTF16(kFourthArgName)); - CommandLine::StringType expected_fifth_arg(base::UTF8ToUTF16(kFifthArgName)); + CommandLine::StringType expected_first_arg(UTF8ToUTF16(kFirstArgName)); + CommandLine::StringType expected_second_arg(UTF8ToUTF16(kSecondArgName)); + CommandLine::StringType expected_third_arg(UTF8ToUTF16(kThirdArgName)); + CommandLine::StringType expected_fourth_arg(UTF8ToUTF16(kFourthArgName)); + CommandLine::StringType expected_fifth_arg(UTF8ToUTF16(kFifthArgName)); #elif defined(OS_POSIX) CommandLine::StringType expected_first_arg(kFirstArgName); CommandLine::StringType expected_second_arg(kSecondArgName); @@ -368,7 +364,7 @@ TEST(CommandLineTest, ProgramQuotes) { EXPECT_EQ(L"\"Program Path\"", cmd_string); // Check the optional quoting of placeholders in programs. - CommandLine cl_quote_placeholder(base::FilePath(L"%1")); + CommandLine cl_quote_placeholder(FilePath(L"%1")); EXPECT_EQ(L"%1", cl_quote_placeholder.GetCommandLineString()); EXPECT_EQ(L"\"%1\"", cl_quote_placeholder.GetCommandLineStringWithPlaceholders()); @@ -382,3 +378,5 @@ TEST(CommandLineTest, Init) { CommandLine* current = CommandLine::ForCurrentProcess(); EXPECT_EQ(initial, current); } + +} // namespace base diff --git a/chromium/base/compiler_specific.h b/chromium/base/compiler_specific.h index 47ca9778b67..63297dcaf00 100644 --- a/chromium/base/compiler_specific.h +++ b/chromium/base/compiler_specific.h @@ -128,13 +128,11 @@ #define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment))) #endif -// Return the byte alignment of the given type (available at compile time). Use -// sizeof(type) prior to checking __alignof to workaround Visual C++ bug: -// http://goo.gl/isH0C +// Return the byte alignment of the given type (available at compile time). // Use like: // ALIGNOF(int32) // this would be 4 #if defined(COMPILER_MSVC) -#define ALIGNOF(type) (sizeof(type) - sizeof(type) + __alignof(type)) +#define ALIGNOF(type) __alignof(type) #elif defined(COMPILER_GCC) #define ALIGNOF(type) __alignof__(type) #endif @@ -175,9 +173,17 @@ // Mark a memory region fully initialized. // Use this to annotate code that deliberately reads uninitialized data, for // example a GC scavenging root set pointers from the stack. -#define MSAN_UNPOISON(p, s) __msan_unpoison(p, s) +#define MSAN_UNPOISON(p, size) __msan_unpoison(p, size) + +// Check a memory region for initializedness, as if it was being used here. +// If any bits are uninitialized, crash with an MSan report. +// Use this to sanitize data which MSan won't be able to track, e.g. before +// passing data to another process via shared memory. +#define MSAN_CHECK_MEM_IS_INITIALIZED(p, size) \ + __msan_check_mem_is_initialized(p, size) #else // MEMORY_SANITIZER -#define MSAN_UNPOISON(p, s) +#define MSAN_UNPOISON(p, size) +#define MSAN_CHECK_MEM_IS_INITIALIZED(p, size) #endif // MEMORY_SANITIZER // Macro useful for writing cross-platform function pointers. diff --git a/chromium/base/containers/hash_tables.h b/chromium/base/containers/hash_tables.h index 6bf029e4565..d13c46948ed 100644 --- a/chromium/base/containers/hash_tables.h +++ b/chromium/base/containers/hash_tables.h @@ -320,7 +320,7 @@ struct hash<std::pair<Type1, Type2> > { } }; -} +} // namespace BASE_HASH_NAMESPACE #undef DEFINE_PAIR_HASH_FUNCTION_START #undef DEFINE_PAIR_HASH_FUNCTION_END diff --git a/chromium/base/containers/mru_cache.h b/chromium/base/containers/mru_cache.h index 15ea2fccdc4..ed1ad251a2f 100644 --- a/chromium/base/containers/mru_cache.h +++ b/chromium/base/containers/mru_cache.h @@ -213,8 +213,7 @@ class MRUCacheBase { template<class PayloadType> class MRUCacheNullDeletor { public: - void operator()(PayloadType& payload) { - } + void operator()(const PayloadType& payload) {} }; // A container that does not do anything to free its data. Use this when storing @@ -244,9 +243,7 @@ class MRUCache : public MRUCacheBase<KeyType, template<class PayloadType> class MRUCachePointerDeletor { public: - void operator()(PayloadType& payload) { - delete payload; - } + void operator()(const PayloadType& payload) { delete payload; } }; // A cache that owns the payload type, which must be a non-const pointer type. diff --git a/chromium/base/containers/scoped_ptr_hash_map.h b/chromium/base/containers/scoped_ptr_hash_map.h index dedf21365be..8fe550e7317 100644 --- a/chromium/base/containers/scoped_ptr_hash_map.h +++ b/chromium/base/containers/scoped_ptr_hash_map.h @@ -16,12 +16,12 @@ namespace base { -// This type acts like a hash_map<K, scoped_ptr<V> >, based on top of +// This type acts like a hash_map<K, scoped_ptr<V, D> >, based on top of // base::hash_map. The ScopedPtrHashMap has ownership of all values in the data // structure. -template <typename Key, typename Value> +template <typename Key, typename ScopedPtr> class ScopedPtrHashMap { - typedef base::hash_map<Key, Value*> Container; + typedef base::hash_map<Key, typename ScopedPtr::element_type*> Container; public: typedef typename Container::key_type key_type; @@ -34,15 +34,17 @@ class ScopedPtrHashMap { ~ScopedPtrHashMap() { clear(); } - void swap(ScopedPtrHashMap<Key, Value>& other) { + void swap(ScopedPtrHashMap<Key, ScopedPtr>& other) { data_.swap(other.data_); } // Replaces value but not key if key is already present. - iterator set(const Key& key, scoped_ptr<Value> data) { + iterator set(const Key& key, ScopedPtr data) { iterator it = find(key); if (it != end()) { - delete it->second; + // Let ScopedPtr decide how to delete. For example, it may use custom + // deleter. + ScopedPtr(it->second).reset(); it->second = data.release(); return it; } @@ -51,7 +53,7 @@ class ScopedPtrHashMap { } // Does nothing if key is already present - std::pair<iterator, bool> add(const Key& key, scoped_ptr<Value> data) { + std::pair<iterator, bool> add(const Key& key, ScopedPtr data) { std::pair<iterator, bool> result = data_.insert(std::make_pair(key, data.get())); if (result.second) @@ -60,7 +62,8 @@ class ScopedPtrHashMap { } void erase(iterator it) { - delete it->second; + // Let ScopedPtr decide how to delete. + ScopedPtr(it->second).reset(); data_.erase(it); } @@ -72,45 +75,45 @@ class ScopedPtrHashMap { return 1; } - scoped_ptr<Value> take(iterator it) { + ScopedPtr take(iterator it) { DCHECK(it != data_.end()); if (it == data_.end()) - return scoped_ptr<Value>(); + return ScopedPtr(); - scoped_ptr<Value> ret(it->second); + ScopedPtr ret(it->second); it->second = NULL; return ret.Pass(); } - scoped_ptr<Value> take(const Key& k) { + ScopedPtr take(const Key& k) { iterator it = find(k); if (it == data_.end()) - return scoped_ptr<Value>(); + return ScopedPtr(); return take(it); } - scoped_ptr<Value> take_and_erase(iterator it) { + ScopedPtr take_and_erase(iterator it) { DCHECK(it != data_.end()); if (it == data_.end()) - return scoped_ptr<Value>(); + return ScopedPtr(); - scoped_ptr<Value> ret(it->second); + ScopedPtr ret(it->second); data_.erase(it); return ret.Pass(); } - scoped_ptr<Value> take_and_erase(const Key& k) { + ScopedPtr take_and_erase(const Key& k) { iterator it = find(k); if (it == data_.end()) - return scoped_ptr<Value>(); + return ScopedPtr(); return take_and_erase(it); } // Returns the element in the hash_map that matches the given key. // If no such element exists it returns NULL. - Value* get(const Key& k) const { + typename ScopedPtr::element_type* get(const Key& k) const { const_iterator it = find(k); if (it == end()) return NULL; @@ -119,7 +122,19 @@ class ScopedPtrHashMap { inline bool contains(const Key& k) const { return data_.count(k) > 0; } - inline void clear() { STLDeleteValues(&data_); } + inline void clear() { + auto it = data_.begin(); + while (it != data_.end()) { + // NOTE: Like STLDeleteContainerPointers, deleting behind the iterator. + // Deleting the value does not always invalidate the iterator, but it may + // do so if the key is a pointer into the value object. + auto temp = it; + ++it; + // Let ScopedPtr decide how to delete. + ScopedPtr(temp->second).reset(); + } + data_.clear(); + } inline const_iterator find(const Key& k) const { return data_.find(k); } inline iterator find(const Key& k) { return data_.find(k); } diff --git a/chromium/base/containers/scoped_ptr_hash_map_unittest.cc b/chromium/base/containers/scoped_ptr_hash_map_unittest.cc new file mode 100644 index 00000000000..88fe41f9a0d --- /dev/null +++ b/chromium/base/containers/scoped_ptr_hash_map_unittest.cc @@ -0,0 +1,85 @@ +// 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_hash_map.h" + +#include "base/memory/scoped_ptr.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { +namespace { + +struct DeleteCounter { + public: + DeleteCounter() {} + ~DeleteCounter() { g_delete_count++; } + + static void ResetCounter() { g_delete_count = 0; } + static int delete_count() { return g_delete_count; } + + private: + static int g_delete_count; +}; + +int DeleteCounter::g_delete_count = 0; + +struct CountingDeleter { + public: + inline void operator()(DeleteCounter* ptr) const { + g_deleter_call_count++; + delete ptr; + } + + static int count() { return g_deleter_call_count; } + static void ResetCounter() { g_deleter_call_count = 0; } + + private: + static int g_deleter_call_count; +}; + +int CountingDeleter::g_deleter_call_count = 0; + +TEST(ScopedPtrHashMapTest, CustomDeleter) { + int key = 123; + + // Test dtor. + DeleteCounter::ResetCounter(); + CountingDeleter::ResetCounter(); + { + ScopedPtrHashMap<int, scoped_ptr<DeleteCounter, CountingDeleter>> map; + map.set(key, scoped_ptr<DeleteCounter, CountingDeleter>(new DeleteCounter)); + } + EXPECT_EQ(1, DeleteCounter::delete_count()); + EXPECT_EQ(1, CountingDeleter::count()); + + // Test set and erase. + DeleteCounter::ResetCounter(); + CountingDeleter::ResetCounter(); + { + ScopedPtrHashMap<int, scoped_ptr<DeleteCounter, CountingDeleter>> map; + map.erase(map.set( + key, scoped_ptr<DeleteCounter, CountingDeleter>(new DeleteCounter))); + EXPECT_EQ(1, DeleteCounter::delete_count()); + EXPECT_EQ(1, CountingDeleter::count()); + } + EXPECT_EQ(1, DeleteCounter::delete_count()); + EXPECT_EQ(1, CountingDeleter::count()); + + // Test set more than once. + DeleteCounter::ResetCounter(); + CountingDeleter::ResetCounter(); + { + ScopedPtrHashMap<int, scoped_ptr<DeleteCounter, CountingDeleter>> map; + map.set(key, scoped_ptr<DeleteCounter, CountingDeleter>(new DeleteCounter)); + map.set(key, scoped_ptr<DeleteCounter, CountingDeleter>(new DeleteCounter)); + map.set(key, scoped_ptr<DeleteCounter, CountingDeleter>(new DeleteCounter)); + EXPECT_EQ(2, DeleteCounter::delete_count()); + EXPECT_EQ(2, CountingDeleter::count()); + } + EXPECT_EQ(3, DeleteCounter::delete_count()); + EXPECT_EQ(3, CountingDeleter::count()); +} + +} // namespace +} // namespace base diff --git a/chromium/base/containers/stack_container.h b/chromium/base/containers/stack_container.h index 87fa0369b6a..54090d3cd99 100644 --- a/chromium/base/containers/stack_container.h +++ b/chromium/base/containers/stack_container.h @@ -176,6 +176,7 @@ class StackContainer { Allocator allocator_; ContainerType container_; + private: DISALLOW_COPY_AND_ASSIGN(StackContainer); }; diff --git a/chromium/base/critical_closure.h b/chromium/base/critical_closure.h index ac07911089d..75e37045a77 100644 --- a/chromium/base/critical_closure.h +++ b/chromium/base/critical_closure.h @@ -52,7 +52,7 @@ class CriticalClosure { // returned. // // Example: -// file_message_loop_proxy_->PostTask( +// file_task_runner_->PostTask( // FROM_HERE, // MakeCriticalClosure(base::Bind(&WriteToDiskTask, path_, data))); // diff --git a/chromium/base/debug/BUILD.gn b/chromium/base/debug/BUILD.gn new file mode 100644 index 00000000000..8ed623b03f9 --- /dev/null +++ b/chromium/base/debug/BUILD.gn @@ -0,0 +1,76 @@ +# Copyright (c) 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. + +source_set("debug") { + sources = [ + "alias.cc", + "alias.h", + "asan_invalid_access.cc", + "asan_invalid_access.h", + "crash_logging.cc", + "crash_logging.h", + "debugger.cc", + "debugger.h", + "debugger_posix.cc", + "debugger_win.cc", + "dump_without_crashing.cc", + "dump_without_crashing.h", + "gdi_debug_util_win.cc", + "gdi_debug_util_win.h", + + # This file depends on files from the "allocator" target, + # but this target does not depend on "allocator" (see + # allocator.gyp for details). + "leak_annotations.h", + "leak_tracker.h", + "proc_maps_linux.cc", + "proc_maps_linux.h", + "profiler.cc", + "profiler.h", + "stack_trace.cc", + "stack_trace.h", + "stack_trace_android.cc", + "stack_trace_posix.cc", + "stack_trace_win.cc", + "task_annotator.cc", + "task_annotator.h", + ] + + if (is_android) { + # Android uses some Linux sources, put those back. + set_sources_assignment_filter([]) + sources += [ "proc_maps_linux.cc" ] + set_sources_assignment_filter(sources_assignment_filter) + + sources -= [ "stack_trace_posix.cc" ] + } + + if (is_nacl) { + sources -= [ + "crash_logging.cc", + "crash_logging.h", + "stack_trace.cc", + "stack_trace_posix.cc", + ] + } + + configs += [ "//base:base_implementation" ] + + deps = [ + "//base/memory", + "//base/process", + ] + + if (is_linux) { + defines = [ "USE_SYMBOLIZE" ] + deps += [ "//base/third_party/symbolize" ] + } + + allow_circular_includes_from = [ + "//base/memory", + "//base/process", + ] + + visibility = [ "//base/*" ] +} diff --git a/chromium/base/debug/crash_logging_unittest.cc b/chromium/base/debug/crash_logging_unittest.cc index cb11f13f193..4cd9f3e3b09 100644 --- a/chromium/base/debug/crash_logging_unittest.cc +++ b/chromium/base/debug/crash_logging_unittest.cc @@ -18,14 +18,14 @@ std::map<std::string, std::string>* key_values_ = NULL; class CrashLoggingTest : public testing::Test { public: - virtual void SetUp() { + void SetUp() override { key_values_ = new std::map<std::string, std::string>; base::debug::SetCrashKeyReportingFunctions( &CrashLoggingTest::SetKeyValue, &CrashLoggingTest::ClearKeyValue); } - virtual void TearDown() { + void TearDown() override { base::debug::ResetCrashLoggingForTesting(); delete key_values_; diff --git a/chromium/base/debug/debugger.h b/chromium/base/debug/debugger.h index d62ea3f7e17..8680e281ed1 100644 --- a/chromium/base/debug/debugger.h +++ b/chromium/base/debug/debugger.h @@ -6,8 +6,8 @@ // debuggers. You should use this to test if you're running under a debugger, // and if you would like to yield (breakpoint) into the debugger. -#ifndef BASE_DEBUG_DEBUGGER_H -#define BASE_DEBUG_DEBUGGER_H +#ifndef BASE_DEBUG_DEBUGGER_H_ +#define BASE_DEBUG_DEBUGGER_H_ #include "base/base_export.h" @@ -41,4 +41,4 @@ BASE_EXPORT bool IsDebugUISuppressed(); } // namespace debug } // namespace base -#endif // BASE_DEBUG_DEBUGGER_H +#endif // BASE_DEBUG_DEBUGGER_H_ diff --git a/chromium/base/debug/debugger_unittest.cc b/chromium/base/debug/debugger_unittest.cc new file mode 100644 index 00000000000..0a5a0390e8a --- /dev/null +++ b/chromium/base/debug/debugger_unittest.cc @@ -0,0 +1,43 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/debug/debugger.h" + +#include "build/build_config.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +#if defined(GTEST_HAS_DEATH_TEST) && !defined(OS_ANDROID) +void CrashWithBreakDebugger() { + base::debug::SetSuppressDebugUI(false); + base::debug::BreakDebugger(); + +#if defined(OS_WIN) + // This should not be executed. + _exit(125); +#endif +} +#endif // defined(GTEST_HAS_DEATH_TEST) + +} // namespace + +// Death tests misbehave on Android. +#if defined(GTEST_HAS_DEATH_TEST) && !defined(OS_ANDROID) + +TEST(Debugger, CrashAtBreakpoint) { + EXPECT_DEATH(CrashWithBreakDebugger(), ""); +} + +#if defined(OS_WIN) +TEST(Debugger, DoesntExecuteBeyondBreakpoint) { + EXPECT_EXIT(CrashWithBreakDebugger(), + ::testing::ExitedWithCode(0x80000003), ""); +} +#endif // defined(OS_WIN) + +#else // defined(GTEST_HAS_DEATH_TEST) && !defined(OS_ANDROID) +TEST(Debugger, NoTest) { +} +#endif // defined(GTEST_HAS_DEATH_TEST) && !defined(OS_ANDROID) diff --git a/chromium/base/debug/debugger_win.cc b/chromium/base/debug/debugger_win.cc index ccc9c1604bc..a1d86e4453d 100644 --- a/chromium/base/debug/debugger_win.cc +++ b/chromium/base/debug/debugger_win.cc @@ -17,10 +17,8 @@ bool BeingDebugged() { void BreakDebugger() { if (IsDebugUISuppressed()) _exit(1); + __debugbreak(); -#if defined(NDEBUG) - _exit(1); -#endif } } // namespace debug diff --git a/chromium/base/debug/leak_annotations.h b/chromium/base/debug/leak_annotations.h index 27fe663bddb..ef37959aadc 100644 --- a/chromium/base/debug/leak_annotations.h +++ b/chromium/base/debug/leak_annotations.h @@ -21,15 +21,7 @@ #if defined(LEAK_SANITIZER) && !defined(OS_NACL) -// Public LSan API from <sanitizer/lsan_interface.h>. -extern "C" { -void __lsan_disable(); -void __lsan_enable(); -void __lsan_ignore_object(const void *p); - -// Invoke leak detection immediately. If leaks are found, the process will exit. -void __lsan_do_leak_check(); -} // extern "C" +#include <sanitizer/lsan_interface.h> class ScopedLeakSanitizerDisabler { public: @@ -46,7 +38,6 @@ class ScopedLeakSanitizerDisabler { #else -// If neither HeapChecker nor LSan are used, the annotations should be no-ops. #define ANNOTATE_SCOPED_MEMORY_LEAK ((void)0) #define ANNOTATE_LEAKING_OBJECT_PTR(X) ((void)0) diff --git a/chromium/base/debug/proc_maps_linux_unittest.cc b/chromium/base/debug/proc_maps_linux_unittest.cc index d5d1b835f4e..cbc0dd036ad 100644 --- a/chromium/base/debug/proc_maps_linux_unittest.cc +++ b/chromium/base/debug/proc_maps_linux_unittest.cc @@ -7,6 +7,7 @@ #include "base/path_service.h" #include "base/strings/stringprintf.h" #include "base/third_party/dynamic_annotations/dynamic_annotations.h" +#include "base/threading/platform_thread.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { @@ -180,23 +181,23 @@ TEST(ProcMapsTest, Permissions) { } } -TEST(ProcMapsTest, ReadProcMaps) { - std::string proc_maps; - ASSERT_TRUE(ReadProcMaps(&proc_maps)); - - std::vector<MappedMemoryRegion> regions; - ASSERT_TRUE(ParseProcMaps(proc_maps, ®ions)); - ASSERT_FALSE(regions.empty()); - +#if defined(ADDRESS_SANITIZER) +// AddressSanitizer may move local variables to a dedicated "fake stack" which +// is outside the stack region listed in /proc/self/maps. We disable ASan +// instrumentation for this function to force the variable to be local. +__attribute__((no_sanitize_address)) +#endif +void CheckProcMapsRegions(const std::vector<MappedMemoryRegion> ®ions) { // We should be able to find both the current executable as well as the stack - // mapped into memory. Use the address of |proc_maps| as a way of finding the + // mapped into memory. Use the address of |exe_path| as a way of finding the // stack. FilePath exe_path; EXPECT_TRUE(PathService::Get(FILE_EXE, &exe_path)); - uintptr_t address = reinterpret_cast<uintptr_t>(&proc_maps); + uintptr_t address = reinterpret_cast<uintptr_t>(&exe_path); bool found_exe = false; bool found_stack = false; bool found_address = false; + for (size_t i = 0; i < regions.size(); ++i) { if (regions[i].path == exe_path.value()) { // It's OK to find the executable mapped multiple times as there'll be @@ -204,14 +205,25 @@ TEST(ProcMapsTest, ReadProcMaps) { found_exe = true; } - if (regions[i].path == "[stack]") { - // Only check if |address| lies within the real stack when not running - // Valgrind, otherwise |address| will be on a stack that Valgrind creates. - if (!RunningOnValgrind()) { - EXPECT_GE(address, regions[i].start); - EXPECT_LT(address, regions[i].end); - } - + // Valgrind uses its own allocated stacks instead of the kernel-provided + // stack without letting the kernel know via prctl(PR_SET_MM_START_STACK). + // Depending on which kernel you're running it'll impact the output of + // /proc/self/maps. + // + // Prior to version 3.4, the kernel completely ignores other stacks and + // always prints out the vma lying within mm->start_stack as [stack] even + // if the program was currently executing on a different stack. + // + // Starting in 3.4, the kernel will print out the vma containing the current + // stack pointer as [stack:TID] as long as that vma does not lie within + // mm->start_stack. + // + // Because this has gotten too complicated and brittle of a test, completely + // ignore checking for the stack and address when running under Valgrind. + // See http://crbug.com/431702 for more details. + if (!RunningOnValgrind() && regions[i].path == "[stack]") { + EXPECT_GE(address, regions[i].start); + EXPECT_LT(address, regions[i].end); EXPECT_TRUE(regions[i].permissions & MappedMemoryRegion::READ); EXPECT_TRUE(regions[i].permissions & MappedMemoryRegion::WRITE); EXPECT_FALSE(regions[i].permissions & MappedMemoryRegion::EXECUTE); @@ -227,8 +239,21 @@ TEST(ProcMapsTest, ReadProcMaps) { } EXPECT_TRUE(found_exe); - EXPECT_TRUE(found_stack); - EXPECT_TRUE(found_address); + if (!RunningOnValgrind()) { + EXPECT_TRUE(found_stack); + EXPECT_TRUE(found_address); + } +} + +TEST(ProcMapsTest, ReadProcMaps) { + std::string proc_maps; + ASSERT_TRUE(ReadProcMaps(&proc_maps)); + + std::vector<MappedMemoryRegion> regions; + ASSERT_TRUE(ParseProcMaps(proc_maps, ®ions)); + ASSERT_FALSE(regions.empty()); + + CheckProcMapsRegions(regions); } TEST(ProcMapsTest, ReadProcMapsNonEmptyString) { diff --git a/chromium/base/debug/profiler.h b/chromium/base/debug/profiler.h index e1dda897426..2920d8a2fbe 100644 --- a/chromium/base/debug/profiler.h +++ b/chromium/base/debug/profiler.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_DEBUG_PROFILER_H -#define BASE_DEBUG_PROFILER_H +#ifndef BASE_DEBUG_PROFILER_H_ +#define BASE_DEBUG_PROFILER_H_ #include <string> @@ -87,4 +87,4 @@ BASE_EXPORT MoveDynamicSymbol GetProfilerMoveDynamicSymbolFunc(); } // namespace debug } // namespace base -#endif // BASE_DEBUG_DEBUGGER_H +#endif // BASE_DEBUG_PROFILER_H_ diff --git a/chromium/base/debug/stack_trace.h b/chromium/base/debug/stack_trace.h index 572f285beba..fb271b6da4a 100644 --- a/chromium/base/debug/stack_trace.h +++ b/chromium/base/debug/stack_trace.h @@ -17,6 +17,7 @@ #if defined(OS_WIN) struct _EXCEPTION_POINTERS; +struct _CONTEXT; #endif namespace base { @@ -54,6 +55,7 @@ class BASE_EXPORT StackTrace { // Note: this function will throw an import not found (StackWalk64) exception // on system without dbghelp 5.1. StackTrace(const _EXCEPTION_POINTERS* exception_pointers); + StackTrace(const _CONTEXT* context); #endif // Copying and assignment are allowed with the default functions. @@ -76,6 +78,10 @@ class BASE_EXPORT StackTrace { std::string ToString() const; private: +#if defined(OS_WIN) + void InitTrace(_CONTEXT* context_record); +#endif + // From http://msdn.microsoft.com/en-us/library/bb204633.aspx, // the sum of FramesToSkip and FramesToCapture must be less than 63, // so set it to 62. Even if on POSIX it could be a larger value, it usually diff --git a/chromium/base/debug/stack_trace_unittest.cc b/chromium/base/debug/stack_trace_unittest.cc index eb0bd9ad7f3..15c90939a2e 100644 --- a/chromium/base/debug/stack_trace_unittest.cc +++ b/chromium/base/debug/stack_trace_unittest.cc @@ -146,9 +146,11 @@ MULTIPROCESS_TEST_MAIN(MismatchedMallocChildProcess) { // and e.g. mismatched new[]/delete would cause a hang because // of re-entering malloc. TEST_F(StackTraceTest, AsyncSignalUnsafeSignalHandlerHang) { - ProcessHandle child = SpawnChild("MismatchedMallocChildProcess"); - ASSERT_NE(kNullProcessHandle, child); - ASSERT_TRUE(WaitForSingleProcess(child, TestTimeouts::action_timeout())); + Process child = SpawnChild("MismatchedMallocChildProcess"); + ASSERT_TRUE(child.IsValid()); + int exit_code; + ASSERT_TRUE(child.WaitForExitWithTimeout(TestTimeouts::action_timeout(), + &exit_code)); } #endif // !defined(OS_IOS) diff --git a/chromium/base/debug/stack_trace_win.cc b/chromium/base/debug/stack_trace_win.cc index 8df6fc6e3e6..55d55624088 100644 --- a/chromium/base/debug/stack_trace_win.cc +++ b/chromium/base/debug/stack_trace_win.cc @@ -152,10 +152,6 @@ class SymbolContext { init_error_ = ERROR_SUCCESS; - // Work around a mysterious hang on Windows XP. - if (base::win::GetVersion() < base::win::VERSION_VISTA) - return; - // When transferring the binaries e.g. between bots, path put // into the executable will get off. To still retrieve symbols correctly, // add the directory of the executable to symbol search path. @@ -214,24 +210,35 @@ StackTrace::StackTrace() { #endif StackTrace::StackTrace(const EXCEPTION_POINTERS* exception_pointers) { - // When walking an exception stack, we need to use StackWalk64(). - count_ = 0; // StackWalk64() may modify context record passed to it, so we will // use a copy. CONTEXT context_record = *exception_pointers->ContextRecord; + InitTrace(&context_record); +} + +StackTrace::StackTrace(const CONTEXT* context) { + // StackWalk64() may modify context record passed to it, so we will + // use a copy. + CONTEXT context_record = *context; + InitTrace(&context_record); +} + +void StackTrace::InitTrace(CONTEXT* context_record) { + // When walking an exception stack, we need to use StackWalk64(). + count_ = 0; // Initialize stack walking. STACKFRAME64 stack_frame; memset(&stack_frame, 0, sizeof(stack_frame)); #if defined(_WIN64) int machine_type = IMAGE_FILE_MACHINE_AMD64; - stack_frame.AddrPC.Offset = context_record.Rip; - stack_frame.AddrFrame.Offset = context_record.Rbp; - stack_frame.AddrStack.Offset = context_record.Rsp; + stack_frame.AddrPC.Offset = context_record->Rip; + stack_frame.AddrFrame.Offset = context_record->Rbp; + stack_frame.AddrStack.Offset = context_record->Rsp; #else int machine_type = IMAGE_FILE_MACHINE_I386; - stack_frame.AddrPC.Offset = context_record.Eip; - stack_frame.AddrFrame.Offset = context_record.Ebp; - stack_frame.AddrStack.Offset = context_record.Esp; + stack_frame.AddrPC.Offset = context_record->Eip; + stack_frame.AddrFrame.Offset = context_record->Ebp; + stack_frame.AddrStack.Offset = context_record->Esp; #endif stack_frame.AddrPC.Mode = AddrModeFlat; stack_frame.AddrFrame.Mode = AddrModeFlat; @@ -240,7 +247,7 @@ StackTrace::StackTrace(const EXCEPTION_POINTERS* exception_pointers) { GetCurrentProcess(), GetCurrentThread(), &stack_frame, - &context_record, + context_record, NULL, &SymFunctionTableAccess64, &SymGetModuleBase64, diff --git a/chromium/base/debug/task_annotator.cc b/chromium/base/debug/task_annotator.cc index ae2d7975c2e..19df8cb39e5 100644 --- a/chromium/base/debug/task_annotator.cc +++ b/chromium/base/debug/task_annotator.cc @@ -5,8 +5,8 @@ #include "base/debug/task_annotator.h" #include "base/debug/alias.h" -#include "base/debug/trace_event.h" #include "base/pending_task.h" +#include "base/trace_event/trace_event.h" #include "base/tracked_objects.h" namespace base { @@ -28,7 +28,6 @@ void TaskAnnotator::DidQueueTask(const char* queue_function, void TaskAnnotator::RunTask(const char* queue_function, const char* run_function, const PendingTask& pending_task) { - tracked_objects::ThreadData::PrepareForStartOfRun(pending_task.birth_tally); tracked_objects::TaskStopwatch stopwatch; stopwatch.Start(); tracked_objects::Duration queue_duration = diff --git a/chromium/base/debug/trace_event_unittest.h b/chromium/base/debug/trace_event_unittest.h deleted file mode 100644 index 599fec7dcd8..00000000000 --- a/chromium/base/debug/trace_event_unittest.h +++ /dev/null @@ -1,14 +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/time/time.h" - -namespace base { -namespace debug { - -// Sleep until HighResNow has advanced by at least |elapsed|. -void HighResSleepForTraceTest(base::TimeDelta elapsed); - -} // namespace debug -} // namespace base diff --git a/chromium/base/deferred_sequenced_task_runner.h b/chromium/base/deferred_sequenced_task_runner.h index bc8db7a9fde..110d988615d 100644 --- a/chromium/base/deferred_sequenced_task_runner.h +++ b/chromium/base/deferred_sequenced_task_runner.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_DEFERRED_SEQUENCED_TASKRUNNER_H_ -#define BASE_DEFERRED_SEQUENCED_TASKRUNNER_H_ +#ifndef BASE_DEFERRED_SEQUENCED_TASK_RUNNER_H_ +#define BASE_DEFERRED_SEQUENCED_TASK_RUNNER_H_ #include <vector> @@ -75,4 +75,4 @@ class BASE_EXPORT DeferredSequencedTaskRunner : public SequencedTaskRunner { } // namespace base -#endif // BASE_DEFERRED_SEQUENCED_TASKRUNNER_H_ +#endif // BASE_DEFERRED_SEQUENCED_TASK_RUNNER_H_ diff --git a/chromium/base/deferred_sequenced_task_runner_unittest.cc b/chromium/base/deferred_sequenced_task_runner_unittest.cc index 81f2a0a00c9..6e17ad93a8f 100644 --- a/chromium/base/deferred_sequenced_task_runner_unittest.cc +++ b/chromium/base/deferred_sequenced_task_runner_unittest.cc @@ -7,9 +7,9 @@ #include "base/basictypes.h" #include "base/bind.h" #include "base/bind_helpers.h" +#include "base/location.h" #include "base/memory/ref_counted.h" -#include "base/message_loop/message_loop.h" -#include "base/message_loop/message_loop_proxy.h" +#include "base/single_thread_task_runner.h" #include "base/threading/non_thread_safe.h" #include "base/threading/thread.h" #include "testing/gmock/include/gmock/gmock.h" @@ -58,11 +58,9 @@ class DeferredSequencedTaskRunnerTest : public testing::Test, } protected: - DeferredSequencedTaskRunnerTest() : - loop_(), - runner_( - new base::DeferredSequencedTaskRunner(loop_.message_loop_proxy())) { - } + DeferredSequencedTaskRunnerTest() + : loop_(), + runner_(new base::DeferredSequencedTaskRunner(loop_.task_runner())) {} base::MessageLoop loop_; scoped_refptr<base::DeferredSequencedTaskRunner> runner_; @@ -126,21 +124,18 @@ TEST_F(DeferredSequencedTaskRunnerTest, DeferredStartWithMultipleThreads) { thread1.Start(); thread2.Start(); for (int i = 0; i < 5; ++i) { - thread1.message_loop()->PostTask( + thread1.task_runner()->PostTask( FROM_HERE, base::Bind(&DeferredSequencedTaskRunnerTest::PostExecuteTask, - base::Unretained(this), - 2 * i)); - thread2.message_loop()->PostTask( + base::Unretained(this), 2 * i)); + thread2.task_runner()->PostTask( FROM_HERE, base::Bind(&DeferredSequencedTaskRunnerTest::PostExecuteTask, - base::Unretained(this), - 2 * i + 1)); + base::Unretained(this), 2 * i + 1)); if (i == 2) { - thread1.message_loop()->PostTask( - FROM_HERE, - base::Bind(&DeferredSequencedTaskRunnerTest::StartRunner, - base::Unretained(this))); + thread1.task_runner()->PostTask( + FROM_HERE, base::Bind(&DeferredSequencedTaskRunnerTest::StartRunner, + base::Unretained(this))); } } } @@ -154,8 +149,7 @@ TEST_F(DeferredSequencedTaskRunnerTest, ObjectDestructionOrder) { { base::Thread thread("DeferredSequencedTaskRunnerTestThread"); thread.Start(); - runner_ = - new base::DeferredSequencedTaskRunner(thread.message_loop_proxy()); + runner_ = new base::DeferredSequencedTaskRunner(thread.task_runner()); for (int i = 0; i < 5; ++i) { { // Use a block to ensure that no reference to |short_lived_object| diff --git a/chromium/base/event_recorder.h b/chromium/base/event_recorder.h deleted file mode 100644 index bff87ed494b..00000000000 --- a/chromium/base/event_recorder.h +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_EVENT_RECORDER_H_ -#define BASE_EVENT_RECORDER_H_ - -#include "base/base_export.h" -#include "base/basictypes.h" -#include "build/build_config.h" - -#if defined(OS_WIN) -#include <stdio.h> -#include <string.h> -#include <windows.h> -#endif - -namespace base { - -class FilePath; - -// A class for recording and playing back keyboard and mouse input events. -// -// Note - if you record events, and the playback with the windows in -// different sizes or positions, the playback will fail. When -// recording and playing, you should move the relevant windows -// to constant sizes and locations. -// TODO(mbelshe) For now this is a singleton. I believe that this class -// could be easily modified to: -// support two simultaneous recorders -// be playing back events while already recording events. -// Why? Imagine if the product had a "record a macro" feature. -// You might be recording globally, while recording or playing back -// a macro. I don't think two playbacks make sense. -class BASE_EXPORT EventRecorder { - public: - // Get the singleton EventRecorder. - // We can only handle one recorder/player at a time. - static EventRecorder* current() { - if (!current_) - current_ = new EventRecorder(); - return current_; - } - - // Starts recording events. - // Will clobber the file if it already exists. - // Returns true on success, or false if an error occurred. - bool StartRecording(const FilePath& filename); - - // Stops recording. - void StopRecording(); - - // Is the EventRecorder currently recording. - bool is_recording() const { return is_recording_; } - - // Plays events previously recorded. - // Returns true on success, or false if an error occurred. - bool StartPlayback(const FilePath& filename); - - // Stops playback. - void StopPlayback(); - - // Is the EventRecorder currently playing. - bool is_playing() const { return is_playing_; } - -#if defined(OS_WIN) - // C-style callbacks for the EventRecorder. - // Used for internal purposes only. - LRESULT RecordWndProc(int nCode, WPARAM wParam, LPARAM lParam); - LRESULT PlaybackWndProc(int nCode, WPARAM wParam, LPARAM lParam); -#endif - - private: - // Create a new EventRecorder. Events are saved to the file filename. - // If the file already exists, it will be deleted before recording - // starts. - EventRecorder() - : is_recording_(false), - is_playing_(false), -#if defined(OS_WIN) - journal_hook_(NULL), - file_(NULL), -#endif - playback_first_msg_time_(0), - playback_start_time_(0) { -#if defined(OS_WIN) - memset(&playback_msg_, 0, sizeof(playback_msg_)); -#endif - } - ~EventRecorder(); - - static EventRecorder* current_; // Our singleton. - - bool is_recording_; - bool is_playing_; -#if defined(OS_WIN) - HHOOK journal_hook_; - FILE* file_; - EVENTMSG playback_msg_; -#endif - int playback_first_msg_time_; - int playback_start_time_; - - DISALLOW_COPY_AND_ASSIGN(EventRecorder); -}; - -} // namespace base - -#endif // BASE_EVENT_RECORDER_H_ diff --git a/chromium/base/event_recorder_stubs.cc b/chromium/base/event_recorder_stubs.cc deleted file mode 100644 index 91f2e072fc9..00000000000 --- a/chromium/base/event_recorder_stubs.cc +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/event_recorder.h" - -// This file implements a link stub for EventRecorder that can be used on -// platforms that don't have a working EventRecorder implementation. - -namespace base { - -EventRecorder* EventRecorder::current_; // Our singleton. - -bool EventRecorder::StartRecording(const FilePath& filename) { - return true; -} - -void EventRecorder::StopRecording() { -} - -bool EventRecorder::StartPlayback(const FilePath& filename) { - return false; -} - -void EventRecorder::StopPlayback() { -} - -} // namespace diff --git a/chromium/base/event_recorder_win.cc b/chromium/base/event_recorder_win.cc deleted file mode 100644 index b3076a11318..00000000000 --- a/chromium/base/event_recorder_win.cc +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <stddef.h> -#include <windows.h> -#include <mmsystem.h> - -#include "base/event_recorder.h" -#include "base/files/file_util.h" -#include "base/logging.h" - -// A note about time. -// For perfect playback of events, you'd like a very accurate timer -// so that events are played back at exactly the same time that -// they were recorded. However, windows has a clock which is only -// granular to ~15ms. We see more consistent event playback when -// using a higher resolution timer. To do this, we use the -// timeGetTime API instead of the default GetTickCount() API. - -namespace base { - -EventRecorder* EventRecorder::current_ = NULL; - -LRESULT CALLBACK StaticRecordWndProc(int nCode, WPARAM wParam, - LPARAM lParam) { - DCHECK(EventRecorder::current()); - return EventRecorder::current()->RecordWndProc(nCode, wParam, lParam); -} - -LRESULT CALLBACK StaticPlaybackWndProc(int nCode, WPARAM wParam, - LPARAM lParam) { - DCHECK(EventRecorder::current()); - return EventRecorder::current()->PlaybackWndProc(nCode, wParam, lParam); -} - -EventRecorder::~EventRecorder() { - // Try to assert early if the caller deletes the recorder - // while it is still in use. - DCHECK(!journal_hook_); - DCHECK(!is_recording_ && !is_playing_); -} - -bool EventRecorder::StartRecording(const FilePath& filename) { - if (journal_hook_ != NULL) - return false; - if (is_recording_ || is_playing_) - return false; - - // Open the recording file. - DCHECK(!file_); - file_ = OpenFile(filename, "wb+"); - if (!file_) { - DLOG(ERROR) << "EventRecorder could not open log file"; - return false; - } - - // Set the faster clock, if possible. - ::timeBeginPeriod(1); - - // Set the recording hook. JOURNALRECORD can only be used as a global hook. - journal_hook_ = ::SetWindowsHookEx(WH_JOURNALRECORD, StaticRecordWndProc, - GetModuleHandle(NULL), 0); - if (!journal_hook_) { - DLOG(ERROR) << "EventRecorder Record Hook failed"; - CloseFile(file_); - return false; - } - - is_recording_ = true; - return true; -} - -void EventRecorder::StopRecording() { - if (is_recording_) { - DCHECK(journal_hook_ != NULL); - - if (!::UnhookWindowsHookEx(journal_hook_)) { - DLOG(ERROR) << "EventRecorder Unhook failed"; - // Nothing else we can really do here. - return; - } - - ::timeEndPeriod(1); - - DCHECK(file_ != NULL); - CloseFile(file_); - file_ = NULL; - - journal_hook_ = NULL; - is_recording_ = false; - } -} - -bool EventRecorder::StartPlayback(const FilePath& filename) { - if (journal_hook_ != NULL) - return false; - if (is_recording_ || is_playing_) - return false; - - // Open the recording file. - DCHECK(!file_); - file_ = OpenFile(filename, "rb"); - if (!file_) { - DLOG(ERROR) << "EventRecorder Playback could not open log file"; - return false; - } - // Read the first event from the record. - if (fread(&playback_msg_, sizeof(EVENTMSG), 1, file_) != 1) { - DLOG(ERROR) << "EventRecorder Playback has no records!"; - CloseFile(file_); - return false; - } - - // Set the faster clock, if possible. - ::timeBeginPeriod(1); - - // Playback time is tricky. When playing back, we read a series of events, - // each with timeouts. Simply subtracting the delta between two timers will - // lead to fast playback (about 2x speed). The API has two events, one - // which advances to the next event (HC_SKIP), and another that requests the - // event (HC_GETNEXT). The same event will be requested multiple times. - // Each time the event is requested, we must calculate the new delay. - // To do this, we track the start time of the playback, and constantly - // re-compute the delay. I mention this only because I saw two examples - // of how to use this code on the net, and both were broken :-) - playback_start_time_ = timeGetTime(); - playback_first_msg_time_ = playback_msg_.time; - - // Set the hook. JOURNALPLAYBACK can only be used as a global hook. - journal_hook_ = ::SetWindowsHookEx(WH_JOURNALPLAYBACK, StaticPlaybackWndProc, - GetModuleHandle(NULL), 0); - if (!journal_hook_) { - DLOG(ERROR) << "EventRecorder Playback Hook failed"; - return false; - } - - is_playing_ = true; - - return true; -} - -void EventRecorder::StopPlayback() { - if (is_playing_) { - DCHECK(journal_hook_ != NULL); - - if (!::UnhookWindowsHookEx(journal_hook_)) { - DLOG(ERROR) << "EventRecorder Unhook failed"; - // Nothing else we can really do here. - } - - DCHECK(file_ != NULL); - CloseFile(file_); - file_ = NULL; - - ::timeEndPeriod(1); - - journal_hook_ = NULL; - is_playing_ = false; - } -} - -// Windows callback hook for the recorder. -LRESULT EventRecorder::RecordWndProc(int nCode, WPARAM wParam, LPARAM lParam) { - static bool recording_enabled = true; - EVENTMSG* msg_ptr = NULL; - - // The API says we have to do this. - // See http://msdn2.microsoft.com/en-us/library/ms644983(VS.85).aspx - if (nCode < 0) - return ::CallNextHookEx(journal_hook_, nCode, wParam, lParam); - - // Check for the break key being pressed and stop recording. - if (::GetKeyState(VK_CANCEL) & 0x8000) { - StopRecording(); - return ::CallNextHookEx(journal_hook_, nCode, wParam, lParam); - } - - // The Journal Recorder must stop recording events when system modal - // dialogs are present. (see msdn link above) - switch (nCode) { - case HC_SYSMODALON: - recording_enabled = false; - break; - case HC_SYSMODALOFF: - recording_enabled = true; - break; - } - - if (nCode == HC_ACTION && recording_enabled) { - // Aha - we have an event to record. - msg_ptr = reinterpret_cast<EVENTMSG*>(lParam); - msg_ptr->time = timeGetTime(); - fwrite(msg_ptr, sizeof(EVENTMSG), 1, file_); - fflush(file_); - } - - return CallNextHookEx(journal_hook_, nCode, wParam, lParam); -} - -// Windows callback for the playback mode. -LRESULT EventRecorder::PlaybackWndProc(int nCode, WPARAM wParam, - LPARAM lParam) { - static bool playback_enabled = true; - int delay = 0; - - switch (nCode) { - // A system modal dialog box is being displayed. Stop playing back - // messages. - case HC_SYSMODALON: - playback_enabled = false; - break; - - // A system modal dialog box is destroyed. We can start playing back - // messages again. - case HC_SYSMODALOFF: - playback_enabled = true; - break; - - // Prepare to copy the next mouse or keyboard event to playback. - case HC_SKIP: - if (!playback_enabled) - break; - - // Read the next event from the record. - if (fread(&playback_msg_, sizeof(EVENTMSG), 1, file_) != 1) - this->StopPlayback(); - break; - - // Copy the mouse or keyboard event to the EVENTMSG structure in lParam. - case HC_GETNEXT: - if (!playback_enabled) - break; - - memcpy(reinterpret_cast<void*>(lParam), &playback_msg_, - sizeof(playback_msg_)); - - // The return value is the amount of time (in milliseconds) to wait - // before playing back the next message in the playback queue. Each - // time this is called, we recalculate the delay relative to our current - // wall clock. - delay = (playback_msg_.time - playback_first_msg_time_) - - (timeGetTime() - playback_start_time_); - if (delay < 0) - delay = 0; - return delay; - - // An application has called PeekMessage with wRemoveMsg set to PM_NOREMOVE - // indicating that the message is not removed from the message queue after - // PeekMessage processing. - case HC_NOREMOVE: - break; - } - - return CallNextHookEx(journal_hook_, nCode, wParam, lParam); -} - -} // namespace base diff --git a/chromium/base/event_types.h b/chromium/base/event_types.h index af586e46ec9..9905800d2e8 100644 --- a/chromium/base/event_types.h +++ b/chromium/base/event_types.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_EVENT_TYPES_H -#define BASE_EVENT_TYPES_H +#ifndef BASE_EVENT_TYPES_H_ +#define BASE_EVENT_TYPES_H_ #include "build/build_config.h" @@ -34,4 +34,4 @@ typedef void* NativeEvent; } // namespace base -#endif // BASE_EVENT_TYPES_H +#endif // BASE_EVENT_TYPES_H_ diff --git a/chromium/base/file_version_info.h b/chromium/base/file_version_info.h index e18ba1367f0..57b837c24b0 100644 --- a/chromium/base/file_version_info.h +++ b/chromium/base/file_version_info.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_FILE_VERSION_INFO_H__ -#define BASE_FILE_VERSION_INFO_H__ +#ifndef BASE_FILE_VERSION_INFO_H_ +#define BASE_FILE_VERSION_INFO_H_ #include "build/build_config.h" @@ -32,22 +32,21 @@ class FilePath; // version returns values from the Info.plist as appropriate. TODO(avi): make // this a less-obvious Windows-ism. -class FileVersionInfo { +class BASE_EXPORT FileVersionInfo { public: virtual ~FileVersionInfo() {} #if defined(OS_WIN) || defined(OS_MACOSX) // Creates a FileVersionInfo for the specified path. Returns NULL if something // goes wrong (typically the file does not exit or cannot be opened). The // returned object should be deleted when you are done with it. - BASE_EXPORT static FileVersionInfo* CreateFileVersionInfo( + static FileVersionInfo* CreateFileVersionInfo( const base::FilePath& file_path); #endif // OS_WIN || OS_MACOSX #if defined(OS_WIN) // Creates a FileVersionInfo for the specified module. Returns NULL in case // of error. The returned object should be deleted when you are done with it. - BASE_EXPORT static FileVersionInfo* CreateFileVersionInfoForModule( - HMODULE module); + static FileVersionInfo* CreateFileVersionInfoForModule(HMODULE module); // Creates a FileVersionInfo for the current module. Returns NULL in case // of error. The returned object should be deleted when you are done with it. @@ -61,7 +60,7 @@ class FileVersionInfo { #else // Creates a FileVersionInfo for the current module. Returns NULL in case // of error. The returned object should be deleted when you are done with it. - BASE_EXPORT static FileVersionInfo* CreateFileVersionInfoForCurrentModule(); + static FileVersionInfo* CreateFileVersionInfoForCurrentModule(); #endif // OS_WIN // Accessors to the different version properties. @@ -84,4 +83,4 @@ class FileVersionInfo { virtual bool is_official_build() = 0; }; -#endif // BASE_FILE_VERSION_INFO_H__ +#endif // BASE_FILE_VERSION_INFO_H_ diff --git a/chromium/base/file_version_info_mac.h b/chromium/base/file_version_info_mac.h index a18dbbdbefb..e67783834df 100644 --- a/chromium/base/file_version_info_mac.h +++ b/chromium/base/file_version_info_mac.h @@ -5,16 +5,13 @@ #ifndef BASE_FILE_VERSION_INFO_MAC_H_ #define BASE_FILE_VERSION_INFO_MAC_H_ +#include <CoreFoundation/CoreFoundation.h> #include <string> #include "base/file_version_info.h" #include "base/mac/scoped_nsobject.h" -#ifdef __OBJC__ @class NSBundle; -#else -class NSBundle; -#endif class FileVersionInfoMac : public FileVersionInfo { public: diff --git a/chromium/base/file_version_info_win.h b/chromium/base/file_version_info_win.h index 0e0f271a32b..09d8d37cc0c 100644 --- a/chromium/base/file_version_info_win.h +++ b/chromium/base/file_version_info_win.h @@ -15,36 +15,36 @@ struct tagVS_FIXEDFILEINFO; typedef tagVS_FIXEDFILEINFO VS_FIXEDFILEINFO; -class FileVersionInfoWin : public FileVersionInfo { +class BASE_EXPORT FileVersionInfoWin : public FileVersionInfo { public: - BASE_EXPORT FileVersionInfoWin(void* data, WORD language, WORD code_page); - BASE_EXPORT ~FileVersionInfoWin(); + FileVersionInfoWin(void* data, WORD language, WORD code_page); + ~FileVersionInfoWin() override; // Accessors to the different version properties. // Returns an empty string if the property is not found. - virtual base::string16 company_name() override; - virtual base::string16 company_short_name() override; - virtual base::string16 product_name() override; - virtual base::string16 product_short_name() override; - virtual base::string16 internal_name() override; - virtual base::string16 product_version() override; - virtual base::string16 private_build() override; - virtual base::string16 special_build() override; - virtual base::string16 comments() override; - virtual base::string16 original_filename() override; - virtual base::string16 file_description() override; - virtual base::string16 file_version() override; - virtual base::string16 legal_copyright() override; - virtual base::string16 legal_trademarks() override; - virtual base::string16 last_change() override; - virtual bool is_official_build() override; + base::string16 company_name() override; + base::string16 company_short_name() override; + base::string16 product_name() override; + base::string16 product_short_name() override; + base::string16 internal_name() override; + base::string16 product_version() override; + base::string16 private_build() override; + base::string16 special_build() override; + base::string16 comments() override; + base::string16 original_filename() override; + base::string16 file_description() override; + base::string16 file_version() override; + base::string16 legal_copyright() override; + base::string16 legal_trademarks() override; + base::string16 last_change() override; + bool is_official_build() override; // Lets you access other properties not covered above. - BASE_EXPORT bool GetValue(const wchar_t* name, std::wstring* value); + bool GetValue(const wchar_t* name, std::wstring* value); // Similar to GetValue but returns a wstring (empty string if the property // does not exist). - BASE_EXPORT std::wstring GetStringValue(const wchar_t* name); + std::wstring GetStringValue(const wchar_t* name); // Get the fixed file info if it exists. Otherwise NULL VS_FIXEDFILEINFO* fixed_file_info() { return fixed_file_info_; } diff --git a/chromium/base/files/dir_reader_linux.h b/chromium/base/files/dir_reader_linux.h index cb0cbd38e56..abf259530b3 100644 --- a/chromium/base/files/dir_reader_linux.h +++ b/chromium/base/files/dir_reader_linux.h @@ -95,4 +95,4 @@ class DirReaderLinux { } // namespace base -#endif // BASE_FILES_DIR_READER_LINUX_H_ +#endif // BASE_FILES_DIR_READER_LINUX_H_ diff --git a/chromium/base/files/dir_reader_posix.h b/chromium/base/files/dir_reader_posix.h index 6a20ced0223..6a32d9fd480 100644 --- a/chromium/base/files/dir_reader_posix.h +++ b/chromium/base/files/dir_reader_posix.h @@ -33,4 +33,4 @@ typedef DirReaderFallback DirReaderPosix; } // namespace base -#endif // BASE_FILES_DIR_READER_POSIX_H_ +#endif // BASE_FILES_DIR_READER_POSIX_H_ diff --git a/chromium/base/files/file.cc b/chromium/base/files/file.cc index ea8dbf27ac1..58f80c52322 100644 --- a/chromium/base/files/file.cc +++ b/chromium/base/files/file.cc @@ -4,6 +4,9 @@ #include "base/files/file.h" #include "base/files/file_path.h" +#include "base/files/file_tracing.h" +#include "base/metrics/histogram.h" +#include "base/timer/elapsed_timer.h" namespace base { @@ -23,11 +26,11 @@ File::File() } #if !defined(OS_NACL) -File::File(const FilePath& name, uint32 flags) +File::File(const FilePath& path, uint32 flags) : error_details_(FILE_OK), created_(false), async_(false) { - Initialize(name, flags); + Initialize(path, flags); } #endif @@ -49,6 +52,7 @@ File::File(Error error_details) File::File(RValue other) : file_(other.object->TakePlatformFile()), + path_(other.object->path_), error_details_(other.object->error_details()), created_(other.object->created()), async_(other.object->async_) { @@ -63,6 +67,7 @@ File& File::operator=(RValue other) { if (this != other.object) { Close(); SetPlatformFile(other.object->TakePlatformFile()); + path_ = other.object->path_; error_details_ = other.object->error_details(); created_ = other.object->created(); async_ = other.object->async_; @@ -71,12 +76,14 @@ File& File::operator=(RValue other) { } #if !defined(OS_NACL) -void File::Initialize(const FilePath& name, uint32 flags) { - if (name.ReferencesParent()) { +void File::Initialize(const FilePath& path, uint32 flags) { + if (path.ReferencesParent()) { error_details_ = FILE_ERROR_ACCESS_DENIED; return; } - InitializeUnsafe(name, flags); + path_ = path; + SCOPED_FILE_TRACE("Initialize"); + DoInitialize(flags); } #endif @@ -124,4 +131,12 @@ std::string File::ErrorToString(Error error) { return ""; } +bool File::Flush() { + ElapsedTimer timer; + SCOPED_FILE_TRACE("Flush"); + bool return_value = DoFlush(); + UMA_HISTOGRAM_TIMES("PlatformFile.FlushTime", timer.Elapsed()); + return return_value; +} + } // namespace base diff --git a/chromium/base/files/file.h b/chromium/base/files/file.h index 7b6366c1c2e..b21b15972bc 100644 --- a/chromium/base/files/file.h +++ b/chromium/base/files/file.h @@ -18,6 +18,8 @@ #include "base/base_export.h" #include "base/basictypes.h" +#include "base/files/file_path.h" +#include "base/files/file_tracing.h" #include "base/files/scoped_file.h" #include "base/gtest_prod_util.h" #include "base/move.h" @@ -31,8 +33,6 @@ FORWARD_DECLARE_TEST(FileTest, MemoryCorruption); namespace base { -class FilePath; - #if defined(OS_WIN) typedef HANDLE PlatformFile; #elif defined(OS_POSIX) @@ -128,9 +128,9 @@ class BASE_EXPORT File { // Used to hold information about a given file. // If you add more fields to this structure (platform-specific fields are OK), - // make sure to update all functions that use it in file_util_{win|posix}.cc - // too, and the ParamTraits<base::PlatformFileInfo> implementation in - // chrome/common/common_param_traits.cc. + // make sure to update all functions that use it in file_util_{win|posix}.cc, + // too, and the ParamTraits<base::File::Info> implementation in + // ipc/ipc_message_utils.cc. struct BASE_EXPORT Info { Info(); ~Info(); @@ -145,24 +145,25 @@ class BASE_EXPORT File { // True if the file corresponds to a directory. bool is_directory; - // True if the file corresponds to a symbolic link. + // True if the file corresponds to a symbolic link. For Windows currently + // not supported and thus always false. bool is_symbolic_link; // The last modified time of a file. - base::Time last_modified; + Time last_modified; // The last accessed time of a file. - base::Time last_accessed; + Time last_accessed; // The creation time of a file. - base::Time creation_time; + Time creation_time; }; File(); // Creates or opens the given file. This will fail with 'access denied' if the - // |name| contains path traversal ('..') components. - File(const FilePath& name, uint32 flags); + // |path| contains path traversal ('..') components. + File(const FilePath& path, uint32 flags); // Takes ownership of |platform_file|. explicit File(PlatformFile platform_file); @@ -179,11 +180,7 @@ class BASE_EXPORT File { File& operator=(RValue other); // Creates or opens the given file. - void Initialize(const FilePath& name, uint32 flags); - - // Creates or opens the given file, allowing paths with traversal ('..') - // components. Use only with extreme care. - void InitializeUnsafe(const FilePath& name, uint32 flags); + void Initialize(const FilePath& path, uint32 flags); bool IsValid() const; @@ -194,7 +191,7 @@ class BASE_EXPORT File { // Returns the OS result of opening this file. Note that the way to verify // the success of the operation is to use IsValid(), not this method: - // File file(name, flags); + // File file(path, flags); // if (!file.IsValid()) // return; Error error_details() const { return error_details_; } @@ -287,6 +284,13 @@ class BASE_EXPORT File { // Unlock a file previously locked. Error Unlock(); + // Returns a new object referencing this file for use within the current + // process. Handling of FLAG_DELETE_ON_CLOSE varies by OS. On POSIX, the File + // object that was created or initialized with this flag will have unlinked + // the underlying file when it was created or opened. On Windows, the + // underlying file is deleted when the last handle to it is closed. + File Duplicate(); + bool async() const { return async_; } #if defined(OS_WIN) @@ -301,6 +305,8 @@ class BASE_EXPORT File { private: FRIEND_TEST_ALL_PREFIXES(::FileTest, MemoryCorruption); + friend class FileTracing::ScopedTrace; + #if defined(OS_POSIX) // Encloses a single ScopedFD, saving a cheap tamper resistent memory checksum // alongside it. This checksum is validated at every access, allowing early @@ -346,6 +352,14 @@ class BASE_EXPORT File { }; #endif + // Creates or opens the given file. Only called if |path_| has no + // traversal ('..') components. + void DoInitialize(uint32 flags); + + // TODO(tnagel): Reintegrate into Flush() once histogram isn't needed anymore, + // cf. issue 473337. + bool DoFlush(); + void SetPlatformFile(PlatformFile file); #if defined(OS_WIN) @@ -354,6 +368,12 @@ class BASE_EXPORT File { MemoryCheckingScopedFD file_; #endif + // Path that |Initialize()| was called with. Only set if safe (i.e. no '..'). + FilePath path_; + + // Object tied to the lifetime of |this| that enables/disables tracing. + FileTracing::ScopedEnabler trace_enabler_; + Error error_details_; bool created_; bool async_; diff --git a/chromium/base/files/file_enumerator_win.cc b/chromium/base/files/file_enumerator_win.cc index 6da1667ed99..931d1549816 100644 --- a/chromium/base/files/file_enumerator_win.cc +++ b/chromium/base/files/file_enumerator_win.cc @@ -147,7 +147,8 @@ FilePath FileEnumerator::Next() { // add it to pending_paths_ so we scan it after we finish scanning this // directory. However, don't do recursion through reparse points or we // may end up with an infinite cycle. - if (!(find_data_.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) + DWORD attributes = GetFileAttributes(cur_file.value().c_str()); + if (!(attributes & FILE_ATTRIBUTE_REPARSE_POINT)) pending_paths_.push(cur_file); } if (file_type_ & FileEnumerator::DIRECTORIES) diff --git a/chromium/base/files/file_path.cc b/chromium/base/files/file_path.cc index bf37be615b1..33d5ca1b884 100644 --- a/chromium/base/files/file_path.cc +++ b/chromium/base/files/file_path.cc @@ -593,7 +593,7 @@ std::string FilePath::MaybeAsASCII() const { } std::string FilePath::AsUTF8Unsafe() const { -#if defined(OS_MACOSX) || defined(OS_CHROMEOS) +#if defined(SYSTEM_NATIVE_UTF8) return value(); #else return WideToUTF8(SysNativeMBToWide(value())); @@ -601,7 +601,7 @@ std::string FilePath::AsUTF8Unsafe() const { } string16 FilePath::AsUTF16Unsafe() const { -#if defined(OS_MACOSX) || defined(OS_CHROMEOS) +#if defined(SYSTEM_NATIVE_UTF8) return UTF8ToUTF16(value()); #else return WideToUTF16(SysNativeMBToWide(value())); @@ -610,7 +610,7 @@ string16 FilePath::AsUTF16Unsafe() const { // static FilePath FilePath::FromUTF8Unsafe(const std::string& utf8) { -#if defined(OS_MACOSX) || defined(OS_CHROMEOS) +#if defined(SYSTEM_NATIVE_UTF8) return FilePath(utf8); #else return FilePath(SysWideToNativeMB(UTF8ToWide(utf8))); @@ -619,7 +619,7 @@ FilePath FilePath::FromUTF8Unsafe(const std::string& utf8) { // static FilePath FilePath::FromUTF16Unsafe(const string16& utf16) { -#if defined(OS_MACOSX) || defined(OS_CHROMEOS) +#if defined(SYSTEM_NATIVE_UTF8) return FilePath(UTF16ToUTF8(utf16)); #else return FilePath(SysWideToNativeMB(UTF16ToWide(utf16))); diff --git a/chromium/base/files/file_path.h b/chromium/base/files/file_path.h index ad42b954927..5225b12ffd4 100644 --- a/chromium/base/files/file_path.h +++ b/chromium/base/files/file_path.h @@ -53,7 +53,7 @@ // between char[]-based pathnames on POSIX systems and wchar_t[]-based // pathnames on Windows. // -// Paths can't contain NULs as a precaution agaist premature truncation. +// As a precaution against premature truncation, paths can't contain NULs. // // Because a FilePath object should not be instantiated at the global scope, // instead, use a FilePath::CharType[] and initialize it with @@ -83,9 +83,9 @@ // in case it ever comes across such a system. FilePath needs this support // for Windows UNC paths, anyway. // References: -// The Open Group Base Specifications Issue 7, sections 3.266 ("Pathname") +// The Open Group Base Specifications Issue 7, sections 3.267 ("Pathname") // and 4.12 ("Pathname Resolution"), available at: -// http://www.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_266 +// http://www.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_267 // http://www.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_12 // // - Windows treats c:\\ the same way it treats \\. This was intended to @@ -107,6 +107,7 @@ #include <vector> #include "base/base_export.h" +#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. @@ -237,7 +238,7 @@ class BASE_EXPORT FilePath { // ASSERT(new_path == path.value()); // NOTE: this is different from the original file_util implementation which // returned the extension without a leading "." ("jpg" instead of ".jpg") - StringType Extension() const; + StringType Extension() const WARN_UNUSED_RESULT; // Returns the path's file extension, as in Extension(), but will // never return a double extension. @@ -246,7 +247,7 @@ class BASE_EXPORT FilePath { // we can rename this to Extension() and the other to something like // LongExtension(), defaulting to short extensions and leaving the // long "extensions" to logic like base::GetUniquePathNumber(). - StringType FinalExtension() const; + StringType FinalExtension() const WARN_UNUSED_RESULT; // Returns "C:\pics\jojo" for path "C:\pics\jojo.jpg" // NOTE: this is slightly different from the similar file_util implementation @@ -442,11 +443,9 @@ BASE_EXPORT extern void PrintTo(const base::FilePath& path, std::ostream* out); #if defined(OS_POSIX) #define FILE_PATH_LITERAL(x) x #define PRFilePath "s" -#define PRFilePathLiteral "%s" #elif defined(OS_WIN) #define FILE_PATH_LITERAL(x) L ## x #define PRFilePath "ls" -#define PRFilePathLiteral L"%ls" #endif // OS_WIN // Provide a hash function so that hash_sets and maps can contain FilePath diff --git a/chromium/base/files/file_path_unittest.cc b/chromium/base/files/file_path_unittest.cc index 956faea3218..c16293861ce 100644 --- a/chromium/base/files/file_path_unittest.cc +++ b/chromium/base/files/file_path_unittest.cc @@ -48,15 +48,7 @@ struct UTF8TestData { // file_util winds up using autoreleased objects on the Mac, so this needs // to be a PlatformTest -class FilePathTest : public PlatformTest { - protected: - virtual void SetUp() override { - PlatformTest::SetUp(); - } - virtual void TearDown() override { - PlatformTest::TearDown(); - } -}; +typedef PlatformTest FilePathTest; TEST_F(FilePathTest, DirName) { const struct UnaryTestData cases[] = { diff --git a/chromium/base/files/file_path_watcher.cc b/chromium/base/files/file_path_watcher.cc index b17354197d4..59ae7059caf 100644 --- a/chromium/base/files/file_path_watcher.cc +++ b/chromium/base/files/file_path_watcher.cc @@ -32,7 +32,7 @@ bool FilePathWatcher::RecursiveWatchAvailable() { // FSEvents isn't available on iOS and is broken on OSX 10.6 and earlier. // See http://crbug.com/54822#c31 return mac::IsOSLionOrLater(); -#elif defined(OS_WIN) || defined(OS_LINUX) +#elif defined(OS_WIN) || defined(OS_LINUX) || defined(OS_ANDROID) return true; #else return false; diff --git a/chromium/base/files/file_path_watcher.h b/chromium/base/files/file_path_watcher.h index d26fa060446..4f132aff78a 100644 --- a/chromium/base/files/file_path_watcher.h +++ b/chromium/base/files/file_path_watcher.h @@ -12,7 +12,7 @@ #include "base/callback.h" #include "base/files/file_path.h" #include "base/memory/ref_counted.h" -#include "base/message_loop/message_loop_proxy.h" +#include "base/single_thread_task_runner.h" namespace base { @@ -58,12 +58,13 @@ class BASE_EXPORT FilePathWatcher { // check |is_cancelled()| to avoid duplicate work. virtual void CancelOnMessageLoopThread() = 0; - scoped_refptr<base::MessageLoopProxy> message_loop() const { - return message_loop_; + scoped_refptr<base::SingleThreadTaskRunner> task_runner() const { + return task_runner_; } - void set_message_loop(const scoped_refptr<base::MessageLoopProxy>& loop) { - message_loop_ = loop; + void set_task_runner( + scoped_refptr<base::SingleThreadTaskRunner> task_runner) { + task_runner_ = task_runner.Pass(); } // Must be called before the PlatformDelegate is deleted. @@ -76,7 +77,7 @@ class BASE_EXPORT FilePathWatcher { } private: - scoped_refptr<base::MessageLoopProxy> message_loop_; + scoped_refptr<base::SingleThreadTaskRunner> task_runner_; bool cancelled_; }; diff --git a/chromium/base/files/file_path_watcher_fsevents.cc b/chromium/base/files/file_path_watcher_fsevents.cc index f240e33dd6a..da01c431bfe 100644 --- a/chromium/base/files/file_path_watcher_fsevents.cc +++ b/chromium/base/files/file_path_watcher_fsevents.cc @@ -13,6 +13,7 @@ #include "base/mac/libdispatch_task_runner.h" #include "base/mac/scoped_cftyperef.h" #include "base/message_loop/message_loop.h" +#include "base/thread_task_runner_handle.h" namespace base { @@ -75,11 +76,51 @@ FilePath ResolvePath(const FilePath& path) { return result; } -// The callback passed to FSEventStreamCreate(). -void FSEventsCallback(ConstFSEventStreamRef stream, - void* event_watcher, size_t num_events, - void* event_paths, const FSEventStreamEventFlags flags[], - const FSEventStreamEventId event_ids[]) { +} // namespace + +FilePathWatcherFSEvents::FilePathWatcherFSEvents() : fsevent_stream_(NULL) { +} + +bool FilePathWatcherFSEvents::Watch(const FilePath& path, + bool recursive, + const FilePathWatcher::Callback& callback) { + DCHECK(MessageLoopForIO::current()); + DCHECK(!callback.is_null()); + DCHECK(callback_.is_null()); + + // This class could support non-recursive watches, but that is currently + // left to FilePathWatcherKQueue. + if (!recursive) + return false; + + set_task_runner(ThreadTaskRunnerHandle::Get()); + callback_ = callback; + + FSEventStreamEventId start_event = FSEventsGetCurrentEventId(); + g_task_runner.Get().PostTask( + FROM_HERE, Bind(&FilePathWatcherFSEvents::StartEventStream, this, + start_event, path)); + return true; +} + +void FilePathWatcherFSEvents::Cancel() { + set_cancelled(); + callback_.Reset(); + + // Switch to the dispatch queue thread to tear down the event stream. + g_task_runner.Get().PostTask( + FROM_HERE, + Bind(&FilePathWatcherFSEvents::CancelOnMessageLoopThread, this)); +} + +// static +void FilePathWatcherFSEvents::FSEventsCallback( + ConstFSEventStreamRef stream, + void* event_watcher, + size_t num_events, + void* event_paths, + const FSEventStreamEventFlags flags[], + const FSEventStreamEventId event_ids[]) { FilePathWatcherFSEvents* watcher = reinterpret_cast<FilePathWatcherFSEvents*>(event_watcher); DCHECK(g_task_runner.Get().RunsTasksOnCurrentThread()); @@ -111,83 +152,51 @@ void FSEventsCallback(ConstFSEventStreamRef stream, watcher->OnFilePathsChanged(paths); } -} // namespace - -FilePathWatcherFSEvents::FilePathWatcherFSEvents() : fsevent_stream_(NULL) { +FilePathWatcherFSEvents::~FilePathWatcherFSEvents() { + // This method may be called on either the libdispatch or task_runner() + // thread. Checking callback_ on the libdispatch thread here is safe because + // it is executing in a task posted by Cancel() which first reset callback_. + // PostTask forms a sufficient memory barrier to ensure that the value is + // consistent on the target thread. + DCHECK(callback_.is_null()) + << "Cancel() must be called before FilePathWatcher is destroyed."; } void FilePathWatcherFSEvents::OnFilePathsChanged( const std::vector<FilePath>& paths) { - if (!message_loop()->BelongsToCurrentThread()) { - message_loop()->PostTask( - FROM_HERE, - Bind(&FilePathWatcherFSEvents::OnFilePathsChanged, this, paths)); - return; - } - - DCHECK(message_loop()->BelongsToCurrentThread()); - if (resolved_target_.empty()) - return; - - for (size_t i = 0; i < paths.size(); i++) { - if (resolved_target_.IsParent(paths[i]) || resolved_target_ == paths[i]) { - callback_.Run(target_, false); - return; - } - } + DCHECK(g_task_runner.Get().RunsTasksOnCurrentThread()); + DCHECK(!resolved_target_.empty()); + task_runner()->PostTask( + FROM_HERE, Bind(&FilePathWatcherFSEvents::DispatchEvents, this, paths, + target_, resolved_target_)); } -bool FilePathWatcherFSEvents::Watch(const FilePath& path, - bool recursive, - const FilePathWatcher::Callback& callback) { - DCHECK(resolved_target_.empty()); - DCHECK(MessageLoopForIO::current()); - DCHECK(!callback.is_null()); +void FilePathWatcherFSEvents::DispatchEvents(const std::vector<FilePath>& paths, + const FilePath& target, + const FilePath& resolved_target) { + DCHECK(task_runner()->RunsTasksOnCurrentThread()); - // This class could support non-recursive watches, but that is currently - // left to FilePathWatcherKQueue. - if (!recursive) - return false; - - set_message_loop(MessageLoopProxy::current()); - callback_ = callback; - target_ = path; - - FSEventStreamEventId start_event = FSEventsGetCurrentEventId(); - g_task_runner.Get().PostTask( - FROM_HERE, - Bind(&FilePathWatcherFSEvents::StartEventStream, this, start_event)); - return true; -} - -void FilePathWatcherFSEvents::Cancel() { - if (callback_.is_null()) { - // Watch was never called, so exit. - set_cancelled(); + // Don't issue callbacks after Cancel() has been called. + if (is_cancelled() || callback_.is_null()) { return; } - // Switch to the dispatch queue thread if necessary, so we can tear down - // the event stream. - if (!g_task_runner.Get().RunsTasksOnCurrentThread()) { - g_task_runner.Get().PostTask( - FROM_HERE, - Bind(&FilePathWatcherFSEvents::CancelOnMessageLoopThread, this)); - } else { - CancelOnMessageLoopThread(); + for (const FilePath& path : paths) { + if (resolved_target.IsParent(path) || resolved_target == path) { + callback_.Run(target, false); + return; + } } } void FilePathWatcherFSEvents::CancelOnMessageLoopThread() { // For all other implementations, the "message loop thread" is the IO thread, - // as returned by message_loop(). This implementation, however, needs to - // cancel pending work on the Dipatch Queue thread. + // as returned by task_runner(). This implementation, however, needs to + // cancel pending work on the Dispatch Queue thread. DCHECK(g_task_runner.Get().RunsTasksOnCurrentThread()); - set_cancelled(); if (fsevent_stream_) { DestroyEventStream(); - callback_.Reset(); target_.clear(); resolved_target_.clear(); } @@ -199,7 +208,7 @@ void FilePathWatcherFSEvents::UpdateEventStream( // It can happen that the watcher gets canceled while tasks that call this // function are still in flight, so abort if this situation is detected. - if (is_cancelled() || resolved_target_.empty()) + if (resolved_target_.empty()) return; if (fsevent_stream_) @@ -230,8 +239,10 @@ void FilePathWatcherFSEvents::UpdateEventStream( FSEventStreamSetDispatchQueue(fsevent_stream_, g_task_runner.Get().GetDispatchQueue()); - if (!FSEventStreamStart(fsevent_stream_)) - message_loop()->PostTask(FROM_HERE, Bind(callback_, target_, true)); + if (!FSEventStreamStart(fsevent_stream_)) { + task_runner()->PostTask( + FROM_HERE, Bind(&FilePathWatcherFSEvents::ReportError, this, target_)); + } } bool FilePathWatcherFSEvents::ResolveTargetPath() { @@ -239,11 +250,20 @@ bool FilePathWatcherFSEvents::ResolveTargetPath() { FilePath resolved = ResolvePath(target_).StripTrailingSeparators(); bool changed = resolved != resolved_target_; resolved_target_ = resolved; - if (resolved_target_.empty()) - message_loop()->PostTask(FROM_HERE, Bind(callback_, target_, true)); + if (resolved_target_.empty()) { + task_runner()->PostTask( + FROM_HERE, Bind(&FilePathWatcherFSEvents::ReportError, this, target_)); + } return changed; } +void FilePathWatcherFSEvents::ReportError(const FilePath& target) { + DCHECK(task_runner()->RunsTasksOnCurrentThread()); + if (!callback_.is_null()) { + callback_.Run(target, true); + } +} + void FilePathWatcherFSEvents::DestroyEventStream() { FSEventStreamStop(fsevent_stream_); FSEventStreamInvalidate(fsevent_stream_); @@ -251,13 +271,14 @@ void FilePathWatcherFSEvents::DestroyEventStream() { fsevent_stream_ = NULL; } -void FilePathWatcherFSEvents::StartEventStream( - FSEventStreamEventId start_event) { +void FilePathWatcherFSEvents::StartEventStream(FSEventStreamEventId start_event, + const FilePath& path) { DCHECK(g_task_runner.Get().RunsTasksOnCurrentThread()); + DCHECK(resolved_target_.empty()); + + target_ = path; ResolveTargetPath(); UpdateEventStream(start_event); } -FilePathWatcherFSEvents::~FilePathWatcherFSEvents() {} - } // namespace base diff --git a/chromium/base/files/file_path_watcher_fsevents.h b/chromium/base/files/file_path_watcher_fsevents.h index 800c5b43f68..300aa761dfa 100644 --- a/chromium/base/files/file_path_watcher_fsevents.h +++ b/chromium/base/files/file_path_watcher_fsevents.h @@ -24,9 +24,35 @@ class FilePathWatcherFSEvents : public FilePathWatcher::PlatformDelegate { public: FilePathWatcherFSEvents(); - // Called from the FSEvents callback whenever there is a change to the paths. + // FilePathWatcher::PlatformDelegate overrides. + bool Watch(const FilePath& path, + bool recursive, + const FilePathWatcher::Callback& callback) override; + void Cancel() override; + + private: + static void FSEventsCallback(ConstFSEventStreamRef stream, + void* event_watcher, + size_t num_events, + void* event_paths, + const FSEventStreamEventFlags flags[], + const FSEventStreamEventId event_ids[]); + + ~FilePathWatcherFSEvents() override; + + // Called from FSEventsCallback whenever there is a change to the paths. void OnFilePathsChanged(const std::vector<FilePath>& paths); + // Called on the message_loop() thread to dispatch path events. Can't access + // target_ and resolved_target_ directly as those are modified on the + // libdispatch thread. + void DispatchEvents(const std::vector<FilePath>& paths, + const FilePath& target, + const FilePath& resolved_target); + + // Cleans up and stops the event stream. + void CancelOnMessageLoopThread() override; + // (Re-)Initialize the event stream to start reporting events from // |start_event|. void UpdateEventStream(FSEventStreamEventId start_event); @@ -35,34 +61,29 @@ class FilePathWatcherFSEvents : public FilePathWatcher::PlatformDelegate { // last time it was done. bool ResolveTargetPath(); - // FilePathWatcher::PlatformDelegate overrides. - bool Watch(const FilePath& path, - bool recursive, - const FilePathWatcher::Callback& callback) override; - void Cancel() override; - - private: - ~FilePathWatcherFSEvents() override; + // Report an error watching the given target. + void ReportError(const FilePath& target); // Destroy the event stream. void DestroyEventStream(); // Start watching the FSEventStream. - void StartEventStream(FSEventStreamEventId start_event); - - // Cleans up and stops the event stream. - void CancelOnMessageLoopThread() override; + void StartEventStream(FSEventStreamEventId start_event, const FilePath& path); // Callback to notify upon changes. + // (Only accessed from the message_loop() thread.) FilePathWatcher::Callback callback_; // Target path to watch (passed to callback). + // (Only accessed from the libdispatch thread.) FilePath target_; // Target path with all symbolic links resolved. + // (Only accessed from the libdispatch thread.) FilePath resolved_target_; // Backend stream we receive event callbacks from (strong reference). + // (Only accessed from the libdispatch thread.) FSEventStreamRef fsevent_stream_; DISALLOW_COPY_AND_ASSIGN(FilePathWatcherFSEvents); diff --git a/chromium/base/files/file_path_watcher_kqueue.cc b/chromium/base/files/file_path_watcher_kqueue.cc index 8941d2e419f..e15cba7d341 100644 --- a/chromium/base/files/file_path_watcher_kqueue.cc +++ b/chromium/base/files/file_path_watcher_kqueue.cc @@ -11,6 +11,7 @@ #include "base/files/file_util.h" #include "base/logging.h" #include "base/strings/stringprintf.h" +#include "base/thread_task_runner_handle.h" // On some platforms these are not defined. #if !defined(EV_RECEIPT) @@ -327,7 +328,7 @@ bool FilePathWatcherKQueue::Watch(const FilePath& path, target_ = path; MessageLoop::current()->AddDestructionObserver(this); - io_message_loop_ = base::MessageLoopProxy::current(); + io_task_runner_ = ThreadTaskRunnerHandle::Get(); kqueue_ = kqueue(); if (kqueue_ == -1) { @@ -356,14 +357,14 @@ bool FilePathWatcherKQueue::Watch(const FilePath& path, } void FilePathWatcherKQueue::Cancel() { - base::MessageLoopProxy* proxy = io_message_loop_.get(); - if (!proxy) { + SingleThreadTaskRunner* task_runner = io_task_runner_.get(); + if (!task_runner) { set_cancelled(); return; } - if (!proxy->BelongsToCurrentThread()) { - proxy->PostTask(FROM_HERE, - base::Bind(&FilePathWatcherKQueue::Cancel, this)); + if (!task_runner->BelongsToCurrentThread()) { + task_runner->PostTask(FROM_HERE, + base::Bind(&FilePathWatcherKQueue::Cancel, this)); return; } CancelOnMessageLoopThread(); @@ -380,7 +381,7 @@ void FilePathWatcherKQueue::CancelOnMessageLoopThread() { kqueue_ = -1; std::for_each(events_.begin(), events_.end(), ReleaseEvent); events_.clear(); - io_message_loop_ = NULL; + io_task_runner_ = NULL; MessageLoop::current()->RemoveDestructionObserver(this); callback_.Reset(); } diff --git a/chromium/base/files/file_path_watcher_kqueue.h b/chromium/base/files/file_path_watcher_kqueue.h index 87af8913e55..69555a30032 100644 --- a/chromium/base/files/file_path_watcher_kqueue.h +++ b/chromium/base/files/file_path_watcher_kqueue.h @@ -11,7 +11,7 @@ #include "base/files/file_path.h" #include "base/files/file_path_watcher.h" #include "base/message_loop/message_loop.h" -#include "base/message_loop/message_loop_proxy.h" +#include "base/single_thread_task_runner.h" namespace base { @@ -59,7 +59,7 @@ class FilePathWatcherKQueue : public FilePathWatcher::PlatformDelegate, typedef std::vector<struct kevent> EventVector; - // Can only be called on |io_message_loop_|'s thread. + // Can only be called on |io_task_runner_|'s thread. void CancelOnMessageLoopThread() override; // Returns true if the kevent values are error free. @@ -118,7 +118,7 @@ class FilePathWatcherKQueue : public FilePathWatcher::PlatformDelegate, } EventVector events_; - scoped_refptr<base::MessageLoopProxy> io_message_loop_; + scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_; MessageLoopForIO::FileDescriptorWatcher kqueue_watcher_; FilePathWatcher::Callback callback_; FilePath target_; diff --git a/chromium/base/files/file_path_watcher_linux.cc b/chromium/base/files/file_path_watcher_linux.cc index 4078920aac9..ba2f1d96c84 100644 --- a/chromium/base/files/file_path_watcher_linux.cc +++ b/chromium/base/files/file_path_watcher_linux.cc @@ -19,7 +19,6 @@ #include "base/bind.h" #include "base/containers/hash_tables.h" -#include "base/debug/trace_event.h" #include "base/files/file_enumerator.h" #include "base/files/file_path.h" #include "base/files/file_util.h" @@ -27,11 +26,12 @@ #include "base/location.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" -#include "base/message_loop/message_loop.h" -#include "base/message_loop/message_loop_proxy.h" #include "base/posix/eintr_wrapper.h" +#include "base/single_thread_task_runner.h" #include "base/synchronization/lock.h" +#include "base/thread_task_runner_handle.h" #include "base/threading/thread.h" +#include "base/trace_event/trace_event.h" namespace base { @@ -125,10 +125,13 @@ class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate, // cleanup thread, in case it quits before Cancel() is called. void WillDestroyCurrentMessageLoop() override; - // Inotify watches are installed for all directory components of |target_|. A - // WatchEntry instance holds the watch descriptor for a component and the - // subdirectory for that identifies the next component. If a symbolic link - // is being watched, the target of the link is also kept. + // Inotify watches are installed for all directory components of |target_|. + // A WatchEntry instance holds: + // - |watch|: the watch descriptor for a component. + // - |subdir|: the subdirectory that identifies the next component. + // - For the last component, there is no next component, so it is empty. + // - |linkname|: the target of the symlink. + // - Only if the target being watched is a symbolic link. struct WatchEntry { explicit WatchEntry(const FilePath::StringType& dirname) : watch(InotifyReader::kInvalidWatch), @@ -197,7 +200,7 @@ void InotifyReaderCallback(InotifyReader* reader, int inotify_fd, CHECK_LE(0, shutdown_fd); CHECK_GT(FD_SETSIZE, shutdown_fd); - debug::TraceLog::GetInstance()->SetCurrentThreadBlocksMessageLoop(); + trace_event::TraceLog::GetInstance()->SetCurrentThreadBlocksMessageLoop(); while (true) { fd_set rfds; @@ -261,7 +264,7 @@ InotifyReader::InotifyReader() shutdown_pipe_[0] = -1; shutdown_pipe_[1] = -1; if (inotify_fd_ >= 0 && pipe(shutdown_pipe_) == 0 && thread_.Start()) { - thread_.message_loop()->PostTask( + thread_.task_runner()->PostTask( FROM_HERE, Bind(&InotifyReaderCallback, this, inotify_fd_, shutdown_pipe_[0])); valid_ = true; @@ -346,12 +349,11 @@ void FilePathWatcherImpl::OnFilePathChanged(InotifyReader::Watch fired_watch, bool created, bool deleted, bool is_dir) { - if (!message_loop()->BelongsToCurrentThread()) { - // Switch to message_loop() to access |watches_| safely. - message_loop()->PostTask( - FROM_HERE, - Bind(&FilePathWatcherImpl::OnFilePathChanged, this, - fired_watch, child, created, deleted, is_dir)); + if (!task_runner()->BelongsToCurrentThread()) { + // Switch to task_runner() to access |watches_| safely. + task_runner()->PostTask(FROM_HERE, + Bind(&FilePathWatcherImpl::OnFilePathChanged, this, + fired_watch, child, created, deleted, is_dir)); return; } @@ -381,11 +383,31 @@ void FilePathWatcherImpl::OnFilePathChanged(InotifyReader::Watch fired_watch, (child == watch_entry.subdir); // Check if the change references |target_| or a direct child of |target_|. - bool is_watch_for_target = watch_entry.subdir.empty(); - bool target_changed = - (is_watch_for_target && (child == watch_entry.linkname)) || - (is_watch_for_target && watch_entry.linkname.empty()) || - (watch_entry.subdir == child && watches_[i + 1].subdir.empty()); + bool target_changed; + if (watch_entry.subdir.empty()) { + // The fired watch is for a WatchEntry without a subdir. Thus for a given + // |target_| = "/path/to/foo", this is for "foo". Here, check either: + // - the target has no symlink: it is the target and it changed. + // - the target has a symlink, and it matches |child|. + target_changed = (watch_entry.linkname.empty() || + child == watch_entry.linkname); + } else { + // The fired watch is for a WatchEntry with a subdir. Thus for a given + // |target_| = "/path/to/foo", this is for {"/", "/path", "/path/to"}. + // So we can safely access the next WatchEntry since we have not reached + // the end yet. Check |watch_entry| is for "/path/to", i.e. the next + // element is "foo". + bool next_watch_may_be_for_target = watches_[i + 1].subdir.empty(); + if (next_watch_may_be_for_target) { + // The current |watch_entry| is for "/path/to", so check if the |child| + // that changed is "foo". + target_changed = watch_entry.subdir == child; + } else { + // The current |watch_entry| is not for "/path/to", so the next entry + // cannot be "foo". Thus |target_| has not changed. + target_changed = false; + } + } // Update watches if a directory component of the |target_| path // (dis)appears. Note that we don't add the additional restriction of @@ -429,7 +451,7 @@ bool FilePathWatcherImpl::Watch(const FilePath& path, DCHECK(target_.empty()); DCHECK(MessageLoopForIO::current()); - set_message_loop(MessageLoopProxy::current()); + set_task_runner(ThreadTaskRunnerHandle::Get()); callback_ = callback; target_ = path; recursive_ = recursive; @@ -453,17 +475,16 @@ void FilePathWatcherImpl::Cancel() { } // Switch to the message_loop() if necessary so we can access |watches_|. - if (!message_loop()->BelongsToCurrentThread()) { - message_loop()->PostTask(FROM_HERE, - Bind(&FilePathWatcher::CancelWatch, - make_scoped_refptr(this))); + if (!task_runner()->BelongsToCurrentThread()) { + task_runner()->PostTask(FROM_HERE, Bind(&FilePathWatcher::CancelWatch, + make_scoped_refptr(this))); } else { CancelOnMessageLoopThread(); } } void FilePathWatcherImpl::CancelOnMessageLoopThread() { - DCHECK(message_loop()->BelongsToCurrentThread()); + DCHECK(task_runner()->BelongsToCurrentThread()); set_cancelled(); if (!callback_.is_null()) { @@ -487,7 +508,7 @@ void FilePathWatcherImpl::WillDestroyCurrentMessageLoop() { void FilePathWatcherImpl::UpdateWatches() { // Ensure this runs on the message_loop() exclusively in order to avoid // concurrency issues. - DCHECK(message_loop()->BelongsToCurrentThread()); + DCHECK(task_runner()->BelongsToCurrentThread()); DCHECK(HasValidWatchVector()); // Walk the list of watches and update them as we go. diff --git a/chromium/base/files/file_path_watcher_stub.cc b/chromium/base/files/file_path_watcher_stub.cc index d7ad2066aab..8138692ede1 100644 --- a/chromium/base/files/file_path_watcher_stub.cc +++ b/chromium/base/files/file_path_watcher_stub.cc @@ -13,18 +13,18 @@ namespace { class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate { public: - virtual bool Watch(const FilePath& path, - bool recursive, - const FilePathWatcher::Callback& callback) override { + bool Watch(const FilePath& path, + bool recursive, + const FilePathWatcher::Callback& callback) override { return false; } - virtual void Cancel() override {} + void Cancel() override {} - virtual void CancelOnMessageLoopThread() override {} + void CancelOnMessageLoopThread() override {} protected: - virtual ~FilePathWatcherImpl() {} + ~FilePathWatcherImpl() override {} }; } // namespace diff --git a/chromium/base/files/file_path_watcher_browsertest.cc b/chromium/base/files/file_path_watcher_unittest.cc index 0e19b2ed9ae..21e9dd137e2 100644 --- a/chromium/base/files/file_path_watcher_browsertest.cc +++ b/chromium/base/files/file_path_watcher_unittest.cc @@ -20,17 +20,22 @@ #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" -#include "base/message_loop/message_loop.h" -#include "base/message_loop/message_loop_proxy.h" +#include "base/location.h" #include "base/run_loop.h" +#include "base/single_thread_task_runner.h" #include "base/stl_util.h" #include "base/strings/stringprintf.h" #include "base/synchronization/waitable_event.h" #include "base/test/test_file_util.h" #include "base/test/test_timeouts.h" +#include "base/thread_task_runner_handle.h" #include "base/threading/thread.h" #include "testing/gtest/include/gtest/gtest.h" +#if defined(OS_ANDROID) +#include "base/android/path_utils.h" +#endif // defined(OS_ANDROID) + namespace base { namespace { @@ -42,14 +47,13 @@ class TestDelegate; class NotificationCollector : public base::RefCountedThreadSafe<NotificationCollector> { public: - NotificationCollector() - : loop_(base::MessageLoopProxy::current()) {} + NotificationCollector() : task_runner_(base::ThreadTaskRunnerHandle::Get()) {} // Called from the file thread by the delegates. void OnChange(TestDelegate* delegate) { - loop_->PostTask(FROM_HERE, - base::Bind(&NotificationCollector::RecordChange, this, - base::Unretained(delegate))); + task_runner_->PostTask( + FROM_HERE, base::Bind(&NotificationCollector::RecordChange, this, + base::Unretained(delegate))); } void Register(TestDelegate* delegate) { @@ -70,13 +74,13 @@ class NotificationCollector void RecordChange(TestDelegate* delegate) { // Warning: |delegate| is Unretained. Do not dereference. - ASSERT_TRUE(loop_->BelongsToCurrentThread()); + ASSERT_TRUE(task_runner_->BelongsToCurrentThread()); ASSERT_TRUE(delegates_.count(delegate)); signaled_.insert(delegate); // Check whether all delegates have been signaled. if (signaled_ == delegates_) - loop_->PostTask(FROM_HERE, MessageLoop::QuitWhenIdleClosure()); + task_runner_->PostTask(FROM_HERE, MessageLoop::QuitWhenIdleClosure()); } // Set of registered delegates. @@ -86,7 +90,7 @@ class NotificationCollector std::set<TestDelegate*> signaled_; // The loop we should break after all delegates signaled. - scoped_refptr<base::MessageLoopProxy> loop_; + scoped_refptr<base::SingleThreadTaskRunner> task_runner_; }; class TestDelegateBase : public SupportsWeakPtr<TestDelegateBase> { @@ -143,23 +147,31 @@ class FilePathWatcherTest : public testing::Test { FilePathWatcherTest() : file_thread_("FilePathWatcherTest") {} - virtual ~FilePathWatcherTest() {} + ~FilePathWatcherTest() override {} protected: - virtual void SetUp() override { + void SetUp() override { // Create a separate file thread in order to test proper thread usage. base::Thread::Options options(MessageLoop::TYPE_IO, 0); ASSERT_TRUE(file_thread_.StartWithOptions(options)); +#if defined(OS_ANDROID) + // Watching files is only permitted when all parent directories are + // accessible, which is not the case for the default temp directory + // on Android which is under /data/data. Use /sdcard instead. + // TODO(pauljensen): Remove this when crbug.com/475568 is fixed. + FilePath parent_dir; + ASSERT_TRUE(android::GetExternalStorageDirectory(&parent_dir)); + ASSERT_TRUE(temp_dir_.CreateUniqueTempDirUnderPath(parent_dir)); +#else // defined(OS_ANDROID) ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); +#endif // defined(OS_ANDROID) collector_ = new NotificationCollector(); } - virtual void TearDown() override { - RunLoop().RunUntilIdle(); - } + void TearDown() override { RunLoop().RunUntilIdle(); } void DeleteDelegateOnFileThread(TestDelegate* delegate) { - file_thread_.message_loop_proxy()->DeleteSoon(FROM_HERE, delegate); + file_thread_.task_runner()->DeleteSoon(FROM_HERE, delegate); } FilePath test_file() { @@ -194,6 +206,7 @@ class FilePathWatcherTest : public testing::Test { ScopedTempDir temp_dir_; scoped_refptr<NotificationCollector> collector_; + private: DISALLOW_COPY_AND_ASSIGN(FilePathWatcherTest); }; @@ -203,10 +216,9 @@ bool FilePathWatcherTest::SetupWatch(const FilePath& target, bool recursive_watch) { base::WaitableEvent completion(false, false); bool result; - file_thread_.message_loop_proxy()->PostTask( - FROM_HERE, - base::Bind(SetupWatchCallback, target, watcher, delegate, recursive_watch, - &result, &completion)); + file_thread_.task_runner()->PostTask( + FROM_HERE, base::Bind(SetupWatchCallback, target, watcher, delegate, + recursive_watch, &result, &completion)); completion.Wait(); return result; } @@ -276,7 +288,8 @@ class Deleter : public TestDelegateBase { void OnFileChanged(const FilePath&, bool) override { watcher_.reset(); - loop_->PostTask(FROM_HERE, MessageLoop::QuitWhenIdleClosure()); + loop_->task_runner()->PostTask(FROM_HERE, + MessageLoop::QuitWhenIdleClosure()); } FilePathWatcher* watcher() const { return watcher_.get(); } @@ -311,7 +324,7 @@ TEST_F(FilePathWatcherTest, DISABLED_DestroyWithPendingNotification) { FilePathWatcher* watcher = new FilePathWatcher; ASSERT_TRUE(SetupWatch(test_file(), watcher, delegate.get(), false)); ASSERT_TRUE(WriteFile(test_file(), "content")); - file_thread_.message_loop_proxy()->DeleteSoon(FROM_HERE, watcher); + file_thread_.task_runner()->DeleteSoon(FROM_HERE, watcher); DeleteDelegateOnFileThread(delegate.release()); } @@ -527,9 +540,16 @@ TEST_F(FilePathWatcherTest, RecursiveWatch) { ASSERT_TRUE(WriteFile(child_dir_file1, "content")); ASSERT_TRUE(WaitForEvents()); +// Apps cannot change file attributes on Android in /sdcard as /sdcard uses the +// "fuse" file system, while /data uses "ext4". Running these tests in /data +// would be preferable and allow testing file attributes and symlinks. +// TODO(pauljensen): Re-enable when crbug.com/475568 is fixed and SetUp() places +// the |temp_dir_| in /data. +#if !defined(OS_ANDROID) // Modify "$dir/subdir/subdir_child_dir/child_dir_file1" attributes. ASSERT_TRUE(base::MakeFileUnreadable(child_dir_file1)); ASSERT_TRUE(WaitForEvents()); +#endif // Delete "$dir/subdir/subdir_file1". ASSERT_TRUE(base::DeleteFile(subdir_file1, false)); @@ -542,6 +562,14 @@ TEST_F(FilePathWatcherTest, RecursiveWatch) { } #if defined(OS_POSIX) +#if defined(OS_ANDROID) +// Apps cannot create symlinks on Android in /sdcard as /sdcard uses the +// "fuse" file system, while /data uses "ext4". Running these tests in /data +// would be preferable and allow testing file attributes and symlinks. +// TODO(pauljensen): Re-enable when crbug.com/475568 is fixed and SetUp() places +// the |temp_dir_| in /data. +#define RecursiveWithSymLink DISABLED_RecursiveWithSymLink +#endif // defined(OS_ANDROID) TEST_F(FilePathWatcherTest, RecursiveWithSymLink) { if (!FilePathWatcher::RecursiveWatchAvailable()) return; @@ -611,6 +639,14 @@ TEST_F(FilePathWatcherTest, MoveChild) { } // Verify that changing attributes on a file is caught +#if defined(OS_ANDROID) +// Apps cannot change file attributes on Android in /sdcard as /sdcard uses the +// "fuse" file system, while /data uses "ext4". Running these tests in /data +// would be preferable and allow testing file attributes and symlinks. +// TODO(pauljensen): Re-enable when crbug.com/475568 is fixed and SetUp() places +// the |temp_dir_| in /data. +#define FileAttributesChanged DISABLED_FileAttributesChanged +#endif // defined(OS_ANDROID TEST_F(FilePathWatcherTest, FileAttributesChanged) { ASSERT_TRUE(WriteFile(test_file(), "content")); FilePathWatcher watcher; diff --git a/chromium/base/files/file_path_watcher_win.cc b/chromium/base/files/file_path_watcher_win.cc index 73f9cfb332a..081698f8df1 100644 --- a/chromium/base/files/file_path_watcher_win.cc +++ b/chromium/base/files/file_path_watcher_win.cc @@ -10,8 +10,7 @@ #include "base/files/file_util.h" #include "base/logging.h" #include "base/memory/ref_counted.h" -#include "base/message_loop/message_loop_proxy.h" -#include "base/profiler/scoped_tracker.h" +#include "base/thread_task_runner_handle.h" #include "base/time/time.h" #include "base/win/object_watcher.h" @@ -28,21 +27,21 @@ class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate, recursive_watch_(false) {} // FilePathWatcher::PlatformDelegate overrides. - virtual bool Watch(const FilePath& path, - bool recursive, - const FilePathWatcher::Callback& callback) override; - virtual void Cancel() override; + bool Watch(const FilePath& path, + bool recursive, + const FilePathWatcher::Callback& callback) override; + void Cancel() override; // Deletion of the FilePathWatcher will call Cancel() to dispose of this // object in the right thread. This also observes destruction of the required // cleanup thread, in case it quits before Cancel() is called. - virtual void WillDestroyCurrentMessageLoop() override; + void WillDestroyCurrentMessageLoop() override; // Callback from MessageLoopForIO. - virtual void OnObjectSignaled(HANDLE object) override; + void OnObjectSignaled(HANDLE object) override; private: - virtual ~FilePathWatcherImpl() {} + ~FilePathWatcherImpl() override {} // Setup a watch handle for directory |dir|. Set |recursive| to true to watch // the directory sub trees. Returns true if no fatal error occurs. |handle| @@ -58,7 +57,7 @@ class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate, // Destroy the watch handle. void DestroyWatch(); - // Cleans up and stops observing the |message_loop_| thread. + // Cleans up and stops observing the |task_runner_| thread. void CancelOnMessageLoopThread() override; // Callback to notify upon changes. @@ -92,7 +91,7 @@ bool FilePathWatcherImpl::Watch(const FilePath& path, const FilePathWatcher::Callback& callback) { DCHECK(target_.value().empty()); // Can only watch one path. - set_message_loop(MessageLoopProxy::current()); + set_task_runner(ThreadTaskRunnerHandle::Get()); callback_ = callback; target_ = path; recursive_watch_ = recursive; @@ -114,23 +113,22 @@ bool FilePathWatcherImpl::Watch(const FilePath& path, void FilePathWatcherImpl::Cancel() { if (callback_.is_null()) { - // Watch was never called, or the |message_loop_| has already quit. + // Watch was never called, or the |task_runner_| has already quit. set_cancelled(); return; } // Switch to the file thread if necessary so we can stop |watcher_|. - if (!message_loop()->BelongsToCurrentThread()) { - message_loop()->PostTask(FROM_HERE, - Bind(&FilePathWatcher::CancelWatch, - make_scoped_refptr(this))); + if (!task_runner()->BelongsToCurrentThread()) { + task_runner()->PostTask(FROM_HERE, Bind(&FilePathWatcher::CancelWatch, + make_scoped_refptr(this))); } else { CancelOnMessageLoopThread(); } } void FilePathWatcherImpl::CancelOnMessageLoopThread() { - DCHECK(message_loop()->BelongsToCurrentThread()); + DCHECK(task_runner()->BelongsToCurrentThread()); set_cancelled(); if (handle_ != INVALID_HANDLE_VALUE) @@ -147,10 +145,6 @@ void FilePathWatcherImpl::WillDestroyCurrentMessageLoop() { } void FilePathWatcherImpl::OnObjectSignaled(HANDLE object) { - // TODO(vadimt): Remove ScopedTracker below once crbug.com/418183 is fixed. - tracked_objects::ScopedTracker tracking_profile( - FROM_HERE_WITH_EXPLICIT_FUNCTION("FilePathWatcherImpl_OnObjectSignaled")); - DCHECK(object == handle_); // Make sure we stay alive through the body of this function. scoped_refptr<FilePathWatcherImpl> keep_alive(this); diff --git a/chromium/base/files/file_posix.cc b/chromium/base/files/file_posix.cc index 3d229e4155e..bb49d2dd730 100644 --- a/chromium/base/files/file_posix.cc +++ b/chromium/base/files/file_posix.cc @@ -9,7 +9,6 @@ #include <sys/stat.h> #include <unistd.h> -#include "base/files/file_path.h" #include "base/logging.h" #include "base/metrics/sparse_histogram.h" #include "base/posix/eintr_wrapper.h" @@ -31,12 +30,12 @@ namespace { #if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) static int CallFstat(int fd, stat_wrapper_t *sb) { - base::ThreadRestrictions::AssertIOAllowed(); + ThreadRestrictions::AssertIOAllowed(); return fstat(fd, sb); } #else static int CallFstat(int fd, stat_wrapper_t *sb) { - base::ThreadRestrictions::AssertIOAllowed(); + ThreadRestrictions::AssertIOAllowed(); return fstat64(fd, sb); } #endif @@ -52,10 +51,6 @@ static int CallFtruncate(PlatformFile file, int64 length) { return HANDLE_EINTR(ftruncate(file, length)); } -static int CallFsync(PlatformFile file) { - return HANDLE_EINTR(fsync(file)); -} - static int CallFutimes(PlatformFile file, const struct timeval times[2]) { #ifdef __USE_XOPEN2K8 // futimens should be available, but futimes might not be @@ -97,11 +92,6 @@ static int CallFtruncate(PlatformFile file, int64 length) { return 0; } -static int CallFsync(PlatformFile file) { - NOTIMPLEMENTED(); // NaCl doesn't implement fsync. - return 0; -} - static int CallFutimes(PlatformFile file, const struct timeval times[2]) { NOTIMPLEMENTED(); // NaCl doesn't implement futimes. return 0; @@ -166,95 +156,6 @@ void File::Info::FromStat(const stat_wrapper_t& stat_info) { Time::kNanosecondsPerMicrosecond); } -// 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::InitializeUnsafe(const FilePath& name, uint32 flags) { - base::ThreadRestrictions::AssertIOAllowed(); - DCHECK(!IsValid()); - - int open_flags = 0; - if (flags & FLAG_CREATE) - open_flags = O_CREAT | O_EXCL; - - created_ = false; - - if (flags & FLAG_CREATE_ALWAYS) { - DCHECK(!open_flags); - DCHECK(flags & FLAG_WRITE); - open_flags = O_CREAT | O_TRUNC; - } - - if (flags & FLAG_OPEN_TRUNCATED) { - DCHECK(!open_flags); - DCHECK(flags & FLAG_WRITE); - open_flags = O_TRUNC; - } - - if (!open_flags && !(flags & FLAG_OPEN) && !(flags & FLAG_OPEN_ALWAYS)) { - NOTREACHED(); - errno = EOPNOTSUPP; - error_details_ = FILE_ERROR_FAILED; - return; - } - - if (flags & FLAG_WRITE && flags & FLAG_READ) { - open_flags |= O_RDWR; - } else if (flags & FLAG_WRITE) { - open_flags |= O_WRONLY; - } else if (!(flags & FLAG_READ) && - !(flags & FLAG_WRITE_ATTRIBUTES) && - !(flags & FLAG_APPEND) && - !(flags & FLAG_OPEN_ALWAYS)) { - NOTREACHED(); - } - - if (flags & FLAG_TERMINAL_DEVICE) - open_flags |= O_NOCTTY | O_NDELAY; - - if (flags & FLAG_APPEND && flags & FLAG_READ) - open_flags |= O_APPEND | O_RDWR; - else if (flags & FLAG_APPEND) - open_flags |= O_APPEND | O_WRONLY; - - COMPILE_ASSERT(O_RDONLY == 0, O_RDONLY_must_equal_zero); - - int mode = S_IRUSR | S_IWUSR; -#if defined(OS_CHROMEOS) - mode |= S_IRGRP | S_IROTH; -#endif - - int descriptor = HANDLE_EINTR(open(name.value().c_str(), open_flags, mode)); - - if (flags & FLAG_OPEN_ALWAYS) { - if (descriptor < 0) { - open_flags |= O_CREAT; - if (flags & FLAG_EXCLUSIVE_READ || flags & FLAG_EXCLUSIVE_WRITE) - open_flags |= O_EXCL; // together with O_CREAT implies O_NOFOLLOW - - descriptor = HANDLE_EINTR(open(name.value().c_str(), open_flags, mode)); - if (descriptor >= 0) - created_ = true; - } - } - - if (descriptor < 0) { - error_details_ = File::OSErrorToFileError(errno); - return; - } - - if (flags & (FLAG_CREATE_ALWAYS | FLAG_CREATE)) - created_ = true; - - if (flags & FLAG_DELETE_ON_CLOSE) - unlink(name.value().c_str()); - - async_ = ((flags & FLAG_ASYNC) == FLAG_ASYNC); - error_details_ = FILE_OK; - file_.reset(descriptor); -} -#endif // !defined(OS_NACL) - bool File::IsValid() const { return file_.is_valid(); } @@ -271,14 +172,17 @@ void File::Close() { if (!IsValid()) return; - base::ThreadRestrictions::AssertIOAllowed(); + SCOPED_FILE_TRACE("Close"); + ThreadRestrictions::AssertIOAllowed(); file_.reset(); } int64 File::Seek(Whence whence, int64 offset) { - base::ThreadRestrictions::AssertIOAllowed(); + ThreadRestrictions::AssertIOAllowed(); DCHECK(IsValid()); + SCOPED_FILE_TRACE_WITH_SIZE("Seek", offset); + #if defined(OS_ANDROID) COMPILE_ASSERT(sizeof(int64) == sizeof(off64_t), off64_t_64_bit); return lseek64(file_.get(), static_cast<off64_t>(offset), @@ -291,11 +195,13 @@ int64 File::Seek(Whence whence, int64 offset) { } int File::Read(int64 offset, char* data, int size) { - base::ThreadRestrictions::AssertIOAllowed(); + ThreadRestrictions::AssertIOAllowed(); DCHECK(IsValid()); if (size < 0) return -1; + SCOPED_FILE_TRACE_WITH_SIZE("Read", size); + int bytes_read = 0; int rv; do { @@ -311,11 +217,13 @@ int File::Read(int64 offset, char* data, int size) { } int File::ReadAtCurrentPos(char* data, int size) { - base::ThreadRestrictions::AssertIOAllowed(); + ThreadRestrictions::AssertIOAllowed(); DCHECK(IsValid()); if (size < 0) return -1; + SCOPED_FILE_TRACE_WITH_SIZE("ReadAtCurrentPos", size); + int bytes_read = 0; int rv; do { @@ -330,23 +238,24 @@ int File::ReadAtCurrentPos(char* data, int size) { } int File::ReadNoBestEffort(int64 offset, char* data, int size) { - base::ThreadRestrictions::AssertIOAllowed(); + ThreadRestrictions::AssertIOAllowed(); DCHECK(IsValid()); - + SCOPED_FILE_TRACE_WITH_SIZE("ReadNoBestEffort", size); return HANDLE_EINTR(pread(file_.get(), data, size, offset)); } int File::ReadAtCurrentPosNoBestEffort(char* data, int size) { - base::ThreadRestrictions::AssertIOAllowed(); + ThreadRestrictions::AssertIOAllowed(); DCHECK(IsValid()); if (size < 0) return -1; + SCOPED_FILE_TRACE_WITH_SIZE("ReadAtCurrentPosNoBestEffort", size); return HANDLE_EINTR(read(file_.get(), data, size)); } int File::Write(int64 offset, const char* data, int size) { - base::ThreadRestrictions::AssertIOAllowed(); + ThreadRestrictions::AssertIOAllowed(); if (IsOpenAppend(file_.get())) return WriteAtCurrentPos(data, size); @@ -355,6 +264,8 @@ int File::Write(int64 offset, const char* data, int size) { if (size < 0) return -1; + SCOPED_FILE_TRACE_WITH_SIZE("Write", size); + int bytes_written = 0; int rv; do { @@ -370,11 +281,13 @@ int File::Write(int64 offset, const char* data, int size) { } int File::WriteAtCurrentPos(const char* data, int size) { - base::ThreadRestrictions::AssertIOAllowed(); + ThreadRestrictions::AssertIOAllowed(); DCHECK(IsValid()); if (size < 0) return -1; + SCOPED_FILE_TRACE_WITH_SIZE("WriteAtCurrentPos", size); + int bytes_written = 0; int rv; do { @@ -390,17 +303,20 @@ int File::WriteAtCurrentPos(const char* data, int size) { } int File::WriteAtCurrentPosNoBestEffort(const char* data, int size) { - base::ThreadRestrictions::AssertIOAllowed(); + ThreadRestrictions::AssertIOAllowed(); DCHECK(IsValid()); if (size < 0) return -1; + SCOPED_FILE_TRACE_WITH_SIZE("WriteAtCurrentPosNoBestEffort", size); return HANDLE_EINTR(write(file_.get(), data, size)); } int64 File::GetLength() { DCHECK(IsValid()); + SCOPED_FILE_TRACE("GetLength"); + stat_wrapper_t file_info; if (CallFstat(file_.get(), &file_info)) return false; @@ -409,21 +325,19 @@ int64 File::GetLength() { } bool File::SetLength(int64 length) { - base::ThreadRestrictions::AssertIOAllowed(); + ThreadRestrictions::AssertIOAllowed(); DCHECK(IsValid()); - return !CallFtruncate(file_.get(), length); -} -bool File::Flush() { - base::ThreadRestrictions::AssertIOAllowed(); - DCHECK(IsValid()); - return !CallFsync(file_.get()); + SCOPED_FILE_TRACE_WITH_SIZE("SetLength", length); + return !CallFtruncate(file_.get(), length); } bool File::SetTimes(Time last_access_time, Time last_modified_time) { - base::ThreadRestrictions::AssertIOAllowed(); + ThreadRestrictions::AssertIOAllowed(); DCHECK(IsValid()); + SCOPED_FILE_TRACE("SetTimes"); + timeval times[2]; times[0] = last_access_time.ToTimeVal(); times[1] = last_modified_time.ToTimeVal(); @@ -434,6 +348,8 @@ bool File::SetTimes(Time last_access_time, Time last_modified_time) { bool File::GetInfo(Info* info) { DCHECK(IsValid()); + SCOPED_FILE_TRACE("GetInfo"); + stat_wrapper_t file_info; if (CallFstat(file_.get(), &file_info)) return false; @@ -443,13 +359,31 @@ bool File::GetInfo(Info* info) { } File::Error File::Lock() { + SCOPED_FILE_TRACE("Lock"); return CallFctnlFlock(file_.get(), true); } File::Error File::Unlock() { + SCOPED_FILE_TRACE("Unlock"); return CallFctnlFlock(file_.get(), false); } +File File::Duplicate() { + if (!IsValid()) + return File(); + + SCOPED_FILE_TRACE("Duplicate"); + + PlatformFile other_fd = dup(GetPlatformFile()); + if (other_fd == -1) + return File(OSErrorToFileError(errno)); + + File other(other_fd); + if (async()) + other.async_ = true; + return other.Pass(); +} + // Static. File::Error File::OSErrorToFileError(int saved_errno) { switch (saved_errno) { @@ -458,12 +392,15 @@ File::Error File::OSErrorToFileError(int saved_errno) { case EROFS: case EPERM: return FILE_ERROR_ACCESS_DENIED; + case EBUSY: #if !defined(OS_NACL) // ETXTBSY not defined by NaCl. case ETXTBSY: - return FILE_ERROR_IN_USE; #endif + return FILE_ERROR_IN_USE; case EEXIST: return FILE_ERROR_EXISTS; + case EIO: + return FILE_ERROR_IO; case ENOENT: return FILE_ERROR_NOT_FOUND; case EMFILE: @@ -526,6 +463,109 @@ void File::MemoryCheckingScopedFD::UpdateChecksum() { ComputeMemoryChecksum(&file_memory_checksum_); } +// 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) { + ThreadRestrictions::AssertIOAllowed(); + DCHECK(!IsValid()); + + int open_flags = 0; + if (flags & FLAG_CREATE) + open_flags = O_CREAT | O_EXCL; + + created_ = false; + + if (flags & FLAG_CREATE_ALWAYS) { + DCHECK(!open_flags); + DCHECK(flags & FLAG_WRITE); + open_flags = O_CREAT | O_TRUNC; + } + + if (flags & FLAG_OPEN_TRUNCATED) { + DCHECK(!open_flags); + DCHECK(flags & FLAG_WRITE); + open_flags = O_TRUNC; + } + + if (!open_flags && !(flags & FLAG_OPEN) && !(flags & FLAG_OPEN_ALWAYS)) { + NOTREACHED(); + errno = EOPNOTSUPP; + error_details_ = FILE_ERROR_FAILED; + return; + } + + if (flags & FLAG_WRITE && flags & FLAG_READ) { + open_flags |= O_RDWR; + } else if (flags & FLAG_WRITE) { + open_flags |= O_WRONLY; + } else if (!(flags & FLAG_READ) && + !(flags & FLAG_WRITE_ATTRIBUTES) && + !(flags & FLAG_APPEND) && + !(flags & FLAG_OPEN_ALWAYS)) { + NOTREACHED(); + } + + if (flags & FLAG_TERMINAL_DEVICE) + open_flags |= O_NOCTTY | O_NDELAY; + + if (flags & FLAG_APPEND && flags & FLAG_READ) + open_flags |= O_APPEND | O_RDWR; + else if (flags & FLAG_APPEND) + open_flags |= O_APPEND | O_WRONLY; + + COMPILE_ASSERT(O_RDONLY == 0, O_RDONLY_must_equal_zero); + + int mode = S_IRUSR | S_IWUSR; +#if defined(OS_CHROMEOS) + mode |= S_IRGRP | S_IROTH; +#endif + + int descriptor = HANDLE_EINTR(open(path_.value().c_str(), open_flags, mode)); + + if (flags & FLAG_OPEN_ALWAYS) { + if (descriptor < 0) { + open_flags |= O_CREAT; + 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)); + if (descriptor >= 0) + created_ = true; + } + } + + if (descriptor < 0) { + error_details_ = File::OSErrorToFileError(errno); + return; + } + + if (flags & (FLAG_CREATE_ALWAYS | FLAG_CREATE)) + created_ = true; + + if (flags & FLAG_DELETE_ON_CLOSE) + unlink(path_.value().c_str()); + + async_ = ((flags & FLAG_ASYNC) == FLAG_ASYNC); + error_details_ = FILE_OK; + file_.reset(descriptor); +} +#endif // !defined(OS_NACL) + +bool File::DoFlush() { + ThreadRestrictions::AssertIOAllowed(); + DCHECK(IsValid()); + +#if defined(OS_NACL) + NOTIMPLEMENTED(); // NaCl doesn't implement fsync. + return true; +#elif defined(OS_LINUX) || defined(OS_ANDROID) + return !HANDLE_EINTR(fdatasync(file_.get())); +#else + return !HANDLE_EINTR(fsync(file_.get())); +#endif +} + void File::SetPlatformFile(PlatformFile file) { DCHECK(!file_.is_valid()); file_.reset(file); diff --git a/chromium/base/files/file_proxy.cc b/chromium/base/files/file_proxy.cc index 53b14fef2fd..f995735d5bc 100644 --- a/chromium/base/files/file_proxy.cc +++ b/chromium/base/files/file_proxy.cc @@ -9,7 +9,6 @@ #include "base/files/file.h" #include "base/files/file_util.h" #include "base/location.h" -#include "base/message_loop/message_loop_proxy.h" #include "base/task_runner.h" #include "base/task_runner_util.h" diff --git a/chromium/base/files/file_proxy_unittest.cc b/chromium/base/files/file_proxy_unittest.cc index b5ed3950a05..df0bbc869c2 100644 --- a/chromium/base/files/file_proxy_unittest.cc +++ b/chromium/base/files/file_proxy_unittest.cc @@ -9,7 +9,6 @@ #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/memory/weak_ptr.h" -#include "base/message_loop/message_loop.h" #include "base/threading/thread.h" #include "base/threading/thread_restrictions.h" #include "testing/gtest/include/gtest/gtest.h" @@ -24,7 +23,7 @@ class FileProxyTest : public testing::Test { bytes_written_(-1), weak_factory_(this) {} - virtual void SetUp() override { + void SetUp() override { ASSERT_TRUE(dir_.CreateUniqueTempDir()); ASSERT_TRUE(file_thread_.Start()); } @@ -79,7 +78,7 @@ class FileProxyTest : public testing::Test { } TaskRunner* file_task_runner() const { - return file_thread_.message_loop_proxy().get(); + return file_thread_.task_runner().get(); } const FilePath& test_dir_path() const { return dir_.path(); } const FilePath test_path() const { return dir_.path().AppendASCII("test"); } diff --git a/chromium/base/files/file_tracing.cc b/chromium/base/files/file_tracing.cc new file mode 100644 index 00000000000..a1919c464bb --- /dev/null +++ b/chromium/base/files/file_tracing.cc @@ -0,0 +1,56 @@ +// 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/files/file_tracing.h" + +#include "base/files/file.h" + +namespace base { + +namespace { +FileTracing::Provider* g_provider = nullptr; +} + +// static +void FileTracing::SetProvider(FileTracing::Provider* provider) { + g_provider = provider; +} + +FileTracing::ScopedEnabler::ScopedEnabler() { + if (g_provider) + g_provider->FileTracingEnable(this); +} + +FileTracing::ScopedEnabler::~ScopedEnabler() { + if (g_provider) + g_provider->FileTracingDisable(this); +} + +FileTracing::ScopedTrace::ScopedTrace() : initialized_(false) {} + +FileTracing::ScopedTrace::~ScopedTrace() { + if (initialized_ && g_provider) { + g_provider->FileTracingEventEnd( + name_, &file_->trace_enabler_, file_->path_, size_); + } +} + +bool FileTracing::ScopedTrace::ShouldInitialize() const { + return g_provider && g_provider->FileTracingCategoryIsEnabled(); +} + +void FileTracing::ScopedTrace::Initialize( + const char* name, File* file, int64 size) { + file_ = file; + name_ = name; + size_ = size; + initialized_ = true; + + if (g_provider) { + g_provider->FileTracingEventBegin( + name_, &file_->trace_enabler_, file_->path_, size_); + } +} + +} // namespace base diff --git a/chromium/base/files/file_tracing.h b/chromium/base/files/file_tracing.h new file mode 100644 index 00000000000..8452037790c --- /dev/null +++ b/chromium/base/files/file_tracing.h @@ -0,0 +1,95 @@ +// 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_FILES_FILE_TRACING_H_ +#define BASE_FILES_FILE_TRACING_H_ + +#include "base/base_export.h" +#include "base/basictypes.h" +#include "base/macros.h" + +#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) + +#define SCOPED_FILE_TRACE(name) SCOPED_FILE_TRACE_WITH_SIZE(name, 0) + +namespace base { + +class File; +class FilePath; + +class BASE_EXPORT FileTracing { + public: + class Provider { + public: + // Whether the file tracing category is currently enabled. + virtual bool FileTracingCategoryIsEnabled() const = 0; + + // Enables file tracing for |id|. Must be called before recording events. + virtual void FileTracingEnable(void* id) = 0; + + // Disables file tracing for |id|. + virtual void FileTracingDisable(void* id) = 0; + + // Begins an event for |id| with |name|. |path| tells where in the directory + // structure the event is happening (and may be blank). |size| is reported + // if not 0. + virtual void FileTracingEventBegin( + const char* name, void* id, const FilePath& path, int64 size) = 0; + + // Ends an event for |id| with |name|. |path| tells where in the directory + // structure the event is happening (and may be blank). |size| is reported + // if not 0. + virtual void FileTracingEventEnd( + const char* name, void* id, const FilePath& path, int64 size) = 0; + }; + + // Sets a global file tracing provider to query categories and record events. + static void SetProvider(Provider* provider); + + // Enables file tracing while in scope. + class ScopedEnabler { + public: + ScopedEnabler(); + ~ScopedEnabler(); + }; + + class ScopedTrace { + public: + ScopedTrace(); + ~ScopedTrace(); + + // Whether this trace should be initialized or not. + bool ShouldInitialize() const; + + // Called only if the tracing category is enabled. + void Initialize(const char* event, File* file, int64 size); + + private: + // True if |Initialize()| has been called. Don't touch |path_|, |event_|, + // or |bytes_| if |initialized_| is false. + bool initialized_; + + // The event name to trace (e.g. "Read", "Write"). Prefixed with "File". + const char* name_; + + // The file being traced. Must outlive this class. + File* file_; + + // The size (in bytes) of this trace. Not reported if 0. + int64 size_; + + DISALLOW_COPY_AND_ASSIGN(ScopedTrace); + }; + + DISALLOW_COPY_AND_ASSIGN(FileTracing); +}; + +} // namespace base + +#endif // BASE_FILES_FILE_TRACING_H_ diff --git a/chromium/base/files/file_unittest.cc b/chromium/base/files/file_unittest.cc index 3bc2db60f0e..5c594242bc8 100644 --- a/chromium/base/files/file_unittest.cc +++ b/chromium/base/files/file_unittest.cc @@ -443,6 +443,49 @@ TEST(FileTest, Seek) { EXPECT_EQ(kOffset, file.Seek(base::File::FROM_END, -kOffset)); } +TEST(FileTest, Duplicate) { + base::ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + FilePath file_path = temp_dir.path().AppendASCII("file"); + File file(file_path,(base::File::FLAG_CREATE | + base::File::FLAG_READ | + base::File::FLAG_WRITE)); + ASSERT_TRUE(file.IsValid()); + + File file2(file.Duplicate()); + ASSERT_TRUE(file2.IsValid()); + + // Write through one handle, close it, read through the other. + static const char kData[] = "now is a good time."; + static const int kDataLen = sizeof(kData) - 1; + + ASSERT_EQ(0, file.Seek(base::File::FROM_CURRENT, 0)); + ASSERT_EQ(0, file2.Seek(base::File::FROM_CURRENT, 0)); + ASSERT_EQ(kDataLen, file.WriteAtCurrentPos(kData, kDataLen)); + ASSERT_EQ(kDataLen, file.Seek(base::File::FROM_CURRENT, 0)); + ASSERT_EQ(kDataLen, file2.Seek(base::File::FROM_CURRENT, 0)); + file.Close(); + char buf[kDataLen]; + ASSERT_EQ(kDataLen, file2.Read(0, &buf[0], kDataLen)); + ASSERT_EQ(std::string(kData, kDataLen), std::string(&buf[0], kDataLen)); +} + +TEST(FileTest, DuplicateDeleteOnClose) { + base::ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + FilePath file_path = temp_dir.path().AppendASCII("file"); + File file(file_path,(base::File::FLAG_CREATE | + base::File::FLAG_READ | + base::File::FLAG_WRITE | + base::File::FLAG_DELETE_ON_CLOSE)); + ASSERT_TRUE(file.IsValid()); + File file2(file.Duplicate()); + ASSERT_TRUE(file2.IsValid()); + file.Close(); + file2.Close(); + ASSERT_FALSE(base::PathExists(file_path)); +} + #if defined(OS_WIN) TEST(FileTest, GetInfoForDirectory) { base::ScopedTempDir temp_dir; diff --git a/chromium/base/files/file_util.cc b/chromium/base/files/file_util.cc index e60dcd9d346..32dab6bd40d 100644 --- a/chromium/base/files/file_util.cc +++ b/chromium/base/files/file_util.cc @@ -47,12 +47,6 @@ bool Move(const FilePath& from_path, const FilePath& to_path) { return internal::MoveUnsafe(from_path, to_path); } -bool CopyFile(const FilePath& from_path, const FilePath& to_path) { - if (from_path.ReferencesParent() || to_path.ReferencesParent()) - return false; - return internal::CopyFileUnsafe(from_path, to_path); -} - bool ContentsEqual(const FilePath& filename1, const FilePath& filename2) { // We open the file in binary format even if they are text files because // we are just comparing that bytes are exactly same in both files and not diff --git a/chromium/base/files/file_util.h b/chromium/base/files/file_util.h index ecc0d581dab..7f169f1c54b 100644 --- a/chromium/base/files/file_util.h +++ b/chromium/base/files/file_util.h @@ -421,11 +421,6 @@ namespace internal { BASE_EXPORT bool MoveUnsafe(const FilePath& from_path, const FilePath& to_path); -// Same as CopyFile but allows paths with traversal components. -// Use only with extreme care. -BASE_EXPORT bool CopyFileUnsafe(const FilePath& from_path, - const FilePath& to_path); - #if defined(OS_WIN) // Copy from_path to to_path recursively and then delete from_path recursively. // Returns true if all operations succeed. diff --git a/chromium/base/files/file_util_linux.cc b/chromium/base/files/file_util_linux.cc index 532962f195c..b230fd96484 100644 --- a/chromium/base/files/file_util_linux.cc +++ b/chromium/base/files/file_util_linux.cc @@ -10,21 +10,6 @@ #include "base/files/file_path.h" -// Make sure some of the newer macros from magic.h are defined. -// TODO(mostynb@opera.com): remove this after 2014. -#ifndef BTRFS_SUPER_MAGIC -#define BTRFS_SUPER_MAGIC 0x9123683E -#endif -#ifndef HUGETLBFS_MAGIC -#define HUGETLBFS_MAGIC 0x958458f6 -#endif -#ifndef RAMFS_MAGIC -#define RAMFS_MAGIC 0x858458f6 -#endif -#ifndef TMPFS_MAGIC -#define TMPFS_MAGIC 0x01021994 -#endif - namespace base { bool GetFileSystemType(const FilePath& path, FileSystemType* type) { diff --git a/chromium/base/files/file_util_mac.mm b/chromium/base/files/file_util_mac.mm index 695935a2e31..acac8d738ea 100644 --- a/chromium/base/files/file_util_mac.mm +++ b/chromium/base/files/file_util_mac.mm @@ -14,16 +14,15 @@ #include "base/threading/thread_restrictions.h" namespace base { -namespace internal { -bool CopyFileUnsafe(const FilePath& from_path, const FilePath& to_path) { +bool CopyFile(const FilePath& from_path, const FilePath& to_path) { ThreadRestrictions::AssertIOAllowed(); + if (from_path.ReferencesParent() || to_path.ReferencesParent()) + return false; return (copyfile(from_path.value().c_str(), to_path.value().c_str(), NULL, COPYFILE_DATA) == 0); } -} // namespace internal - bool GetTempDir(base::FilePath* path) { NSString* tmp = NSTemporaryDirectory(); if (tmp == nil) diff --git a/chromium/base/files/file_util_posix.cc b/chromium/base/files/file_util_posix.cc index b8c0eeb94c0..b4a64ba9eee 100644 --- a/chromium/base/files/file_util_posix.cc +++ b/chromium/base/files/file_util_posix.cc @@ -28,8 +28,6 @@ #include <glib.h> // for g_get_home_dir() #endif -#include <fstream> - #include "base/basictypes.h" #include "base/files/file_enumerator.h" #include "base/files/file_path.h" @@ -59,7 +57,6 @@ namespace base { -#if !defined(OS_NACL_NONSFI) namespace { #if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) @@ -82,6 +79,7 @@ static int CallLstat(const char *path, stat_wrapper_t *sb) { } #endif // !(defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL)) +#if !defined(OS_NACL_NONSFI) // Helper for NormalizeFilePath(), defined below. bool RealPath(const FilePath& path, FilePath* real_path) { ThreadRestrictions::AssertIOAllowed(); // For realpath(). @@ -184,9 +182,11 @@ bool DetermineDevShmExecutable() { return result; } #endif // defined(OS_LINUX) +#endif // !defined(OS_NACL_NONSFI) } // namespace +#if !defined(OS_NACL_NONSFI) FilePath MakeAbsoluteFilePath(const FilePath& input) { ThreadRestrictions::AssertIOAllowed(); char full_path[PATH_MAX]; @@ -365,6 +365,7 @@ bool PathIsWritable(const FilePath& path) { ThreadRestrictions::AssertIOAllowed(); return access(path.value().c_str(), W_OK) == 0; } +#endif // !defined(OS_NACL_NONSFI) bool DirectoryExists(const FilePath& path) { ThreadRestrictions::AssertIOAllowed(); @@ -373,7 +374,6 @@ bool DirectoryExists(const FilePath& path) { return S_ISDIR(file_info.st_mode); return false; } -#endif // !defined(OS_NACL_NONSFI) bool ReadFromFD(int fd, char* buffer, size_t bytes) { size_t total_read = 0; @@ -428,7 +428,7 @@ bool GetPosixFilePermissions(const FilePath& path, int* mode) { bool SetPosixFilePermissions(const FilePath& path, int mode) { ThreadRestrictions::AssertIOAllowed(); - DCHECK((mode & ~FILE_PERMISSION_MASK) == 0); + DCHECK_EQ(mode & ~FILE_PERMISSION_MASK, 0); // Calls stat() so that we can preserve the higher bits like S_ISGID. stat_wrapper_t stat_buf; @@ -847,55 +847,33 @@ bool GetShmemTempDir(bool executable, FilePath* path) { } #endif // !defined(OS_ANDROID) -// ----------------------------------------------------------------------------- - -namespace internal { - -bool MoveUnsafe(const FilePath& from_path, const FilePath& to_path) { - ThreadRestrictions::AssertIOAllowed(); - // Windows compatibility: if to_path exists, from_path and to_path - // must be the same type, either both files, or both directories. - stat_wrapper_t to_file_info; - if (CallStat(to_path.value().c_str(), &to_file_info) == 0) { - stat_wrapper_t from_file_info; - if (CallStat(from_path.value().c_str(), &from_file_info) == 0) { - if (S_ISDIR(to_file_info.st_mode) != S_ISDIR(from_file_info.st_mode)) - return false; - } else { - return false; - } - } - - if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0) - return true; - - if (!CopyDirectory(from_path, to_path, true)) - return false; - - DeleteFile(from_path, true); - return true; -} - #if !defined(OS_MACOSX) // Mac has its own implementation, this is for all other Posix systems. -bool CopyFileUnsafe(const FilePath& from_path, const FilePath& to_path) { +bool CopyFile(const FilePath& from_path, const FilePath& to_path) { ThreadRestrictions::AssertIOAllowed(); - int infile = HANDLE_EINTR(open(from_path.value().c_str(), O_RDONLY)); - if (infile < 0) + File infile; +#if defined(OS_ANDROID) + if (from_path.IsContentUri()) { + infile = OpenContentUriForRead(from_path); + } else { + infile = File(from_path, File::FLAG_OPEN | File::FLAG_READ); + } +#else + infile = File(from_path, File::FLAG_OPEN | File::FLAG_READ); +#endif + if (!infile.IsValid()) return false; - int outfile = HANDLE_EINTR(creat(to_path.value().c_str(), 0666)); - if (outfile < 0) { - close(infile); + File outfile(to_path, File::FLAG_WRITE | File::FLAG_CREATE_ALWAYS); + if (!outfile.IsValid()) return false; - } const size_t kBufferSize = 32768; std::vector<char> buffer(kBufferSize); bool result = true; while (result) { - ssize_t bytes_read = HANDLE_EINTR(read(infile, &buffer[0], buffer.size())); + ssize_t bytes_read = infile.ReadAtCurrentPos(&buffer[0], buffer.size()); if (bytes_read < 0) { result = false; break; @@ -905,10 +883,8 @@ bool CopyFileUnsafe(const FilePath& from_path, const FilePath& to_path) { // Allow for partial writes ssize_t bytes_written_per_read = 0; do { - ssize_t bytes_written_partial = HANDLE_EINTR(write( - outfile, - &buffer[bytes_written_per_read], - bytes_read - bytes_written_per_read)); + ssize_t bytes_written_partial = outfile.WriteAtCurrentPos( + &buffer[bytes_written_per_read], bytes_read - bytes_written_per_read); if (bytes_written_partial < 0) { result = false; break; @@ -917,15 +893,39 @@ bool CopyFileUnsafe(const FilePath& from_path, const FilePath& to_path) { } while (bytes_written_per_read < bytes_read); } - if (IGNORE_EINTR(close(infile)) < 0) - result = false; - if (IGNORE_EINTR(close(outfile)) < 0) - result = false; - return result; } #endif // !defined(OS_MACOSX) +// ----------------------------------------------------------------------------- + +namespace internal { + +bool MoveUnsafe(const FilePath& from_path, const FilePath& to_path) { + ThreadRestrictions::AssertIOAllowed(); + // Windows compatibility: if to_path exists, from_path and to_path + // must be the same type, either both files, or both directories. + stat_wrapper_t to_file_info; + if (CallStat(to_path.value().c_str(), &to_file_info) == 0) { + stat_wrapper_t from_file_info; + if (CallStat(from_path.value().c_str(), &from_file_info) == 0) { + if (S_ISDIR(to_file_info.st_mode) != S_ISDIR(from_file_info.st_mode)) + return false; + } else { + return false; + } + } + + if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0) + return true; + + if (!CopyDirectory(from_path, to_path, true)) + return false; + + DeleteFile(from_path, true); + return true; +} + } // namespace internal #endif // !defined(OS_NACL_NONSFI) diff --git a/chromium/base/files/file_util_proxy_unittest.cc b/chromium/base/files/file_util_proxy_unittest.cc index 87ae66a6a0d..74083699f00 100644 --- a/chromium/base/files/file_util_proxy_unittest.cc +++ b/chromium/base/files/file_util_proxy_unittest.cc @@ -8,7 +8,6 @@ #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/memory/weak_ptr.h" -#include "base/message_loop/message_loop.h" #include "base/threading/thread.h" #include "testing/gtest/include/gtest/gtest.h" @@ -19,11 +18,9 @@ class FileUtilProxyTest : public testing::Test { FileUtilProxyTest() : file_thread_("FileUtilProxyTestFileThread"), error_(File::FILE_OK), - created_(false), - bytes_written_(-1), weak_factory_(this) {} - virtual void SetUp() override { + void SetUp() override { ASSERT_TRUE(dir_.CreateUniqueTempDir()); ASSERT_TRUE(file_thread_.Start()); } @@ -42,7 +39,7 @@ class FileUtilProxyTest : public testing::Test { protected: TaskRunner* file_task_runner() const { - return file_thread_.message_loop_proxy().get(); + return file_thread_.task_runner().get(); } const FilePath& test_dir_path() const { return dir_.path(); } const FilePath test_path() const { return dir_.path().AppendASCII("test"); } @@ -52,11 +49,9 @@ class FileUtilProxyTest : public testing::Test { ScopedTempDir dir_; File::Error error_; - bool created_; FilePath path_; File::Info file_info_; std::vector<char> buffer_; - int bytes_written_; WeakPtrFactory<FileUtilProxyTest> weak_factory_; }; diff --git a/chromium/base/files/file_util_unittest.cc b/chromium/base/files/file_util_unittest.cc index 08c9587cc06..b107b0f9c89 100644 --- a/chromium/base/files/file_util_unittest.cc +++ b/chromium/base/files/file_util_unittest.cc @@ -30,6 +30,7 @@ #include "base/files/scoped_file.h" #include "base/files/scoped_temp_dir.h" #include "base/path_service.h" +#include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/test/test_file_util.h" #include "base/threading/platform_thread.h" @@ -184,7 +185,7 @@ const int FILES_AND_DIRECTORIES = // to be a PlatformTest class FileUtilTest : public PlatformTest { protected: - virtual void SetUp() override { + void SetUp() override { PlatformTest::SetUp(); ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); } @@ -196,9 +197,9 @@ class FileUtilTest : public PlatformTest { // interface to query whether a given file is present. class FindResultCollector { public: - explicit FindResultCollector(FileEnumerator& enumerator) { + explicit FindResultCollector(FileEnumerator* enumerator) { FilePath cur_file; - while (!(cur_file = enumerator.Next()).value().empty()) { + while (!(cur_file = enumerator->Next()).value().empty()) { FilePath::StringType path = cur_file.value(); // The file should not be returned twice. EXPECT_TRUE(files_.end() == files_.find(path)) @@ -326,6 +327,13 @@ TEST_F(FileUtilTest, NormalizeFilePathReparsePoints) { // |-> to_sub_long (reparse point to temp_dir\sub_a\long_name_\sub_long) FilePath base_a = temp_dir_.path().Append(FPL("base_a")); +#if defined(OS_WIN) + // TEMP can have a lower case drive letter. + string16 temp_base_a = base_a.value(); + ASSERT_FALSE(temp_base_a.empty()); + *temp_base_a.begin() = base::ToUpperASCII(*temp_base_a.begin()); + base_a = FilePath(temp_base_a); +#endif ASSERT_TRUE(CreateDirectory(base_a)); FilePath sub_a = base_a.Append(FPL("sub_a")); @@ -428,6 +436,7 @@ TEST_F(FileUtilTest, NormalizeFilePathReparsePoints) { TEST_F(FileUtilTest, DevicePathToDriveLetter) { // Get a drive letter. std::wstring real_drive_letter = temp_dir_.path().value().substr(0, 2); + StringToUpperASCII(&real_drive_letter); if (!isalpha(real_drive_letter[0]) || ':' != real_drive_letter[1]) { LOG(ERROR) << "Can't get a drive letter to test with."; return; @@ -815,7 +824,7 @@ TEST_F(FileUtilTest, ChangeDirectoryPermissionsAndEnumerate) { // Make sure the file in the directory can't be enumerated. FileEnumerator f1(subdir_path, true, FileEnumerator::FILES); EXPECT_TRUE(PathExists(subdir_path)); - FindResultCollector c1(f1); + FindResultCollector c1(&f1); EXPECT_EQ(0, c1.size()); EXPECT_FALSE(GetPosixFilePermissions(file_name, &mode)); @@ -826,7 +835,7 @@ TEST_F(FileUtilTest, ChangeDirectoryPermissionsAndEnumerate) { // Make sure the file in the directory can be enumerated. FileEnumerator f2(subdir_path, true, FileEnumerator::FILES); - FindResultCollector c2(f2); + FindResultCollector c2(&f2); EXPECT_TRUE(c2.HasFile(file_name)); EXPECT_EQ(1, c2.size()); @@ -1388,19 +1397,15 @@ void SetReadOnly(const FilePath& path, bool read_only) { path.value().c_str(), read_only ? (attrs | FILE_ATTRIBUTE_READONLY) : (attrs & ~FILE_ATTRIBUTE_READONLY))); - // Files in the temporary directory should not be indexed ever. If this - // assumption change, fix this unit test accordingly. - // FILE_ATTRIBUTE_NOT_CONTENT_INDEXED doesn't exist on XP. + DWORD expected = read_only ? ((attrs & (FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_DIRECTORY)) | FILE_ATTRIBUTE_READONLY) : (attrs & (FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_DIRECTORY)); - // TODO(ripp@yandex-team.ru): this seems out of place here. If we really think - // it is important to verify that temp files are not indexed there should be - // a dedicated test for that (create a file, inspect the attributes) - if (win::GetVersion() >= win::VERSION_VISTA) - expected |= FILE_ATTRIBUTE_NOT_CONTENT_INDEXED; - attrs = GetFileAttributes(path.value().c_str()); + + // Ignore FILE_ATTRIBUTE_NOT_CONTENT_INDEXED if present. + attrs = GetFileAttributes(path.value().c_str()) & + ~FILE_ATTRIBUTE_NOT_CONTENT_INDEXED; ASSERT_EQ(expected, attrs); #else // On all other platforms, it involves removing/setting the write bit. @@ -1471,30 +1476,29 @@ TEST_F(FileUtilTest, CopyFile) { FilePath dest_file = dir_name_from.Append(FILE_PATH_LITERAL("DestFile.txt")); ASSERT_TRUE(CopyFile(file_name_from, dest_file)); - // Copy the file to another location using '..' in the path. + // Try to copy the file to another location using '..' in the path. FilePath dest_file2(dir_name_from); dest_file2 = dest_file2.AppendASCII(".."); dest_file2 = dest_file2.AppendASCII("DestFile.txt"); ASSERT_FALSE(CopyFile(file_name_from, dest_file2)); - ASSERT_TRUE(internal::CopyFileUnsafe(file_name_from, dest_file2)); FilePath dest_file2_test(dir_name_from); dest_file2_test = dest_file2_test.DirName(); dest_file2_test = dest_file2_test.AppendASCII("DestFile.txt"); - // Check everything has been copied. + // Check expected copy results. EXPECT_TRUE(PathExists(file_name_from)); EXPECT_TRUE(PathExists(dest_file)); const std::wstring read_contents = ReadTextFile(dest_file); EXPECT_EQ(file_contents, read_contents); - EXPECT_TRUE(PathExists(dest_file2_test)); - EXPECT_TRUE(PathExists(dest_file2)); + EXPECT_FALSE(PathExists(dest_file2_test)); + EXPECT_FALSE(PathExists(dest_file2)); } TEST_F(FileUtilTest, CopyFileACL) { // While FileUtilTest.CopyFile asserts the content is correctly copied over, // this test case asserts the access control bits are meeting expectations in - // CopyFileUnsafe(). + // CopyFile(). FilePath src = temp_dir_.path().Append(FILE_PATH_LITERAL("src.txt")); const std::wstring file_contents(L"Gooooooooooooooooooooogle"); CreateTextFile(src, file_contents); @@ -1867,7 +1871,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) { // Only enumerate files. FileEnumerator f1(temp_dir_.path(), true, FileEnumerator::FILES); - FindResultCollector c1(f1); + FindResultCollector c1(&f1); EXPECT_TRUE(c1.HasFile(file1)); EXPECT_TRUE(c1.HasFile(file2_abs)); EXPECT_TRUE(c1.HasFile(dir2file)); @@ -1876,7 +1880,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) { // Only enumerate directories. FileEnumerator f2(temp_dir_.path(), true, FileEnumerator::DIRECTORIES); - FindResultCollector c2(f2); + FindResultCollector c2(&f2); EXPECT_TRUE(c2.HasFile(dir1)); EXPECT_TRUE(c2.HasFile(dir2)); EXPECT_TRUE(c2.HasFile(dir2inner)); @@ -1885,7 +1889,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) { // Only enumerate directories non-recursively. FileEnumerator f2_non_recursive( temp_dir_.path(), false, FileEnumerator::DIRECTORIES); - FindResultCollector c2_non_recursive(f2_non_recursive); + FindResultCollector c2_non_recursive(&f2_non_recursive); EXPECT_TRUE(c2_non_recursive.HasFile(dir1)); EXPECT_TRUE(c2_non_recursive.HasFile(dir2)); EXPECT_EQ(2, c2_non_recursive.size()); @@ -1894,7 +1898,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) { FileEnumerator f2_dotdot(temp_dir_.path(), false, FileEnumerator::DIRECTORIES | FileEnumerator::INCLUDE_DOT_DOT); - FindResultCollector c2_dotdot(f2_dotdot); + FindResultCollector c2_dotdot(&f2_dotdot); EXPECT_TRUE(c2_dotdot.HasFile(dir1)); EXPECT_TRUE(c2_dotdot.HasFile(dir2)); EXPECT_TRUE(c2_dotdot.HasFile(temp_dir_.path().Append(FPL("..")))); @@ -1902,7 +1906,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) { // Enumerate files and directories. FileEnumerator f3(temp_dir_.path(), true, FILES_AND_DIRECTORIES); - FindResultCollector c3(f3); + FindResultCollector c3(&f3); EXPECT_TRUE(c3.HasFile(dir1)); EXPECT_TRUE(c3.HasFile(dir2)); EXPECT_TRUE(c3.HasFile(file1)); @@ -1914,7 +1918,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) { // Non-recursive operation. FileEnumerator f4(temp_dir_.path(), false, FILES_AND_DIRECTORIES); - FindResultCollector c4(f4); + FindResultCollector c4(&f4); EXPECT_TRUE(c4.HasFile(dir2)); EXPECT_TRUE(c4.HasFile(dir2)); EXPECT_TRUE(c4.HasFile(file1)); @@ -1923,7 +1927,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) { // Enumerate with a pattern. FileEnumerator f5(temp_dir_.path(), true, FILES_AND_DIRECTORIES, FPL("dir*")); - FindResultCollector c5(f5); + FindResultCollector c5(&f5); EXPECT_TRUE(c5.HasFile(dir1)); EXPECT_TRUE(c5.HasFile(dir2)); EXPECT_TRUE(c5.HasFile(dir2file)); @@ -1942,7 +1946,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) { // the file system so skip this test for XP. // Enumerate the reparse point. FileEnumerator f6(dir1, true, FILES_AND_DIRECTORIES); - FindResultCollector c6(f6); + FindResultCollector c6(&f6); FilePath inner2 = dir1.Append(FPL("inner")); EXPECT_TRUE(c6.HasFile(inner2)); EXPECT_TRUE(c6.HasFile(inner2.Append(FPL("innerfile.txt")))); @@ -1952,7 +1956,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) { // No changes for non recursive operation. FileEnumerator f7(temp_dir_.path(), false, FILES_AND_DIRECTORIES); - FindResultCollector c7(f7); + FindResultCollector c7(&f7); EXPECT_TRUE(c7.HasFile(dir2)); EXPECT_TRUE(c7.HasFile(dir2)); EXPECT_TRUE(c7.HasFile(file1)); @@ -1961,7 +1965,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) { // Should not enumerate inside dir1 when using recursion. FileEnumerator f8(temp_dir_.path(), true, FILES_AND_DIRECTORIES); - FindResultCollector c8(f8); + FindResultCollector c8(&f8); EXPECT_TRUE(c8.HasFile(dir1)); EXPECT_TRUE(c8.HasFile(dir2)); EXPECT_TRUE(c8.HasFile(file1)); @@ -2171,7 +2175,7 @@ TEST_F(FileUtilTest, IsDirectoryEmpty) { // with a common SetUp() method. class VerifyPathControlledByUserTest : public FileUtilTest { protected: - virtual void SetUp() override { + void SetUp() override { FileUtilTest::SetUp(); // Create a basic structure used by each test. diff --git a/chromium/base/files/file_util_win.cc b/chromium/base/files/file_util_win.cc index 733c32c27da..e254232f663 100644 --- a/chromium/base/files/file_util_win.cc +++ b/chromium/base/files/file_util_win.cc @@ -210,7 +210,7 @@ bool CopyDirectory(const FilePath& from_path, const FilePath& to_path, << target_path.value().c_str(); success = false; } - } else if (!internal::CopyFileUnsafe(current, target_path)) { + } else if (!CopyFile(current, target_path)) { DLOG(ERROR) << "CopyDirectory() couldn't create file: " << target_path.value().c_str(); success = false; @@ -480,7 +480,7 @@ bool DevicePathToDriveLetterPath(const FilePath& nt_device_path, } // Move to the next drive letter string, which starts one // increment after the '\0' that terminates the current string. - while (*drive_map_ptr++); + while (*drive_map_ptr++) {} } // No drive matched. The path does not start with a device junction @@ -496,7 +496,7 @@ bool NormalizeToNativeFilePath(const FilePath& path, FilePath* nt_path) { // code below to a call to GetFinalPathNameByHandle(). The method this // function uses is explained in the following msdn article: // http://msdn.microsoft.com/en-us/library/aa366789(VS.85).aspx - base::win::ScopedHandle file_handle( + win::ScopedHandle file_handle( ::CreateFile(path.value().c_str(), GENERIC_READ, kFileShareAll, @@ -510,7 +510,7 @@ bool NormalizeToNativeFilePath(const FilePath& path, FilePath* nt_path) { // Create a file mapping object. Can't easily use MemoryMappedFile, because // we only map the first byte, and need direct access to the handle. You can // not map an empty file, this call fails in that case. - base::win::ScopedHandle file_map_handle( + win::ScopedHandle file_map_handle( ::CreateFileMapping(file_handle.Get(), NULL, PAGE_READONLY, @@ -575,7 +575,7 @@ bool GetFileInfo(const FilePath& file_path, File::Info* results) { FILE* OpenFile(const FilePath& filename, const char* mode) { ThreadRestrictions::AssertIOAllowed(); - std::wstring w_mode = ASCIIToWide(std::string(mode)); + string16 w_mode = ASCIIToUTF16(mode); return _wfsopen(filename.value().c_str(), w_mode.c_str(), _SH_DENYNO); } @@ -595,13 +595,13 @@ FILE* FileToFILE(File file, const char* mode) { int ReadFile(const FilePath& filename, char* data, int max_size) { ThreadRestrictions::AssertIOAllowed(); - base::win::ScopedHandle file(CreateFile(filename.value().c_str(), - GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, - OPEN_EXISTING, - FILE_FLAG_SEQUENTIAL_SCAN, - NULL)); + win::ScopedHandle file(CreateFile(filename.value().c_str(), + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_FLAG_SEQUENTIAL_SCAN, + NULL)); if (!file.IsValid()) return -1; @@ -614,13 +614,13 @@ int ReadFile(const FilePath& filename, char* data, int max_size) { int WriteFile(const FilePath& filename, const char* data, int size) { ThreadRestrictions::AssertIOAllowed(); - base::win::ScopedHandle file(CreateFile(filename.value().c_str(), - GENERIC_WRITE, - 0, - NULL, - CREATE_ALWAYS, - 0, - NULL)); + win::ScopedHandle file(CreateFile(filename.value().c_str(), + GENERIC_WRITE, + 0, + NULL, + CREATE_ALWAYS, + 0, + NULL)); if (!file.IsValid()) { DPLOG(WARNING) << "CreateFile failed for path " << UTF16ToUTF8(filename.value()); @@ -646,13 +646,13 @@ int WriteFile(const FilePath& filename, const char* data, int size) { bool AppendToFile(const FilePath& filename, const char* data, int size) { ThreadRestrictions::AssertIOAllowed(); - base::win::ScopedHandle file(CreateFile(filename.value().c_str(), - FILE_APPEND_DATA, - 0, - NULL, - OPEN_EXISTING, - 0, - NULL)); + win::ScopedHandle file(CreateFile(filename.value().c_str(), + FILE_APPEND_DATA, + 0, + NULL, + OPEN_EXISTING, + 0, + NULL)); if (!file.IsValid()) { VPLOG(1) << "CreateFile failed for path " << UTF16ToUTF8(filename.value()); return false; @@ -722,6 +722,37 @@ int GetMaximumPathComponentLength(const FilePath& path) { return std::min(whole_path_limit, static_cast<int>(max_length)); } +bool CopyFile(const FilePath& from_path, const FilePath& to_path) { + ThreadRestrictions::AssertIOAllowed(); + if (from_path.ReferencesParent() || to_path.ReferencesParent()) + return false; + + // NOTE: I suspect we could support longer paths, but that would involve + // analyzing all our usage of files. + if (from_path.value().length() >= MAX_PATH || + to_path.value().length() >= MAX_PATH) { + return false; + } + + // Unlike the posix implementation that copies the file manually and discards + // the ACL bits, CopyFile() copies the complete SECURITY_DESCRIPTOR and access + // bits, which is usually not what we want. We can't do much about the + // SECURITY_DESCRIPTOR but at least remove the read only bit. + const wchar_t* dest = to_path.value().c_str(); + if (!::CopyFile(from_path.value().c_str(), dest, false)) { + // Copy failed. + return false; + } + DWORD attrs = GetFileAttributes(dest); + if (attrs == INVALID_FILE_ATTRIBUTES) { + return false; + } + if (attrs & FILE_ATTRIBUTE_READONLY) { + SetFileAttributes(dest, attrs & ~FILE_ATTRIBUTE_READONLY); + } + return true; +} + // ----------------------------------------------------------------------------- namespace internal { @@ -760,35 +791,6 @@ bool MoveUnsafe(const FilePath& from_path, const FilePath& to_path) { return ret; } -bool CopyFileUnsafe(const FilePath& from_path, const FilePath& to_path) { - ThreadRestrictions::AssertIOAllowed(); - - // NOTE: I suspect we could support longer paths, but that would involve - // analyzing all our usage of files. - if (from_path.value().length() >= MAX_PATH || - to_path.value().length() >= MAX_PATH) { - return false; - } - - // Unlike the posix implementation that copies the file manually and discards - // the ACL bits, CopyFile() copies the complete SECURITY_DESCRIPTOR and access - // bits, which is usually not what we want. We can't do much about the - // SECURITY_DESCRIPTOR but at least remove the read only bit. - const wchar_t* dest = to_path.value().c_str(); - if (!::CopyFile(from_path.value().c_str(), dest, false)) { - // Copy failed. - return false; - } - DWORD attrs = GetFileAttributes(dest); - if (attrs == INVALID_FILE_ATTRIBUTES) { - return false; - } - if (attrs & FILE_ATTRIBUTE_READONLY) { - SetFileAttributes(dest, attrs & ~FILE_ATTRIBUTE_READONLY); - } - return true; -} - bool CopyAndDeleteDirectory(const FilePath& from_path, const FilePath& to_path) { ThreadRestrictions::AssertIOAllowed(); diff --git a/chromium/base/files/file_win.cc b/chromium/base/files/file_win.cc index 727b5ce1dbb..97928522f10 100644 --- a/chromium/base/files/file_win.cc +++ b/chromium/base/files/file_win.cc @@ -6,7 +6,6 @@ #include <io.h> -#include "base/files/file_path.h" #include "base/logging.h" #include "base/metrics/sparse_histogram.h" #include "base/threading/thread_restrictions.h" @@ -18,90 +17,6 @@ COMPILE_ASSERT(File::FROM_BEGIN == FILE_BEGIN && File::FROM_CURRENT == FILE_CURRENT && File::FROM_END == FILE_END, whence_matches_system); -void File::InitializeUnsafe(const FilePath& name, uint32 flags) { - base::ThreadRestrictions::AssertIOAllowed(); - DCHECK(!IsValid()); - - DWORD disposition = 0; - - if (flags & FLAG_OPEN) - disposition = OPEN_EXISTING; - - if (flags & FLAG_CREATE) { - DCHECK(!disposition); - disposition = CREATE_NEW; - } - - if (flags & FLAG_OPEN_ALWAYS) { - DCHECK(!disposition); - disposition = OPEN_ALWAYS; - } - - if (flags & FLAG_CREATE_ALWAYS) { - DCHECK(!disposition); - DCHECK(flags & FLAG_WRITE); - disposition = CREATE_ALWAYS; - } - - if (flags & FLAG_OPEN_TRUNCATED) { - DCHECK(!disposition); - DCHECK(flags & FLAG_WRITE); - disposition = TRUNCATE_EXISTING; - } - - if (!disposition) { - NOTREACHED(); - return; - } - - DWORD access = 0; - if (flags & FLAG_WRITE) - access = GENERIC_WRITE; - if (flags & FLAG_APPEND) { - DCHECK(!access); - access = FILE_APPEND_DATA; - } - if (flags & FLAG_READ) - access |= GENERIC_READ; - if (flags & FLAG_WRITE_ATTRIBUTES) - access |= FILE_WRITE_ATTRIBUTES; - if (flags & FLAG_EXECUTE) - access |= GENERIC_EXECUTE; - - DWORD sharing = (flags & FLAG_EXCLUSIVE_READ) ? 0 : FILE_SHARE_READ; - if (!(flags & FLAG_EXCLUSIVE_WRITE)) - sharing |= FILE_SHARE_WRITE; - if (flags & FLAG_SHARE_DELETE) - sharing |= FILE_SHARE_DELETE; - - DWORD create_flags = 0; - if (flags & FLAG_ASYNC) - create_flags |= FILE_FLAG_OVERLAPPED; - if (flags & FLAG_TEMPORARY) - create_flags |= FILE_ATTRIBUTE_TEMPORARY; - if (flags & FLAG_HIDDEN) - create_flags |= FILE_ATTRIBUTE_HIDDEN; - if (flags & FLAG_DELETE_ON_CLOSE) - create_flags |= FILE_FLAG_DELETE_ON_CLOSE; - if (flags & FLAG_BACKUP_SEMANTICS) - create_flags |= FILE_FLAG_BACKUP_SEMANTICS; - - file_.Set(CreateFile(name.value().c_str(), access, sharing, NULL, - disposition, create_flags, NULL)); - - if (file_.IsValid()) { - error_details_ = FILE_OK; - async_ = ((flags & FLAG_ASYNC) == FLAG_ASYNC); - - if (flags & (FLAG_OPEN_ALWAYS)) - created_ = (ERROR_ALREADY_EXISTS != GetLastError()); - else if (flags & (FLAG_CREATE_ALWAYS | FLAG_CREATE)) - created_ = true; - } else { - error_details_ = OSErrorToFileError(GetLastError()); - } -} - bool File::IsValid() const { return file_.IsValid(); } @@ -115,16 +30,20 @@ PlatformFile File::TakePlatformFile() { } void File::Close() { - if (file_.IsValid()) { - base::ThreadRestrictions::AssertIOAllowed(); - file_.Close(); - } + if (!file_.IsValid()) + return; + + ThreadRestrictions::AssertIOAllowed(); + SCOPED_FILE_TRACE("Close"); + file_.Close(); } int64 File::Seek(Whence whence, int64 offset) { - base::ThreadRestrictions::AssertIOAllowed(); + ThreadRestrictions::AssertIOAllowed(); DCHECK(IsValid()); + SCOPED_FILE_TRACE_WITH_SIZE("Seek", offset); + LARGE_INTEGER distance, res; distance.QuadPart = offset; DWORD move_method = static_cast<DWORD>(whence); @@ -134,12 +53,14 @@ int64 File::Seek(Whence whence, int64 offset) { } int File::Read(int64 offset, char* data, int size) { - base::ThreadRestrictions::AssertIOAllowed(); + ThreadRestrictions::AssertIOAllowed(); DCHECK(IsValid()); DCHECK(!async_); if (size < 0) return -1; + SCOPED_FILE_TRACE_WITH_SIZE("Read", size); + LARGE_INTEGER offset_li; offset_li.QuadPart = offset; @@ -157,12 +78,14 @@ int File::Read(int64 offset, char* data, int size) { } int File::ReadAtCurrentPos(char* data, int size) { - base::ThreadRestrictions::AssertIOAllowed(); + ThreadRestrictions::AssertIOAllowed(); DCHECK(IsValid()); DCHECK(!async_); if (size < 0) return -1; + SCOPED_FILE_TRACE_WITH_SIZE("ReadAtCurrentPos", size); + DWORD bytes_read; if (::ReadFile(file_.Get(), data, size, &bytes_read, NULL)) return bytes_read; @@ -173,18 +96,22 @@ int File::ReadAtCurrentPos(char* data, int size) { } int File::ReadNoBestEffort(int64 offset, char* data, int size) { + // TODO(dbeam): trace this separately? return Read(offset, data, size); } int File::ReadAtCurrentPosNoBestEffort(char* data, int size) { + // TODO(dbeam): trace this separately? return ReadAtCurrentPos(data, size); } int File::Write(int64 offset, const char* data, int size) { - base::ThreadRestrictions::AssertIOAllowed(); + ThreadRestrictions::AssertIOAllowed(); DCHECK(IsValid()); DCHECK(!async_); + SCOPED_FILE_TRACE_WITH_SIZE("Write", size); + LARGE_INTEGER offset_li; offset_li.QuadPart = offset; @@ -200,12 +127,14 @@ int File::Write(int64 offset, const char* data, int size) { } int File::WriteAtCurrentPos(const char* data, int size) { - base::ThreadRestrictions::AssertIOAllowed(); + ThreadRestrictions::AssertIOAllowed(); DCHECK(IsValid()); DCHECK(!async_); if (size < 0) return -1; + SCOPED_FILE_TRACE_WITH_SIZE("WriteAtCurrentPos", size); + DWORD bytes_written; if (::WriteFile(file_.Get(), data, size, &bytes_written, NULL)) return bytes_written; @@ -218,8 +147,11 @@ int File::WriteAtCurrentPosNoBestEffort(const char* data, int size) { } int64 File::GetLength() { - base::ThreadRestrictions::AssertIOAllowed(); + ThreadRestrictions::AssertIOAllowed(); DCHECK(IsValid()); + + SCOPED_FILE_TRACE("GetLength"); + LARGE_INTEGER size; if (!::GetFileSizeEx(file_.Get(), &size)) return -1; @@ -228,9 +160,11 @@ int64 File::GetLength() { } bool File::SetLength(int64 length) { - base::ThreadRestrictions::AssertIOAllowed(); + ThreadRestrictions::AssertIOAllowed(); DCHECK(IsValid()); + SCOPED_FILE_TRACE_WITH_SIZE("SetLength", length); + // Get the current file pointer. LARGE_INTEGER file_pointer; LARGE_INTEGER zero; @@ -256,16 +190,12 @@ bool File::SetLength(int64 length) { FALSE)); } -bool File::Flush() { - base::ThreadRestrictions::AssertIOAllowed(); - DCHECK(IsValid()); - return ::FlushFileBuffers(file_.Get()) != FALSE; -} - bool File::SetTimes(Time last_access_time, Time last_modified_time) { - base::ThreadRestrictions::AssertIOAllowed(); + ThreadRestrictions::AssertIOAllowed(); DCHECK(IsValid()); + SCOPED_FILE_TRACE("SetTimes"); + FILETIME last_access_filetime = last_access_time.ToFileTime(); FILETIME last_modified_filetime = last_modified_time.ToFileTime(); return (::SetFileTime(file_.Get(), NULL, &last_access_filetime, @@ -273,9 +203,11 @@ bool File::SetTimes(Time last_access_time, Time last_modified_time) { } bool File::GetInfo(Info* info) { - base::ThreadRestrictions::AssertIOAllowed(); + ThreadRestrictions::AssertIOAllowed(); DCHECK(IsValid()); + SCOPED_FILE_TRACE("GetInfo"); + BY_HANDLE_FILE_INFORMATION file_info; if (!GetFileInformationByHandle(file_.Get(), &file_info)) return false; @@ -287,14 +219,17 @@ bool File::GetInfo(Info* info) { info->is_directory = (file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; info->is_symbolic_link = false; // Windows doesn't have symbolic links. - info->last_modified = base::Time::FromFileTime(file_info.ftLastWriteTime); - info->last_accessed = base::Time::FromFileTime(file_info.ftLastAccessTime); - info->creation_time = base::Time::FromFileTime(file_info.ftCreationTime); + info->last_modified = Time::FromFileTime(file_info.ftLastWriteTime); + info->last_accessed = Time::FromFileTime(file_info.ftLastAccessTime); + info->creation_time = Time::FromFileTime(file_info.ftCreationTime); return true; } -File::Error base::File::Lock() { +File::Error File::Lock() { DCHECK(IsValid()); + + SCOPED_FILE_TRACE("Lock"); + BOOL result = LockFile(file_.Get(), 0, 0, MAXDWORD, MAXDWORD); if (!result) return OSErrorToFileError(GetLastError()); @@ -303,12 +238,39 @@ File::Error base::File::Lock() { File::Error File::Unlock() { DCHECK(IsValid()); + + SCOPED_FILE_TRACE("Unlock"); + BOOL result = UnlockFile(file_.Get(), 0, 0, MAXDWORD, MAXDWORD); if (!result) return OSErrorToFileError(GetLastError()); return FILE_OK; } +File File::Duplicate() { + if (!IsValid()) + return File(); + + SCOPED_FILE_TRACE("Duplicate"); + + HANDLE other_handle = nullptr; + + if (!::DuplicateHandle(GetCurrentProcess(), // hSourceProcessHandle + GetPlatformFile(), + GetCurrentProcess(), // hTargetProcessHandle + &other_handle, + 0, // dwDesiredAccess ignored due to SAME_ACCESS + FALSE, // !bInheritHandle + DUPLICATE_SAME_ACCESS)) { + return File(OSErrorToFileError(GetLastError())); + } + + File other(other_handle); + if (async()) + other.async_ = true; + return other.Pass(); +} + // Static. File::Error File::OSErrorToFileError(DWORD last_error) { switch (last_error) { @@ -346,6 +308,96 @@ File::Error File::OSErrorToFileError(DWORD last_error) { } } +void File::DoInitialize(uint32 flags) { + ThreadRestrictions::AssertIOAllowed(); + DCHECK(!IsValid()); + + DWORD disposition = 0; + + if (flags & FLAG_OPEN) + disposition = OPEN_EXISTING; + + if (flags & FLAG_CREATE) { + DCHECK(!disposition); + disposition = CREATE_NEW; + } + + if (flags & FLAG_OPEN_ALWAYS) { + DCHECK(!disposition); + disposition = OPEN_ALWAYS; + } + + if (flags & FLAG_CREATE_ALWAYS) { + DCHECK(!disposition); + DCHECK(flags & FLAG_WRITE); + disposition = CREATE_ALWAYS; + } + + if (flags & FLAG_OPEN_TRUNCATED) { + DCHECK(!disposition); + DCHECK(flags & FLAG_WRITE); + disposition = TRUNCATE_EXISTING; + } + + if (!disposition) { + NOTREACHED(); + return; + } + + DWORD access = 0; + if (flags & FLAG_WRITE) + access = GENERIC_WRITE; + if (flags & FLAG_APPEND) { + DCHECK(!access); + access = FILE_APPEND_DATA; + } + if (flags & FLAG_READ) + access |= GENERIC_READ; + if (flags & FLAG_WRITE_ATTRIBUTES) + access |= FILE_WRITE_ATTRIBUTES; + if (flags & FLAG_EXECUTE) + access |= GENERIC_EXECUTE; + + DWORD sharing = (flags & FLAG_EXCLUSIVE_READ) ? 0 : FILE_SHARE_READ; + if (!(flags & FLAG_EXCLUSIVE_WRITE)) + sharing |= FILE_SHARE_WRITE; + if (flags & FLAG_SHARE_DELETE) + sharing |= FILE_SHARE_DELETE; + + DWORD create_flags = 0; + if (flags & FLAG_ASYNC) + create_flags |= FILE_FLAG_OVERLAPPED; + if (flags & FLAG_TEMPORARY) + create_flags |= FILE_ATTRIBUTE_TEMPORARY; + if (flags & FLAG_HIDDEN) + create_flags |= FILE_ATTRIBUTE_HIDDEN; + if (flags & FLAG_DELETE_ON_CLOSE) + create_flags |= FILE_FLAG_DELETE_ON_CLOSE; + 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)); + + if (file_.IsValid()) { + error_details_ = FILE_OK; + async_ = ((flags & FLAG_ASYNC) == FLAG_ASYNC); + + if (flags & (FLAG_OPEN_ALWAYS)) + created_ = (ERROR_ALREADY_EXISTS != GetLastError()); + else if (flags & (FLAG_CREATE_ALWAYS | FLAG_CREATE)) + created_ = true; + } else { + error_details_ = OSErrorToFileError(GetLastError()); + } +} + +bool File::DoFlush() { + ThreadRestrictions::AssertIOAllowed(); + DCHECK(IsValid()); + return ::FlushFileBuffers(file_.Get()) != FALSE; +} + void File::SetPlatformFile(PlatformFile file) { file_.Set(file); } diff --git a/chromium/base/files/important_file_writer.cc b/chromium/base/files/important_file_writer.cc index d7579abb112..814fc7b9add 100644 --- a/chromium/base/files/important_file_writer.cc +++ b/chromium/base/files/important_file_writer.cc @@ -10,12 +10,14 @@ #include "base/bind.h" #include "base/critical_closure.h" +#include "base/debug/alias.h" #include "base/files/file.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/logging.h" #include "base/metrics/histogram.h" #include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" #include "base/task_runner.h" #include "base/task_runner_util.h" #include "base/threading/thread.h" @@ -27,10 +29,14 @@ namespace { const int kDefaultCommitIntervalMs = 10000; +// This enum is used to define the buckets for an enumerated UMA histogram. +// Hence, +// (a) existing enumerated constants should never be deleted or reordered, and +// (b) new constants should only be appended at the end of the enumeration. enum TempFileFailure { FAILED_CREATING, FAILED_OPENING, - FAILED_CLOSING, + FAILED_CLOSING, // Unused. FAILED_WRITING, FAILED_RENAMING, FAILED_FLUSHING, @@ -45,11 +51,31 @@ void LogFailure(const FilePath& path, TempFileFailure failure_code, << " : " << message; } +// Helper function to call WriteFileAtomically() with a scoped_ptr<std::string>. +bool WriteScopedStringToFileAtomically(const FilePath& path, + scoped_ptr<std::string> data) { + return ImportantFileWriter::WriteFileAtomically(path, *data); +} + } // namespace // static bool ImportantFileWriter::WriteFileAtomically(const FilePath& path, const std::string& data) { +#if defined(OS_CHROMEOS) + // On Chrome OS, chrome gets killed when it cannot finish shutdown quickly, + // and this function seems to be one of the slowest shutdown steps. + // Include some info to the report for investigation. crbug.com/418627 + // TODO(hashimoto): Remove this. + struct { + size_t data_size; + char path[128]; + } file_info; + file_info.data_size = data.size(); + base::strlcpy(file_info.path, path.value().c_str(), + arraysize(file_info.path)); + base::debug::Alias(&file_info); +#endif // Write the data to a temp file then rename to avoid data loss if we crash // while writing the file. Ensure that the temp file is on the same volume // as target file, so it can be moved in one step, and that the temp file @@ -104,7 +130,7 @@ ImportantFileWriter::ImportantFileWriter( commit_interval_(TimeDelta::FromMilliseconds(kDefaultCommitIntervalMs)), weak_factory_(this) { DCHECK(CalledOnValidThread()); - DCHECK(task_runner_.get()); + DCHECK(task_runner_); } ImportantFileWriter::~ImportantFileWriter() { @@ -119,9 +145,9 @@ bool ImportantFileWriter::HasPendingWrite() const { return timer_.IsRunning(); } -void ImportantFileWriter::WriteNow(const std::string& data) { +void ImportantFileWriter::WriteNow(scoped_ptr<std::string> data) { DCHECK(CalledOnValidThread()); - if (data.length() > static_cast<size_t>(kint32max)) { + if (data->length() > static_cast<size_t>(kint32max)) { NOTREACHED(); return; } @@ -129,13 +155,14 @@ void ImportantFileWriter::WriteNow(const std::string& data) { if (HasPendingWrite()) timer_.Stop(); - if (!PostWriteTask(data)) { + auto task = Bind(&WriteScopedStringToFileAtomically, path_, Passed(&data)); + if (!PostWriteTask(task)) { // Posting the task to background message loop is not expected // to fail, but if it does, avoid losing data and just hit the disk // on the current thread. NOTREACHED(); - WriteFileAtomically(path_, data); + task.Run(); } } @@ -153,9 +180,9 @@ void ImportantFileWriter::ScheduleWrite(DataSerializer* serializer) { void ImportantFileWriter::DoScheduledWrite() { DCHECK(serializer_); - std::string data; - if (serializer_->SerializeData(&data)) { - WriteNow(data); + scoped_ptr<std::string> data(new std::string); + if (serializer_->SerializeData(data.get())) { + WriteNow(data.Pass()); } else { DLOG(WARNING) << "failed to serialize data to be saved in " << path_.value().c_str(); @@ -169,7 +196,7 @@ void ImportantFileWriter::RegisterOnNextSuccessfulWriteCallback( on_next_successful_write_ = on_next_successful_write; } -bool ImportantFileWriter::PostWriteTask(const std::string& data) { +bool ImportantFileWriter::PostWriteTask(const Callback<bool()>& task) { // TODO(gab): This code could always use PostTaskAndReplyWithResult and let // ForwardSuccessfulWrite() no-op if |on_next_successful_write_| is null, but // PostTaskAndReply causes memory leaks in tests (crbug.com/371974) and @@ -179,16 +206,13 @@ bool ImportantFileWriter::PostWriteTask(const std::string& data) { return base::PostTaskAndReplyWithResult( task_runner_.get(), FROM_HERE, - MakeCriticalClosure( - Bind(&ImportantFileWriter::WriteFileAtomically, path_, data)), + MakeCriticalClosure(task), Bind(&ImportantFileWriter::ForwardSuccessfulWrite, weak_factory_.GetWeakPtr())); } return task_runner_->PostTask( FROM_HERE, - MakeCriticalClosure( - Bind(IgnoreResult(&ImportantFileWriter::WriteFileAtomically), - path_, data))); + MakeCriticalClosure(base::Bind(IgnoreResult(task)))); } void ImportantFileWriter::ForwardSuccessfulWrite(bool result) { diff --git a/chromium/base/files/important_file_writer.h b/chromium/base/files/important_file_writer.h index 9ead9c14bf2..99f1a7c6814 100644 --- a/chromium/base/files/important_file_writer.h +++ b/chromium/base/files/important_file_writer.h @@ -78,7 +78,7 @@ class BASE_EXPORT ImportantFileWriter : public NonThreadSafe { // Save |data| to target filename. Does not block. If there is a pending write // scheduled by ScheduleWrite, it is cancelled. - void WriteNow(const std::string& data); + void WriteNow(scoped_ptr<std::string> data); // Schedule a save to target filename. Data will be serialized and saved // to disk after the commit interval. If another ScheduleWrite is issued @@ -106,7 +106,7 @@ class BASE_EXPORT ImportantFileWriter : public NonThreadSafe { private: // Helper method for WriteNow(). - bool PostWriteTask(const std::string& data); + bool PostWriteTask(const Callback<bool()>& task); // If |result| is true and |on_next_successful_write_| is set, invokes // |on_successful_write_| and then resets it; no-ops otherwise. diff --git a/chromium/base/files/important_file_writer_unittest.cc b/chromium/base/files/important_file_writer_unittest.cc index 96d0d059bb8..d376cdc35ab 100644 --- a/chromium/base/files/important_file_writer_unittest.cc +++ b/chromium/base/files/important_file_writer_unittest.cc @@ -9,9 +9,11 @@ #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" +#include "base/location.h" #include "base/logging.h" -#include "base/message_loop/message_loop.h" #include "base/run_loop.h" +#include "base/single_thread_task_runner.h" +#include "base/thread_task_runner_handle.h" #include "base/threading/thread.h" #include "base/time/time.h" #include "testing/gtest/include/gtest/gtest.h" @@ -82,7 +84,7 @@ bool SuccessfulWriteObserver::GetAndResetObservationState() { class ImportantFileWriterTest : public testing::Test { public: ImportantFileWriterTest() { } - virtual void SetUp() { + void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); file_ = temp_dir_.path().AppendASCII("test-file"); } @@ -97,10 +99,10 @@ class ImportantFileWriterTest : public testing::Test { }; TEST_F(ImportantFileWriterTest, Basic) { - ImportantFileWriter writer(file_, MessageLoopProxy::current().get()); + ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get()); EXPECT_FALSE(PathExists(writer.path())); EXPECT_FALSE(successful_write_observer_.GetAndResetObservationState()); - writer.WriteNow("foo"); + writer.WriteNow(make_scoped_ptr(new std::string("foo"))); RunLoop().RunUntilIdle(); EXPECT_FALSE(successful_write_observer_.GetAndResetObservationState()); @@ -109,11 +111,11 @@ TEST_F(ImportantFileWriterTest, Basic) { } TEST_F(ImportantFileWriterTest, BasicWithSuccessfulWriteObserver) { - ImportantFileWriter writer(file_, MessageLoopProxy::current().get()); + ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get()); EXPECT_FALSE(PathExists(writer.path())); EXPECT_FALSE(successful_write_observer_.GetAndResetObservationState()); successful_write_observer_.ObserveNextSuccessfulWrite(&writer); - writer.WriteNow("foo"); + writer.WriteNow(make_scoped_ptr(new std::string("foo"))); RunLoop().RunUntilIdle(); // Confirm that the observer is invoked. @@ -124,7 +126,7 @@ TEST_F(ImportantFileWriterTest, BasicWithSuccessfulWriteObserver) { // Confirm that re-installing the observer works for another write. EXPECT_FALSE(successful_write_observer_.GetAndResetObservationState()); successful_write_observer_.ObserveNextSuccessfulWrite(&writer); - writer.WriteNow("bar"); + writer.WriteNow(make_scoped_ptr(new std::string("bar"))); RunLoop().RunUntilIdle(); EXPECT_TRUE(successful_write_observer_.GetAndResetObservationState()); @@ -134,7 +136,7 @@ TEST_F(ImportantFileWriterTest, BasicWithSuccessfulWriteObserver) { // Confirm that writing again without re-installing the observer doesn't // result in a notification. EXPECT_FALSE(successful_write_observer_.GetAndResetObservationState()); - writer.WriteNow("baz"); + writer.WriteNow(make_scoped_ptr(new std::string("baz"))); RunLoop().RunUntilIdle(); EXPECT_FALSE(successful_write_observer_.GetAndResetObservationState()); @@ -143,15 +145,14 @@ TEST_F(ImportantFileWriterTest, BasicWithSuccessfulWriteObserver) { } TEST_F(ImportantFileWriterTest, ScheduleWrite) { - ImportantFileWriter writer(file_, MessageLoopProxy::current().get()); + ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get()); writer.set_commit_interval(TimeDelta::FromMilliseconds(25)); EXPECT_FALSE(writer.HasPendingWrite()); DataSerializer serializer("foo"); writer.ScheduleWrite(&serializer); EXPECT_TRUE(writer.HasPendingWrite()); - MessageLoop::current()->PostDelayedTask( - FROM_HERE, - MessageLoop::QuitWhenIdleClosure(), + ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, MessageLoop::QuitWhenIdleClosure(), TimeDelta::FromMilliseconds(100)); MessageLoop::current()->Run(); EXPECT_FALSE(writer.HasPendingWrite()); @@ -160,15 +161,14 @@ TEST_F(ImportantFileWriterTest, ScheduleWrite) { } TEST_F(ImportantFileWriterTest, DoScheduledWrite) { - ImportantFileWriter writer(file_, MessageLoopProxy::current().get()); + ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get()); EXPECT_FALSE(writer.HasPendingWrite()); DataSerializer serializer("foo"); writer.ScheduleWrite(&serializer); EXPECT_TRUE(writer.HasPendingWrite()); writer.DoScheduledWrite(); - MessageLoop::current()->PostDelayedTask( - FROM_HERE, - MessageLoop::QuitWhenIdleClosure(), + ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, MessageLoop::QuitWhenIdleClosure(), TimeDelta::FromMilliseconds(100)); MessageLoop::current()->Run(); EXPECT_FALSE(writer.HasPendingWrite()); @@ -177,15 +177,14 @@ TEST_F(ImportantFileWriterTest, DoScheduledWrite) { } TEST_F(ImportantFileWriterTest, BatchingWrites) { - ImportantFileWriter writer(file_, MessageLoopProxy::current().get()); + ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get()); writer.set_commit_interval(TimeDelta::FromMilliseconds(25)); DataSerializer foo("foo"), bar("bar"), baz("baz"); writer.ScheduleWrite(&foo); writer.ScheduleWrite(&bar); writer.ScheduleWrite(&baz); - MessageLoop::current()->PostDelayedTask( - FROM_HERE, - MessageLoop::QuitWhenIdleClosure(), + ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, MessageLoop::QuitWhenIdleClosure(), TimeDelta::FromMilliseconds(100)); MessageLoop::current()->Run(); ASSERT_TRUE(PathExists(writer.path())); diff --git a/chromium/base/files/memory_mapped_file.cc b/chromium/base/files/memory_mapped_file.cc index 745a5ff1f45..891fcffc4cb 100644 --- a/chromium/base/files/memory_mapped_file.cc +++ b/chromium/base/files/memory_mapped_file.cc @@ -31,6 +31,7 @@ MemoryMappedFile::~MemoryMappedFile() { CloseHandles(); } +#if !defined(OS_NACL) bool MemoryMappedFile::Initialize(const FilePath& file_name) { if (IsValid()) return false; @@ -85,5 +86,6 @@ void MemoryMappedFile::CalculateVMAlignedBoundaries(int64 start, *aligned_start = start & ~mask; *aligned_size = (size + *offset + mask) & ~mask; } +#endif } // namespace base diff --git a/chromium/base/files/memory_mapped_file_posix.cc b/chromium/base/files/memory_mapped_file_posix.cc index ebf38779f03..168da923d46 100644 --- a/chromium/base/files/memory_mapped_file_posix.cc +++ b/chromium/base/files/memory_mapped_file_posix.cc @@ -16,6 +16,7 @@ namespace base { MemoryMappedFile::MemoryMappedFile() : data_(NULL), length_(0) { } +#if !defined(OS_NACL) bool MemoryMappedFile::MapFileRegionToMemory( const MemoryMappedFile::Region& region) { ThreadRestrictions::AssertIOAllowed(); @@ -74,6 +75,7 @@ bool MemoryMappedFile::MapFileRegionToMemory( data_ += data_offset; return true; } +#endif void MemoryMappedFile::CloseHandles() { ThreadRestrictions::AssertIOAllowed(); diff --git a/chromium/base/files/memory_mapped_file_unittest.cc b/chromium/base/files/memory_mapped_file_unittest.cc index 36999bfba92..d0833b5cc9a 100644 --- a/chromium/base/files/memory_mapped_file_unittest.cc +++ b/chromium/base/files/memory_mapped_file_unittest.cc @@ -29,12 +29,12 @@ bool CheckBufferContents(const uint8* data, size_t size, size_t offset) { class MemoryMappedFileTest : public PlatformTest { protected: - virtual void SetUp() override { + void SetUp() override { PlatformTest::SetUp(); CreateTemporaryFile(&temp_file_path_); } - virtual void TearDown() { EXPECT_TRUE(DeleteFile(temp_file_path_, false)); } + void TearDown() override { EXPECT_TRUE(DeleteFile(temp_file_path_, false)); } void CreateTemporaryTestFile(size_t size) { File file(temp_file_path_, diff --git a/chromium/base/float_util.h b/chromium/base/float_util.h deleted file mode 100644 index 90273106cd6..00000000000 --- a/chromium/base/float_util.h +++ /dev/null @@ -1,36 +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_FLOAT_UTIL_H_ -#define BASE_FLOAT_UTIL_H_ - -#include "build/build_config.h" - -#include <float.h> - -#include <cmath> - -namespace base { - -template <typename Float> -inline bool IsFinite(const Float& number) { -#if defined(OS_POSIX) - return std::isfinite(number) != 0; -#elif defined(OS_WIN) - return _finite(number) != 0; -#endif -} - -template <typename Float> -inline bool IsNaN(const Float& number) { -#if defined(OS_POSIX) - return std::isnan(number) != 0; -#elif defined(OS_WIN) - return _isnan(number) != 0; -#endif -} - -} // namespace base - -#endif // BASE_FLOAT_UTIL_H_ diff --git a/chromium/base/format_macros.h b/chromium/base/format_macros.h index 4d90c593a61..d58658d2b91 100644 --- a/chromium/base/format_macros.h +++ b/chromium/base/format_macros.h @@ -23,19 +23,20 @@ #include "build/build_config.h" -#if defined(OS_POSIX) - -#if (defined(_INTTYPES_H) || defined(_INTTYPES_H_)) && !defined(PRId64) +#if defined(OS_POSIX) && (defined(_INTTYPES_H) || defined(_INTTYPES_H_)) && \ + !defined(PRId64) #error "inttypes.h has already been included before this header file, but " #error "without __STDC_FORMAT_MACROS defined." #endif -#if !defined(__STDC_FORMAT_MACROS) +#if defined(OS_POSIX) && !defined(__STDC_FORMAT_MACROS) #define __STDC_FORMAT_MACROS #endif #include <inttypes.h> +#if defined(OS_POSIX) + // GCC will concatenate wide and narrow strings correctly, so nothing needs to // be done here. #define WidePRId64 PRId64 @@ -76,16 +77,8 @@ #else // OS_WIN -#if !defined(PRId64) -#define PRId64 "I64d" -#endif - -#if !defined(PRIu64) -#define PRIu64 "I64u" -#endif - -#if !defined(PRIx64) -#define PRIx64 "I64x" +#if !defined(PRId64) || !defined(PRIu64) || !defined(PRIx64) +#error "inttypes.h provided by win toolchain should define these." #endif #define WidePRId64 L"I64d" diff --git a/chromium/base/guid_posix.cc b/chromium/base/guid_posix.cc index 9681b084fa3..f0fedc27e57 100644 --- a/chromium/base/guid_posix.cc +++ b/chromium/base/guid_posix.cc @@ -39,4 +39,4 @@ std::string RandomDataToGUIDString(const uint64 bytes[2]) { bytes[1] & 0x0000ffffffffffffULL); } -} // namespace guid +} // namespace base diff --git a/chromium/base/guid_win.cc b/chromium/base/guid_win.cc index cdb860a0b9f..da3453dc73c 100644 --- a/chromium/base/guid_win.cc +++ b/chromium/base/guid_win.cc @@ -35,4 +35,4 @@ std::string GenerateGUID() { return WideToUTF8(guid_string.substr(1, guid_string.length() - 2)); } -} // namespace guid +} // namespace base diff --git a/chromium/base/i18n/break_iterator.cc b/chromium/base/i18n/break_iterator.cc index e3aaa2b5b0b..e2ed667572f 100644 --- a/chromium/base/i18n/break_iterator.cc +++ b/chromium/base/i18n/break_iterator.cc @@ -74,7 +74,8 @@ bool BreakIterator::Init() { static_cast<int32_t>(string_.size()), &status); if (U_FAILURE(status)) { - NOTREACHED() << "ubrk_open failed"; + NOTREACHED() << "ubrk_open failed for type " << break_type + << " with error " << status; } } diff --git a/chromium/base/i18n/build_utf8_validator_tables.cc b/chromium/base/i18n/build_utf8_validator_tables.cc index 45fc6cbf92b..ae5b1a71e9d 100644 --- a/chromium/base/i18n/build_utf8_validator_tables.cc +++ b/chromium/base/i18n/build_utf8_validator_tables.cc @@ -248,7 +248,7 @@ void MoveAllCharsToSets(PairVector* pairs) { for (int i = 0; i < 4; ++i) { MoveRightMostCharToSet(pairs); } -#if DCHECK_IS_ON +#if DCHECK_IS_ON() for (PairVector::const_iterator it = pairs->begin(); it != pairs->end(); ++it) { DCHECK(it->character.empty()); @@ -425,16 +425,16 @@ void PrintStates(const std::vector<State>& states, FILE* stream) { } // namespace int main(int argc, char* argv[]) { - CommandLine::Init(argc, argv); + base::CommandLine::Init(argc, argv); logging::LoggingSettings settings; settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; logging::InitLogging(settings); - if (CommandLine::ForCurrentProcess()->HasSwitch("help")) { + if (base::CommandLine::ForCurrentProcess()->HasSwitch("help")) { fwrite(kHelpText, 1, arraysize(kHelpText), stdout); exit(EXIT_SUCCESS); } base::FilePath filename = - CommandLine::ForCurrentProcess()->GetSwitchValuePath("output"); + base::CommandLine::ForCurrentProcess()->GetSwitchValuePath("output"); FILE* output = stdout; if (!filename.empty()) { diff --git a/chromium/base/i18n/char_iterator.h b/chromium/base/i18n/char_iterator.h index 46928b37d0b..8174ef48f22 100644 --- a/chromium/base/i18n/char_iterator.h +++ b/chromium/base/i18n/char_iterator.h @@ -15,7 +15,7 @@ // UTF16 strings. Example usage: // // UTF8CharIterator iter(&str); -// while (!iter.End()) { +// while (!iter.end()) { // VLOG(1) << iter.get(); // iter.Advance(); // } diff --git a/chromium/base/i18n/file_util_icu.cc b/chromium/base/i18n/file_util_icu.cc index b5c0b9d8f5f..f6e2c2956f4 100644 --- a/chromium/base/i18n/file_util_icu.cc +++ b/chromium/base/i18n/file_util_icu.cc @@ -30,12 +30,19 @@ class IllegalCharacters { return Singleton<IllegalCharacters>::get(); } - bool contains(UChar32 ucs4) { - return !!set->contains(ucs4); + bool DisallowedEverywhere(UChar32 ucs4) { + return !!illegal_anywhere_->contains(ucs4); } - bool containsNone(const string16 &s) { - return !!set->containsNone(icu::UnicodeString(s.c_str(), s.size())); + bool DisallowedLeadingOrTrailing(UChar32 ucs4) { + return !!illegal_at_ends_->contains(ucs4); + } + + bool IsAllowedName(const string16& s) { + return s.empty() || (!!illegal_anywhere_->containsNone( + icu::UnicodeString(s.c_str(), s.size())) && + !illegal_at_ends_->contains(*s.begin()) && + !illegal_at_ends_->contains(*s.rbegin())); } private: @@ -45,60 +52,61 @@ class IllegalCharacters { IllegalCharacters(); ~IllegalCharacters() { } - scoped_ptr<icu::UnicodeSet> set; + // set of characters considered invalid anywhere inside a filename. + scoped_ptr<icu::UnicodeSet> illegal_anywhere_; + + // set of characters considered invalid at either end of a filename. + scoped_ptr<icu::UnicodeSet> illegal_at_ends_; DISALLOW_COPY_AND_ASSIGN(IllegalCharacters); }; IllegalCharacters::IllegalCharacters() { - UErrorCode status = U_ZERO_ERROR; - // Control characters, formatting characters, non-characters, and - // some printable ASCII characters regarded as dangerous ('"*/:<>?\\'). + UErrorCode everywhere_status = U_ZERO_ERROR; + UErrorCode ends_status = U_ZERO_ERROR; + // Control characters, formatting characters, non-characters, path separators, + // and some printable ASCII characters regarded as dangerous ('"*/:<>?\\'). // See http://blogs.msdn.com/michkap/archive/2006/11/03/941420.aspx // and http://msdn2.microsoft.com/en-us/library/Aa365247.aspx - // TODO(jungshik): Revisit the set. ZWJ and ZWNJ are excluded because they - // are legitimate in Arabic and some S/SE Asian scripts. However, when used - // elsewhere, they can be confusing/problematic. - // Also, consider wrapping the set with our Singleton class to create and - // freeze it only once. Note that there's a trade-off between memory and - // speed. -#if defined(WCHAR_T_IS_UTF16) - set.reset(new icu::UnicodeSet(icu::UnicodeString( - L"[[\"*/:<>?\\\\|][:Cc:][:Cf:] - [\u200c\u200d]]"), status)); -#else - set.reset(new icu::UnicodeSet(UNICODE_STRING_SIMPLE( - "[[\"*/:<>?\\\\|][:Cc:][:Cf:] - [\\u200c\\u200d]]").unescape(), - status)); -#endif - DCHECK(U_SUCCESS(status)); + // Note that code points in the "Other, Format" (Cf) category are ignored on + // HFS+ despite the ZERO_WIDTH_JOINER and ZERO_WIDTH_NON-JOINER being + // legitimate in Arabic and some S/SE Asian scripts. In addition tilde (~) is + // also excluded due to the possibility of interacting poorly with short + // filenames on VFAT. (Related to CVE-2014-9390) + illegal_anywhere_.reset(new icu::UnicodeSet( + UNICODE_STRING_SIMPLE("[[\"~*/:<>?\\\\|][:Cc:][:Cf:]]"), + everywhere_status)); + illegal_at_ends_.reset(new icu::UnicodeSet( + UNICODE_STRING_SIMPLE("[[:WSpace:][.]]"), ends_status)); + DCHECK(U_SUCCESS(everywhere_status)); + DCHECK(U_SUCCESS(ends_status)); + // Add non-characters. If this becomes a performance bottleneck by // any chance, do not add these to |set| and change IsFilenameLegal() // to check |ucs4 & 0xFFFEu == 0xFFFEu|, in addiition to calling - // containsNone(). - set->add(0xFDD0, 0xFDEF); + // IsAllowedName(). + illegal_anywhere_->add(0xFDD0, 0xFDEF); for (int i = 0; i <= 0x10; ++i) { int plane_base = 0x10000 * i; - set->add(plane_base + 0xFFFE, plane_base + 0xFFFF); + illegal_anywhere_->add(plane_base + 0xFFFE, plane_base + 0xFFFF); } - set->freeze(); + illegal_anywhere_->freeze(); + illegal_at_ends_->freeze(); } } // namespace bool IsFilenameLegal(const string16& file_name) { - return IllegalCharacters::GetInstance()->containsNone(file_name); + return IllegalCharacters::GetInstance()->IsAllowedName(file_name); } void ReplaceIllegalCharactersInPath(FilePath::StringType* file_name, char replace_char) { - DCHECK(file_name); - - DCHECK(!(IllegalCharacters::GetInstance()->contains(replace_char))); + IllegalCharacters* illegal = IllegalCharacters::GetInstance(); - // Remove leading and trailing whitespace. - TrimWhitespace(*file_name, TRIM_ALL, file_name); + DCHECK(!(illegal->DisallowedEverywhere(replace_char))); + DCHECK(!(illegal->DisallowedLeadingOrTrailing(replace_char))); - IllegalCharacters* illegal = IllegalCharacters::GetInstance(); int cursor = 0; // The ICU macros expect an int. while (cursor < static_cast<int>(file_name->size())) { int char_begin = cursor; @@ -122,7 +130,9 @@ void ReplaceIllegalCharactersInPath(FilePath::StringType* file_name, NOTREACHED(); #endif - if (illegal->contains(code_point)) { + if (illegal->DisallowedEverywhere(code_point) || + ((char_begin == 0 || cursor == static_cast<int>(file_name->length())) && + illegal->DisallowedLeadingOrTrailing(code_point))) { file_name->replace(char_begin, cursor - char_begin, 1, replace_char); // We just made the potentially multi-byte/word char into one that only // takes one byte/word, so need to adjust the cursor to point to the next @@ -142,16 +152,15 @@ bool LocaleAwareCompareFilenames(const FilePath& a, const FilePath& b) { collator->setStrength(icu::Collator::TERTIARY); #if defined(OS_WIN) - return CompareString16WithCollator(collator.get(), - WideToUTF16(a.value()), WideToUTF16(b.value())) == UCOL_LESS; + return CompareString16WithCollator(*collator, WideToUTF16(a.value()), + WideToUTF16(b.value())) == UCOL_LESS; #elif defined(OS_POSIX) // On linux, the file system encoding is not defined. We assume // SysNativeMBToWide takes care of it. return CompareString16WithCollator( - collator.get(), - WideToUTF16(SysNativeMBToWide(a.value().c_str())), - WideToUTF16(SysNativeMBToWide(b.value().c_str()))) == UCOL_LESS; + *collator, WideToUTF16(SysNativeMBToWide(a.value().c_str())), + WideToUTF16(SysNativeMBToWide(b.value().c_str()))) == UCOL_LESS; #else #error Not implemented on your system #endif diff --git a/chromium/base/i18n/file_util_icu.h b/chromium/base/i18n/file_util_icu.h index 7dd94cd9b06..6d7f2ec6000 100644 --- a/chromium/base/i18n/file_util_icu.h +++ b/chromium/base/i18n/file_util_icu.h @@ -18,13 +18,23 @@ namespace i18n { // param has the same restriction as that for ReplaceIllegalCharacters. BASE_I18N_EXPORT bool IsFilenameLegal(const string16& file_name); -// Replaces characters in 'file_name' that are illegal for file names with -// 'replace_char'. 'file_name' must not be a full or relative path, but just the +// Replaces characters in |file_name| that are illegal for file names with +// |replace_char|. |file_name| must not be a full or relative path, but just the // file name component (since slashes are considered illegal). Any leading or -// trailing whitespace in 'file_name' is removed. +// trailing whitespace or periods in |file_name| is also replaced with the +// |replace_char|. +// // Example: -// file_name == "bad:file*name?.txt", changed to: "bad-file-name-.txt" when -// 'replace_char' is '-'. +// "bad:file*name?.txt" will be turned into "bad_file_name_.txt" when +// |replace_char| is '_'. +// +// Warning: Do not use this function as the sole means of sanitizing a filename. +// While the resulting filename itself would be legal, it doesn't necessarily +// mean that the file will behave safely. On Windows, certain reserved names +// refer to devices rather than files (E.g. LPT1), and some filenames could be +// interpreted as shell namespace extensions (E.g. Foo.{<GUID>}). +// +// TODO(asanka): Move full filename sanitization logic here. BASE_I18N_EXPORT void ReplaceIllegalCharactersInPath( FilePath::StringType* file_name, char replace_char); diff --git a/chromium/base/i18n/file_util_icu_unittest.cc b/chromium/base/i18n/file_util_icu_unittest.cc index 369345b6add..8fa7f6a261d 100644 --- a/chromium/base/i18n/file_util_icu_unittest.cc +++ b/chromium/base/i18n/file_util_icu_unittest.cc @@ -20,24 +20,28 @@ class FileUtilICUTest : public PlatformTest { #if defined(OS_POSIX) && !defined(OS_MACOSX) // Linux disallows some evil ASCII characters, but passes all non-ASCII. -static const struct goodbad_pair { +static const struct GoodBadPairLinux { const char* bad_name; const char* good_name; -} kIllegalCharacterCases[] = { - {"bad*file:name?.jpg", "bad-file-name-.jpg"}, +} kLinuxIllegalCharacterCases[] = { + {"bad*\\/file:name?.jpg", "bad---file-name-.jpg"}, {"**********::::.txt", "--------------.txt"}, {"\xe9\xf0zzzz.\xff", "\xe9\xf0zzzz.\xff"}, + {" _ ", "-_-"}, + {".", "-"}, + {" .( ). ", "-.( ).-"}, + {" ", "- -"}, }; TEST_F(FileUtilICUTest, ReplaceIllegalCharacersInPathLinuxTest) { - for (size_t i = 0; i < arraysize(kIllegalCharacterCases); ++i) { - std::string bad_name(kIllegalCharacterCases[i].bad_name); + for (size_t i = 0; i < arraysize(kLinuxIllegalCharacterCases); ++i) { + std::string bad_name(kLinuxIllegalCharacterCases[i].bad_name); ReplaceIllegalCharactersInPath(&bad_name, '-'); - EXPECT_EQ(kIllegalCharacterCases[i].good_name, bad_name); + EXPECT_EQ(kLinuxIllegalCharacterCases[i].good_name, bad_name); } } -#else +#endif // For Mac & Windows, which both do Unicode validation on filenames. These // characters are given as wide strings since its more convenient to specify @@ -46,29 +50,36 @@ static const struct goodbad_pair { const wchar_t* bad_name; const wchar_t* good_name; } kIllegalCharacterCases[] = { - {L"bad*file:name?.jpg", L"bad-file-name-.jpg"}, - {L"**********::::.txt", L"--------------.txt"}, - // We can't use UCNs (universal character names) for C0/C1 characters and - // U+007F, but \x escape is interpreted by MSVC and gcc as we intend. - {L"bad\x0003\x0091 file\u200E\u200Fname.png", L"bad-- file--name.png"}, -#if defined(OS_WIN) - {L"bad*file\\name.jpg", L"bad-file-name.jpg"}, - {L"\t bad*file\\name/.jpg ", L"bad-file-name-.jpg"}, -#elif defined(OS_MACOSX) - {L"bad*file?name.jpg", L"bad-file-name.jpg"}, - {L"\t bad*file?name/.jpg ", L"bad-file-name-.jpg"}, -#endif - {L"this_file_name is okay!.mp3", L"this_file_name is okay!.mp3"}, - {L"\u4E00\uAC00.mp3", L"\u4E00\uAC00.mp3"}, - {L"\u0635\u200C\u0644.mp3", L"\u0635\u200C\u0644.mp3"}, - {L"\U00010330\U00010331.mp3", L"\U00010330\U00010331.mp3"}, - // Unassigned codepoints are ok. - {L"\u0378\U00040001.mp3", L"\u0378\U00040001.mp3"}, - // Non-characters are not allowed. - {L"bad\uFFFFfile\U0010FFFEname.jpg ", L"bad-file-name.jpg"}, - {L"bad\uFDD0file\uFDEFname.jpg ", L"bad-file-name.jpg"}, + {L"bad*file:name?.jpg", L"bad-file-name-.jpg"}, + {L"**********::::.txt", L"--------------.txt"}, + // We can't use UCNs (universal character names) for C0/C1 characters and + // U+007F, but \x escape is interpreted by MSVC and gcc as we intend. + {L"bad\x0003\x0091 file\u200E\u200Fname.png", L"bad-- file--name.png"}, + {L"bad*file\\?name.jpg", L"bad-file--name.jpg"}, + {L"\t bad*file\\name/.jpg", L"- bad-file-name-.jpg"}, + {L"this_file_name is okay!.mp3", L"this_file_name is okay!.mp3"}, + {L"\u4E00\uAC00.mp3", L"\u4E00\uAC00.mp3"}, + {L"\u0635\u200C\u0644.mp3", L"\u0635-\u0644.mp3"}, + {L"\U00010330\U00010331.mp3", L"\U00010330\U00010331.mp3"}, + // Unassigned codepoints are ok. + {L"\u0378\U00040001.mp3", L"\u0378\U00040001.mp3"}, + // Non-characters are not allowed. + {L"bad\uFFFFfile\U0010FFFEname.jpg", L"bad-file-name.jpg"}, + {L"bad\uFDD0file\uFDEFname.jpg", L"bad-file-name.jpg"}, + // CVE-2014-9390 + {L"(\u200C.\u200D.\u200E.\u200F.\u202A.\u202B.\u202C.\u202D.\u202E.\u206A." + L"\u206B.\u206C.\u206D.\u206F.\uFEFF)", + L"(-.-.-.-.-.-.-.-.-.-.-.-.-.-.-)"}, + {L"config~1", L"config-1"}, + {L" _ ", L"-_-"}, + {L" ", L"-"}, + {L"\u2008.(\u2007).\u3000", L"-.(\u2007).-"}, + {L" ", L"- -"}, + {L". ", L"- -"} }; +#if defined(OS_WIN) || defined(OS_MACOSX) + TEST_F(FileUtilICUTest, ReplaceIllegalCharactersInPathTest) { for (size_t i = 0; i < arraysize(kIllegalCharacterCases); ++i) { #if defined(OS_WIN) @@ -85,6 +96,19 @@ TEST_F(FileUtilICUTest, ReplaceIllegalCharactersInPathTest) { #endif +TEST_F(FileUtilICUTest, IsFilenameLegalTest) { + EXPECT_TRUE(IsFilenameLegal(string16())); + + for (const auto& test_case : kIllegalCharacterCases) { + string16 bad_name = WideToUTF16(test_case.bad_name); + string16 good_name = WideToUTF16(test_case.good_name); + + EXPECT_TRUE(IsFilenameLegal(good_name)) << good_name; + if (good_name != bad_name) + EXPECT_FALSE(IsFilenameLegal(bad_name)) << bad_name; + } +} + #if defined(OS_CHROMEOS) static const struct normalize_name_encoding_test_cases { const char* original_path; diff --git a/chromium/base/i18n/i18n_constants.cc b/chromium/base/i18n/i18n_constants.cc index 9b8c571564f..7d2f5fc0538 100644 --- a/chromium/base/i18n/i18n_constants.cc +++ b/chromium/base/i18n/i18n_constants.cc @@ -8,8 +8,6 @@ namespace base { const char kCodepageLatin1[] = "ISO-8859-1"; const char kCodepageUTF8[] = "UTF-8"; -const char kCodepageUTF16BE[] = "UTF-16BE"; -const char kCodepageUTF16LE[] = "UTF-16LE"; } // namespace base diff --git a/chromium/base/i18n/i18n_constants.h b/chromium/base/i18n/i18n_constants.h index c2de842da1d..c1bd87dc485 100644 --- a/chromium/base/i18n/i18n_constants.h +++ b/chromium/base/i18n/i18n_constants.h @@ -12,8 +12,9 @@ namespace base { // Names of codepages (charsets) understood by icu. BASE_I18N_EXPORT extern const char kCodepageLatin1[]; // a.k.a. ISO 8859-1 BASE_I18N_EXPORT extern const char kCodepageUTF8[]; -BASE_I18N_EXPORT extern const char kCodepageUTF16BE[]; -BASE_I18N_EXPORT extern const char kCodepageUTF16LE[]; + +// The other possible options are UTF-16BE and UTF-16LE, but they are unused in +// Chromium as of this writing. } // namespace base diff --git a/chromium/base/i18n/icu_string_conversions.cc b/chromium/base/i18n/icu_string_conversions.cc index 1530117f1e4..edb31c3525f 100644 --- a/chromium/base/i18n/icu_string_conversions.cc +++ b/chromium/base/i18n/icu_string_conversions.cc @@ -134,14 +134,6 @@ void SetUpErrorHandlerForToUChars(OnStringConversionError::Type on_error, } } -inline UConverterType utf32_platform_endian() { -#if U_IS_BIG_ENDIAN - return UCNV_UTF32_BigEndian; -#else - return UCNV_UTF32_LittleEndian; -#endif -} - } // namespace // Codepage <-> Wide/UTF-16 --------------------------------------------------- @@ -197,74 +189,6 @@ bool CodepageToUTF16(const std::string& encoded, return true; } -bool WideToCodepage(const std::wstring& wide, - const char* codepage_name, - OnStringConversionError::Type on_error, - std::string* encoded) { -#if defined(WCHAR_T_IS_UTF16) - return UTF16ToCodepage(wide, codepage_name, on_error, encoded); -#elif defined(WCHAR_T_IS_UTF32) - encoded->clear(); - - UErrorCode status = U_ZERO_ERROR; - UConverter* converter = ucnv_open(codepage_name, &status); - if (!U_SUCCESS(status)) - return false; - - int utf16_len; - // When wchar_t is wider than UChar (16 bits), transform |wide| into a - // UChar* string. Size the UChar* buffer to be large enough to hold twice - // as many UTF-16 code units (UChar's) as there are Unicode code points, - // in case each code points translates to a UTF-16 surrogate pair, - // and leave room for a NUL terminator. - std::vector<UChar> utf16(wide.length() * 2 + 1); - u_strFromUTF32(&utf16[0], utf16.size(), &utf16_len, - reinterpret_cast<const UChar32*>(wide.c_str()), - wide.length(), &status); - DCHECK(U_SUCCESS(status)) << "failed to convert wstring to UChar*"; - - return ConvertFromUTF16(converter, &utf16[0], utf16_len, on_error, encoded); -#endif // defined(WCHAR_T_IS_UTF32) -} - -bool CodepageToWide(const std::string& encoded, - const char* codepage_name, - OnStringConversionError::Type on_error, - std::wstring* wide) { -#if defined(WCHAR_T_IS_UTF16) - return CodepageToUTF16(encoded, codepage_name, on_error, wide); -#elif defined(WCHAR_T_IS_UTF32) - wide->clear(); - - UErrorCode status = U_ZERO_ERROR; - UConverter* converter = ucnv_open(codepage_name, &status); - if (!U_SUCCESS(status)) - return false; - - // The maximum length in 4 byte unit of UTF-32 output would be - // at most the same as the number of bytes in input. In the worst - // case of GB18030 (excluding escaped-based encodings like ISO-2022-JP), - // this can be 4 times larger than actually needed. - size_t wchar_max_length = encoded.length() + 1; - - SetUpErrorHandlerForToUChars(on_error, converter, &status); - scoped_ptr<wchar_t[]> buffer(new wchar_t[wchar_max_length]); - int actual_size = ucnv_toAlgorithmic(utf32_platform_endian(), converter, - reinterpret_cast<char*>(buffer.get()), - static_cast<int>(wchar_max_length) * sizeof(wchar_t), encoded.data(), - static_cast<int>(encoded.length()), &status); - ucnv_close(converter); - if (!U_SUCCESS(status)) { - wide->clear(); // Make sure the output is empty on error. - return false; - } - - // actual_size is # of bytes. - wide->assign(buffer.get(), actual_size / sizeof(wchar_t)); - return true; -#endif // defined(WCHAR_T_IS_UTF32) -} - bool ConvertToUtf8AndNormalize(const std::string& text, const std::string& charset, std::string* result) { diff --git a/chromium/base/i18n/icu_string_conversions.h b/chromium/base/i18n/icu_string_conversions.h index fd2ae46c529..9135da86a21 100644 --- a/chromium/base/i18n/icu_string_conversions.h +++ b/chromium/base/i18n/icu_string_conversions.h @@ -13,8 +13,7 @@ namespace base { -// Defines the error handling modes of UTF16ToCodepage, CodepageToUTF16, -// WideToCodepage and CodepageToWide. +// Defines the error handling modes of UTF16ToCodepage and CodepageToUTF16. class OnStringConversionError { public: enum Type { @@ -47,18 +46,6 @@ BASE_I18N_EXPORT bool CodepageToUTF16(const std::string& encoded, OnStringConversionError::Type on_error, string16* utf16); -// Converts between wide strings and the encoding specified. If the -// encoding doesn't exist or the encoding fails (when on_error is FAIL), -// returns false. -BASE_I18N_EXPORT bool WideToCodepage(const std::wstring& wide, - const char* codepage_name, - OnStringConversionError::Type on_error, - std::string* encoded); -BASE_I18N_EXPORT bool CodepageToWide(const std::string& encoded, - const char* codepage_name, - OnStringConversionError::Type on_error, - std::wstring* wide); - // Converts from any codepage to UTF-8 and ensures the resulting UTF-8 is // normalized. BASE_I18N_EXPORT bool ConvertToUtf8AndNormalize(const std::string& text, diff --git a/chromium/base/i18n/icu_string_conversions_unittest.cc b/chromium/base/i18n/icu_string_conversions_unittest.cc index d4d32518575..821529ddcee 100644 --- a/chromium/base/i18n/icu_string_conversions_unittest.cc +++ b/chromium/base/i18n/icu_string_conversions_unittest.cc @@ -42,49 +42,8 @@ string16 BuildString16(const wchar_t* s) { #endif } -const wchar_t* const kConvertRoundtripCases[] = { - L"Google Video", - // "网页 图片 资讯更多 »" - L"\x7f51\x9875\x0020\x56fe\x7247\x0020\x8d44\x8baf\x66f4\x591a\x0020\x00bb", - // "Παγκόσμιος Ιστός" - L"\x03a0\x03b1\x03b3\x03ba\x03cc\x03c3\x03bc\x03b9" - L"\x03bf\x03c2\x0020\x0399\x03c3\x03c4\x03cc\x03c2", - // "Поиск страниц на русском" - L"\x041f\x043e\x0438\x0441\x043a\x0020\x0441\x0442" - L"\x0440\x0430\x043d\x0438\x0446\x0020\x043d\x0430" - L"\x0020\x0440\x0443\x0441\x0441\x043a\x043e\x043c", - // "전체서비스" - L"\xc804\xccb4\xc11c\xbe44\xc2a4", - - // Test characters that take more than 16 bits. This will depend on whether - // wchar_t is 16 or 32 bits. -#if defined(WCHAR_T_IS_UTF16) - L"\xd800\xdf00", - // ????? (Mathematical Alphanumeric Symbols (U+011d40 - U+011d44 : A,B,C,D,E) - L"\xd807\xdd40\xd807\xdd41\xd807\xdd42\xd807\xdd43\xd807\xdd44", -#elif defined(WCHAR_T_IS_UTF32) - L"\x10300", - // ????? (Mathematical Alphanumeric Symbols (U+011d40 - U+011d44 : A,B,C,D,E) - L"\x11d40\x11d41\x11d42\x11d43\x11d44", -#endif -}; - } // namespace -TEST(ICUStringConversionsTest, ConvertCodepageUTF8) { - // Make sure WideToCodepage works like WideToUTF8. - for (size_t i = 0; i < arraysize(kConvertRoundtripCases); ++i) { - SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]: %ls", - i, kConvertRoundtripCases[i])); - - std::string expected(WideToUTF8(kConvertRoundtripCases[i])); - std::string utf8; - EXPECT_TRUE(WideToCodepage(kConvertRoundtripCases[i], kCodepageUTF8, - OnStringConversionError::SKIP, &utf8)); - EXPECT_EQ(expected, utf8); - } -} - // kConverterCodepageCases is not comprehensive. There are a number of cases // to add if we really want to have a comprehensive coverage of various // codepages and their 'idiosyncrasies'. Currently, the only implementation @@ -233,73 +192,6 @@ static const struct { NULL}, }; -TEST(ICUStringConversionsTest, ConvertBetweenCodepageAndWide) { - for (size_t i = 0; i < arraysize(kConvertCodepageCases); ++i) { - SCOPED_TRACE(base::StringPrintf( - "Test[%" PRIuS "]: <encoded: %s> <codepage: %s>", i, - kConvertCodepageCases[i].encoded, - kConvertCodepageCases[i].codepage_name)); - - std::wstring wide; - bool success = CodepageToWide(kConvertCodepageCases[i].encoded, - kConvertCodepageCases[i].codepage_name, - kConvertCodepageCases[i].on_error, - &wide); - EXPECT_EQ(kConvertCodepageCases[i].success, success); - EXPECT_EQ(kConvertCodepageCases[i].wide, wide); - - // When decoding was successful and nothing was skipped, we also check the - // reverse conversion. Not all conversions are round-trippable, but - // kConverterCodepageCases does not have any one-way conversion at the - // moment. - if (success && - kConvertCodepageCases[i].on_error == - OnStringConversionError::FAIL) { - std::string encoded; - success = WideToCodepage(wide, kConvertCodepageCases[i].codepage_name, - kConvertCodepageCases[i].on_error, &encoded); - EXPECT_EQ(kConvertCodepageCases[i].success, success); - EXPECT_EQ(kConvertCodepageCases[i].encoded, encoded); - } - } - - // The above cases handled codepage->wide errors, but not wide->codepage. - // Test that here. - std::string encoded("Temp data"); // Make sure the string gets cleared. - - // First test going to an encoding that can not represent that character. - EXPECT_FALSE(WideToCodepage(L"Chinese\xff27", "iso-8859-1", - OnStringConversionError::FAIL, &encoded)); - EXPECT_TRUE(encoded.empty()); - EXPECT_TRUE(WideToCodepage(L"Chinese\xff27", "iso-8859-1", - OnStringConversionError::SKIP, &encoded)); - EXPECT_STREQ("Chinese", encoded.c_str()); - // From Unicode, SUBSTITUTE is the same as SKIP for now. - EXPECT_TRUE(WideToCodepage(L"Chinese\xff27", "iso-8859-1", - OnStringConversionError::SUBSTITUTE, - &encoded)); - EXPECT_STREQ("Chinese", encoded.c_str()); - -#if defined(WCHAR_T_IS_UTF16) - // When we're in UTF-16 mode, test an invalid UTF-16 character in the input. - EXPECT_FALSE(WideToCodepage(L"a\xd800z", "iso-8859-1", - OnStringConversionError::FAIL, &encoded)); - EXPECT_TRUE(encoded.empty()); - EXPECT_TRUE(WideToCodepage(L"a\xd800z", "iso-8859-1", - OnStringConversionError::SKIP, &encoded)); - EXPECT_STREQ("az", encoded.c_str()); -#endif // WCHAR_T_IS_UTF16 - - // Invalid characters should fail. - EXPECT_TRUE(WideToCodepage(L"a\xffffz", "iso-8859-1", - OnStringConversionError::SKIP, &encoded)); - EXPECT_STREQ("az", encoded.c_str()); - - // Invalid codepages should fail. - EXPECT_FALSE(WideToCodepage(L"Hello, world", "awesome-8571-2", - OnStringConversionError::SKIP, &encoded)); -} - TEST(ICUStringConversionsTest, ConvertBetweenCodepageAndUTF16) { for (size_t i = 0; i < arraysize(kConvertCodepageCases); ++i) { SCOPED_TRACE(base::StringPrintf( diff --git a/chromium/base/i18n/icu_util.cc b/chromium/base/i18n/icu_util.cc index e0bd62cc50b..a9f0b129633 100644 --- a/chromium/base/i18n/icu_util.cc +++ b/chromium/base/i18n/icu_util.cc @@ -10,6 +10,7 @@ #include <string> +#include "base/debug/alias.h" #include "base/files/file_path.h" #include "base/files/memory_mapped_file.h" #include "base/logging.h" @@ -18,6 +19,9 @@ #include "base/strings/sys_string_conversions.h" #include "third_party/icu/source/common/unicode/putil.h" #include "third_party/icu/source/common/unicode/udata.h" +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) +#include "third_party/icu/source/i18n/unicode/timezone.h" +#endif #if defined(OS_MACOSX) #include "base/mac/foundation_util.h" @@ -27,35 +31,38 @@ #define ICU_UTIL_DATA_SHARED 1 #define ICU_UTIL_DATA_STATIC 2 -#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE +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. -#define ICU_UTIL_DATA_FILE_NAME "icudtl.dat" -#elif ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_SHARED +// 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) #define ICU_UTIL_DATA_SHARED_MODULE_NAME "icudt.dll" #endif #endif -namespace base { -namespace i18n { - namespace { #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; #endif } - -#if defined(OS_ANDROID) -bool InitializeICUWithFileDescriptor(int data_fd) { +#if !defined(OS_NACL) +bool InitializeICUWithFileDescriptor( + PlatformFile data_fd, + MemoryMappedFile::Region data_region) { #if !defined(NDEBUG) DCHECK(!g_check_called_once || !g_called_once); g_called_once = true; @@ -65,9 +72,9 @@ bool InitializeICUWithFileDescriptor(int data_fd) { // The ICU data is statically linked. return true; #elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE) - CR_DEFINE_STATIC_LOCAL(base::MemoryMappedFile, mapped_file, ()); + CR_DEFINE_STATIC_LOCAL(MemoryMappedFile, mapped_file, ()); if (!mapped_file.IsValid()) { - if (!mapped_file.Initialize(base::File(data_fd))) { + if (!mapped_file.Initialize(File(data_fd), data_region)) { LOG(ERROR) << "Couldn't mmap icu data file"; return false; } @@ -77,7 +84,6 @@ bool InitializeICUWithFileDescriptor(int data_fd) { return err == U_ZERO_ERROR; #endif // ICU_UTIL_DATA_FILE } -#endif bool InitializeICU() { @@ -86,10 +92,11 @@ bool InitializeICU() { g_called_once = true; #endif + bool result; #if (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_SHARED) // We expect to find the ICU data module alongside the current module. FilePath data_path; - PathService::Get(base::DIR_MODULE, &data_path); + PathService::Get(DIR_MODULE, &data_path); data_path = data_path.AppendASCII(ICU_UTIL_DATA_SHARED_MODULE_NAME); HMODULE module = LoadLibrary(data_path.value().c_str()); @@ -107,10 +114,10 @@ bool InitializeICU() { UErrorCode err = U_ZERO_ERROR; udata_setCommonData(reinterpret_cast<void*>(addr), &err); - return err == U_ZERO_ERROR; + result = (err == U_ZERO_ERROR); #elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_STATIC) // The ICU data is statically linked. - return true; + result = true; #elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE) // If the ICU data directory is set, ICU won't actually load the data until // it is needed. This can fail if the process is sandboxed at that time. @@ -119,42 +126,74 @@ bool InitializeICU() { // Chrome doesn't normally shut down ICU, so the mapped data shouldn't ever // be released. - CR_DEFINE_STATIC_LOCAL(base::MemoryMappedFile, mapped_file, ()); + 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(base::DIR_MODULE, &data_path); + 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(base::DIR_ANDROID_APP_DATA, &data_path); + 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(base::DIR_EXE, &data_path); + bool path_ok = PathService::Get(DIR_EXE, &data_path); #endif DCHECK(path_ok); - data_path = data_path.AppendASCII(ICU_UTIL_DATA_FILE_NAME); + 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 = - base::mac::PathForFrameworkBundleResource(CFSTR(ICU_UTIL_DATA_FILE_NAME)); + mac::PathForFrameworkBundleResource(data_file_name); if (data_path.empty()) { - LOG(ERROR) << ICU_UTIL_DATA_FILE_NAME << " not found in bundle"; + 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); - return err == U_ZERO_ERROR; + result = (err == U_ZERO_ERROR); +#if defined(OS_WIN) + CHECK(result); // TODO(scottmg): http://crbug.com/445616 +#endif #endif + +// To respond to the timezone change properly, the default timezone +// cache in ICU has to be populated on starting up. +// TODO(jungshik): Some callers do not care about tz at all. If necessary, +// add a boolean argument to this function to init'd the default tz only +// when requested. +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) + if (result) + scoped_ptr<icu::TimeZone> zone(icu::TimeZone::createDefault()); +#endif + return result; } +#endif void AllowMultipleInitializeCallsForTesting() { #if !defined(NDEBUG) diff --git a/chromium/base/i18n/icu_util.h b/chromium/base/i18n/icu_util.h index b0a5dbcce6d..65de0ad7e21 100644 --- a/chromium/base/i18n/icu_util.h +++ b/chromium/base/i18n/icu_util.h @@ -5,20 +5,25 @@ #ifndef BASE_I18N_ICU_UTIL_H_ #define BASE_I18N_ICU_UTIL_H_ -#include "build/build_config.h" +#include "base/files/memory_mapped_file.h" #include "base/i18n/base_i18n_export.h" +#include "build/build_config.h" 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 defined(OS_ANDROID) -// Android uses a file descriptor passed by browser process to initialize ICU -// in render processes. -BASE_I18N_EXPORT bool InitializeICUWithFileDescriptor(int data_fd); +// 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 // In a test binary, the call above might occur twice. diff --git a/chromium/base/i18n/rtl.cc b/chromium/base/i18n/rtl.cc index 392cb13bdb7..1cccae28937 100644 --- a/chromium/base/i18n/rtl.cc +++ b/chromium/base/i18n/rtl.cc @@ -74,8 +74,8 @@ std::string GetConfiguredLocale() { } // Convert the ICU canonicalized locale to a string. -std::string GetCanonicalLocale(const char* locale) { - return GetLocaleString(icu::Locale::createCanonical(locale)); +std::string GetCanonicalLocale(const std::string& locale) { + return GetLocaleString(icu::Locale::createCanonical(locale.c_str())); } // Convert Chrome locale name to ICU locale name diff --git a/chromium/base/i18n/rtl.h b/chromium/base/i18n/rtl.h index aa5f6810a9b..9b9a0dcd31c 100644 --- a/chromium/base/i18n/rtl.h +++ b/chromium/base/i18n/rtl.h @@ -40,7 +40,7 @@ enum TextDirection { BASE_I18N_EXPORT std::string GetConfiguredLocale(); // Canonicalize a string (eg. a POSIX locale string) to a Chrome locale name. -BASE_I18N_EXPORT std::string GetCanonicalLocale(const char* locale); +BASE_I18N_EXPORT std::string GetCanonicalLocale(const std::string& locale); // Sets the default locale of ICU. // Once the application locale of Chrome in GetApplicationLocale is determined, diff --git a/chromium/base/i18n/streaming_utf8_validator_perftest.cc b/chromium/base/i18n/streaming_utf8_validator_perftest.cc index ac2eb0820ba..6b8c17b654b 100644 --- a/chromium/base/i18n/streaming_utf8_validator_perftest.cc +++ b/chromium/base/i18n/streaming_utf8_validator_perftest.cc @@ -134,6 +134,10 @@ struct TestFunctionDescription { const char* function_name; }; +bool IsStringUTF8(const std::string& str) { + return base::IsStringUTF8(base::StringPiece(str)); +} + // IsString7Bit is intentionally placed last so it can be excluded easily. const TestFunctionDescription kTestFunctions[] = { {&StreamingUtf8Validator::Validate, "StreamingUtf8Validator"}, diff --git a/chromium/base/i18n/string_compare.cc b/chromium/base/i18n/string_compare.cc index 1ac7284359c..2851e7d2dce 100644 --- a/chromium/base/i18n/string_compare.cc +++ b/chromium/base/i18n/string_compare.cc @@ -12,12 +12,11 @@ namespace i18n { // Compares the character data stored in two different string16 strings by // specified Collator instance. -UCollationResult CompareString16WithCollator(const icu::Collator* collator, +UCollationResult CompareString16WithCollator(const icu::Collator& collator, const string16& lhs, const string16& rhs) { - DCHECK(collator); UErrorCode error = U_ZERO_ERROR; - UCollationResult result = collator->compare( + UCollationResult result = collator.compare( static_cast<const UChar*>(lhs.c_str()), static_cast<int>(lhs.length()), static_cast<const UChar*>(rhs.c_str()), static_cast<int>(rhs.length()), error); diff --git a/chromium/base/i18n/string_compare.h b/chromium/base/i18n/string_compare.h index f0f3e297f09..5fcc5feaede 100644 --- a/chromium/base/i18n/string_compare.h +++ b/chromium/base/i18n/string_compare.h @@ -17,12 +17,12 @@ namespace base { namespace i18n { // Compares the two strings using the specified collator. -BASE_I18N_EXPORT UCollationResult CompareString16WithCollator( - const icu::Collator* collator, - const string16& lhs, - const string16& rhs); +BASE_I18N_EXPORT UCollationResult +CompareString16WithCollator(const icu::Collator& collator, + const string16& lhs, + const string16& rhs); } // namespace i18n } // namespace base -#endif // BASE_I18N_STRING_COMPARATOR_H_ +#endif // BASE_I18N_STRING_COMPARE_H_ diff --git a/chromium/base/i18n/time_formatting.cc b/chromium/base/i18n/time_formatting.cc index 917ba43b9ec..15b34a31f91 100644 --- a/chromium/base/i18n/time_formatting.cc +++ b/chromium/base/i18n/time_formatting.cc @@ -106,6 +106,12 @@ string16 TimeFormatShortDateAndTime(const Time& time) { return TimeFormat(formatter.get(), time); } +string16 TimeFormatShortDateAndTimeWithTimeZone(const Time& time) { + scoped_ptr<icu::DateFormat> formatter(icu::DateFormat::createDateTimeInstance( + icu::DateFormat::kShort, icu::DateFormat::kLong)); + return TimeFormat(formatter.get(), time); +} + string16 TimeFormatFriendlyDateAndTime(const Time& time) { scoped_ptr<icu::DateFormat> formatter( icu::DateFormat::createDateTimeInstance(icu::DateFormat::kFull)); diff --git a/chromium/base/i18n/time_formatting.h b/chromium/base/i18n/time_formatting.h index 226d1a91a22..2053c0b47da 100644 --- a/chromium/base/i18n/time_formatting.h +++ b/chromium/base/i18n/time_formatting.h @@ -48,6 +48,11 @@ BASE_I18N_EXPORT string16 TimeFormatShortDateNumeric(const Time& time); // Returns a numeric date and time such as "12/13/52 2:44:30 PM". BASE_I18N_EXPORT string16 TimeFormatShortDateAndTime(const Time& time); +// Returns a numeric date and time with time zone such as +// "12/13/52 2:44:30 PM PST". +BASE_I18N_EXPORT string16 +TimeFormatShortDateAndTimeWithTimeZone(const Time& time); + // Formats a time in a friendly sentence format, e.g. // "Monday, March 6, 2008 2:44:30 PM". BASE_I18N_EXPORT string16 TimeFormatFriendlyDateAndTime(const Time& time); diff --git a/chromium/base/i18n/time_formatting_unittest.cc b/chromium/base/i18n/time_formatting_unittest.cc index 03a3aa3ec77..df0c1ed5ea1 100644 --- a/chromium/base/i18n/time_formatting_unittest.cc +++ b/chromium/base/i18n/time_formatting_unittest.cc @@ -5,10 +5,13 @@ #include "base/i18n/time_formatting.h" #include "base/i18n/rtl.h" +#include "base/memory/scoped_ptr.h" #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" #include "testing/gtest/include/gtest/gtest.h" #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" namespace base { namespace { @@ -18,6 +21,13 @@ const Time::Exploded kTestDateTimeExploded = { 15, 42, 7, 0 // 15:42:07.000 }; +base::string16 GetShortTimeZone() { + scoped_ptr<icu::TimeZone> zone(icu::TimeZone::createDefault()); + icu::UnicodeString name; + zone->getDisplayName(true, icu::TimeZone::SHORT, name); + return base::string16(name.getBuffer(), name.length()); +} + TEST(TimeFormattingTest, TimeFormatTimeOfDayDefault12h) { // Test for a locale defaulted to 12h clock. // As an instance, we use third_party/icu/source/data/locales/en.txt. @@ -58,12 +68,7 @@ TEST(TimeFormattingTest, TimeFormatTimeOfDayDefault24h) { Time time(Time::FromLocalExploded(kTestDateTimeExploded)); string16 clock24h(ASCIIToUTF16("15:42")); -#if U_ICU_VERSION_MAJOR_NUM >= 50 string16 clock12h_pm(ASCIIToUTF16("3:42 pm")); -#else - // TODO(phajdan.jr): Clean up after bundled ICU gets updated to 50. - string16 clock12h_pm(ASCIIToUTF16("3:42 PM")); -#endif string16 clock12h(ASCIIToUTF16("3:42")); // The default is 24h clock. @@ -132,23 +137,13 @@ TEST(TimeFormattingTest, TimeFormatDateUS) { EXPECT_EQ(ASCIIToUTF16("Apr 30, 2011"), TimeFormatShortDate(time)); EXPECT_EQ(ASCIIToUTF16("4/30/11"), TimeFormatShortDateNumeric(time)); -#if U_ICU_VERSION_MAJOR_NUM >= 50 EXPECT_EQ(ASCIIToUTF16("4/30/11, 3:42:07 PM"), TimeFormatShortDateAndTime(time)); -#else - // TODO(phajdan.jr): Clean up after bundled ICU gets updated to 50. - EXPECT_EQ(ASCIIToUTF16("4/30/11 3:42:07 PM"), - TimeFormatShortDateAndTime(time)); -#endif + EXPECT_EQ(ASCIIToUTF16("4/30/11, 3:42:07 PM ") + GetShortTimeZone(), + TimeFormatShortDateAndTimeWithTimeZone(time)); -#if U_ICU_VERSION_MAJOR_NUM >= 50 EXPECT_EQ(ASCIIToUTF16("Saturday, April 30, 2011 at 3:42:07 PM"), TimeFormatFriendlyDateAndTime(time)); -#else - // TODO(phajdan.jr): Clean up after bundled ICU gets updated to 50. - EXPECT_EQ(ASCIIToUTF16("Saturday, April 30, 2011 3:42:07 PM"), - TimeFormatFriendlyDateAndTime(time)); -#endif EXPECT_EQ(ASCIIToUTF16("Saturday, April 30, 2011"), TimeFormatFriendlyDate(time)); @@ -163,9 +158,11 @@ TEST(TimeFormattingTest, TimeFormatDateGB) { EXPECT_EQ(ASCIIToUTF16("30 Apr 2011"), TimeFormatShortDate(time)); EXPECT_EQ(ASCIIToUTF16("30/04/2011"), TimeFormatShortDateNumeric(time)); - EXPECT_EQ(ASCIIToUTF16("30/04/2011 15:42:07"), + EXPECT_EQ(ASCIIToUTF16("30/04/2011, 15:42:07"), TimeFormatShortDateAndTime(time)); - EXPECT_EQ(ASCIIToUTF16("Saturday, 30 April 2011 15:42:07"), + EXPECT_EQ(ASCIIToUTF16("30/04/2011, 15:42:07 ") + GetShortTimeZone(), + TimeFormatShortDateAndTimeWithTimeZone(time)); + EXPECT_EQ(ASCIIToUTF16("Saturday, 30 April 2011 at 15:42:07"), TimeFormatFriendlyDateAndTime(time)); EXPECT_EQ(ASCIIToUTF16("Saturday, 30 April 2011"), TimeFormatFriendlyDate(time)); diff --git a/chromium/base/i18n/timezone.h b/chromium/base/i18n/timezone.h index b7275f7b00b..f7fda941188 100644 --- a/chromium/base/i18n/timezone.h +++ b/chromium/base/i18n/timezone.h @@ -18,4 +18,4 @@ BASE_I18N_EXPORT std::string CountryCodeForCurrentTimezone(); } // namespace base -#endif // BASE_TIME_TIMEZONE_H_ +#endif // BASE_I18N_TIMEZONE_H_ diff --git a/chromium/base/id_map.h b/chromium/base/id_map.h index 9cbc1f8978f..852c1380471 100644 --- a/chromium/base/id_map.h +++ b/chromium/base/id_map.h @@ -53,13 +53,14 @@ class IDMap : public base::NonThreadSafe { Releaser<OS, 0>::release_all(&data_); } - // Sets whether Add should CHECK if passed in NULL data. Default is false. + // Sets whether Add and Replace should DCHECK if passed in NULL data. + // Default is false. void set_check_on_null_data(bool value) { check_on_null_data_ = value; } // Adds a view with an automatically generated unique ID. See AddWithID. KeyType Add(T* data) { DCHECK(CalledOnValidThread()); - CHECK(!check_on_null_data_ || data); + DCHECK(!check_on_null_data_ || data); KeyType this_id = next_id_; DCHECK(data_.find(this_id) == data_.end()) << "Inserting duplicate item"; data_[this_id] = data; @@ -73,7 +74,7 @@ class IDMap : public base::NonThreadSafe { // two methods may not be mixed, or duplicate IDs may be generated void AddWithID(T* data, KeyType id) { DCHECK(CalledOnValidThread()); - CHECK(!check_on_null_data_ || data); + DCHECK(!check_on_null_data_ || data); DCHECK(data_.find(id) == data_.end()) << "Inserting duplicate item"; data_[id] = data; } @@ -94,6 +95,25 @@ class IDMap : public base::NonThreadSafe { } } + // Replaces the value for |id| with |new_data| and returns a pointer to the + // existing value. If there is no entry for |id|, the map is not altered and + // nullptr is returned. The OwnershipSemantics of the map have no effect on + // how the existing value is treated, the IDMap does not delete the existing + // value being replaced. + T* Replace(KeyType id, T* new_data) { + DCHECK(CalledOnValidThread()); + DCHECK(!check_on_null_data_ || new_data); + typename HashTable::iterator i = data_.find(id); + if (i == data_.end()) { + NOTREACHED() << "Attempting to replace an item not in the list"; + return nullptr; + } + + T* temp = i->second; + i->second = new_data; + return temp; + } + void Clear() { DCHECK(CalledOnValidThread()); if (iteration_depth_ == 0) { diff --git a/chromium/base/id_map_unittest.cc b/chromium/base/id_map_unittest.cc index c005a690578..a9fb2b9decd 100644 --- a/chromium/base/id_map_unittest.cc +++ b/chromium/base/id_map_unittest.cc @@ -53,6 +53,9 @@ TEST(IDMapTest, Basic) { EXPECT_EQ(&obj1, map.Lookup(1)); EXPECT_EQ(&obj2, map.Lookup(2)); + EXPECT_EQ(&obj2, map.Replace(2, &obj1)); + EXPECT_EQ(&obj1, map.Lookup(2)); + EXPECT_EQ(0, map.iteration_depth()); } diff --git a/chromium/base/ios/block_types.h b/chromium/base/ios/block_types.h new file mode 100644 index 00000000000..e4dde798aa4 --- /dev/null +++ b/chromium/base/ios/block_types.h @@ -0,0 +1,14 @@ +// 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. + +#ifndef BASE_IOS_BLOCK_TYPES_H_ +#define BASE_IOS_BLOCK_TYPES_H_ + +// A generic procedural block type that takes no arguments and returns nothing. +typedef void (^ProceduralBlock)(void); + +// A block that takes no arguments and returns a bool. +typedef bool (^ConditionBlock)(void); + +#endif // BASE_IOS_BLOCK_TYPES_H_ diff --git a/chromium/base/ios/crb_protocol_observers.h b/chromium/base/ios/crb_protocol_observers.h new file mode 100644 index 00000000000..15d16569ff8 --- /dev/null +++ b/chromium/base/ios/crb_protocol_observers.h @@ -0,0 +1,36 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_IOS_CRB_PROTOCOL_OBSERVERS_H_ +#define BASE_IOS_CRB_PROTOCOL_OBSERVERS_H_ + +#import <Foundation/Foundation.h> + +typedef void (^ExecutionWithObserverBlock)(id); + +// Implements a container for observers that implement a specific Objective-C +// protocol. The container forwards method invocations to its contained +// observers, so that sending a message to all the observers is as simple as +// sending the message to the container. +@interface CRBProtocolObservers : NSObject + +// The Objective-C protocol that the observers in this container conform to. +@property(nonatomic, readonly) Protocol* protocol; + +// Returns a CRBProtocolObservers container for observers that conform to +// |protocol|. ++ (CRBProtocolObservers*)observersWithProtocol:(Protocol*)protocol; + +// Adds |observer| to this container. +- (void)addObserver:(id)observer; + +// Remove |observer| from this container. +- (void)removeObserver:(id)observer; + +// Executes callback on every observer. |callback| cannot be nil. +- (void)executeOnObservers:(ExecutionWithObserverBlock)callback; + +@end + +#endif // BASE_IOS_CRB_PROTOCOL_OBSERVERS_H_ diff --git a/chromium/base/ios/crb_protocol_observers.mm b/chromium/base/ios/crb_protocol_observers.mm new file mode 100644 index 00000000000..ee9e23fcb4e --- /dev/null +++ b/chromium/base/ios/crb_protocol_observers.mm @@ -0,0 +1,99 @@ +// 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. + +#import "base/ios/crb_protocol_observers.h" + +#include <objc/runtime.h> + +#include "base/logging.h" +#include "base/mac/scoped_nsobject.h" + +@interface CRBProtocolObservers () + +// Designated initializer. +- (id)initWithProtocol:(Protocol*)protocol; + +@end + +@implementation CRBProtocolObservers { + base::scoped_nsobject<Protocol> _protocol; + base::scoped_nsobject<NSHashTable> _observers; +} + ++ (CRBProtocolObservers*)observersWithProtocol:(Protocol*)protocol { + return [[[self alloc] initWithProtocol:protocol] autorelease]; +} + +- (id)init { + NOTREACHED(); + return nil; +} + +- (id)initWithProtocol:(Protocol*)protocol { + self = [super init]; + if (self) { + _protocol.reset([protocol retain]); + _observers.reset([[NSHashTable weakObjectsHashTable] retain]); + } + return self; +} + +- (Protocol*)protocol { + return _protocol.get(); +} + +- (void)addObserver:(id)observer { + DCHECK([observer conformsToProtocol:self.protocol]); + [_observers addObject:observer]; +} + +- (void)removeObserver:(id)observer { + [_observers removeObject:observer]; +} + +#pragma mark - NSObject + +- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector { + NSMethodSignature* signature = [super methodSignatureForSelector:selector]; + if (signature) + return signature; + + // Look for a required method in the protocol. protocol_getMethodDescription + // returns a struct whose fields are null if a method for the selector was + // not found. + struct objc_method_description description = + protocol_getMethodDescription(self.protocol, selector, YES, YES); + if (description.types) + return [NSMethodSignature signatureWithObjCTypes:description.types]; + + // Look for an optional method in the protocol. + description = protocol_getMethodDescription(self.protocol, selector, NO, YES); + if (description.types) + return [NSMethodSignature signatureWithObjCTypes:description.types]; + + // There is neither a required nor optional method with this selector in the + // protocol, so invoke -[NSObject doesNotRecognizeSelector:] to raise + // NSInvalidArgumentException. + [self doesNotRecognizeSelector:selector]; + return nil; +} + +- (void)forwardInvocation:(NSInvocation*)invocation { + SEL selector = [invocation selector]; + base::scoped_nsobject<NSArray> observers([[_observers allObjects] retain]); + for (id observer in observers.get()) { + if ([observer respondsToSelector:selector]) + [invocation invokeWithTarget:observer]; + } +} + +- (void)executeOnObservers:(ExecutionWithObserverBlock)callback { + DCHECK(callback); + base::scoped_nsobject<NSArray> observers([[_observers allObjects] retain]); + for (id observer in observers.get()) { + callback(observer); + } +} + +@end diff --git a/chromium/base/ios/crb_protocol_observers_unittest.mm b/chromium/base/ios/crb_protocol_observers_unittest.mm new file mode 100644 index 00000000000..d235c98b21f --- /dev/null +++ b/chromium/base/ios/crb_protocol_observers_unittest.mm @@ -0,0 +1,159 @@ +// 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. + +#import "base/ios/crb_protocol_observers.h" +#include "base/ios/weak_nsobject.h" +#include "base/mac/scoped_nsautorelease_pool.h" +#include "base/mac/scoped_nsobject.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/gtest_mac.h" +#include "testing/platform_test.h" + +@protocol TestObserver + +@required +- (void)requiredMethod; +- (void)reset; + +@optional +- (void)optionalMethod; + +@end + +// Implements only the required methods in the TestObserver protocol. +@interface TestPartialObserver : NSObject<TestObserver> +@property(nonatomic, readonly) BOOL requiredMethodInvoked; +@end + +// Implements all the methods in the TestObserver protocol. +@interface TestCompleteObserver : TestPartialObserver<TestObserver> +@property(nonatomic, readonly) BOOL optionalMethodInvoked; +@end + +namespace { + +class CRBProtocolObserversTest : public PlatformTest { + public: + CRBProtocolObserversTest() {} + + protected: + void SetUp() override { + PlatformTest::SetUp(); + + observers_.reset([[CRBProtocolObservers observersWithProtocol: + @protocol(TestObserver)] retain]); + + partial_observer_.reset([[TestPartialObserver alloc] init]); + EXPECT_FALSE([partial_observer_ requiredMethodInvoked]); + + complete_observer_.reset([[TestCompleteObserver alloc] init]); + EXPECT_FALSE([complete_observer_ requiredMethodInvoked]); + EXPECT_FALSE([complete_observer_ optionalMethodInvoked]); + } + + base::scoped_nsobject<id> observers_; + base::scoped_nsobject<TestPartialObserver> partial_observer_; + base::scoped_nsobject<TestCompleteObserver> complete_observer_; +}; + +// Verifies basic functionality of -[CRBProtocolObservers addObserver:] and +// -[CRBProtocolObservers removeObserver:]. +TEST_F(CRBProtocolObserversTest, AddRemoveObserver) { + // Add an observer and verify that the CRBProtocolObservers instance forwards + // an invocation to it. + [observers_ addObserver:partial_observer_]; + [observers_ requiredMethod]; + EXPECT_TRUE([partial_observer_ requiredMethodInvoked]); + + [partial_observer_ reset]; + EXPECT_FALSE([partial_observer_ requiredMethodInvoked]); + + // Remove the observer and verify that the CRBProtocolObservers instance no + // longer forwards an invocation to it. + [observers_ removeObserver:partial_observer_]; + [observers_ requiredMethod]; + EXPECT_FALSE([partial_observer_ requiredMethodInvoked]); +} + +// Verifies that CRBProtocolObservers correctly forwards the invocation of a +// required method in the protocol. +TEST_F(CRBProtocolObserversTest, RequiredMethods) { + [observers_ addObserver:partial_observer_]; + [observers_ addObserver:complete_observer_]; + [observers_ requiredMethod]; + EXPECT_TRUE([partial_observer_ requiredMethodInvoked]); + EXPECT_TRUE([complete_observer_ requiredMethodInvoked]); +} + +// Verifies that CRBProtocolObservers correctly forwards the invocation of an +// optional method in the protocol. +TEST_F(CRBProtocolObserversTest, OptionalMethods) { + [observers_ addObserver:partial_observer_]; + [observers_ addObserver:complete_observer_]; + [observers_ optionalMethod]; + EXPECT_FALSE([partial_observer_ requiredMethodInvoked]); + EXPECT_FALSE([complete_observer_ requiredMethodInvoked]); + EXPECT_TRUE([complete_observer_ optionalMethodInvoked]); +} + +// Verifies that CRBProtocolObservers only holds a weak reference to an +// observer. +TEST_F(CRBProtocolObserversTest, WeakReference) { + base::WeakNSObject<TestPartialObserver> weak_observer( + partial_observer_); + EXPECT_TRUE(weak_observer); + + [observers_ addObserver:partial_observer_]; + + { + // Need an autorelease pool here, because + // -[CRBProtocolObservers forwardInvocation:] creates a temporary + // autoreleased array that holds all the observers. + base::mac::ScopedNSAutoreleasePool pool; + [observers_ requiredMethod]; + EXPECT_TRUE([partial_observer_ requiredMethodInvoked]); + } + + partial_observer_.reset(); + EXPECT_FALSE(weak_observer.get()); +} + +} // namespace + +@implementation TestPartialObserver { + BOOL _requiredMethodInvoked; +} + +- (BOOL)requiredMethodInvoked { + return _requiredMethodInvoked; +} + +- (void)requiredMethod { + _requiredMethodInvoked = YES; +} + +- (void)reset { + _requiredMethodInvoked = NO; +} + +@end + +@implementation TestCompleteObserver { + BOOL _optionalMethodInvoked; +} + +- (BOOL)optionalMethodInvoked { + return _optionalMethodInvoked; +} + +- (void)optionalMethod { + _optionalMethodInvoked = YES; +} + +- (void)reset { + [super reset]; + _optionalMethodInvoked = NO; +} + +@end diff --git a/chromium/base/ios/device_util.mm b/chromium/base/ios/device_util.mm index ff7be36875c..1234562944d 100644 --- a/chromium/base/ios/device_util.mm +++ b/chromium/base/ios/device_util.mm @@ -13,7 +13,6 @@ #include <sys/socket.h> #include <sys/sysctl.h> -#include "base/ios/ios_util.h" #include "base/logging.h" #include "base/mac/scoped_cftyperef.h" #include "base/memory/scoped_ptr.h" diff --git a/chromium/base/ios/device_util_unittest.mm b/chromium/base/ios/device_util_unittest.mm index 3494e00a73e..82d421723de 100644 --- a/chromium/base/ios/device_util_unittest.mm +++ b/chromium/base/ios/device_util_unittest.mm @@ -5,7 +5,6 @@ #import <UIKit/UIKit.h> #include "base/ios/device_util.h" -#include "base/ios/ios_util.h" #include "base/strings/sys_string_conversions.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest_mac.h" diff --git a/chromium/base/ios/ios_util.h b/chromium/base/ios/ios_util.h index fca9a509f1c..d9d7e193dd5 100644 --- a/chromium/base/ios/ios_util.h +++ b/chromium/base/ios/ios_util.h @@ -11,9 +11,6 @@ namespace base { namespace ios { -// Returns whether the operating system is iOS 7 or later. -BASE_EXPORT bool IsRunningOnIOS7OrLater(); - // Returns whether the operating system is iOS 8 or later. BASE_EXPORT bool IsRunningOnIOS8OrLater(); diff --git a/chromium/base/ios/ios_util.mm b/chromium/base/ios/ios_util.mm index 91fce0cc273..ca0a24d2a52 100644 --- a/chromium/base/ios/ios_util.mm +++ b/chromium/base/ios/ios_util.mm @@ -20,10 +20,6 @@ const int32* OSVersionAsArray() { namespace base { namespace ios { -bool IsRunningOnIOS7OrLater() { - return IsRunningOnOrLater(7, 0, 0); -} - bool IsRunningOnIOS8OrLater() { return IsRunningOnOrLater(8, 0, 0); } diff --git a/chromium/base/ios/weak_nsobject.h b/chromium/base/ios/weak_nsobject.h new file mode 100644 index 00000000000..fc3a7c38dc4 --- /dev/null +++ b/chromium/base/ios/weak_nsobject.h @@ -0,0 +1,189 @@ +// 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. + +#ifndef BASE_IOS_WEAK_NSOBJECT_H_ +#define BASE_IOS_WEAK_NSOBJECT_H_ + +#import <Foundation/Foundation.h> +#import <objc/runtime.h> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "base/threading/non_thread_safe.h" +#include "base/threading/thread_checker.h" + +// WeakNSObject<> is patterned after scoped_nsobject<>, but instead of +// maintaining ownership of an NSObject subclass object, it will nil itself out +// when the object is deallocated. +// +// WeakNSProtocol<> has the same behavior as WeakNSObject, but can be used +// with protocols. +// +// Example usage (base::WeakNSObject<T>): +// scoped_nsobject<Foo> foo([[Foo alloc] init]); +// WeakNSObject<Foo> weak_foo; // No pointer +// weak_foo.reset(foo) // Now a weak reference is kept. +// [weak_foo description]; // Returns [foo description]. +// foo.reset(); // The reference is released. +// [weak_foo description]; // Returns nil, as weak_foo is pointing to nil. +// +// +// Implementation wise a WeakNSObject keeps a reference to a refcounted +// WeakContainer. There is one unique instance of a WeakContainer per watched +// NSObject, this relationship is maintained via the ObjectiveC associated +// object API, indirectly via an ObjectiveC CRBWeakNSProtocolSentinel class. +// +// Threading restrictions: +// - Several WeakNSObject pointing to the same underlying object must all be +// created and dereferenced on the same thread; +// - thread safety is enforced by the implementation, except in two cases: +// (1) it is allowed to copy a WeakNSObject on a different thread. However, +// that copy must return to the original thread before being dereferenced, +// (2) it is allowed to destroy a WeakNSObject on any thread; +// - the implementation assumes that the tracked object will be released on the +// same thread that the WeakNSObject is created on. +namespace base { + +// WeakContainer keeps a weak pointer to an object and clears it when it +// receives nullify() from the object's sentinel. +class WeakContainer : public base::RefCountedThreadSafe<WeakContainer> { + public: + explicit WeakContainer(id object) : object_(object) {} + + id object() { + DCHECK(checker_.CalledOnValidThread()); + return object_; + } + + void nullify() { + DCHECK(checker_.CalledOnValidThread()); + object_ = nil; + } + + private: + friend base::RefCountedThreadSafe<WeakContainer>; + ~WeakContainer() {} + base::ThreadChecker checker_; + id object_; +}; + +} // namespace base + +// Sentinel for observing the object contained in the weak pointer. The object +// will be deleted when the weak object is deleted and will notify its +// container. +@interface CRBWeakNSProtocolSentinel : NSObject +// Return the only associated container for this object. There can be only one. +// Will return null if object is nil . ++ (scoped_refptr<base::WeakContainer>)containerForObject:(id)object; +@end + +namespace base { + +// Base class for all WeakNSObject derivatives. +template <typename NST> +class WeakNSProtocol { + public: + explicit WeakNSProtocol(NST object = nil) { + container_ = [CRBWeakNSProtocolSentinel containerForObject:object]; + } + + WeakNSProtocol(const WeakNSProtocol<NST>& that) { + // A WeakNSProtocol object can be copied on one thread and used on + // another. + checker_.DetachFromThread(); + container_ = that.container_; + } + + ~WeakNSProtocol() { + // A WeakNSProtocol object can be used on one thread and released on + // another. This is not the case for the contained object. + checker_.DetachFromThread(); + } + + void reset(NST object = nil) { + DCHECK(checker_.CalledOnValidThread()); + container_ = [CRBWeakNSProtocolSentinel containerForObject:object]; + } + + NST get() const { + DCHECK(checker_.CalledOnValidThread()); + if (!container_.get()) + return nil; + return container_->object(); + } + + WeakNSProtocol& operator=(const WeakNSProtocol<NST>& that) { + // A WeakNSProtocol object can be copied on one thread and used on + // another. + checker_.DetachFromThread(); + container_ = that.container_; + return *this; + } + + bool operator==(NST that) const { + DCHECK(checker_.CalledOnValidThread()); + return get() == that; + } + + bool operator!=(NST that) const { + DCHECK(checker_.CalledOnValidThread()); + return get() != that; + } + + operator NST() const { + DCHECK(checker_.CalledOnValidThread()); + return get(); + } + + private: + // Refecounted reference to the container tracking the ObjectiveC object this + // class encapsulates. + scoped_refptr<base::WeakContainer> container_; + base::ThreadChecker checker_; +}; + +// Free functions +template <class NST> +bool operator==(NST p1, const WeakNSProtocol<NST>& p2) { + return p1 == p2.get(); +} + +template <class NST> +bool operator!=(NST p1, const WeakNSProtocol<NST>& p2) { + return p1 != p2.get(); +} + +template <typename NST> +class WeakNSObject : public WeakNSProtocol<NST*> { + public: + explicit WeakNSObject(NST* object = nil) : WeakNSProtocol<NST*>(object) {} + + WeakNSObject(const WeakNSObject<NST>& that) : WeakNSProtocol<NST*>(that) {} + + WeakNSObject& operator=(const WeakNSObject<NST>& that) { + WeakNSProtocol<NST*>::operator=(that); + return *this; + } +}; + +// Specialization to make WeakNSObject<id> work. +template <> +class WeakNSObject<id> : public WeakNSProtocol<id> { + public: + explicit WeakNSObject(id object = nil) : WeakNSProtocol<id>(object) {} + + WeakNSObject(const WeakNSObject<id>& that) : WeakNSProtocol<id>(that) {} + + WeakNSObject& operator=(const WeakNSObject<id>& that) { + WeakNSProtocol<id>::operator=(that); + return *this; + } +}; + +} // namespace base + +#endif // BASE_IOS_WEAK_NSOBJECT_H_ diff --git a/chromium/base/ios/weak_nsobject.mm b/chromium/base/ios/weak_nsobject.mm new file mode 100644 index 00000000000..36f9d3ea7cd --- /dev/null +++ b/chromium/base/ios/weak_nsobject.mm @@ -0,0 +1,61 @@ +// 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. + +#include "base/ios/weak_nsobject.h" + +#include "base/mac/scoped_nsautorelease_pool.h" +#include "base/mac/scoped_nsobject.h" + +namespace { +// The key needed by objc_setAssociatedObject. +char sentinelObserverKey_; +} + +@interface CRBWeakNSProtocolSentinel () +// Container to notify on dealloc. +@property(readonly, assign) scoped_refptr<base::WeakContainer> container; +// Designed initializer. +- (id)initWithContainer:(scoped_refptr<base::WeakContainer>)container; +@end + +@implementation CRBWeakNSProtocolSentinel + +@synthesize container = container_; + ++ (scoped_refptr<base::WeakContainer>)containerForObject:(id)object { + if (object == nil) + return nullptr; + // The autoreleasePool is needed here as the call to objc_getAssociatedObject + // returns an autoreleased object which is better released sooner than later. + base::mac::ScopedNSAutoreleasePool pool; + CRBWeakNSProtocolSentinel* sentinel = + objc_getAssociatedObject(object, &sentinelObserverKey_); + if (!sentinel) { + base::scoped_nsobject<CRBWeakNSProtocolSentinel> newSentinel( + [[CRBWeakNSProtocolSentinel alloc] + initWithContainer:new base::WeakContainer(object)]); + sentinel = newSentinel; + objc_setAssociatedObject(object, &sentinelObserverKey_, sentinel, + OBJC_ASSOCIATION_RETAIN); + // The retain count is 2. One retain is due to the alloc, the other to the + // association with the weak object. + DCHECK_EQ(2u, [sentinel retainCount]); + } + return [sentinel container]; +} + +- (id)initWithContainer:(scoped_refptr<base::WeakContainer>)container { + DCHECK(container.get()); + self = [super init]; + if (self) + container_ = container; + return self; +} + +- (void)dealloc { + self.container->nullify(); + [super dealloc]; +} + +@end diff --git a/chromium/base/ios/weak_nsobject_unittest.mm b/chromium/base/ios/weak_nsobject_unittest.mm new file mode 100644 index 00000000000..81de993cf25 --- /dev/null +++ b/chromium/base/ios/weak_nsobject_unittest.mm @@ -0,0 +1,140 @@ +// 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. + +#include "base/basictypes.h" +#include "base/bind.h" +#include "base/ios/weak_nsobject.h" +#include "base/mac/scoped_nsobject.h" +#include "base/message_loop/message_loop.h" +#include "base/single_thread_task_runner.h" +#include "base/threading/thread.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { +namespace { + +TEST(WeakNSObjectTest, WeakNSObject) { + scoped_nsobject<NSObject> p1([[NSObject alloc] init]); + WeakNSObject<NSObject> w1(p1); + EXPECT_TRUE(w1); + p1.reset(); + EXPECT_FALSE(w1); +} + +TEST(WeakNSObjectTest, MultipleWeakNSObject) { + scoped_nsobject<NSObject> p1([[NSObject alloc] init]); + WeakNSObject<NSObject> w1(p1); + WeakNSObject<NSObject> w2(w1); + EXPECT_TRUE(w1); + EXPECT_TRUE(w2); + EXPECT_TRUE(w1.get() == w2.get()); + p1.reset(); + EXPECT_FALSE(w1); + EXPECT_FALSE(w2); +} + +TEST(WeakNSObjectTest, WeakNSObjectDies) { + scoped_nsobject<NSObject> p1([[NSObject alloc] init]); + { + WeakNSObject<NSObject> w1(p1); + EXPECT_TRUE(w1); + } +} + +TEST(WeakNSObjectTest, WeakNSObjectReset) { + scoped_nsobject<NSObject> p1([[NSObject alloc] init]); + WeakNSObject<NSObject> w1(p1); + EXPECT_TRUE(w1); + w1.reset(); + EXPECT_FALSE(w1); + EXPECT_TRUE(p1); + EXPECT_TRUE([p1 description]); +} + +TEST(WeakNSObjectTest, WeakNSObjectResetWithObject) { + scoped_nsobject<NSObject> p1([[NSObject alloc] init]); + scoped_nsobject<NSObject> p2([[NSObject alloc] init]); + WeakNSObject<NSObject> w1(p1); + EXPECT_TRUE(w1); + w1.reset(p2); + EXPECT_TRUE(w1); + EXPECT_TRUE([p1 description]); + EXPECT_TRUE([p2 description]); +} + +TEST(WeakNSObjectTest, WeakNSObjectEmpty) { + scoped_nsobject<NSObject> p1([[NSObject alloc] init]); + WeakNSObject<NSObject> w1; + EXPECT_FALSE(w1); + w1.reset(p1); + EXPECT_TRUE(w1); + p1.reset(); + EXPECT_FALSE(w1); +} + +TEST(WeakNSObjectTest, WeakNSObjectCopy) { + scoped_nsobject<NSObject> p1([[NSObject alloc] init]); + WeakNSObject<NSObject> w1(p1); + WeakNSObject<NSObject> w2(w1); + EXPECT_TRUE(w1); + EXPECT_TRUE(w2); + p1.reset(); + EXPECT_FALSE(w1); + EXPECT_FALSE(w2); +} + +TEST(WeakNSObjectTest, WeakNSObjectAssignment) { + scoped_nsobject<NSObject> p1([[NSObject alloc] init]); + WeakNSObject<NSObject> w1(p1); + WeakNSObject<NSObject> w2; + EXPECT_FALSE(w2); + w2 = w1; + EXPECT_TRUE(w1); + EXPECT_TRUE(w2); + p1.reset(); + EXPECT_FALSE(w1); + EXPECT_FALSE(w2); +} + +// Touches |weak_data| by increasing its length by 1. Used to check that the +// weak object can be dereferenced. +void TouchWeakData(const WeakNSObject<NSMutableData>& weak_data) { + if (!weak_data) + return; + [weak_data increaseLengthBy:1]; +} + +// Makes a copy of |weak_object| on the current thread and posts a task to touch +// the weak object on its original thread. +void CopyWeakNSObjectAndPost(const WeakNSObject<NSMutableData>& weak_object, + scoped_refptr<SingleThreadTaskRunner> runner) { + // Copy using constructor. + WeakNSObject<NSMutableData> weak_copy1(weak_object); + runner->PostTask(FROM_HERE, Bind(&TouchWeakData, weak_copy1)); + // Copy using assignment operator. + WeakNSObject<NSMutableData> weak_copy2 = weak_object; + runner->PostTask(FROM_HERE, Bind(&TouchWeakData, weak_copy2)); +} + +// Tests that the weak object can be copied on a different thread. +TEST(WeakNSObjectTest, WeakNSObjectCopyOnOtherThread) { + MessageLoop loop; + Thread other_thread("WeakNSObjectCopyOnOtherThread"); + other_thread.Start(); + + scoped_nsobject<NSMutableData> data([[NSMutableData alloc] init]); + WeakNSObject<NSMutableData> weak(data); + + scoped_refptr<SingleThreadTaskRunner> runner = loop.task_runner(); + other_thread.task_runner()->PostTask( + FROM_HERE, Bind(&CopyWeakNSObjectAndPost, weak, runner)); + other_thread.Stop(); + loop.RunUntilIdle(); + + // Check that TouchWeakData was called and the object touched twice. + EXPECT_EQ(2u, [data length]); +} + +} // namespace +} // namespace base diff --git a/chromium/base/json/BUILD.gn b/chromium/base/json/BUILD.gn new file mode 100644 index 00000000000..70830c15e55 --- /dev/null +++ b/chromium/base/json/BUILD.gn @@ -0,0 +1,37 @@ +# Copyright (c) 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. + +source_set("json") { + sources = [ + "json_file_value_serializer.cc", + "json_file_value_serializer.h", + "json_parser.cc", + "json_parser.h", + "json_reader.cc", + "json_reader.h", + "json_string_value_serializer.cc", + "json_string_value_serializer.h", + "json_value_converter.cc", + "json_value_converter.h", + "json_writer.cc", + "json_writer.h", + "string_escape.cc", + "string_escape.h", + ] + + if (is_nacl) { + sources -= [ + "json_file_value_serializer.cc", + "json_file_value_serializer.h", + ] + } + + configs += [ "//base:base_implementation" ] + + deps = [ + "//base/memory", + ] + + visibility = [ "//base/*" ] +} diff --git a/chromium/base/json/json_file_value_serializer.cc b/chromium/base/json/json_file_value_serializer.cc index d60f800cfcf..72a09700161 100644 --- a/chromium/base/json/json_file_value_serializer.cc +++ b/chromium/base/json/json_file_value_serializer.cc @@ -10,10 +10,18 @@ using base::FilePath; -const char JSONFileValueSerializer::kAccessDenied[] = "Access denied."; -const char JSONFileValueSerializer::kCannotReadFile[] = "Can't read file."; -const char JSONFileValueSerializer::kFileLocked[] = "File locked."; -const char JSONFileValueSerializer::kNoSuchFile[] = "File doesn't exist."; +const char JSONFileValueDeserializer::kAccessDenied[] = "Access denied."; +const char JSONFileValueDeserializer::kCannotReadFile[] = "Can't read file."; +const char JSONFileValueDeserializer::kFileLocked[] = "File locked."; +const char JSONFileValueDeserializer::kNoSuchFile[] = "File doesn't exist."; + +JSONFileValueSerializer::JSONFileValueSerializer( + const base::FilePath& json_file_path) + : json_file_path_(json_file_path) { +} + +JSONFileValueSerializer::~JSONFileValueSerializer() { +} bool JSONFileValueSerializer::Serialize(const base::Value& root) { return SerializeInternal(root, false); @@ -43,7 +51,17 @@ bool JSONFileValueSerializer::SerializeInternal(const base::Value& root, return true; } -int JSONFileValueSerializer::ReadFileToString(std::string* json_string) { +JSONFileValueDeserializer::JSONFileValueDeserializer( + const base::FilePath& json_file_path) + : json_file_path_(json_file_path), + allow_trailing_comma_(false), + last_read_size_(0U) { +} + +JSONFileValueDeserializer::~JSONFileValueDeserializer() { +} + +int JSONFileValueDeserializer::ReadFileToString(std::string* json_string) { DCHECK(json_string); if (!base::ReadFileToString(json_file_path_, json_string)) { #if defined(OS_WIN) @@ -59,10 +77,12 @@ int JSONFileValueSerializer::ReadFileToString(std::string* json_string) { else return JSON_CANNOT_READ_FILE; } + + last_read_size_ = json_string->size(); return JSON_NO_ERROR; } -const char* JSONFileValueSerializer::GetErrorMessageForCode(int error_code) { +const char* JSONFileValueDeserializer::GetErrorMessageForCode(int error_code) { switch (error_code) { case JSON_NO_ERROR: return ""; @@ -80,8 +100,8 @@ const char* JSONFileValueSerializer::GetErrorMessageForCode(int error_code) { } } -base::Value* JSONFileValueSerializer::Deserialize(int* error_code, - std::string* error_str) { +base::Value* JSONFileValueDeserializer::Deserialize(int* error_code, + std::string* error_str) { std::string json_string; int error = ReadFileToString(&json_string); if (error != JSON_NO_ERROR) { @@ -92,7 +112,7 @@ base::Value* JSONFileValueSerializer::Deserialize(int* error_code, return NULL; } - JSONStringValueSerializer serializer(json_string); - serializer.set_allow_trailing_comma(allow_trailing_comma_); - return serializer.Deserialize(error_code, error_str); + JSONStringValueDeserializer deserializer(json_string); + deserializer.set_allow_trailing_comma(allow_trailing_comma_); + return deserializer.Deserialize(error_code, error_str); } diff --git a/chromium/base/json/json_file_value_serializer.h b/chromium/base/json/json_file_value_serializer.h index f0f556c2dc6..aab47eec2dc 100644 --- a/chromium/base/json/json_file_value_serializer.h +++ b/chromium/base/json/json_file_value_serializer.h @@ -14,15 +14,12 @@ class BASE_EXPORT JSONFileValueSerializer : public base::ValueSerializer { public: - // json_file_patch is the path of a file that will be source of the - // deserialization or the destination of the serialization. - // When deserializing, the file should exist, but when serializing, the - // serializer will attempt to create the file at the specified location. - explicit JSONFileValueSerializer(const base::FilePath& json_file_path) - : json_file_path_(json_file_path), - allow_trailing_comma_(false) {} + // |json_file_path_| is the path of a file that will be destination of the + // serialization. The serializer will attempt to create the file at the + // specified location. + explicit JSONFileValueSerializer(const base::FilePath& json_file_path); - ~JSONFileValueSerializer() override {} + ~JSONFileValueSerializer() override; // DO NOT USE except in unit tests to verify the file was written properly. // We should never serialize directly to a file since this will block the @@ -38,6 +35,22 @@ class BASE_EXPORT JSONFileValueSerializer : public base::ValueSerializer { // output. bool SerializeAndOmitBinaryValues(const base::Value& root); + private: + bool SerializeInternal(const base::Value& root, bool omit_binary_values); + + const base::FilePath json_file_path_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(JSONFileValueSerializer); +}; + +class BASE_EXPORT JSONFileValueDeserializer : public base::ValueDeserializer { + public: + // |json_file_path_| is the path of a file that will be source of the + // deserialization. + explicit JSONFileValueDeserializer(const base::FilePath& json_file_path); + + ~JSONFileValueDeserializer() override; + // Attempt to deserialize the data structure encoded in the file passed // in to the constructor into a structure of Value objects. If the return // value is NULL, and if |error_code| is non-null, |error_code| will @@ -71,17 +84,20 @@ class BASE_EXPORT JSONFileValueSerializer : public base::ValueSerializer { allow_trailing_comma_ = new_value; } - private: - bool SerializeInternal(const base::Value& root, bool omit_binary_values); - - base::FilePath json_file_path_; - bool allow_trailing_comma_; + // Returns the size (in bytes) of JSON string read from disk in the last + // successful |Deserialize()| call. + size_t get_last_read_size() const { return last_read_size_; } + private: // A wrapper for ReadFileToString which returns a non-zero JsonFileError if // there were file errors. int ReadFileToString(std::string* json_string); - DISALLOW_IMPLICIT_CONSTRUCTORS(JSONFileValueSerializer); + const base::FilePath json_file_path_; + bool allow_trailing_comma_; + size_t last_read_size_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(JSONFileValueDeserializer); }; #endif // BASE_JSON_JSON_FILE_VALUE_SERIALIZER_H_ diff --git a/chromium/base/json/json_parser.cc b/chromium/base/json/json_parser.cc index 6a25bc7b481..4d79be36194 100644 --- a/chromium/base/json/json_parser.cc +++ b/chromium/base/json/json_parser.cc @@ -4,7 +4,8 @@ #include "base/json/json_parser.h" -#include "base/float_util.h" +#include <cmath> + #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/strings/string_number_conversions.h" @@ -428,7 +429,7 @@ bool JSONParser::EatComment() { if (next_char == '/') { // Single line comment, read to newline. while (CanConsume(1)) { - char next_char = *NextChar(); + next_char = *NextChar(); if (next_char == '\n' || next_char == '\r') return true; } @@ -872,7 +873,7 @@ Value* JSONParser::ConsumeNumber() { double num_double; if (base::StringToDouble(num_string.as_string(), &num_double) && - IsFinite(num_double)) { + std::isfinite(num_double)) { return new FundamentalValue(num_double); } @@ -931,7 +932,7 @@ Value* JSONParser::ConsumeLiteral() { return NULL; } NextNChars(kNullLen - 1); - return Value::CreateNullValue(); + return Value::CreateNullValue().release(); } default: ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1); diff --git a/chromium/base/json/json_string_value_serializer.cc b/chromium/base/json/json_string_value_serializer.cc index 59f030dff2f..debf9f088d1 100644 --- a/chromium/base/json/json_string_value_serializer.cc +++ b/chromium/base/json/json_string_value_serializer.cc @@ -10,6 +10,11 @@ using base::Value; +JSONStringValueSerializer::JSONStringValueSerializer(std::string* json_string) + : json_string_(json_string), + pretty_print_(false) { +} + JSONStringValueSerializer::~JSONStringValueSerializer() {} bool JSONStringValueSerializer::Serialize(const Value& root) { @@ -23,7 +28,7 @@ bool JSONStringValueSerializer::SerializeAndOmitBinaryValues( bool JSONStringValueSerializer::SerializeInternal(const Value& root, bool omit_binary_values) { - if (!json_string_ || initialized_with_const_string_) + if (!json_string_) return false; int options = 0; @@ -35,12 +40,17 @@ bool JSONStringValueSerializer::SerializeInternal(const Value& root, return base::JSONWriter::WriteWithOptions(&root, options, json_string_); } -Value* JSONStringValueSerializer::Deserialize(int* error_code, - std::string* error_str) { - if (!json_string_) - return NULL; +JSONStringValueDeserializer::JSONStringValueDeserializer( + const base::StringPiece& json_string) + : json_string_(json_string), + allow_trailing_comma_(false) { +} + +JSONStringValueDeserializer::~JSONStringValueDeserializer() {} - return base::JSONReader::ReadAndReturnError(*json_string_, +Value* JSONStringValueDeserializer::Deserialize(int* error_code, + std::string* error_str) { + return base::JSONReader::ReadAndReturnError(json_string_, allow_trailing_comma_ ? base::JSON_ALLOW_TRAILING_COMMAS : base::JSON_PARSE_RFC, error_code, error_str); diff --git a/chromium/base/json/json_string_value_serializer.h b/chromium/base/json/json_string_value_serializer.h index 6435051aa19..bc0e66d127f 100644 --- a/chromium/base/json/json_string_value_serializer.h +++ b/chromium/base/json/json_string_value_serializer.h @@ -10,28 +10,15 @@ #include "base/base_export.h" #include "base/basictypes.h" #include "base/files/file_path.h" +#include "base/strings/string_piece.h" #include "base/values.h" class BASE_EXPORT JSONStringValueSerializer : public base::ValueSerializer { public: - // json_string is the string that will be source of the deserialization - // or the destination of the serialization. The caller of the constructor - // retains ownership of the string. - explicit JSONStringValueSerializer(std::string* json_string) - : json_string_(json_string), - initialized_with_const_string_(false), - pretty_print_(false), - allow_trailing_comma_(false) { - } - - // This version allows initialization with a const string reference for - // deserialization only. - explicit JSONStringValueSerializer(const std::string& json_string) - : json_string_(&const_cast<std::string&>(json_string)), - initialized_with_const_string_(true), - pretty_print_(false), - allow_trailing_comma_(false) { - } + // |json_string| is the string that will be the destination of the + // serialization. The caller of the constructor retains ownership of the + // string. |json_string| must not be null. + explicit JSONStringValueSerializer(std::string* json_string); ~JSONStringValueSerializer() override; @@ -44,9 +31,30 @@ class BASE_EXPORT JSONStringValueSerializer : public base::ValueSerializer { // output. bool SerializeAndOmitBinaryValues(const base::Value& root); + void set_pretty_print(bool new_value) { pretty_print_ = new_value; } + bool pretty_print() { return pretty_print_; } + + private: + bool SerializeInternal(const base::Value& root, bool omit_binary_values); + + // Owned by the caller of the constructor. + std::string* json_string_; + bool pretty_print_; // If true, serialization will span multiple lines. + + DISALLOW_COPY_AND_ASSIGN(JSONStringValueSerializer); +}; + +class BASE_EXPORT JSONStringValueDeserializer : public base::ValueDeserializer { + public: + // This retains a reference to the contents of |json_string|, so the data + // must outlive the JSONStringValueDeserializer. + explicit JSONStringValueDeserializer(const base::StringPiece& json_string); + + ~JSONStringValueDeserializer() override; + // Attempt to deserialize the data structure encoded in the string passed // in to the constructor into a structure of Value objects. If the return - // value is NULL, and if |error_code| is non-null, |error_code| will + // value is null, and if |error_code| is non-null, |error_code| will // contain an integer error code (a JsonParseError in this case). // If |error_message| is non-null, it will be filled in with a formatted // error message including the location of the error if appropriate. @@ -54,24 +62,17 @@ class BASE_EXPORT JSONStringValueSerializer : public base::ValueSerializer { base::Value* Deserialize(int* error_code, std::string* error_message) override; - void set_pretty_print(bool new_value) { pretty_print_ = new_value; } - bool pretty_print() { return pretty_print_; } - void set_allow_trailing_comma(bool new_value) { allow_trailing_comma_ = new_value; } private: - bool SerializeInternal(const base::Value& root, bool omit_binary_values); - - std::string* json_string_; - bool initialized_with_const_string_; - bool pretty_print_; // If true, serialization will span multiple lines. + // Data is owned by the caller of the constructor. + base::StringPiece json_string_; // If true, deserialization will allow trailing commas. bool allow_trailing_comma_; - DISALLOW_COPY_AND_ASSIGN(JSONStringValueSerializer); + DISALLOW_COPY_AND_ASSIGN(JSONStringValueDeserializer); }; #endif // BASE_JSON_JSON_STRING_VALUE_SERIALIZER_H_ - diff --git a/chromium/base/json/json_value_converter.cc b/chromium/base/json/json_value_converter.cc new file mode 100644 index 00000000000..6f772f366b8 --- /dev/null +++ b/chromium/base/json/json_value_converter.cc @@ -0,0 +1,37 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/json/json_value_converter.h" + +namespace base { +namespace internal { + +bool BasicValueConverter<int>::Convert( + const base::Value& value, int* field) const { + return value.GetAsInteger(field); +} + +bool BasicValueConverter<std::string>::Convert( + const base::Value& value, std::string* field) const { + return value.GetAsString(field); +} + +bool BasicValueConverter<string16>::Convert( + const base::Value& value, string16* field) const { + return value.GetAsString(field); +} + +bool BasicValueConverter<double>::Convert( + const base::Value& value, double* field) const { + return value.GetAsDouble(field); +} + +bool BasicValueConverter<bool>::Convert( + const base::Value& value, bool* field) const { + return value.GetAsBoolean(field); +} + +} // namespace internal +} // namespace base + diff --git a/chromium/base/json/json_value_converter.h b/chromium/base/json/json_value_converter.h index f049d9a8588..f94d46e3ccf 100644 --- a/chromium/base/json/json_value_converter.h +++ b/chromium/base/json/json_value_converter.h @@ -8,6 +8,7 @@ #include <string> #include <vector> +#include "base/base_export.h" #include "base/basictypes.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" @@ -122,8 +123,7 @@ class FieldConverter : public FieldConverterBase<StructType> { value_converter_(converter) { } - virtual bool ConvertField( - const base::Value& value, StructType* dst) const override { + bool ConvertField(const base::Value& value, StructType* dst) const override { return value_converter_->Convert(value, &(dst->*field_pointer_)); } @@ -137,67 +137,57 @@ template <typename FieldType> class BasicValueConverter; template <> -class BasicValueConverter<int> : public ValueConverter<int> { +class BASE_EXPORT BasicValueConverter<int> : public ValueConverter<int> { public: BasicValueConverter() {} - virtual bool Convert(const base::Value& value, int* field) const override { - return value.GetAsInteger(field); - } + bool Convert(const base::Value& value, int* field) const override; private: DISALLOW_COPY_AND_ASSIGN(BasicValueConverter); }; template <> -class BasicValueConverter<std::string> : public ValueConverter<std::string> { +class BASE_EXPORT BasicValueConverter<std::string> + : public ValueConverter<std::string> { public: BasicValueConverter() {} - virtual bool Convert( - const base::Value& value, std::string* field) const override { - return value.GetAsString(field); - } + bool Convert(const base::Value& value, std::string* field) const override; private: DISALLOW_COPY_AND_ASSIGN(BasicValueConverter); }; template <> -class BasicValueConverter<string16> : public ValueConverter<string16> { +class BASE_EXPORT BasicValueConverter<string16> + : public ValueConverter<string16> { public: BasicValueConverter() {} - virtual bool Convert( - const base::Value& value, string16* field) const override { - return value.GetAsString(field); - } + bool Convert(const base::Value& value, string16* field) const override; private: DISALLOW_COPY_AND_ASSIGN(BasicValueConverter); }; template <> -class BasicValueConverter<double> : public ValueConverter<double> { +class BASE_EXPORT BasicValueConverter<double> : public ValueConverter<double> { public: BasicValueConverter() {} - virtual bool Convert(const base::Value& value, double* field) const override { - return value.GetAsDouble(field); - } + bool Convert(const base::Value& value, double* field) const override; private: DISALLOW_COPY_AND_ASSIGN(BasicValueConverter); }; template <> -class BasicValueConverter<bool> : public ValueConverter<bool> { +class BASE_EXPORT BasicValueConverter<bool> : public ValueConverter<bool> { public: BasicValueConverter() {} - virtual bool Convert(const base::Value& value, bool* field) const override { - return value.GetAsBoolean(field); - } + bool Convert(const base::Value& value, bool* field) const override; private: DISALLOW_COPY_AND_ASSIGN(BasicValueConverter); @@ -211,8 +201,7 @@ class ValueFieldConverter : public ValueConverter<FieldType> { ValueFieldConverter(ConvertFunc convert_func) : convert_func_(convert_func) {} - virtual bool Convert(const base::Value& value, - FieldType* field) const override { + bool Convert(const base::Value& value, FieldType* field) const override { return convert_func_(&value, field); } @@ -230,8 +219,7 @@ class CustomFieldConverter : public ValueConverter<FieldType> { CustomFieldConverter(ConvertFunc convert_func) : convert_func_(convert_func) {} - virtual bool Convert(const base::Value& value, - FieldType* field) const override { + bool Convert(const base::Value& value, FieldType* field) const override { std::string string_value; return value.GetAsString(&string_value) && convert_func_(string_value, field); @@ -248,8 +236,7 @@ class NestedValueConverter : public ValueConverter<NestedType> { public: NestedValueConverter() {} - virtual bool Convert( - const base::Value& value, NestedType* field) const override { + bool Convert(const base::Value& value, NestedType* field) const override { return converter_.Convert(value, field); } @@ -263,8 +250,8 @@ class RepeatedValueConverter : public ValueConverter<ScopedVector<Element> > { public: RepeatedValueConverter() {} - virtual bool Convert( - const base::Value& value, ScopedVector<Element>* field) const override { + bool Convert(const base::Value& value, + ScopedVector<Element>* field) const override { const base::ListValue* list = NULL; if (!value.GetAsList(&list)) { // The field is not a list. @@ -299,8 +286,8 @@ class RepeatedMessageConverter public: RepeatedMessageConverter() {} - virtual bool Convert(const base::Value& value, - ScopedVector<NestedType>* field) const override { + bool Convert(const base::Value& value, + ScopedVector<NestedType>* field) const override { const base::ListValue* list = NULL; if (!value.GetAsList(&list)) return false; @@ -336,8 +323,8 @@ class RepeatedCustomValueConverter RepeatedCustomValueConverter(ConvertFunc convert_func) : convert_func_(convert_func) {} - virtual bool Convert(const base::Value& value, - ScopedVector<NestedType>* field) const override { + bool Convert(const base::Value& value, + ScopedVector<NestedType>* field) const override { const base::ListValue* list = NULL; if (!value.GetAsList(&list)) return false; diff --git a/chromium/base/json/json_value_serializer_unittest.cc b/chromium/base/json/json_value_serializer_unittest.cc index dc436937a33..b8aebe0656d 100644 --- a/chromium/base/json/json_value_serializer_unittest.cc +++ b/chromium/base/json/json_value_serializer_unittest.cc @@ -12,6 +12,7 @@ #include "base/json/json_writer.h" #include "base/memory/scoped_ptr.h" #include "base/path_service.h" +#include "base/strings/string_piece.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/values.h" @@ -42,11 +43,25 @@ const char kProperJSONWithCommas[] = "\t\"compound\": { \"a\": 1, \"b\": 2, },\n" "}\n"; +// kProperJSON with a few misc characters at the begin and end. +const char kProperJSONPadded[] = + ")]}'\n" + "{\n" + " \"compound\": {\n" + " \"a\": 1,\n" + " \"b\": 2\n" + " },\n" + " \"some_String\": \"1337\",\n" + " \"some_int\": 42,\n" + " \"the_list\": [ \"val1\", \"val2\" ]\n" + "}\n" + "?!ab\n"; + const char kWinLineEnds[] = "\r\n"; const char kLinuxLineEnds[] = "\n"; // Verifies the generated JSON against the expected output. -void CheckJSONIsStillTheSame(Value& value) { +void CheckJSONIsStillTheSame(const Value& value) { // Serialize back the output. std::string serialized_json; JSONStringValueSerializer str_serializer(&serialized_json); @@ -71,11 +86,29 @@ void ValidateJsonList(const std::string& json) { ASSERT_EQ(1, value); } -// Test proper JSON [de]serialization from string is working. -TEST(JSONValueSerializerTest, ReadProperJSONFromString) { +// Test proper JSON deserialization from string is working. +TEST(JSONValueDeserializerTest, ReadProperJSONFromString) { // Try to deserialize it through the serializer. - std::string proper_json(kProperJSON); - JSONStringValueSerializer str_deserializer(proper_json); + JSONStringValueDeserializer str_deserializer(kProperJSON); + + int error_code = 0; + std::string error_message; + scoped_ptr<Value> value( + str_deserializer.Deserialize(&error_code, &error_message)); + ASSERT_TRUE(value.get()); + ASSERT_EQ(0, error_code); + ASSERT_TRUE(error_message.empty()); + // Verify if the same JSON is still there. + CheckJSONIsStillTheSame(*value); +} + +// Test proper JSON deserialization from a StringPiece substring. +TEST(JSONValueDeserializerTest, ReadProperJSONFromStringPiece) { + // Create a StringPiece for the substring of kProperJSONPadded that matches + // kProperJSON. + base::StringPiece proper_json(kProperJSONPadded); + proper_json = proper_json.substr(5, proper_json.length() - 10); + JSONStringValueDeserializer str_deserializer(proper_json); int error_code = 0; std::string error_message; @@ -90,10 +123,9 @@ TEST(JSONValueSerializerTest, ReadProperJSONFromString) { // Test that trialing commas are only properly deserialized from string when // the proper flag for that is set. -TEST(JSONValueSerializerTest, ReadJSONWithTrailingCommasFromString) { +TEST(JSONValueDeserializerTest, ReadJSONWithTrailingCommasFromString) { // Try to deserialize it through the serializer. - std::string proper_json(kProperJSONWithCommas); - JSONStringValueSerializer str_deserializer(proper_json); + JSONStringValueDeserializer str_deserializer(kProperJSONWithCommas); int error_code = 0; std::string error_message; @@ -111,8 +143,8 @@ TEST(JSONValueSerializerTest, ReadJSONWithTrailingCommasFromString) { CheckJSONIsStillTheSame(*value); } -// Test proper JSON [de]serialization from file is working. -TEST(JSONValueSerializerTest, ReadProperJSONFromFile) { +// Test proper JSON deserialization from file is working. +TEST(JSONValueDeserializerTest, ReadProperJSONFromFile) { ScopedTempDir tempdir; ASSERT_TRUE(tempdir.CreateUniqueTempDir()); // Write it down in the file. @@ -121,7 +153,7 @@ TEST(JSONValueSerializerTest, ReadProperJSONFromFile) { WriteFile(temp_file, kProperJSON, strlen(kProperJSON))); // Try to deserialize it through the serializer. - JSONFileValueSerializer file_deserializer(temp_file); + JSONFileValueDeserializer file_deserializer(temp_file); int error_code = 0; std::string error_message; @@ -136,7 +168,7 @@ TEST(JSONValueSerializerTest, ReadProperJSONFromFile) { // Test that trialing commas are only properly deserialized from file when // the proper flag for that is set. -TEST(JSONValueSerializerTest, ReadJSONWithCommasFromFile) { +TEST(JSONValueDeserializerTest, ReadJSONWithCommasFromFile) { ScopedTempDir tempdir; ASSERT_TRUE(tempdir.CreateUniqueTempDir()); // Write it down in the file. @@ -146,7 +178,7 @@ TEST(JSONValueSerializerTest, ReadJSONWithCommasFromFile) { strlen(kProperJSONWithCommas))); // Try to deserialize it through the serializer. - JSONFileValueSerializer file_deserializer(temp_file); + JSONFileValueDeserializer file_deserializer(temp_file); // This must fail without the proper flag. int error_code = 0; std::string error_message; @@ -164,11 +196,27 @@ TEST(JSONValueSerializerTest, ReadJSONWithCommasFromFile) { CheckJSONIsStillTheSame(*value); } +TEST(JSONValueDeserializerTest, AllowTrailingComma) { + scoped_ptr<Value> root; + scoped_ptr<Value> root_expected; + static const char kTestWithCommas[] = "{\"key\": [true,],}"; + static const char kTestNoCommas[] = "{\"key\": [true]}"; + + JSONStringValueDeserializer deserializer(kTestWithCommas); + deserializer.set_allow_trailing_comma(true); + JSONStringValueDeserializer deserializer_expected(kTestNoCommas); + root.reset(deserializer.Deserialize(NULL, NULL)); + ASSERT_TRUE(root.get()); + root_expected.reset(deserializer_expected.Deserialize(NULL, NULL)); + ASSERT_TRUE(root_expected.get()); + ASSERT_TRUE(root->Equals(root_expected.get())); +} + TEST(JSONValueSerializerTest, Roundtrip) { - const std::string original_serialization = + static const char kOriginalSerialization[] = "{\"bool\":true,\"double\":3.14,\"int\":42,\"list\":[1,2],\"null\":null}"; - JSONStringValueSerializer serializer(original_serialization); - scoped_ptr<Value> root(serializer.Deserialize(NULL, NULL)); + JSONStringValueDeserializer deserializer(kOriginalSerialization); + scoped_ptr<Value> root(deserializer.Deserialize(NULL, NULL)); ASSERT_TRUE(root.get()); ASSERT_TRUE(root->IsType(Value::TYPE_DICTIONARY)); @@ -191,14 +239,10 @@ TEST(JSONValueSerializerTest, Roundtrip) { ASSERT_TRUE(root_dict->GetDouble("double", &double_value)); ASSERT_DOUBLE_EQ(3.14, double_value); - // We shouldn't be able to write using this serializer, since it was - // initialized with a const string. - ASSERT_FALSE(serializer.Serialize(*root_dict)); - std::string test_serialization; JSONStringValueSerializer mutable_serializer(&test_serialization); ASSERT_TRUE(mutable_serializer.Serialize(*root_dict)); - ASSERT_EQ(original_serialization, test_serialization); + ASSERT_EQ(kOriginalSerialization, test_serialization); mutable_serializer.set_pretty_print(true); ASSERT_TRUE(mutable_serializer.Serialize(*root_dict)); @@ -273,15 +317,15 @@ TEST(JSONValueSerializerTest, UnicodeStrings) { string16 test(WideToUTF16(L"\x7F51\x9875")); root.SetString("web", test); - std::string expected = "{\"web\":\"\xE7\xBD\x91\xE9\xA1\xB5\"}"; + static const char kExpected[] = "{\"web\":\"\xE7\xBD\x91\xE9\xA1\xB5\"}"; std::string actual; JSONStringValueSerializer serializer(&actual); ASSERT_TRUE(serializer.Serialize(root)); - ASSERT_EQ(expected, actual); + ASSERT_EQ(kExpected, actual); // escaped ascii text -> json - JSONStringValueSerializer deserializer(expected); + JSONStringValueDeserializer deserializer(kExpected); scoped_ptr<Value> deserial_root(deserializer.Deserialize(NULL, NULL)); ASSERT_TRUE(deserial_root.get()); DictionaryValue* dict_root = @@ -297,15 +341,15 @@ TEST(JSONValueSerializerTest, HexStrings) { string16 test(WideToUTF16(L"\x01\x02")); root.SetString("test", test); - std::string expected = "{\"test\":\"\\u0001\\u0002\"}"; + static const char kExpected[] = "{\"test\":\"\\u0001\\u0002\"}"; std::string actual; JSONStringValueSerializer serializer(&actual); ASSERT_TRUE(serializer.Serialize(root)); - ASSERT_EQ(expected, actual); + ASSERT_EQ(kExpected, actual); // escaped ascii text -> json - JSONStringValueSerializer deserializer(expected); + JSONStringValueDeserializer deserializer(kExpected); scoped_ptr<Value> deserial_root(deserializer.Deserialize(NULL, NULL)); ASSERT_TRUE(deserial_root.get()); DictionaryValue* dict_root = @@ -315,8 +359,8 @@ TEST(JSONValueSerializerTest, HexStrings) { ASSERT_EQ(test, test_value); // Test converting escaped regular chars - std::string escaped_chars = "{\"test\":\"\\u0067\\u006f\"}"; - JSONStringValueSerializer deserializer2(escaped_chars); + static const char kEscapedChars[] = "{\"test\":\"\\u0067\\u006f\"}"; + JSONStringValueDeserializer deserializer2(kEscapedChars); deserial_root.reset(deserializer2.Deserialize(NULL, NULL)); ASSERT_TRUE(deserial_root.get()); dict_root = static_cast<DictionaryValue*>(deserial_root.get()); @@ -324,22 +368,6 @@ TEST(JSONValueSerializerTest, HexStrings) { ASSERT_EQ(ASCIIToUTF16("go"), test_value); } -TEST(JSONValueSerializerTest, AllowTrailingComma) { - scoped_ptr<Value> root; - scoped_ptr<Value> root_expected; - std::string test_with_commas("{\"key\": [true,],}"); - std::string test_no_commas("{\"key\": [true]}"); - - JSONStringValueSerializer serializer(test_with_commas); - serializer.set_allow_trailing_comma(true); - JSONStringValueSerializer serializer_expected(test_no_commas); - root.reset(serializer.Deserialize(NULL, NULL)); - ASSERT_TRUE(root.get()); - root_expected.reset(serializer_expected.Deserialize(NULL, NULL)); - ASSERT_TRUE(root_expected.get()); - ASSERT_TRUE(root->Equals(root_expected.get())); -} - TEST(JSONValueSerializerTest, JSONReaderComments) { ValidateJsonList("[ // 2, 3, ignore me ] \n1 ]"); ValidateJsonList("[ /* 2, \n3, ignore me ]*/ \n1 ]"); @@ -372,9 +400,7 @@ TEST(JSONValueSerializerTest, JSONReaderComments) { class JSONFileValueSerializerTest : public testing::Test { protected: - virtual void SetUp() override { - ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); - } + void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); } base::ScopedTempDir temp_dir_; }; @@ -387,7 +413,7 @@ TEST_F(JSONFileValueSerializerTest, Roundtrip) { ASSERT_TRUE(PathExists(original_file_path)); - JSONFileValueSerializer deserializer(original_file_path); + JSONFileValueDeserializer deserializer(original_file_path); scoped_ptr<Value> root; root.reset(deserializer.Deserialize(NULL, NULL)); @@ -435,7 +461,7 @@ TEST_F(JSONFileValueSerializerTest, RoundtripNested) { ASSERT_TRUE(PathExists(original_file_path)); - JSONFileValueSerializer deserializer(original_file_path); + JSONFileValueDeserializer deserializer(original_file_path); scoped_ptr<Value> root; root.reset(deserializer.Deserialize(NULL, NULL)); ASSERT_TRUE(root.get()); @@ -460,9 +486,9 @@ TEST_F(JSONFileValueSerializerTest, NoWhitespace) { source_file_path = source_file_path.Append( FILE_PATH_LITERAL("serializer_test_nowhitespace.json")); ASSERT_TRUE(PathExists(source_file_path)); - JSONFileValueSerializer serializer(source_file_path); + JSONFileValueDeserializer deserializer(source_file_path); scoped_ptr<Value> root; - root.reset(serializer.Deserialize(NULL, NULL)); + root.reset(deserializer.Deserialize(NULL, NULL)); ASSERT_TRUE(root.get()); } diff --git a/chromium/base/json/json_writer_unittest.cc b/chromium/base/json/json_writer_unittest.cc index ae46800dde6..5ac2590a0d8 100644 --- a/chromium/base/json/json_writer_unittest.cc +++ b/chromium/base/json/json_writer_unittest.cc @@ -12,75 +12,63 @@ TEST(JSONWriterTest, BasicTypes) { std::string output_js; // Test null. - Value* root = Value::CreateNullValue(); - EXPECT_TRUE(JSONWriter::Write(root, &output_js)); + EXPECT_TRUE(JSONWriter::Write(Value::CreateNullValue().get(), &output_js)); EXPECT_EQ("null", output_js); - delete root; // Test empty dict. - root = new DictionaryValue; - EXPECT_TRUE(JSONWriter::Write(root, &output_js)); + DictionaryValue dict; + EXPECT_TRUE(JSONWriter::Write(&dict, &output_js)); EXPECT_EQ("{}", output_js); - delete root; // Test empty list. - root = new ListValue; - EXPECT_TRUE(JSONWriter::Write(root, &output_js)); + ListValue list; + EXPECT_TRUE(JSONWriter::Write(&list, &output_js)); EXPECT_EQ("[]", output_js); - delete root; // Test integer values. - root = new FundamentalValue(42); - EXPECT_TRUE(JSONWriter::Write(root, &output_js)); + FundamentalValue int_value(42); + EXPECT_TRUE(JSONWriter::Write(&int_value, &output_js)); EXPECT_EQ("42", output_js); - delete root; // Test boolean values. - root = new FundamentalValue(true); - EXPECT_TRUE(JSONWriter::Write(root, &output_js)); + FundamentalValue bool_value(true); + EXPECT_TRUE(JSONWriter::Write(&bool_value, &output_js)); EXPECT_EQ("true", output_js); - delete root; // Test Real values should always have a decimal or an 'e'. - root = new FundamentalValue(1.0); - EXPECT_TRUE(JSONWriter::Write(root, &output_js)); + FundamentalValue double_value(1.0); + EXPECT_TRUE(JSONWriter::Write(&double_value, &output_js)); EXPECT_EQ("1.0", output_js); - delete root; // Test Real values in the the range (-1, 1) must have leading zeros - root = new FundamentalValue(0.2); - EXPECT_TRUE(JSONWriter::Write(root, &output_js)); + FundamentalValue double_value2(0.2); + EXPECT_TRUE(JSONWriter::Write(&double_value2, &output_js)); EXPECT_EQ("0.2", output_js); - delete root; // Test Real values in the the range (-1, 1) must have leading zeros - root = new FundamentalValue(-0.8); - EXPECT_TRUE(JSONWriter::Write(root, &output_js)); + FundamentalValue double_value3(-0.8); + EXPECT_TRUE(JSONWriter::Write(&double_value3, &output_js)); EXPECT_EQ("-0.8", output_js); - delete root; // Test String values. - root = new StringValue("foo"); - EXPECT_TRUE(JSONWriter::Write(root, &output_js)); + StringValue string_value("foo"); + EXPECT_TRUE(JSONWriter::Write(&string_value, &output_js)); EXPECT_EQ("\"foo\"", output_js); - delete root; } - TEST(JSONWriterTest, NestedTypes) { std::string output_js; // Writer unittests like empty list/dict nesting, // list list nesting, etc. DictionaryValue root_dict; - ListValue* list = new ListValue; - root_dict.Set("list", list); - DictionaryValue* inner_dict = new DictionaryValue; - list->Append(inner_dict); + scoped_ptr<ListValue> list(new ListValue()); + scoped_ptr<DictionaryValue> inner_dict(new DictionaryValue()); inner_dict->SetInteger("inner int", 10); - ListValue* inner_list = new ListValue; - list->Append(inner_list); - list->Append(new FundamentalValue(true)); + list->Append(inner_dict.Pass()); + list->Append(make_scoped_ptr(new ListValue())); + list->AppendBoolean(true); + root_dict.Set("list", list.Pass()); // Test the pretty-printer. EXPECT_TRUE(JSONWriter::Write(&root_dict, &output_js)); @@ -109,17 +97,17 @@ TEST(JSONWriterTest, KeysWithPeriods) { std::string output_js; DictionaryValue period_dict; - period_dict.SetWithoutPathExpansion("a.b", new FundamentalValue(3)); - period_dict.SetWithoutPathExpansion("c", new FundamentalValue(2)); - DictionaryValue* period_dict2 = new DictionaryValue; - period_dict2->SetWithoutPathExpansion("g.h.i.j", new FundamentalValue(1)); - period_dict.SetWithoutPathExpansion("d.e.f", period_dict2); + period_dict.SetIntegerWithoutPathExpansion("a.b", 3); + period_dict.SetIntegerWithoutPathExpansion("c", 2); + scoped_ptr<DictionaryValue> period_dict2(new DictionaryValue()); + period_dict2->SetIntegerWithoutPathExpansion("g.h.i.j", 1); + period_dict.SetWithoutPathExpansion("d.e.f", period_dict2.Pass()); EXPECT_TRUE(JSONWriter::Write(&period_dict, &output_js)); EXPECT_EQ("{\"a.b\":3,\"c\":2,\"d.e.f\":{\"g.h.i.j\":1}}", output_js); DictionaryValue period_dict3; - period_dict3.Set("a.b", new FundamentalValue(2)); - period_dict3.SetWithoutPathExpansion("a.b", new FundamentalValue(1)); + period_dict3.SetInteger("a.b", 2); + period_dict3.SetIntegerWithoutPathExpansion("a.b", 1); EXPECT_TRUE(JSONWriter::Write(&period_dict3, &output_js)); EXPECT_EQ("{\"a\":{\"b\":2},\"a.b\":1}", output_js); } @@ -129,18 +117,17 @@ TEST(JSONWriterTest, BinaryValues) { // Binary values should return errors unless suppressed via the // OPTIONS_OMIT_BINARY_VALUES flag. - Value* root = BinaryValue::CreateWithCopiedBuffer("asdf", 4); - EXPECT_FALSE(JSONWriter::Write(root, &output_js)); + scoped_ptr<Value> root(BinaryValue::CreateWithCopiedBuffer("asdf", 4)); + EXPECT_FALSE(JSONWriter::Write(root.get(), &output_js)); EXPECT_TRUE(JSONWriter::WriteWithOptions( - root, JSONWriter::OPTIONS_OMIT_BINARY_VALUES, &output_js)); + root.get(), JSONWriter::OPTIONS_OMIT_BINARY_VALUES, &output_js)); EXPECT_TRUE(output_js.empty()); - delete root; ListValue binary_list; binary_list.Append(BinaryValue::CreateWithCopiedBuffer("asdf", 4)); - binary_list.Append(new FundamentalValue(5)); + binary_list.Append(make_scoped_ptr(new FundamentalValue(5))); binary_list.Append(BinaryValue::CreateWithCopiedBuffer("asdf", 4)); - binary_list.Append(new FundamentalValue(2)); + binary_list.Append(make_scoped_ptr(new FundamentalValue(2))); binary_list.Append(BinaryValue::CreateWithCopiedBuffer("asdf", 4)); EXPECT_FALSE(JSONWriter::Write(&binary_list, &output_js)); EXPECT_TRUE(JSONWriter::WriteWithOptions( @@ -148,11 +135,14 @@ TEST(JSONWriterTest, BinaryValues) { EXPECT_EQ("[5,2]", output_js); DictionaryValue binary_dict; - binary_dict.Set("a", BinaryValue::CreateWithCopiedBuffer("asdf", 4)); - binary_dict.Set("b", new FundamentalValue(5)); - binary_dict.Set("c", BinaryValue::CreateWithCopiedBuffer("asdf", 4)); - binary_dict.Set("d", new FundamentalValue(2)); - binary_dict.Set("e", BinaryValue::CreateWithCopiedBuffer("asdf", 4)); + binary_dict.Set( + "a", make_scoped_ptr(BinaryValue::CreateWithCopiedBuffer("asdf", 4))); + binary_dict.SetInteger("b", 5); + binary_dict.Set( + "c", make_scoped_ptr(BinaryValue::CreateWithCopiedBuffer("asdf", 4))); + binary_dict.SetInteger("d", 2); + binary_dict.Set( + "e", make_scoped_ptr(BinaryValue::CreateWithCopiedBuffer("asdf", 4))); EXPECT_FALSE(JSONWriter::Write(&binary_dict, &output_js)); EXPECT_TRUE(JSONWriter::WriteWithOptions( &binary_dict, JSONWriter::OPTIONS_OMIT_BINARY_VALUES, &output_js)); diff --git a/chromium/base/json/string_escape_unittest.cc b/chromium/base/json/string_escape_unittest.cc index 3eb4e8e5d55..100373fccd3 100644 --- a/chromium/base/json/string_escape_unittest.cc +++ b/chromium/base/json/string_escape_unittest.cc @@ -159,7 +159,7 @@ TEST(JSONStringEscapeTest, EscapeBytes) { const char* escaped; } cases[] = { {"b\x0f\x7f\xf0\xff!", "b\\u000F\\u007F\\u00F0\\u00FF!"}, - {"\xe5\xc4\x4f\x05\xb6\xfd\0", "\\u00E5\\u00C4O\\u0005\\u00B6\\u00FD"}, + {"\xe5\xc4\x4f\x05\xb6\xfd", "\\u00E5\\u00C4O\\u0005\\u00B6\\u00FD"}, }; for (size_t i = 0; i < arraysize(cases); ++i) { diff --git a/chromium/base/location.cc b/chromium/base/location.cc index 8b32b9785ba..1333e6ec453 100644 --- a/chromium/base/location.cc +++ b/chromium/base/location.cc @@ -31,6 +31,13 @@ Location::Location() program_counter_(NULL) { } +Location::Location(const Location& other) + : function_name_(other.function_name_), + file_name_(other.file_name_), + line_number_(other.line_number_), + program_counter_(other.program_counter_) { +} + std::string Location::ToString() const { return std::string(function_name_) + "@" + file_name_ + ":" + base::IntToString(line_number_); diff --git a/chromium/base/location.h b/chromium/base/location.h index 05a4f661090..477dc022273 100644 --- a/chromium/base/location.h +++ b/chromium/base/location.h @@ -5,10 +5,12 @@ #ifndef BASE_LOCATION_H_ #define BASE_LOCATION_H_ +#include <cassert> #include <string> #include "base/base_export.h" #include "base/basictypes.h" +#include "base/containers/hash_tables.h" namespace tracked_objects { @@ -27,18 +29,15 @@ class BASE_EXPORT Location { // Provide a default constructor for easy of debugging. Location(); - // Comparison operator for insertion into a std::map<> hash tables. - // All we need is *some* (any) hashing distinction. Strings should already - // be unique, so we don't bother with strcmp or such. - // Use line number as the primary key (because it is fast, and usually gets us - // a difference), and then pointers as secondary keys (just to get some - // distinctions). - bool operator < (const Location& other) const { - if (line_number_ != other.line_number_) - return line_number_ < other.line_number_; - if (file_name_ != other.file_name_) - return file_name_ < other.file_name_; - return function_name_ < other.function_name_; + // Copy constructor. + Location(const Location& other); + + // Comparator for hash map insertion. + // No need to use |function_name_| since the other two fields uniquely + // identify this location. + bool operator==(const Location& other) const { + return line_number_ == other.line_number_ && + file_name_ == other.file_name_; } const char* function_name() const { return function_name_; } @@ -48,6 +47,26 @@ class BASE_EXPORT Location { std::string ToString() const; + // Hash operator for hash maps. + struct Hash { + size_t operator()(const Location& location) const { + // Compute the hash value using file name pointer and line number. + // No need to use |function_name_| since the other two fields uniquely + // identify this location. + + // The file name will always be uniquely identified by its pointer since + // it comes from __FILE__, so no need to check the contents of the string. + // See the definition of FROM_HERE in location.h, and how it is used + // elsewhere. + + // Due to inconsistent definitions of uint64_t and uintptr_t, casting the + // file name pointer to a uintptr_t causes a compiler error for some + // platforms. The solution is to explicitly cast it to a uint64_t. + return base::HashPair(reinterpret_cast<uint64_t>(location.file_name()), + location.line_number()); + } + }; + // Translate the some of the state in this instance into a human readable // string with HTML characters in the function names escaped, and append that // string to |output|. Inclusion of the file_name_ and function_name_ are diff --git a/chromium/base/logging.cc b/chromium/base/logging.cc index 150a40815c5..c7a8881456d 100644 --- a/chromium/base/logging.cc +++ b/chromium/base/logging.cc @@ -19,7 +19,7 @@ typedef HANDLE MutexHandle; #include <mach-o/dyld.h> #elif defined(OS_POSIX) #if defined(OS_NACL) -#include <sys/time.h> // timespec doesn't seem to be in <time.h> +#include <sys/time.h> // timespec doesn't seem to be in <time.h> #else #include <sys/syscall.h> #endif @@ -76,8 +76,7 @@ VlogInfo* g_vlog_info_prev = NULL; const char* const log_severity_names[LOG_NUM_SEVERITIES] = { "INFO", "WARNING", "ERROR", "FATAL" }; -const char* log_severity_name(int severity) -{ +const char* log_severity_name(int severity) { if (severity >= 0 && severity < LOG_NUM_SEVERITIES) return log_severity_names[severity]; return "UNKNOWN"; @@ -152,7 +151,7 @@ uint64 TickCount() { void DeleteFilePath(const PathString& log_name) { #if defined(OS_WIN) DeleteFile(log_name.c_str()); -#elif defined (OS_NACL) +#elif defined(OS_NACL) // Do nothing; unlink() isn't supported on NaCl. #else unlink(log_name.c_str()); @@ -165,13 +164,12 @@ PathString GetDefaultLogFile() { wchar_t module_name[MAX_PATH]; GetModuleFileName(NULL, module_name, MAX_PATH); - PathString log_file = module_name; - PathString::size_type last_backslash = - log_file.rfind('\\', log_file.size()); + PathString log_name = module_name; + PathString::size_type last_backslash = log_name.rfind('\\', log_name.size()); if (last_backslash != PathString::npos) - log_file.erase(last_backslash + 1); - log_file += L"debug.log"; - return log_file; + log_name.erase(last_backslash + 1); + log_name += L"debug.log"; + return log_name; #elif defined(OS_POSIX) // On other platforms we just use the current directory. return PathString("debug.log"); @@ -359,7 +357,7 @@ bool BaseInitLoggingImpl(const LoggingSettings& settings) { // Can log only to the system debug log. CHECK_EQ(settings.logging_dest & ~LOG_TO_SYSTEM_DEBUG_LOG, 0); #endif - CommandLine* command_line = CommandLine::ForCurrentProcess(); + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); // Don't bother initializing g_vlog_info unless we use one of the // vlog switches. if (command_line->HasSwitch(switches::kV) || @@ -660,7 +658,7 @@ void LogMessage::Init(const char* file, int line) { if (log_timestamp) { time_t t = time(NULL); struct tm local_time = {0}; -#if _MSC_VER >= 1400 +#ifdef _MSC_VER localtime_s(&local_time, &t); #else localtime_r(&t, &local_time); @@ -706,8 +704,8 @@ SystemErrorCode GetLastSystemErrorCode() { #if defined(OS_WIN) BASE_EXPORT std::string SystemErrorCodeToString(SystemErrorCode error_code) { - const int error_message_buffer_size = 256; - char msgbuf[error_message_buffer_size]; + const int kErrorMessageBufferSize = 256; + char msgbuf[kErrorMessageBufferSize]; DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; DWORD len = FormatMessageA(flags, NULL, error_code, 0, msgbuf, arraysize(msgbuf), NULL); @@ -808,5 +806,5 @@ std::wstring GetLogFileFullPath() { } // namespace logging std::ostream& std::operator<<(std::ostream& out, const wchar_t* wstr) { - return out << base::WideToUTF8(std::wstring(wstr)); + return out << base::WideToUTF8(wstr); } diff --git a/chromium/base/logging.h b/chromium/base/logging.h index 6a1df765895..cc0a5aa11bc 100644 --- a/chromium/base/logging.h +++ b/chromium/base/logging.h @@ -350,7 +350,7 @@ const LogSeverity LOG_0 = LOG_ERROR; ((verboselevel) <= ::logging::GetVlogLevel(__FILE__)) // Helper macro which avoids evaluating the arguments to a stream if -// the condition doesn't hold. +// the condition doesn't hold. Condition is evaluated once and only once. #define LAZY_STREAM(stream, condition) \ !(condition) ? (void) 0 : ::logging::LogMessageVoidify() & (stream) @@ -560,9 +560,9 @@ DEFINE_CHECK_OP_IMPL(GT, > ) #endif #if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON) -#define DCHECK_IS_ON 0 +#define DCHECK_IS_ON() 0 #else -#define DCHECK_IS_ON 1 +#define DCHECK_IS_ON() 1 #endif // Definitions for DLOG et al. @@ -616,14 +616,14 @@ enum { DEBUG_MODE = ENABLE_DLOG }; // Definitions for DCHECK et al. -#if DCHECK_IS_ON +#if DCHECK_IS_ON() #define COMPACT_GOOGLE_LOG_EX_DCHECK(ClassName, ...) \ COMPACT_GOOGLE_LOG_EX_FATAL(ClassName , ##__VA_ARGS__) #define COMPACT_GOOGLE_LOG_DCHECK COMPACT_GOOGLE_LOG_FATAL const LogSeverity LOG_DCHECK = LOG_FATAL; -#else // DCHECK_IS_ON +#else // DCHECK_IS_ON() // These are just dummy values. #define COMPACT_GOOGLE_LOG_EX_DCHECK(ClassName, ...) \ @@ -631,7 +631,7 @@ const LogSeverity LOG_DCHECK = LOG_FATAL; #define COMPACT_GOOGLE_LOG_DCHECK COMPACT_GOOGLE_LOG_INFO const LogSeverity LOG_DCHECK = LOG_INFO; -#endif // DCHECK_IS_ON +#endif // DCHECK_IS_ON() // DCHECK et al. make sure to reference |condition| regardless of // whether DCHECKs are enabled; this is so that we don't get unused @@ -653,26 +653,24 @@ const LogSeverity LOG_DCHECK = LOG_INFO; #else // _PREFAST_ -#define DCHECK(condition) \ - LAZY_STREAM(LOG_STREAM(DCHECK), DCHECK_IS_ON ? !(condition) : false) \ - << "Check failed: " #condition ". " +#define DCHECK(condition) \ + LAZY_STREAM(LOG_STREAM(DCHECK), DCHECK_IS_ON() ? !(condition) : false) \ + << "Check failed: " #condition ". " -#define DPCHECK(condition) \ - LAZY_STREAM(PLOG_STREAM(DCHECK), DCHECK_IS_ON ? !(condition) : false) \ - << "Check failed: " #condition ". " +#define DPCHECK(condition) \ + LAZY_STREAM(PLOG_STREAM(DCHECK), DCHECK_IS_ON() ? !(condition) : false) \ + << "Check failed: " #condition ". " #endif // _PREFAST_ // Helper macro for binary operators. // Don't use this macro directly in your code, use DCHECK_EQ et al below. -#define DCHECK_OP(name, op, val1, val2) \ - if (DCHECK_IS_ON) \ - if (std::string* _result = \ - logging::Check##name##Impl((val1), (val2), \ - #val1 " " #op " " #val2)) \ - logging::LogMessage( \ - __FILE__, __LINE__, ::logging::LOG_DCHECK, \ - _result).stream() +#define DCHECK_OP(name, op, val1, val2) \ + if (DCHECK_IS_ON()) \ + if (std::string* _result = logging::Check##name##Impl( \ + (val1), (val2), #val1 " " #op " " #val2)) \ + logging::LogMessage(__FILE__, __LINE__, ::logging::LOG_DCHECK, _result) \ + .stream() // Equality/Inequality checks - compare two values, and log a // LOG_DCHECK message including the two values when the result is not @@ -701,7 +699,7 @@ const LogSeverity LOG_DCHECK = LOG_INFO; #define DCHECK_GT(val1, val2) DCHECK_OP(GT, > , val1, val2) #define DCHECK_IMPLIES(val1, val2) DCHECK(!(val1) || (val2)) -#if !DCHECK_IS_ON && defined(OS_CHROMEOS) +#if !DCHECK_IS_ON() && defined(OS_CHROMEOS) #define NOTREACHED() LOG(ERROR) << "NOTREACHED() hit in " << \ __FUNCTION__ << ". " #else diff --git a/chromium/base/logging_unittest.cc b/chromium/base/logging_unittest.cc index 95a16f2e4bb..8b9701a545f 100644 --- a/chromium/base/logging_unittest.cc +++ b/chromium/base/logging_unittest.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "base/basictypes.h" +#include "base/compiler_specific.h" #include "base/logging.h" #include "testing/gmock/include/gmock/gmock.h" @@ -186,7 +187,7 @@ TEST_F(LoggingTest, DebugLoggingReleaseBehavior) { TEST_F(LoggingTest, DcheckStreamsAreLazy) { MockLogSource mock_log_source; EXPECT_CALL(mock_log_source, Log()).Times(0); -#if DCHECK_IS_ON +#if DCHECK_IS_ON() DCHECK(true) << mock_log_source.Log(); DCHECK_EQ(0, 0) << mock_log_source.Log(); #else @@ -201,27 +202,27 @@ TEST_F(LoggingTest, DcheckStreamsAreLazy) { TEST_F(LoggingTest, Dcheck) { #if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON) // Release build. - EXPECT_FALSE(DCHECK_IS_ON); + EXPECT_FALSE(DCHECK_IS_ON()); EXPECT_FALSE(DLOG_IS_ON(DCHECK)); #elif defined(NDEBUG) && defined(DCHECK_ALWAYS_ON) // Release build with real DCHECKS. SetLogAssertHandler(&LogSink); - EXPECT_TRUE(DCHECK_IS_ON); + EXPECT_TRUE(DCHECK_IS_ON()); EXPECT_FALSE(DLOG_IS_ON(DCHECK)); #else // Debug build. SetLogAssertHandler(&LogSink); - EXPECT_TRUE(DCHECK_IS_ON); + EXPECT_TRUE(DCHECK_IS_ON()); EXPECT_TRUE(DLOG_IS_ON(DCHECK)); #endif EXPECT_EQ(0, log_sink_call_count); DCHECK(false); - EXPECT_EQ(DCHECK_IS_ON ? 1 : 0, log_sink_call_count); + EXPECT_EQ(DCHECK_IS_ON() ? 1 : 0, log_sink_call_count); DPCHECK(false); - EXPECT_EQ(DCHECK_IS_ON ? 2 : 0, log_sink_call_count); + EXPECT_EQ(DCHECK_IS_ON() ? 2 : 0, log_sink_call_count); DCHECK_EQ(0, 1); - EXPECT_EQ(DCHECK_IS_ON ? 3 : 0, log_sink_call_count); + EXPECT_EQ(DCHECK_IS_ON() ? 3 : 0, log_sink_call_count); } TEST_F(LoggingTest, DcheckReleaseBehavior) { diff --git a/chromium/base/logging_win.h b/chromium/base/logging_win.h index 38508a32466..aa48e22e76d 100644 --- a/chromium/base/logging_win.h +++ b/chromium/base/logging_win.h @@ -61,8 +61,8 @@ class BASE_EXPORT LogEventProvider : public base::win::EtwTraceProvider { protected: // Overridden to manipulate the log level on ETW control callbacks. - virtual void OnEventsEnabled(); - virtual void OnEventsDisabled(); + void OnEventsEnabled() override; + void OnEventsDisabled() override; private: LogEventProvider(); diff --git a/chromium/base/mac/authorization_util.mm b/chromium/base/mac/authorization_util.mm index 6cb8de3f73b..1dfd5a019f2 100644 --- a/chromium/base/mac/authorization_util.mm +++ b/chromium/base/mac/authorization_util.mm @@ -12,8 +12,8 @@ #include "base/basictypes.h" #include "base/logging.h" #include "base/mac/bundle_locations.h" +#include "base/mac/foundation_util.h" #include "base/mac/mac_logging.h" -#import "base/mac/mac_util.h" #include "base/mac/scoped_authorizationref.h" #include "base/posix/eintr_wrapper.h" #include "base/strings/string_number_conversions.h" @@ -28,10 +28,9 @@ AuthorizationRef GetAuthorizationRightsWithPrompt( AuthorizationFlags extraFlags) { // Create an empty AuthorizationRef. ScopedAuthorizationRef authorization; - OSStatus status = AuthorizationCreate(NULL, - kAuthorizationEmptyEnvironment, + OSStatus status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, - &authorization); + authorization.get_pointer()); if (status != errAuthorizationSuccess) { OSSTATUS_LOG(ERROR, status) << "AuthorizationCreate"; return NULL; diff --git a/chromium/base/mac/close_nocancel.cc b/chromium/base/mac/close_nocancel.cc index 134f7ac25cc..8971e731c98 100644 --- a/chromium/base/mac/close_nocancel.cc +++ b/chromium/base/mac/close_nocancel.cc @@ -28,12 +28,14 @@ // // This file operates by providing a close function with the non-$NOCANCEL // symbol name expected for the compilation environment as set by <unistd.h> -// and <sys/cdefs.h> (the DARWIN_ALIAS_C macro). That function calls the -// $NOCANCEL variant, which is resolved from libsyscall. By linking with this -// version of close prior to the libsyscall version, close's implementation is -// overridden. +// and <sys/cdefs.h> (the DARWIN_ALIAS_C macro). That name is set by an asm +// label on the declaration of the close function, so the definition of that +// function receives that name. The function calls the $NOCANCEL variant, which +// is resolved from libsyscall. By linking with this version of close prior to +// the libsyscall version, close's implementation is overridden. #include <sys/cdefs.h> +#include <unistd.h> // If the non-cancelable variants of all system calls have already been // chosen, do nothing. @@ -41,37 +43,26 @@ extern "C" { -#if __DARWIN_UNIX03 && !__DARWIN_ONLY_UNIX_CONFORMANCE +#if !__DARWIN_ONLY_UNIX_CONFORMANCE -// When there's a choice between UNIX2003 and pre-UNIX2003 and UNIX2003 has -// been chosen: -#define close_interface close$UNIX2003 -#define close_implementation close$NOCANCEL$UNIX2003 - -#elif !__DARWIN_UNIX03 && !__DARWIN_ONLY_UNIX_CONFORMANCE - -// When there's a choice between UNIX2003 and pre-UNIX2003 and pre-UNIX2003 -// has been chosen. There's no close$NOCANCEL symbol in this case, so use -// close$NOCANCEL$UNIX2003 as the implementation. It does the same thing -// that close$NOCANCEL would do. -#define close_interface close +// When there's a choice between UNIX2003 and pre-UNIX2003. There's no +// close$NOCANCEL symbol in this case, so use close$NOCANCEL$UNIX2003 as the +// implementation. It does the same thing that close$NOCANCEL would do. #define close_implementation close$NOCANCEL$UNIX2003 #else // __DARWIN_ONLY_UNIX_CONFORMANCE // When only UNIX2003 is supported: -#define close_interface close #define close_implementation close$NOCANCEL #endif int close_implementation(int fd); -int close_interface(int fd) { +int close(int fd) { return close_implementation(fd); } -#undef close_interface #undef close_implementation } // extern "C" diff --git a/chromium/base/mac/cocoa_protocols.h b/chromium/base/mac/cocoa_protocols.h index e10001f6be0..a28795c3a4d 100644 --- a/chromium/base/mac/cocoa_protocols.h +++ b/chromium/base/mac/cocoa_protocols.h @@ -2,15 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_COCOA_PROTOCOLS_MAC_H_ -#define BASE_COCOA_PROTOCOLS_MAC_H_ +#ifndef BASE_MAC_COCOA_PROTOCOLS_H_ +#define BASE_MAC_COCOA_PROTOCOLS_H_ #import <Cocoa/Cocoa.h> -// GTM also maintains a list of empty protocols, but only the ones the library -// requires. Augment that below. -#import "third_party/google_toolbox_for_mac/src/GTMDefines.h" - // New Mac OS X SDKs introduce new protocols used for delegates. These // protocol defintions aren't not present in earlier releases of the Mac OS X // SDK. In order to support building against the new SDK, which requires @@ -32,4 +28,4 @@ DEFINE_EMPTY_PROTOCOL(ICCameraDeviceDownloadDelegate) #undef DEFINE_EMPTY_PROTOCOL -#endif // BASE_COCOA_PROTOCOLS_MAC_H_ +#endif // BASE_MAC_COCOA_PROTOCOLS_H_ diff --git a/chromium/base/mac/dispatch_source_mach.cc b/chromium/base/mac/dispatch_source_mach.cc new file mode 100644 index 00000000000..745c9dec7d1 --- /dev/null +++ b/chromium/base/mac/dispatch_source_mach.cc @@ -0,0 +1,62 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/mac/dispatch_source_mach.h" + +namespace base { + +DispatchSourceMach::DispatchSourceMach(const char* name, + mach_port_t port, + void (^event_handler)()) + // TODO(rsesek): Specify DISPATCH_QUEUE_SERIAL, in the 10.7 SDK. NULL + // means the same thing but is not symbolically clear. + : DispatchSourceMach(dispatch_queue_create(name, NULL), + port, + event_handler) { + // Since the queue was created above in the delegated constructor, and it was + // subsequently retained, release it here. + dispatch_release(queue_); +} + +DispatchSourceMach::DispatchSourceMach(dispatch_queue_t queue, + mach_port_t port, + void (^event_handler)()) + : queue_(queue), + source_(dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, + port, 0, queue_)), + source_canceled_(dispatch_semaphore_create(0)) { + dispatch_retain(queue); + + dispatch_source_set_event_handler(source_, event_handler); + dispatch_source_set_cancel_handler(source_, ^{ + dispatch_semaphore_signal(source_canceled_); + }); +} + +DispatchSourceMach::~DispatchSourceMach() { + Cancel(); +} + +void DispatchSourceMach::Resume() { + dispatch_resume(source_); +} + +void DispatchSourceMach::Cancel() { + if (source_) { + dispatch_source_cancel(source_); + dispatch_release(source_); + source_ = NULL; + + dispatch_semaphore_wait(source_canceled_, DISPATCH_TIME_FOREVER); + dispatch_release(source_canceled_); + source_canceled_ = NULL; + } + + if (queue_) { + dispatch_release(queue_); + queue_ = NULL; + } +} + +} // namespace base diff --git a/chromium/base/mac/dispatch_source_mach.h b/chromium/base/mac/dispatch_source_mach.h new file mode 100644 index 00000000000..e7d5cb2fbb4 --- /dev/null +++ b/chromium/base/mac/dispatch_source_mach.h @@ -0,0 +1,61 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MAC_DISPATCH_SOURCE_MACH_H_ +#define BASE_MAC_DISPATCH_SOURCE_MACH_H_ + +#include <dispatch/dispatch.h> + +#include "base/base_export.h" +#include "base/macros.h" + +namespace base { + +// This class encapsulates a MACH_RECV dispatch source. When this object is +// destroyed, the source will be cancelled and it will wait for the source +// to stop executing work. The source can run on either a user-supplied queue, +// or it can create its own for the source. +class BASE_EXPORT DispatchSourceMach { + public: + // Creates a new dispatch source for the |port| and schedules it on a new + // queue that will be created with |name|. When a Mach message is received, + // the |event_handler| will be called. + DispatchSourceMach(const char* name, + mach_port_t port, + void (^event_handler)()); + + // Creates a new dispatch source with the same semantics as above, but rather + // than creating a new queue, it schedules the source on |queue|. + DispatchSourceMach(dispatch_queue_t queue, + mach_port_t port, + void (^event_handler)()); + + // Cancels the source and waits for it to become fully cancelled before + // releasing the source. + ~DispatchSourceMach(); + + // Resumes the source. This must be called before any Mach messages will + // be received. + void Resume(); + + private: + // Cancels the source, after which this class will no longer receive Mach + // messages. Calling this more than once is a no-op. + void Cancel(); + + // The dispatch queue used to service the source_. + dispatch_queue_t queue_; + + // A MACH_RECV dispatch source. + dispatch_source_t source_; + + // Semaphore used to wait on the |source_|'s cancellation in the destructor. + dispatch_semaphore_t source_canceled_; + + DISALLOW_COPY_AND_ASSIGN(DispatchSourceMach); +}; + +} // namespace base + +#endif // BASE_MAC_DISPATCH_SOURCE_MACH_H_ diff --git a/chromium/base/mac/dispatch_source_mach_unittest.cc b/chromium/base/mac/dispatch_source_mach_unittest.cc new file mode 100644 index 00000000000..82dc13643c4 --- /dev/null +++ b/chromium/base/mac/dispatch_source_mach_unittest.cc @@ -0,0 +1,125 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/mac/dispatch_source_mach.h" + +#include <mach/mach.h> + +#include "base/logging.h" +#include "base/mac/scoped_mach_port.h" +#include "base/memory/scoped_ptr.h" +#include "base/test/test_timeouts.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { + +class DispatchSourceMachTest : public testing::Test { + public: + void SetUp() override { + mach_port_t port = MACH_PORT_NULL; + ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(mach_task_self(), + MACH_PORT_RIGHT_RECEIVE, &port)); + receive_right_.reset(port); + + ASSERT_EQ(KERN_SUCCESS, mach_port_insert_right(mach_task_self(), port, + port, MACH_MSG_TYPE_MAKE_SEND)); + send_right_.reset(port); + } + + mach_port_t GetPort() { return receive_right_.get(); } + + void WaitForSemaphore(dispatch_semaphore_t semaphore) { + dispatch_semaphore_wait(semaphore, dispatch_time( + DISPATCH_TIME_NOW, + TestTimeouts::action_timeout().InSeconds() * NSEC_PER_SEC)); + } + + private: + base::mac::ScopedMachReceiveRight receive_right_; + base::mac::ScopedMachSendRight send_right_; +}; + +TEST_F(DispatchSourceMachTest, ReceiveAfterResume) { + dispatch_semaphore_t signal = dispatch_semaphore_create(0); + mach_port_t port = GetPort(); + + bool __block did_receive = false; + DispatchSourceMach source("org.chromium.base.test.ReceiveAfterResume", + port, ^{ + mach_msg_empty_rcv_t msg = {{0}}; + msg.header.msgh_size = sizeof(msg); + msg.header.msgh_local_port = port; + mach_msg_receive(&msg.header); + did_receive = true; + + dispatch_semaphore_signal(signal); + }); + + mach_msg_empty_send_t msg = {{0}}; + msg.header.msgh_size = sizeof(msg); + msg.header.msgh_remote_port = port; + msg.header.msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_COPY_SEND); + ASSERT_EQ(KERN_SUCCESS, mach_msg_send(&msg.header)); + + EXPECT_FALSE(did_receive); + + source.Resume(); + + WaitForSemaphore(signal); + dispatch_release(signal); + + EXPECT_TRUE(did_receive); +} + +TEST_F(DispatchSourceMachTest, NoMessagesAfterDestruction) { + mach_port_t port = GetPort(); + + scoped_ptr<int> count(new int(0)); + int* __block count_ptr = count.get(); + + scoped_ptr<DispatchSourceMach> source(new DispatchSourceMach( + "org.chromium.base.test.NoMessagesAfterDestruction", + port, ^{ + mach_msg_empty_rcv_t msg = {{0}}; + msg.header.msgh_size = sizeof(msg); + msg.header.msgh_local_port = port; + mach_msg_receive(&msg.header); + LOG(INFO) << "Receieve " << *count_ptr; + ++(*count_ptr); + })); + source->Resume(); + + dispatch_queue_t queue = + dispatch_queue_create("org.chromium.base.test.MessageSend", NULL); + dispatch_semaphore_t signal = dispatch_semaphore_create(0); + for (int i = 0; i < 30; ++i) { + dispatch_async(queue, ^{ + mach_msg_empty_send_t msg = {{0}}; + msg.header.msgh_size = sizeof(msg); + msg.header.msgh_remote_port = port; + msg.header.msgh_bits = + MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_COPY_SEND); + mach_msg_send(&msg.header); + }); + + // After sending five messages, shut down the source and taint the + // pointer the handler dereferences. The test will crash if |count_ptr| + // is being used after "free". + if (i == 5) { + scoped_ptr<DispatchSourceMach>* source_ptr = &source; + dispatch_async(queue, ^{ + source_ptr->reset(); + count_ptr = reinterpret_cast<int*>(0xdeaddead); + dispatch_semaphore_signal(signal); + }); + } + } + + WaitForSemaphore(signal); + dispatch_release(signal); + + dispatch_release(queue); +} + +} // namespace base diff --git a/chromium/base/mac/foundation_util.h b/chromium/base/mac/foundation_util.h index 46ea3c324c3..353ed7c6318 100644 --- a/chromium/base/mac/foundation_util.h +++ b/chromium/base/mac/foundation_util.h @@ -64,6 +64,15 @@ namespace mac { BASE_EXPORT bool AmIBundled(); BASE_EXPORT void SetOverrideAmIBundled(bool value); +#if defined(UNIT_TEST) +// This is required because instantiating some tests requires checking the +// directory structure, which sets the AmIBundled cache state. Individual tests +// may or may not be bundled, and this would trip them up if the cache weren't +// cleared. This should not be called from individual tests, just from test +// instantiation code that gets a path from PathService. +BASE_EXPORT void ClearAmIBundledCache(); +#endif + // Returns true if this process is marked as a "Background only process". BASE_EXPORT bool IsBackgroundOnlyProcess(); @@ -287,6 +296,7 @@ CF_CAST_DECL(CFUUID); CF_CAST_DECL(CGColor); CF_CAST_DECL(CTFont); +CF_CAST_DECL(CTFontDescriptor); CF_CAST_DECL(CTRun); CF_CAST_DECL(SecACL); diff --git a/chromium/base/mac/foundation_util.mm b/chromium/base/mac/foundation_util.mm index 4e9b2248874..27d6e7c4653 100644 --- a/chromium/base/mac/foundation_util.mm +++ b/chromium/base/mac/foundation_util.mm @@ -26,6 +26,8 @@ namespace mac { namespace { +bool g_cached_am_i_bundled_called = false; +bool g_cached_am_i_bundled_value = false; bool g_override_am_i_bundled = false; bool g_override_am_i_bundled_value = false; @@ -48,12 +50,15 @@ bool AmIBundled() { // If the return value is not cached, this function will return different // values depending on when it's called. This confuses some client code, see // http://crbug.com/63183 . - static bool result = UncachedAmIBundled(); - DCHECK_EQ(result, UncachedAmIBundled()) + if (!g_cached_am_i_bundled_called) { + g_cached_am_i_bundled_called = true; + g_cached_am_i_bundled_value = UncachedAmIBundled(); + } + DCHECK_EQ(g_cached_am_i_bundled_value, UncachedAmIBundled()) << "The return value of AmIBundled() changed. This will confuse tests. " << "Call SetAmIBundled() override manually if your test binary " << "delay-loads the framework."; - return result; + return g_cached_am_i_bundled_value; } void SetOverrideAmIBundled(bool value) { @@ -66,6 +71,10 @@ void SetOverrideAmIBundled(bool value) { g_override_am_i_bundled_value = value; } +BASE_EXPORT void ClearAmIBundledCache() { + g_cached_am_i_bundled_called = false; +} + bool IsBackgroundOnlyProcess() { // This function really does want to examine NSBundle's idea of the main // bundle dictionary. It needs to look at the actual running .app's @@ -353,6 +362,7 @@ CF_CAST_DEFN(CFUUID); CF_CAST_DEFN(CGColor); +CF_CAST_DEFN(CTFontDescriptor); CF_CAST_DEFN(CTRun); #if defined(OS_IOS) diff --git a/chromium/base/mac/launch_services_util.cc b/chromium/base/mac/launch_services_util.cc index 61210814040..4c3b417095f 100644 --- a/chromium/base/mac/launch_services_util.cc +++ b/chromium/base/mac/launch_services_util.cc @@ -7,6 +7,7 @@ #include "base/logging.h" #include "base/mac/mac_logging.h" #include "base/mac/mac_util.h" +#include "base/mac/scoped_cftyperef.h" #include "base/strings/sys_string_conversions.h" namespace base { diff --git a/chromium/base/mac/launch_services_util.h b/chromium/base/mac/launch_services_util.h index 0c52ca94c52..0e64316052b 100644 --- a/chromium/base/mac/launch_services_util.h +++ b/chromium/base/mac/launch_services_util.h @@ -5,7 +5,7 @@ #ifndef BASE_MAC_LAUNCH_SERVICES_UTIL_H_ #define BASE_MAC_LAUNCH_SERVICES_UTIL_H_ -#include <ApplicationServices/ApplicationServices.h> +#include <CoreServices/CoreServices.h> #include "base/base_export.h" #include "base/command_line.h" diff --git a/chromium/base/mac/libdispatch_task_runner.h b/chromium/base/mac/libdispatch_task_runner.h index f5fd866006e..b479bc7aa2b 100644 --- a/chromium/base/mac/libdispatch_task_runner.h +++ b/chromium/base/mac/libdispatch_task_runner.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_MAC_LIBDISPATCH_SEQUENCED_TASK_RUNNER_H_ -#define BASE_MAC_LIBDISPATCH_SEQUENCED_TASK_RUNNER_H_ +#ifndef BASE_MAC_LIBDISPATCH_TASK_RUNNER_H_ +#define BASE_MAC_LIBDISPATCH_TASK_RUNNER_H_ #include <dispatch/dispatch.h> @@ -77,4 +77,4 @@ class BASE_EXPORT LibDispatchTaskRunner : public base::SingleThreadTaskRunner { } // namespace mac } // namespace base -#endif // BASE_MAC_LIBDISPATCH_SEQUENCED_TASK_RUNNER_H_ +#endif // BASE_MAC_LIBDISPATCH_TASK_RUNNER_H_ diff --git a/chromium/base/mac/libdispatch_task_runner_unittest.cc b/chromium/base/mac/libdispatch_task_runner_unittest.cc index cad0efaa48e..49b0c9ae9d7 100644 --- a/chromium/base/mac/libdispatch_task_runner_unittest.cc +++ b/chromium/base/mac/libdispatch_task_runner_unittest.cc @@ -12,7 +12,7 @@ class LibDispatchTaskRunnerTest : public testing::Test { public: - virtual void SetUp() override { + void SetUp() override { task_runner_ = new base::mac::LibDispatchTaskRunner( "org.chromium.LibDispatchTaskRunnerTest"); } diff --git a/chromium/base/mac/mac_logging.h b/chromium/base/mac/mac_logging.h index 1081490ec4a..5192b208f1c 100644 --- a/chromium/base/mac/mac_logging.h +++ b/chromium/base/mac/mac_logging.h @@ -87,9 +87,9 @@ class BASE_EXPORT OSStatusLogMessage : public logging::LogMessage { LAZY_STREAM(OSSTATUS_VLOG_STREAM(verbose_level, status), \ MAC_DVLOG_IS_ON(verbose_level) && (condition)) -#define OSSTATUS_DCHECK(condition, status) \ - LAZY_STREAM(OSSTATUS_LOG_STREAM(FATAL, status), \ - DCHECK_IS_ON && !(condition)) \ - << "Check failed: " # condition << ". " +#define OSSTATUS_DCHECK(condition, status) \ + LAZY_STREAM(OSSTATUS_LOG_STREAM(FATAL, status), \ + DCHECK_IS_ON() && !(condition)) \ + << "Check failed: " #condition << ". " #endif // BASE_MAC_MAC_LOGGING_H_ diff --git a/chromium/base/mac/mac_util.h b/chromium/base/mac/mac_util.h index d93b49508dd..f8ffa97545d 100644 --- a/chromium/base/mac/mac_util.h +++ b/chromium/base/mac/mac_util.h @@ -12,9 +12,6 @@ #include "base/base_export.h" #include "base/logging.h" -// TODO(rohitrao): Clean up sites that include mac_util.h and remove this line. -#include "base/mac/foundation_util.h" - #if defined(__OBJC__) #import <Foundation/Foundation.h> #else // __OBJC__ diff --git a/chromium/base/mac/mac_util_unittest.mm b/chromium/base/mac/mac_util_unittest.mm index 3b23e53571b..3982ab00dfc 100644 --- a/chromium/base/mac/mac_util_unittest.mm +++ b/chromium/base/mac/mac_util_unittest.mm @@ -100,7 +100,8 @@ TEST_F(MacUtilTest, TestGetAppBundlePath) { } } -TEST_F(MacUtilTest, TestExcludeFileFromBackups) { +// http://crbug.com/425745 +TEST_F(MacUtilTest, DISABLED_TestExcludeFileFromBackups) { // The file must already exist in order to set its exclusion property. ScopedTempDir temp_dir_; ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); diff --git a/chromium/base/mac/mach_logging.h b/chromium/base/mac/mach_logging.h index a9b3b65d648..b12e274ea80 100644 --- a/chromium/base/mac/mach_logging.h +++ b/chromium/base/mac/mach_logging.h @@ -91,10 +91,10 @@ class BASE_EXPORT MachLogMessage : public logging::LogMessage { LAZY_STREAM(MACH_VLOG_STREAM(verbose_level, mach_err), \ MACH_DVLOG_IS_ON(verbose_level) && (condition)) -#define MACH_DCHECK(condition, mach_err) \ - LAZY_STREAM(MACH_LOG_STREAM(FATAL, mach_err), \ - DCHECK_IS_ON && !(condition)) \ - << "Check failed: " # condition << ". " +#define MACH_DCHECK(condition, mach_err) \ + LAZY_STREAM(MACH_LOG_STREAM(FATAL, mach_err), \ + DCHECK_IS_ON() && !(condition)) \ + << "Check failed: " #condition << ". " #if !defined(OS_IOS) @@ -157,10 +157,10 @@ class BASE_EXPORT BootstrapLogMessage : public logging::LogMessage { LAZY_STREAM(BOOTSTRAP_VLOG_STREAM(verbose_level, bootstrap_err), \ BOOTSTRAP_DVLOG_IS_ON(verbose_level) && (condition)) -#define BOOTSTRAP_DCHECK(condition, bootstrap_err) \ - LAZY_STREAM(BOOTSTRAP_LOG_STREAM(FATAL, bootstrap_err), \ - DCHECK_IS_ON && !(condition)) \ - << "Check failed: " # condition << ". " +#define BOOTSTRAP_DCHECK(condition, bootstrap_err) \ + LAZY_STREAM(BOOTSTRAP_LOG_STREAM(FATAL, bootstrap_err), \ + DCHECK_IS_ON() && !(condition)) \ + << "Check failed: " #condition << ". " #endif // !OS_IOS diff --git a/chromium/base/mac/memory_pressure_monitor.cc b/chromium/base/mac/memory_pressure_monitor.cc new file mode 100644 index 00000000000..5667aa6661c --- /dev/null +++ b/chromium/base/mac/memory_pressure_monitor.cc @@ -0,0 +1,78 @@ +// 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/memory_pressure_monitor.h" + +#include <dlfcn.h> +#include <sys/sysctl.h> + +#include "base/mac/mac_util.h" + +namespace base { +namespace mac { + +MemoryPressureListener::MemoryPressureLevel +MemoryPressureMonitor::MemoryPressureLevelForMacMemoryPressure( + int mac_memory_pressure) { + switch (mac_memory_pressure) { + case DISPATCH_MEMORYPRESSURE_NORMAL: + return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE; + case DISPATCH_MEMORYPRESSURE_WARN: + return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE; + case DISPATCH_MEMORYPRESSURE_CRITICAL: + return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL; + } + return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE; +} + +void MemoryPressureMonitor::NotifyMemoryPressureChanged( + dispatch_source_s* event_source) { + int mac_memory_pressure = dispatch_source_get_data(event_source); + MemoryPressureListener::MemoryPressureLevel memory_pressure_level = + MemoryPressureLevelForMacMemoryPressure(mac_memory_pressure); + MemoryPressureListener::NotifyMemoryPressure(memory_pressure_level); +} + +MemoryPressureMonitor::MemoryPressureMonitor() + : memory_level_event_source_(nullptr) { + // _dispatch_source_type_memorypressure is not available prior to 10.9. + dispatch_source_type_t dispatch_source_memorypressure = + static_cast<dispatch_source_type_t> + (dlsym(RTLD_NEXT, "_dispatch_source_type_memorypressure")); + if (dispatch_source_memorypressure) { + // The MemoryPressureListener doesn't want to know about transitions to + // MEMORY_PRESSURE_LEVEL_NONE so don't watch for + // DISPATCH_MEMORYPRESSURE_NORMAL notifications. + memory_level_event_source_.reset( + dispatch_source_create(dispatch_source_memorypressure, 0, + DISPATCH_MEMORYPRESSURE_WARN | + DISPATCH_MEMORYPRESSURE_CRITICAL, + dispatch_get_global_queue( + DISPATCH_QUEUE_PRIORITY_DEFAULT, 0))); + + dispatch_source_set_event_handler(memory_level_event_source_.get(), ^{ + NotifyMemoryPressureChanged(memory_level_event_source_.get()); + }); + dispatch_retain(memory_level_event_source_.get()); + dispatch_resume(memory_level_event_source_.get()); + } +} + +MemoryPressureMonitor::~MemoryPressureMonitor() { +} + +MemoryPressureListener::MemoryPressureLevel +MemoryPressureMonitor::GetCurrentPressureLevel() const { + if (base::mac::IsOSMountainLionOrEarlier()) { + return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE; + } + int mac_memory_pressure; + size_t length = sizeof(int); + sysctlbyname("kern.memorystatus_vm_pressure_level", &mac_memory_pressure, + &length, nullptr, 0); + return MemoryPressureLevelForMacMemoryPressure(mac_memory_pressure); +} + +} // namespace mac +} // namespace base diff --git a/chromium/base/mac/memory_pressure_monitor.h b/chromium/base/mac/memory_pressure_monitor.h new file mode 100644 index 00000000000..afaec274715 --- /dev/null +++ b/chromium/base/mac/memory_pressure_monitor.h @@ -0,0 +1,63 @@ +// 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_MEMORY_PRESSURE_MONITOR_H_ +#define BASE_MAC_MEMORY_PRESSURE_MONITOR_H_ + +#include <dispatch/dispatch.h> + +#include "base/base_export.h" +#include "base/memory/memory_pressure_listener.h" +#include "base/memory/memory_pressure_monitor.h" + +// The following was added to <dispatch/source.h> after 10.8. +// TODO(shrike): Remove the DISPATCH_MEMORYPRESSURE_NORMAL ifndef once builders +// reach 10.9 or higher. +#ifndef DISPATCH_MEMORYPRESSURE_NORMAL + +#define DISPATCH_MEMORYPRESSURE_NORMAL 0x01 +#define DISPATCH_MEMORYPRESSURE_WARN 0x02 +#define DISPATCH_MEMORYPRESSURE_CRITICAL 0x04 + +#endif // DISPATCH_MEMORYPRESSURE_NORMAL + +namespace base { +namespace mac { + +class TestMemoryPressureMonitor; + +struct DispatchSourceSDeleter { + void operator()(dispatch_source_s* ptr) { + dispatch_source_cancel(ptr); + dispatch_release(ptr); + } +}; + +// Declares the interface for the Mac MemoryPressureMonitor, which reports +// memory pressure events and status. +class BASE_EXPORT MemoryPressureMonitor : public base::MemoryPressureMonitor { + public: + MemoryPressureMonitor(); + ~MemoryPressureMonitor() override; + + // Returns the currently-observed memory pressure. + MemoryPressureLevel GetCurrentPressureLevel() const override; + + private: + friend TestMemoryPressureMonitor; + + static MemoryPressureLevel + MemoryPressureLevelForMacMemoryPressure(int mac_memory_pressure); + static void NotifyMemoryPressureChanged(dispatch_source_s* event_source); + + scoped_ptr<dispatch_source_s, DispatchSourceSDeleter> + memory_level_event_source_; + + DISALLOW_COPY_AND_ASSIGN(MemoryPressureMonitor); +}; + +} // namespace mac +} // namespace base + +#endif // BASE_MAC_MEMORY_PRESSURE_MONITOR_H_ diff --git a/chromium/base/mac/memory_pressure_monitor_unittest.cc b/chromium/base/mac/memory_pressure_monitor_unittest.cc new file mode 100644 index 00000000000..acb366609ef --- /dev/null +++ b/chromium/base/mac/memory_pressure_monitor_unittest.cc @@ -0,0 +1,61 @@ +// 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/memory_pressure_monitor.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { +namespace mac { + +class TestMemoryPressureMonitor : public MemoryPressureMonitor { + public: + using MemoryPressureMonitor::MemoryPressureLevelForMacMemoryPressure; + + TestMemoryPressureMonitor() { } + + private: + DISALLOW_COPY_AND_ASSIGN(TestMemoryPressureMonitor); +}; + +TEST(MacMemoryPressureMonitorTest, MemoryPressureFromMacMemoryPressure) { + EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, + TestMemoryPressureMonitor:: + MemoryPressureLevelForMacMemoryPressure( + DISPATCH_MEMORYPRESSURE_NORMAL)); + EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, + TestMemoryPressureMonitor:: + MemoryPressureLevelForMacMemoryPressure( + DISPATCH_MEMORYPRESSURE_WARN)); + EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL, + TestMemoryPressureMonitor:: + MemoryPressureLevelForMacMemoryPressure( + DISPATCH_MEMORYPRESSURE_CRITICAL)); + EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, + TestMemoryPressureMonitor:: + MemoryPressureLevelForMacMemoryPressure(0)); + EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, + TestMemoryPressureMonitor:: + MemoryPressureLevelForMacMemoryPressure(3)); + EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, + TestMemoryPressureMonitor:: + MemoryPressureLevelForMacMemoryPressure(5)); + EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, + TestMemoryPressureMonitor:: + MemoryPressureLevelForMacMemoryPressure(-1)); +} + +TEST(MacMemoryPressureMonitorTest, CurrentMemoryPressure) { + TestMemoryPressureMonitor monitor; + MemoryPressureListener::MemoryPressureLevel memory_pressure = + monitor.GetCurrentPressureLevel(); + EXPECT_TRUE(memory_pressure == + MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE || + memory_pressure == + MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE || + memory_pressure == + MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL); +} + +} // namespace mac +} // namespace base diff --git a/chromium/base/mac/scoped_authorizationref.h b/chromium/base/mac/scoped_authorizationref.h index 6413f2e4164..18114883800 100644 --- a/chromium/base/mac/scoped_authorizationref.h +++ b/chromium/base/mac/scoped_authorizationref.h @@ -49,9 +49,7 @@ class ScopedAuthorizationRef { return authorization_; } - AuthorizationRef* operator&() { - return &authorization_; - } + AuthorizationRef* get_pointer() { return &authorization_; } AuthorizationRef get() const { return authorization_; diff --git a/chromium/base/mac/scoped_mach_port.cc b/chromium/base/mac/scoped_mach_port.cc index de94602e36e..13307f2c9d2 100644 --- a/chromium/base/mac/scoped_mach_port.cc +++ b/chromium/base/mac/scoped_mach_port.cc @@ -25,6 +25,14 @@ void ReceiveRightTraits::Free(mach_port_t port) { << "ScopedMachReceiveRight mach_port_mod_refs"; } +// static +void PortSetTraits::Free(mach_port_t port) { + kern_return_t kr = + mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_PORT_SET, -1); + MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) + << "ScopedMachPortSet mach_port_mod_refs"; +} + } // namespace internal } // namespace mac } // namespace base diff --git a/chromium/base/mac/scoped_mach_port.h b/chromium/base/mac/scoped_mach_port.h index 36087c9bdec..beb62b03213 100644 --- a/chromium/base/mac/scoped_mach_port.h +++ b/chromium/base/mac/scoped_mach_port.h @@ -20,7 +20,7 @@ struct BASE_EXPORT SendRightTraits { return MACH_PORT_NULL; } - static void Free(mach_port_t port); + BASE_EXPORT static void Free(mach_port_t port); }; struct BASE_EXPORT ReceiveRightTraits { @@ -28,7 +28,15 @@ struct BASE_EXPORT ReceiveRightTraits { return MACH_PORT_NULL; } - static void Free(mach_port_t port); + BASE_EXPORT static void Free(mach_port_t port); +}; + +struct PortSetTraits { + static mach_port_t InvalidValue() { + return MACH_PORT_NULL; + } + + BASE_EXPORT static void Free(mach_port_t port); }; } // namespace internal @@ -59,6 +67,19 @@ class BASE_EXPORT ScopedMachReceiveRight : operator mach_port_t() const { return get(); } }; +// A scoper for handling a Mach port set. A port set can have only one +// reference. This takes ownership of that single reference on construction and +// destroys the port set on destruction. Destroying a port set does not destroy +// the receive rights that are members of the port set. +class BASE_EXPORT ScopedMachPortSet : + public ScopedGeneric<mach_port_t, internal::PortSetTraits> { + public: + explicit ScopedMachPortSet(mach_port_t port = traits_type::InvalidValue()) + : ScopedGeneric(port) {} + + operator mach_port_t() const { return get(); } +}; + } // namespace mac } // namespace base diff --git a/chromium/base/mac/scoped_mach_vm.cc b/chromium/base/mac/scoped_mach_vm.cc index 1c28d9c7c42..d52c77f6398 100644 --- a/chromium/base/mac/scoped_mach_vm.cc +++ b/chromium/base/mac/scoped_mach_vm.cc @@ -8,8 +8,8 @@ namespace base { namespace mac { void ScopedMachVM::reset(vm_address_t address, vm_size_t size) { - DCHECK(address % PAGE_SIZE == 0); - DCHECK(size % PAGE_SIZE == 0); + DCHECK_EQ(address % PAGE_SIZE, 0u); + DCHECK_EQ(size % PAGE_SIZE, 0u); if (size_) { if (address_ < address) { diff --git a/chromium/base/mac/scoped_mach_vm.h b/chromium/base/mac/scoped_mach_vm.h index b130a79fb63..ffc00d5a5d2 100644 --- a/chromium/base/mac/scoped_mach_vm.h +++ b/chromium/base/mac/scoped_mach_vm.h @@ -48,10 +48,9 @@ namespace mac { class BASE_EXPORT ScopedMachVM { public: explicit ScopedMachVM(vm_address_t address = 0, vm_size_t size = 0) - : address_(address), - size_(size) { - DCHECK(address % PAGE_SIZE == 0); - DCHECK(size % PAGE_SIZE == 0); + : address_(address), size_(size) { + DCHECK_EQ(address % PAGE_SIZE, 0u); + DCHECK_EQ(size % PAGE_SIZE, 0u); } ~ScopedMachVM() { diff --git a/chromium/base/mac/scoped_nsobject.h b/chromium/base/mac/scoped_nsobject.h index 8d7bd4a27bd..836bdcc3507 100644 --- a/chromium/base/mac/scoped_nsobject.h +++ b/chromium/base/mac/scoped_nsobject.h @@ -5,10 +5,16 @@ #ifndef BASE_MAC_SCOPED_NSOBJECT_H_ #define BASE_MAC_SCOPED_NSOBJECT_H_ -#import <Foundation/Foundation.h> +// Include NSObject.h directly because Foundation.h pulls in many dependencies. +// (Approx 100k lines of code versus 1.5k for NSObject.h). scoped_nsobject gets +// singled out because it is most typically included from other header files. +#import <Foundation/NSObject.h> + #include "base/basictypes.h" #include "base/compiler_specific.h" +@class NSAutoreleasePool; + namespace base { // scoped_nsobject<> is patterned after scoped_ptr<>, but maintains ownership @@ -40,6 +46,11 @@ class scoped_nsprotocol { : object_([that.object_ retain]) { } + template <typename NSU> + scoped_nsprotocol(const scoped_nsprotocol<NSU>& that) + : object_([that.get() retain]) { + } + ~scoped_nsprotocol() { [object_ release]; } @@ -119,6 +130,11 @@ class scoped_nsobject : public scoped_nsprotocol<NST*> { : scoped_nsprotocol<NST*>(that) { } + template<typename NSU> + scoped_nsobject(const scoped_nsobject<NSU>& that) + : scoped_nsprotocol<NST*>(that) { + } + scoped_nsobject& operator=(const scoped_nsobject<NST>& that) { scoped_nsprotocol<NST*>::operator=(that); return *this; @@ -135,6 +151,11 @@ class scoped_nsobject<id> : public scoped_nsprotocol<id> { : scoped_nsprotocol<id>(that) { } + template<typename NSU> + scoped_nsobject(const scoped_nsobject<NSU>& that) + : scoped_nsprotocol<id>(that) { + } + scoped_nsobject& operator=(const scoped_nsobject<id>& that) { scoped_nsprotocol<id>::operator=(that); return *this; diff --git a/chromium/base/mac/scoped_sending_event_unittest.mm b/chromium/base/mac/scoped_sending_event_unittest.mm index 95f6eba16ab..02ef2dbe292 100644 --- a/chromium/base/mac/scoped_sending_event_unittest.mm +++ b/chromium/base/mac/scoped_sending_event_unittest.mm @@ -27,9 +27,7 @@ class ScopedSendingEventTest : public testing::Test { ScopedSendingEventTest() : app_([[ScopedSendingEventTestCrApp alloc] init]) { NSApp = app_.get(); } - virtual ~ScopedSendingEventTest() { - NSApp = nil; - } + ~ScopedSendingEventTest() override { NSApp = nil; } private: base::scoped_nsobject<ScopedSendingEventTestCrApp> app_; diff --git a/chromium/base/mac/sdk_forward_declarations.h b/chromium/base/mac/sdk_forward_declarations.h index fde0fcc744a..d70a6aa7deb 100644 --- a/chromium/base/mac/sdk_forward_declarations.h +++ b/chromium/base/mac/sdk_forward_declarations.h @@ -18,16 +18,91 @@ #include "base/base_export.h" +// ---------------------------------------------------------------------------- +// Either define or forward declare classes only available in OSX 10.7+. +// ---------------------------------------------------------------------------- + +#if !defined(MAC_OS_X_VERSION_10_7) || \ + MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 + +@interface CWChannel : NSObject +@end + +@interface CBPeripheral : NSObject +@end + +@interface CBCentralManager : NSObject +@end + +@interface CBUUID : NSObject +@end + +#else + +@class CWChannel; +@class CBPeripheral; +@class CBCentralManager; +@class CBUUID; + +#endif // MAC_OS_X_VERSION_10_7 + +#if !defined(MAC_OS_X_VERSION_10_8) || \ + MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_8 + +@interface NSUUID : NSObject +@end + +#else + +@class NSUUID; + +#endif // MAC_OS_X_VERSION_10_8 + +#if !defined(MAC_OS_X_VERSION_10_9) || \ + MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_9 + +// NSProgress is public API in 10.9, but a version of it exists and is usable +// in 10.8. +@interface NSProgress : NSObject +@end + +@interface NSAppearance : NSObject +@end + +#else + +@class NSProgress; +@class NSAppearance; + +#endif // MAC_OS_X_VERSION_10_9 + +#if !defined(MAC_OS_X_VERSION_10_10) || \ + MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_10 + +@interface NSUserActivity : NSObject +@end + +#else + +@class NSUserActivity; + +#endif // MAC_OS_X_VERSION_10_10 + +// ---------------------------------------------------------------------------- +// Define typedefs, enums, and protocols not available in the version of the +// OSX SDK being compiled against. +// ---------------------------------------------------------------------------- + #if !defined(MAC_OS_X_VERSION_10_7) || \ MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 enum { - NSEventPhaseNone = 0, // event not associated with a phase. - NSEventPhaseBegan = 0x1 << 0, - NSEventPhaseStationary = 0x1 << 1, - NSEventPhaseChanged = 0x1 << 2, - NSEventPhaseEnded = 0x1 << 3, - NSEventPhaseCancelled = 0x1 << 4 + NSEventPhaseNone = 0, // event not associated with a phase. + NSEventPhaseBegan = 0x1 << 0, + NSEventPhaseStationary = 0x1 << 1, + NSEventPhaseChanged = 0x1 << 2, + NSEventPhaseEnded = 0x1 << 3, + NSEventPhaseCancelled = 0x1 << 4 }; typedef NSUInteger NSEventPhase; @@ -65,9 +140,141 @@ enum { }; typedef NSUInteger NSWindowButton; +enum CWChannelBand { + kCWChannelBandUnknown = 0, + kCWChannelBand2GHz = 1, + kCWChannelBand5GHz = 2, +}; + +enum { + kCWSecurityNone = 0, + kCWSecurityWEP = 1, + kCWSecurityWPAPersonal = 2, + kCWSecurityWPAPersonalMixed = 3, + kCWSecurityWPA2Personal = 4, + kCWSecurityPersonal = 5, + kCWSecurityDynamicWEP = 6, + kCWSecurityWPAEnterprise = 7, + kCWSecurityWPAEnterpriseMixed = 8, + kCWSecurityWPA2Enterprise = 9, + kCWSecurityEnterprise = 10, + kCWSecurityUnknown = NSIntegerMax, +}; + +typedef NSInteger CWSecurity; + +enum { + kBluetoothFeatureLESupportedController = (1 << 6L), +}; + +@protocol IOBluetoothDeviceInquiryDelegate +- (void)deviceInquiryStarted:(IOBluetoothDeviceInquiry*)sender; +- (void)deviceInquiryDeviceFound:(IOBluetoothDeviceInquiry*)sender + device:(IOBluetoothDevice*)device; +- (void)deviceInquiryComplete:(IOBluetoothDeviceInquiry*)sender + error:(IOReturn)error + aborted:(BOOL)aborted; +@end + +@protocol NSWindowDelegateFullScreenAdditions +- (void)windowDidFailToEnterFullScreen:(NSWindow*)window; +- (void)windowDidFailToExitFullScreen:(NSWindow*)window; +@end + +enum { + CBPeripheralStateDisconnected = 0, + CBPeripheralStateConnecting, + CBPeripheralStateConnected, +}; +typedef NSInteger CBPeripheralState; + +enum { + CBCentralManagerStateUnknown = 0, + CBCentralManagerStateResetting, + CBCentralManagerStateUnsupported, + CBCentralManagerStateUnauthorized, + CBCentralManagerStatePoweredOff, + CBCentralManagerStatePoweredOn, +}; +typedef NSInteger CBCentralManagerState; + +@protocol CBCentralManagerDelegate; + +@protocol CBCentralManagerDelegate<NSObject> +- (void)centralManagerDidUpdateState:(CBCentralManager*)central; +- (void)centralManager:(CBCentralManager*)central + didDiscoverPeripheral:(CBPeripheral*)peripheral + advertisementData:(NSDictionary*)advertisementData + RSSI:(NSNumber*)RSSI; +@end + +#endif // MAC_OS_X_VERSION_10_7 + +#if !defined(MAC_OS_X_VERSION_10_8) || \ + MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_8 + +enum { NSEventPhaseMayBegin = 0x1 << 5 }; + +#endif // MAC_OS_X_VERSION_10_8 + +#if !defined(MAC_OS_X_VERSION_10_9) || \ + MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_9 + +enum { + NSWindowOcclusionStateVisible = 1UL << 1, +}; +typedef NSUInteger NSWindowOcclusionState; + +enum { NSWorkspaceLaunchWithErrorPresentation = 0x00000040 }; + +#endif // MAC_OS_X_VERSION_10_9 + +// ---------------------------------------------------------------------------- +// Define NSStrings only available in newer versions of the OSX SDK to force +// them to be statically linked. +// ---------------------------------------------------------------------------- + +extern "C" { +#if !defined(MAC_OS_X_VERSION_10_7) || \ + MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7 +BASE_EXPORT extern NSString* const NSWindowWillEnterFullScreenNotification; +BASE_EXPORT extern NSString* const NSWindowWillExitFullScreenNotification; +BASE_EXPORT extern NSString* const NSWindowDidEnterFullScreenNotification; +BASE_EXPORT extern NSString* const NSWindowDidExitFullScreenNotification; +BASE_EXPORT extern NSString* const + NSWindowDidChangeBackingPropertiesNotification; +BASE_EXPORT extern NSString* const CBAdvertisementDataServiceDataKey; +BASE_EXPORT extern NSString* const + NSPreferredScrollerStyleDidChangeNotification; +#endif // MAC_OS_X_VERSION_10_7 + +#if !defined(MAC_OS_X_VERSION_10_9) || \ + MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_9 +BASE_EXPORT extern NSString* const NSWindowDidChangeOcclusionStateNotification; +BASE_EXPORT extern NSString* const CBAdvertisementDataIsConnectable; +#endif // MAC_OS_X_VERSION_10_9 + +#if !defined(MAC_OS_X_VERSION_10_10) || \ + MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10 +BASE_EXPORT extern NSString* const NSUserActivityTypeBrowsingWeb; +BASE_EXPORT extern NSString* const NSAppearanceNameVibrantDark; +#endif // MAC_OS_X_VERSION_10_10 +} // extern "C" + +// ---------------------------------------------------------------------------- +// If compiling against an older version of the OSX SDK, declare functions that +// are available in newer versions of the OSX SDK. If compiling against a newer +// version of the OSX SDK, redeclare those same functions to suppress +// -Wpartial-availability warnings. +// ---------------------------------------------------------------------------- + +// Once Chrome no longer supports OSX 10.6, everything within this preprocessor +// block can be removed. +#if !defined(MAC_OS_X_VERSION_10_7) || \ + MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7 + @interface NSEvent (LionSDK) + (BOOL)isSwipeTrackingFromScrollEventsEnabled; - - (NSEventPhase)momentumPhase; - (NSEventPhase)phase; - (BOOL)hasPreciseScrollingDeltas; @@ -79,10 +286,8 @@ typedef NSUInteger NSWindowButton; usingHandler:(void (^)(CGFloat gestureAmount, NSEventPhase phase, BOOL isComplete, - BOOL *stop))trackingHandler; - + BOOL* stop))trackingHandler; - (BOOL)isDirectionInvertedFromDevice; - @end @interface NSApplication (LionSDK) @@ -105,6 +310,7 @@ typedef NSUInteger NSWindowButton; - (void)setAnimationBehavior:(NSWindowAnimationBehavior)newAnimationBehavior; - (void)toggleFullScreen:(id)sender; - (void)setRestorable:(BOOL)flag; +- (NSRect)convertRectFromScreen:(NSRect)aRect; @end @interface NSCursor (LionSDKDeclarations) @@ -112,9 +318,9 @@ typedef NSUInteger NSWindowButton; @end @interface NSAnimationContext (LionSDK) -+ (void)runAnimationGroup:(void (^)(NSAnimationContext *context))changes ++ (void)runAnimationGroup:(void (^)(NSAnimationContext* context))changes completionHandler:(void (^)(void))completionHandler; -@property(copy) void(^completionHandler)(void); +@property(copy) void (^completionHandler)(void); @end @interface NSView (LionSDK) @@ -138,37 +344,13 @@ typedef NSUInteger NSWindowButton; - (BOOL)associateToNetwork:(CWNetwork*)network password:(NSString*)password error:(NSError**)error; -- (NSSet*)scanForNetworksWithName:(NSString*)networkName - error:(NSError**)error; +- (NSSet*)scanForNetworksWithName:(NSString*)networkName error:(NSError**)error; @end -enum CWChannelBand { - kCWChannelBandUnknown = 0, - kCWChannelBand2GHz = 1, - kCWChannelBand5GHz = 2, -}; - -@interface CWChannel : NSObject +@interface CWChannel (LionSDK) @property(readonly) CWChannelBand channelBand; @end -enum { - kCWSecurityNone = 0, - kCWSecurityWEP = 1, - kCWSecurityWPAPersonal = 2, - kCWSecurityWPAPersonalMixed = 3, - kCWSecurityWPA2Personal = 4, - kCWSecurityPersonal = 5, - kCWSecurityDynamicWEP = 6, - kCWSecurityWPAEnterprise = 7, - kCWSecurityWPAEnterpriseMixed = 8, - kCWSecurityWPA2Enterprise = 9, - kCWSecurityEnterprise = 10, - kCWSecurityUnknown = NSIntegerMax, -}; - -typedef NSInteger CWSecurity; - @interface CWNetwork (LionSDK) @property(readonly) CWChannel* wlanChannel; @property(readonly) NSInteger rssiValue; @@ -180,19 +362,6 @@ typedef NSInteger CWSecurity; - (BluetoothHCIPowerState)powerState; @end -enum { - kBluetoothFeatureLESupportedController = (1 << 6L), -}; - -@protocol IOBluetoothDeviceInquiryDelegate -- (void)deviceInquiryStarted:(IOBluetoothDeviceInquiry*)sender; -- (void)deviceInquiryDeviceFound:(IOBluetoothDeviceInquiry*)sender - device:(IOBluetoothDevice*)device; -- (void)deviceInquiryComplete:(IOBluetoothDeviceInquiry*)sender - error:(IOReturn)error - aborted:(BOOL)aborted; -@end - @interface IOBluetoothL2CAPChannel (LionSDK) @property(readonly) BluetoothL2CAPMTU outgoingMTU; @end @@ -206,63 +375,75 @@ enum { - (IOReturn)performSDPQuery:(id)target uuids:(NSArray*)uuids; @end -BASE_EXPORT extern "C" NSString* const NSWindowWillEnterFullScreenNotification; -BASE_EXPORT extern "C" NSString* const NSWindowWillExitFullScreenNotification; -BASE_EXPORT extern "C" NSString* const NSWindowDidEnterFullScreenNotification; -BASE_EXPORT extern "C" NSString* const NSWindowDidExitFullScreenNotification; -BASE_EXPORT extern "C" NSString* const - NSWindowDidChangeBackingPropertiesNotification; +@interface CBPeripheral (LionSDK) +@property(readonly, nonatomic) CFUUIDRef UUID; +@property(retain, readonly) NSString* name; +@property(readonly) BOOL isConnected; +@end -@protocol NSWindowDelegateFullScreenAdditions -- (void)windowDidFailToEnterFullScreen:(NSWindow*)window; -- (void)windowDidFailToExitFullScreen:(NSWindow*)window; +@interface CBCentralManager (LionSDK) +@property(readonly) CBCentralManagerState state; +- (id)initWithDelegate:(id<CBCentralManagerDelegate>)delegate + queue:(dispatch_queue_t)queue; +- (void)scanForPeripheralsWithServices:(NSArray*)serviceUUIDs + options:(NSDictionary*)options; +- (void)stopScan; +@end + +@interface CBUUID (LionSDK) +@property(nonatomic, readonly) NSData* data; ++ (CBUUID*)UUIDWithString:(NSString*)theString; @end #endif // MAC_OS_X_VERSION_10_7 +// Once Chrome no longer supports OSX 10.7, everything within this preprocessor +// block can be removed. #if !defined(MAC_OS_X_VERSION_10_8) || \ - MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_8 - -enum { - NSEventPhaseMayBegin = 0x1 << 5 -}; + MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8 @interface NSColor (MountainLionSDK) - (CGColorRef)CGColor; @end -#endif // MAC_OS_X_VERSION_10_8 +@interface NSUUID (MountainLionSDK) +- (NSString*)UUIDString; +@end +@interface NSControl (MountainLionSDK) +@property BOOL allowsExpansionToolTips; +@end -#if !defined(MAC_OS_X_VERSION_10_9) || \ - MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_9 +#endif // MAC_OS_X_VERSION_10_8 -// NSProgress is public API in 10.9, but a version of it exists and is usable -// in 10.8. +// Once Chrome no longer supports OSX 10.8, everything within this preprocessor +// block can be removed. +#if !defined(MAC_OS_X_VERSION_10_9) || \ + MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_9 -@interface NSProgress : NSObject +@interface NSProgress (MavericksSDK) - (instancetype)initWithParent:(NSProgress*)parentProgressOrNil userInfo:(NSDictionary*)userInfoOrNil; -@property (copy) NSString* kind; +@property(copy) NSString* kind; @property int64_t totalUnitCount; @property int64_t completedUnitCount; -@property (getter=isCancellable) BOOL cancellable; -@property (getter=isPausable) BOOL pausable; -@property (readonly, getter=isCancelled) BOOL cancelled; -@property (readonly, getter=isPaused) BOOL paused; -@property (copy) void (^cancellationHandler)(void); -@property (copy) void (^pausingHandler)(void); +@property(getter=isCancellable) BOOL cancellable; +@property(getter=isPausable) BOOL pausable; +@property(readonly, getter=isCancelled) BOOL cancelled; +@property(readonly, getter=isPaused) BOOL paused; +@property(copy) void (^cancellationHandler)(void); +@property(copy) void (^pausingHandler)(void); - (void)cancel; - (void)pause; - (void)setUserInfoObject:(id)objectOrNil forKey:(NSString*)key; - (NSDictionary*)userInfo; -@property (readonly, getter=isIndeterminate) BOOL indeterminate; -@property (readonly) double fractionCompleted; +@property(readonly, getter=isIndeterminate) BOOL indeterminate; +@property(readonly) double fractionCompleted; - (void)publish; - (void)unpublish; @@ -273,73 +454,84 @@ enum { + (BOOL)screensHaveSeparateSpaces; @end -// NSAppearance is a new class in the 10.9 SDK. New classes cannot be -// forward-declared because they also require an @implementation, which would -// produce conflicting linkage. Instead, just declare the necessary pieces of -// the interface as a protocol, and treat objects of this type as id. -@protocol CrNSAppearance<NSObject> -+ (id<NSObject>)appearanceNamed:(NSString*)name; -@end - @interface NSView (MavericksSDK) - (void)setCanDrawSubviewsIntoLayer:(BOOL)flag; -- (id<CrNSAppearance>)effectiveAppearance; +- (NSAppearance*)effectiveAppearance; @end -enum { - NSWindowOcclusionStateVisible = 1UL << 1, -}; -typedef NSUInteger NSWindowOcclusionState; - @interface NSWindow (MavericksSDK) - (NSWindowOcclusionState)occlusionState; @end - -BASE_EXPORT extern "C" NSString* const - NSWindowDidChangeOcclusionStateNotification; - -enum { - NSWorkspaceLaunchWithErrorPresentation = 0x00000040 -}; - -#else // !MAC_OS_X_VERSION_10_9 - -typedef enum { - kCWSecurityModeOpen = 0, - kCWSecurityModeWEP, - kCWSecurityModeWPA_PSK, - kCWSecurityModeWPA2_PSK, - kCWSecurityModeWPA_Enterprise, - kCWSecurityModeWPA2_Enterprise, - kCWSecurityModeWPS, - kCWSecurityModeDynamicWEP -} CWSecurityMode; - -@interface CWNetwork (SnowLeopardSDK) -@property(readonly) NSNumber* rssi; -@property(readonly) NSNumber* securityMode; +@interface NSAppearance (MavericksSDK) ++ (id<NSObject>)appearanceNamed:(NSString*)name; @end -BASE_EXPORT extern "C" NSString* const kCWSSIDDidChangeNotification; +@interface CBPeripheral (MavericksSDK) +@property(readonly, nonatomic) NSUUID* identifier; +@end #endif // MAC_OS_X_VERSION_10_9 +// Once Chrome no longer supports OSX 10.9, everything within this preprocessor +// block can be removed. #if !defined(MAC_OS_X_VERSION_10_10) || \ - MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_10 + MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10 -@interface NSUserActivity : NSObject +@interface NSUserActivity (YosemiteSDK) -@property (readonly, copy) NSString* activityType; -@property (copy) NSDictionary* userInfo; -@property (copy) NSURL* webPageURL; +@property(readonly, copy) NSString* activityType; +@property(copy) NSDictionary* userInfo; +@property(copy) NSURL* webpageURL; -@end +- (instancetype)initWithActivityType:(NSString*)activityType; +- (void)becomeCurrent; +- (void)invalidate; -BASE_EXPORT extern "C" NSString* const NSUserActivityTypeBrowsingWeb; +@end -BASE_EXPORT extern "C" NSString* const NSAppearanceNameVibrantDark; +@interface CBUUID (YosemiteSDK) +- (NSString*)UUIDString; +@end #endif // MAC_OS_X_VERSION_10_10 +// ---------------------------------------------------------------------------- +// Chrome uses -[CWNetwork securityMode] and -[CWNetwork rssi] on OSX 10.6. The +// former method relies on the enum CWSecurityMode which was removed in the OSX +// 10.9 SDK. In order for Chrome to compile against an OSX 10.9+ SDK, Chrome +// must define this enum. Chrome must also declare these methods. +// +// These declarations and definitions will not be necessary once Chrome no +// longer runs on OSX 10.6. +// ---------------------------------------------------------------------------- +#if defined(MAC_OS_X_VERSION_10_9) && \ + MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_6 +typedef enum { + kCWSecurityModeOpen = 0, + kCWSecurityModeWEP, + kCWSecurityModeWPA_PSK, + kCWSecurityModeWPA2_PSK, + kCWSecurityModeWPA_Enterprise, + kCWSecurityModeWPA2_Enterprise, + kCWSecurityModeWPS, + kCWSecurityModeDynamicWEP +} CWSecurityMode; + +@interface CWNetwork (SnowLeopardSDK) +@property(readonly) NSNumber* rssi; +@property(readonly) NSNumber* securityMode; +@end +#endif + +// ---------------------------------------------------------------------------- +// The symbol for kCWSSIDDidChangeNotification is available in the +// CoreWLAN.framework for OSX versions 10.6 through 10.10. The symbol is not +// declared in the OSX 10.9+ SDK, so when compiling against an OSX 10.9+ SDK, +// declare the symbol. +// ---------------------------------------------------------------------------- +#if defined(MAC_OS_X_VERSION_10_9) && \ + MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9 +BASE_EXPORT extern "C" NSString* const kCWSSIDDidChangeNotification; +#endif #endif // BASE_MAC_SDK_FORWARD_DECLARATIONS_H_ diff --git a/chromium/base/mac/sdk_forward_declarations.mm b/chromium/base/mac/sdk_forward_declarations.mm index 7a5703228f1..2e4b2d98457 100644 --- a/chromium/base/mac/sdk_forward_declarations.mm +++ b/chromium/base/mac/sdk_forward_declarations.mm @@ -4,10 +4,8 @@ #include "base/mac/sdk_forward_declarations.h" -// Replicate specific 10.7 SDK declarations for building with prior SDKs. #if !defined(MAC_OS_X_VERSION_10_7) || \ - MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 - + MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7 NSString* const NSWindowWillEnterFullScreenNotification = @"NSWindowWillEnterFullScreenNotification"; @@ -23,24 +21,24 @@ NSString* const NSWindowDidExitFullScreenNotification = NSString* const NSWindowDidChangeBackingPropertiesNotification = @"NSWindowDidChangeBackingPropertiesNotification"; +NSString* const CBAdvertisementDataServiceDataKey = @"kCBAdvDataServiceData"; + +NSString* const NSPreferredScrollerStyleDidChangeNotification = + @"NSPreferredScrollerStyleDidChangeNotification"; #endif // MAC_OS_X_VERSION_10_7 -// Replicate specific 10.9 SDK declarations for building with prior SDKs. #if !defined(MAC_OS_X_VERSION_10_9) || \ - MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_9 - + MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_9 NSString* const NSWindowDidChangeOcclusionStateNotification = @"NSWindowDidChangeOcclusionStateNotification"; +NSString* const CBAdvertisementDataIsConnectable = @"kCBAdvDataIsConnectable"; #endif // MAC_OS_X_VERSION_10_9 -// Replicate specific 10.10 SDK declarations for building with prior SDKs. #if !defined(MAC_OS_X_VERSION_10_10) || \ - MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_10 - + MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10 NSString* const NSUserActivityTypeBrowsingWeb = @"NSUserActivityTypeBrowsingWeb"; NSString* const NSAppearanceNameVibrantDark = @"NSAppearanceNameVibrantDark"; - #endif // MAC_OS_X_VERSION_10_10 diff --git a/chromium/base/macros.h b/chromium/base/macros.h index b6240da2bee..d9043281cf5 100644 --- a/chromium/base/macros.h +++ b/chromium/base/macros.h @@ -13,8 +13,6 @@ #include <stddef.h> // For size_t. #include <string.h> // For memcpy. -#include "base/compiler_specific.h" // For ALLOW_UNUSED. - // Put this in the private: declarations for a class to be uncopyable. #define DISALLOW_COPY(TypeName) \ TypeName(const TypeName&) @@ -54,17 +52,7 @@ // This template function declaration is used in defining arraysize. // Note that the function doesn't need an implementation, as we only // use its type. -template <typename T, size_t N> -char (&ArraySizeHelper(T (&array)[N]))[N]; - -// That gcc wants both of these prototypes seems mysterious. VC, for -// its part, can't decide which to use (another mystery). Matching of -// template overloads: the final frontier. -#ifndef _MSC_VER -template <typename T, size_t N> -char (&ArraySizeHelper(const T (&array)[N]))[N]; -#endif - +template <typename T, size_t N> char (&ArraySizeHelper(T (&array)[N]))[N]; #define arraysize(array) (sizeof(ArraySizeHelper(array))) diff --git a/chromium/base/md5.cc b/chromium/base/md5.cc index 6227ee66042..72c774d3554 100644 --- a/chromium/base/md5.cc +++ b/chromium/base/md5.cc @@ -23,27 +23,28 @@ #include "base/md5.h" -#include "base/basictypes.h" +#include <stddef.h> namespace { struct Context { - uint32 buf[4]; - uint32 bits[2]; - unsigned char in[64]; + uint32_t buf[4]; + uint32_t bits[2]; + uint8_t in[64]; }; /* * Note: this code is harmless on little-endian machines. */ -void byteReverse(unsigned char *buf, unsigned longs) { - uint32 t; - do { - t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 | - ((unsigned)buf[1]<<8 | buf[0]); - *(uint32 *)buf = t; - buf += 4; - } while (--longs); +void byteReverse(uint8_t* buf, unsigned longs) { + do { + uint32_t temp = static_cast<uint32_t>( + static_cast<unsigned>(buf[3]) << 8 | + buf[2]) << 16 | + (static_cast<unsigned>(buf[1]) << 8 | buf[0]); + *reinterpret_cast<uint32_t*>(buf) = temp; + buf += 4; + } while (--longs); } /* The four core functions - F1 is optimized somewhat */ @@ -56,93 +57,93 @@ void byteReverse(unsigned char *buf, unsigned longs) { /* This is the central step in the MD5 algorithm. */ #define MD5STEP(f, w, x, y, z, data, s) \ - ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x ) + (w += f(x, y, z) + data, w = w << s | w >> (32 - s), w += x) /* * The core of the MD5 algorithm, this alters an existing MD5 hash to * reflect the addition of 16 longwords of new data. MD5Update blocks * the data and converts bytes into longwords for this routine. */ -void MD5Transform(uint32 buf[4], const uint32 in[16]) { - register uint32 a, b, c, d; - - a = buf[0]; - b = buf[1]; - c = buf[2]; - d = buf[3]; - - MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7); - MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12); - MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17); - MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22); - MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7); - MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12); - MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17); - MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22); - MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7); - MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12); - MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17); - MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22); - MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7); - MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12); - MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17); - MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22); - - MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5); - MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9); - MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14); - MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20); - MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5); - MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9); - MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14); - MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20); - MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5); - MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9); - MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14); - MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20); - MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5); - MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9); - MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14); - MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20); - - MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4); - MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11); - MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16); - MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23); - MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4); - MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11); - MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16); - MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23); - MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4); - MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11); - MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16); - MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23); - MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4); - MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11); - MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16); - MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23); - - MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6); - MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10); - MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15); - MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21); - MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6); - MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10); - MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15); - MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21); - MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6); - MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10); - MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15); - MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21); - MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6); - MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10); - MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15); - MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21); - - buf[0] += a; - buf[1] += b; - buf[2] += c; - buf[3] += d; +void MD5Transform(uint32_t buf[4], const uint32_t in[16]) { + uint32_t a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; } } // namespace @@ -154,13 +155,13 @@ namespace base { * initialization constants. */ void MD5Init(MD5Context* context) { - struct Context *ctx = (struct Context *)context; - ctx->buf[0] = 0x67452301; - ctx->buf[1] = 0xefcdab89; - ctx->buf[2] = 0x98badcfe; - ctx->buf[3] = 0x10325476; - ctx->bits[0] = 0; - ctx->bits[1] = 0; + struct Context* ctx = reinterpret_cast<struct Context*>(context); + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + ctx->bits[0] = 0; + ctx->bits[1] = 0; } /* @@ -168,51 +169,49 @@ void MD5Init(MD5Context* context) { * of bytes. */ void MD5Update(MD5Context* context, const StringPiece& data) { - const unsigned char* inbuf = (const unsigned char*)data.data(); - size_t len = data.size(); - struct Context *ctx = (struct Context *)context; - const unsigned char* buf = (const unsigned char*)inbuf; - uint32 t; - - /* Update bitcount */ - - t = ctx->bits[0]; - if ((ctx->bits[0] = t + ((uint32)len << 3)) < t) - ctx->bits[1]++; /* Carry from low to high */ - ctx->bits[1] += static_cast<uint32>(len >> 29); - - t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ - - /* Handle any leading odd-sized chunks */ - - if (t) { - unsigned char *p = (unsigned char *)ctx->in + t; - - t = 64-t; - if (len < t) { - memcpy(p, buf, len); - return; - } - memcpy(p, buf, t); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (uint32 *)ctx->in); - buf += t; - len -= t; - } - - /* Process data in 64-byte chunks */ - - while (len >= 64) { - memcpy(ctx->in, buf, 64); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (uint32 *)ctx->in); - buf += 64; - len -= 64; - } - - /* Handle any remaining bytes of data. */ - - memcpy(ctx->in, buf, len); + struct Context* ctx = reinterpret_cast<struct Context*>(context); + const uint8_t* buf = reinterpret_cast<const uint8_t*>(data.data()); + size_t len = data.size(); + + /* Update bitcount */ + + uint32_t t = ctx->bits[0]; + if ((ctx->bits[0] = t + (static_cast<uint32_t>(len) << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += static_cast<uint32_t>(len >> 29); + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) { + uint8_t* p = static_cast<uint8_t*>(ctx->in + t); + + t = 64 - t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, reinterpret_cast<uint32_t*>(ctx->in)); + buf += t; + len -= t; + } + + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, reinterpret_cast<uint32_t*>(ctx->in)); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in, buf, len); } /* @@ -220,48 +219,46 @@ void MD5Update(MD5Context* context, const StringPiece& data) { * 1 0* (64-bit count of bits processed, MSB-first) */ void MD5Final(MD5Digest* digest, MD5Context* context) { - struct Context *ctx = (struct Context *)context; - unsigned count; - unsigned char *p; - - /* Compute number of bytes mod 64 */ - count = (ctx->bits[0] >> 3) & 0x3F; - - /* Set the first char of padding to 0x80. This is safe since there is - always at least one byte free */ - p = ctx->in + count; - *p++ = 0x80; - - /* Bytes of padding needed to make 64 bytes */ - count = 64 - 1 - count; - - /* Pad out to 56 mod 64 */ - if (count < 8) { - /* Two lots of padding: Pad the first block to 64 bytes */ - memset(p, 0, count); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (uint32 *)ctx->in); - - /* Now fill the next block with 56 bytes */ - memset(ctx->in, 0, 56); - } else { - /* Pad block to 56 bytes */ - memset(p, 0, count-8); - } - byteReverse(ctx->in, 14); - - /* Append length in bits and transform */ - memcpy(&ctx->in[14 * sizeof(ctx->bits[0])], - &ctx->bits[0], - sizeof(ctx->bits[0])); - memcpy(&ctx->in[15 * sizeof(ctx->bits[1])], - &ctx->bits[1], - sizeof(ctx->bits[1])); - - MD5Transform(ctx->buf, (uint32 *)ctx->in); - byteReverse((unsigned char *)ctx->buf, 4); - memcpy(digest->a, ctx->buf, 16); - memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ + struct Context* ctx = reinterpret_cast<struct Context*>(context); + unsigned count; + uint8_t* p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, reinterpret_cast<uint32_t*>(ctx->in)); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); + + /* Append length in bits and transform */ + memcpy(&ctx->in[14 * sizeof(ctx->bits[0])], &ctx->bits[0], + sizeof(ctx->bits[0])); + memcpy(&ctx->in[15 * sizeof(ctx->bits[1])], &ctx->bits[1], + sizeof(ctx->bits[1])); + + MD5Transform(ctx->buf, reinterpret_cast<uint32_t*>(ctx->in)); + byteReverse(reinterpret_cast<uint8_t*>(ctx->buf), 4); + memcpy(digest->a, ctx->buf, 16); + memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ } void MD5IntermediateFinal(MD5Digest* digest, const MD5Context* context) { @@ -278,11 +275,10 @@ std::string MD5DigestToBase16(const MD5Digest& digest) { std::string ret; ret.resize(32); - int j = 0; - for (int i = 0; i < 16; i ++) { - int a = digest.a[i]; - ret[j++] = zEncode[(a>>4)&0xf]; - ret[j++] = zEncode[a & 0xf]; + for (int i = 0, j = 0; i < 16; i++, j += 2) { + uint8_t a = digest.a[i]; + ret[j] = zEncode[(a >> 4) & 0xf]; + ret[j + 1] = zEncode[a & 0xf]; } return ret; } @@ -290,8 +286,7 @@ std::string MD5DigestToBase16(const MD5Digest& digest) { void MD5Sum(const void* data, size_t length, MD5Digest* digest) { MD5Context ctx; MD5Init(&ctx); - MD5Update(&ctx, - StringPiece(reinterpret_cast<const char*>(data), length)); + MD5Update(&ctx, StringPiece(reinterpret_cast<const char*>(data), length)); MD5Final(digest, &ctx); } diff --git a/chromium/base/md5.h b/chromium/base/md5.h index 0a87fcf7c4a..0b4cbcef3a8 100644 --- a/chromium/base/md5.h +++ b/chromium/base/md5.h @@ -5,6 +5,8 @@ #ifndef BASE_MD5_H_ #define BASE_MD5_H_ +#include <stdint.h> + #include "base/base_export.h" #include "base/strings/string_piece.h" @@ -35,17 +37,13 @@ namespace base { // The output of an MD5 operation. struct MD5Digest { - unsigned char a[16]; + uint8_t a[16]; }; // Used for storing intermediate data during an MD5 computation. Callers // should not access the data. typedef char MD5Context[88]; -// Computes the MD5 sum of the given data buffer with the given length. -// The given 'digest' structure will be filled with the result data. -BASE_EXPORT void MD5Sum(const void* data, size_t length, MD5Digest* digest); - // Initializes the given MD5 context structure for subsequent calls to // MD5Update(). BASE_EXPORT void MD5Init(MD5Context* context); @@ -67,6 +65,10 @@ BASE_EXPORT void MD5IntermediateFinal(MD5Digest* digest, // Converts a digest into human-readable hexadecimal. BASE_EXPORT std::string MD5DigestToBase16(const MD5Digest& digest); +// Computes the MD5 sum of the given data buffer with the given length. +// The given 'digest' structure will be filled with the result data. +BASE_EXPORT void MD5Sum(const void* data, size_t length, MD5Digest* digest); + // Returns the MD5 (in hexadecimal) of a string. BASE_EXPORT std::string MD5String(const StringPiece& str); diff --git a/chromium/base/md5_unittest.cc b/chromium/base/md5_unittest.cc index 8d817e9a8e0..08c99f19049 100644 --- a/chromium/base/md5_unittest.cc +++ b/chromium/base/md5_unittest.cc @@ -246,7 +246,7 @@ TEST(MD5, IntermediateFinal) { EXPECT_TRUE(!memcmp(&header_digest, &check_header_digest, sizeof(header_digest))); EXPECT_TRUE(!memcmp(&digest, &check_full_digest, sizeof(digest))); - EXPECT_FALSE(!memcmp(&digest, &header_digest, sizeof(digest))); + EXPECT_TRUE(memcmp(&digest, &header_digest, sizeof(digest))); } } // namespace base diff --git a/chromium/base/memory/BUILD.gn b/chromium/base/memory/BUILD.gn new file mode 100644 index 00000000000..bd864caa245 --- /dev/null +++ b/chromium/base/memory/BUILD.gn @@ -0,0 +1,58 @@ +# Copyright (c) 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. + +source_set("memory") { + sources = [ + "aligned_memory.cc", + "aligned_memory.h", + "discardable_memory.cc", + "discardable_memory.h", + "discardable_memory_allocator.cc", + "discardable_memory_allocator.h", + "discardable_shared_memory.cc", + "discardable_shared_memory.h", + "linked_ptr.h", + "manual_constructor.h", + "memory_pressure_listener.cc", + "memory_pressure_listener.h", + "memory_pressure_monitor.cc", + "memory_pressure_monitor.h", + "raw_scoped_refptr_mismatch_checker.h", + "ref_counted.cc", + "ref_counted.h", + "ref_counted_delete_on_message_loop.h", + "ref_counted_memory.cc", + "ref_counted_memory.h", + "scoped_policy.h", + "scoped_ptr.h", + "scoped_vector.h", + "shared_memory.h", + "shared_memory_android.cc", + "shared_memory_nacl.cc", + "shared_memory_posix.cc", + "shared_memory_win.cc", + "singleton.cc", + "singleton.h", + "weak_ptr.cc", + "weak_ptr.h", + ] + + if (is_nacl) { + sources -= [ + "discardable_memory.cc", + "discardable_memory.h", + "discardable_memory_allocator.cc", + "discardable_memory_allocator.h", + "discardable_shared_memory.cc", + "discardable_shared_memory.h", + "shared_memory_posix.cc", + ] + } else { + sources -= [ "shared_memory_nacl.cc" ] + } + + configs += [ "//base:base_implementation" ] + + visibility = [ "//base/*" ] +} diff --git a/chromium/base/memory/discardable_memory.cc b/chromium/base/memory/discardable_memory.cc index 3d82f91d78f..d50f1853e19 100644 --- a/chromium/base/memory/discardable_memory.cc +++ b/chromium/base/memory/discardable_memory.cc @@ -4,83 +4,12 @@ #include "base/memory/discardable_memory.h" -#include "base/lazy_instance.h" -#include "base/logging.h" - namespace base { -namespace { - -const struct TypeNamePair { - DiscardableMemoryType type; - const char* name; -} kTypeNamePairs[] = { - { DISCARDABLE_MEMORY_TYPE_ASHMEM, "ashmem" }, - { DISCARDABLE_MEMORY_TYPE_MACH, "mach" }, - { DISCARDABLE_MEMORY_TYPE_EMULATED, "emulated" }, - { DISCARDABLE_MEMORY_TYPE_SHMEM, "shmem" } -}; - -DiscardableMemoryType g_preferred_type = DISCARDABLE_MEMORY_TYPE_NONE; - -struct DefaultPreferredType { - DefaultPreferredType() : value(DISCARDABLE_MEMORY_TYPE_NONE) { - std::vector<DiscardableMemoryType> supported_types; - DiscardableMemory::GetSupportedTypes(&supported_types); - DCHECK(!supported_types.empty()); - value = supported_types[0]; - } - DiscardableMemoryType value; -}; -LazyInstance<DefaultPreferredType>::Leaky g_default_preferred_type = - LAZY_INSTANCE_INITIALIZER; - -} // namespace - -// static -DiscardableMemoryType DiscardableMemory::GetNamedType( - const std::string& name) { - for (size_t i = 0; i < arraysize(kTypeNamePairs); ++i) { - if (name == kTypeNamePairs[i].name) - return kTypeNamePairs[i].type; - } - - return DISCARDABLE_MEMORY_TYPE_NONE; -} - -// static -const char* DiscardableMemory::GetTypeName(DiscardableMemoryType type) { - for (size_t i = 0; i < arraysize(kTypeNamePairs); ++i) { - if (type == kTypeNamePairs[i].type) - return kTypeNamePairs[i].name; - } - - return "unknown"; -} - -// static -void DiscardableMemory::SetPreferredType(DiscardableMemoryType type) { - // NONE is a reserved value and not a valid default type. - DCHECK_NE(DISCARDABLE_MEMORY_TYPE_NONE, type); - - // Make sure this function is only called once before the first call - // to GetPreferredType(). - DCHECK_EQ(DISCARDABLE_MEMORY_TYPE_NONE, g_preferred_type); - - g_preferred_type = type; -} - -// static -DiscardableMemoryType DiscardableMemory::GetPreferredType() { - if (g_preferred_type == DISCARDABLE_MEMORY_TYPE_NONE) - g_preferred_type = g_default_preferred_type.Get().value; - return g_preferred_type; +DiscardableMemory::DiscardableMemory() { } -// static -scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemory( - size_t size) { - return CreateLockedMemoryWithType(GetPreferredType(), size); +DiscardableMemory::~DiscardableMemory() { } } // namespace base diff --git a/chromium/base/memory/discardable_memory.h b/chromium/base/memory/discardable_memory.h index d8b7a58fbbb..fc189e74634 100644 --- a/chromium/base/memory/discardable_memory.h +++ b/chromium/base/memory/discardable_memory.h @@ -5,40 +5,24 @@ #ifndef BASE_MEMORY_DISCARDABLE_MEMORY_H_ #define BASE_MEMORY_DISCARDABLE_MEMORY_H_ -#include <string> -#include <vector> - #include "base/base_export.h" #include "base/basictypes.h" #include "base/compiler_specific.h" -#include "base/memory/scoped_ptr.h" namespace base { -enum DiscardableMemoryType { - DISCARDABLE_MEMORY_TYPE_NONE, - DISCARDABLE_MEMORY_TYPE_ASHMEM, - DISCARDABLE_MEMORY_TYPE_MACH, - DISCARDABLE_MEMORY_TYPE_EMULATED, - DISCARDABLE_MEMORY_TYPE_SHMEM -}; - -enum DiscardableMemoryLockStatus { - DISCARDABLE_MEMORY_LOCK_STATUS_FAILED, - DISCARDABLE_MEMORY_LOCK_STATUS_PURGED, - DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS -}; - -// Platform abstraction for discardable memory. DiscardableMemory is used to -// cache large objects without worrying about blowing out memory, both on mobile -// devices where there is no swap, and desktop devices where unused free memory -// should be used to help the user experience. This is preferable to releasing -// memory in response to an OOM signal because it is simpler, though it has less -// flexibility as to which objects get discarded. +// Discardable memory is used to cache large objects without worrying about +// blowing out memory, both on mobile devices where there is no swap, and +// desktop devices where unused free memory should be used to help the user +// experience. This is preferable to releasing memory in response to an OOM +// signal because it is simpler and provides system-wide management of +// purgable memory, though it has less flexibility as to which objects get +// discarded. // // Discardable memory has two states: locked and unlocked. While the memory is -// locked, it will not be discarded. Unlocking the memory allows the OS to -// reclaim it if needed. Locks do not nest. +// locked, it will not be discarded. Unlocking the memory allows the +// discardable memory system and the OS to reclaim it if needed. Locks do not +// nest. // // Notes: // - The paging behavior of memory while it is locked is not specified. While @@ -53,77 +37,28 @@ enum DiscardableMemoryLockStatus { // responsibility of users of discardable memory to ensure there are no // races. // -// References: -// - Linux: http://lwn.net/Articles/452035/ -// - Mac: http://trac.webkit.org/browser/trunk/Source/WebCore/platform/mac/PurgeableBufferMac.cpp -// the comment starting with "vm_object_purgable_control" at -// http://www.opensource.apple.com/source/xnu/xnu-792.13.8/osfmk/vm/vm_object.c -// -// Thread-safety: DiscardableMemory instances are not thread-safe. class BASE_EXPORT DiscardableMemory { public: - virtual ~DiscardableMemory() {} - - // Gets the discardable memory type with a given name. - static DiscardableMemoryType GetNamedType(const std::string& name); - - // Gets the name of a discardable memory type. - static const char* GetTypeName(DiscardableMemoryType type); - - // Gets system supported discardable memory types. Default preferred type - // at the front of vector. - static void GetSupportedTypes(std::vector<DiscardableMemoryType>* types); - - // Sets the preferred discardable memory type. This overrides the default - // preferred type. Can only be called once prior to GetPreferredType() - // or CreateLockedMemory(). Caller is responsible for correct ordering. - static void SetPreferredType(DiscardableMemoryType type); - - // Gets the preferred discardable memory type. - static DiscardableMemoryType GetPreferredType(); - - // Create a DiscardableMemory instance with specified |type| and |size|. - static scoped_ptr<DiscardableMemory> CreateLockedMemoryWithType( - DiscardableMemoryType type, size_t size); - - // Create a DiscardableMemory instance with preferred type and |size|. - static scoped_ptr<DiscardableMemory> CreateLockedMemory(size_t size); - - // Discardable memory implementations might use this to release memory - // or resources assigned to instances that have been purged. - static void ReleaseFreeMemory(); - - // Discardable memory implementations might allow an elevated usage level - // while in frequent use. Call this to have the usage reduced to the base - // level. Returns true if there's no need to call this again until - // memory instances have been used. This indicates that all discardable - // memory implementations have reduced usage to the base level or below. - // Note: calling this too often or while discardable memory is in frequent - // use can hurt performance, whereas calling it too infrequently can result - // in memory bloat. - static bool ReduceMemoryUsage(); + DiscardableMemory(); + virtual ~DiscardableMemory(); // Locks the memory so that it will not be purged by the system. Returns - // DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS on success. If the return value is - // DISCARDABLE_MEMORY_LOCK_STATUS_FAILED then this object should be - // discarded and a new one should be created. If the return value is - // DISCARDABLE_MEMORY_LOCK_STATUS_PURGED then the memory is present but any - // data that was in it is gone. - virtual DiscardableMemoryLockStatus Lock() WARN_UNUSED_RESULT = 0; + // true on success. If the return value is false then this object should be + // discarded and a new one should be created. + virtual bool Lock() WARN_UNUSED_RESULT = 0; // Unlocks the memory so that it can be purged by the system. Must be called // after every successful lock call. virtual void Unlock() = 0; // Returns the memory address held by this object. The object must be locked - // before calling this. Otherwise, this will cause a DCHECK error. - virtual void* Memory() const = 0; - - // Testing utility calls. + // before calling this. + virtual void* data() const = 0; - // Purge all discardable memory in the system. This call has global effects - // across all running processes, so it should only be used for testing! - static void PurgeForTesting(); + // Handy method to simplify calling data() with a reinterpret_cast. + template<typename T> T* data_as() const { + return reinterpret_cast<T*>(data()); + } }; } // namespace base diff --git a/chromium/base/memory/discardable_memory_allocator.cc b/chromium/base/memory/discardable_memory_allocator.cc new file mode 100644 index 00000000000..002a3ba5e47 --- /dev/null +++ b/chromium/base/memory/discardable_memory_allocator.cc @@ -0,0 +1,34 @@ +// 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/discardable_memory_allocator.h" + +#include "base/logging.h" + +namespace base { +namespace { + +DiscardableMemoryAllocator* g_allocator = nullptr; + +} // namespace + +// static +void DiscardableMemoryAllocator::SetInstance( + DiscardableMemoryAllocator* allocator) { + DCHECK(allocator); + + // Make sure this function is only called once before the first call + // to GetInstance(). + DCHECK(!g_allocator); + + g_allocator = allocator; +} + +// static +DiscardableMemoryAllocator* DiscardableMemoryAllocator::GetInstance() { + DCHECK(g_allocator); + return g_allocator; +} + +} // namespace base diff --git a/chromium/base/memory/discardable_memory_allocator.h b/chromium/base/memory/discardable_memory_allocator.h new file mode 100644 index 00000000000..400f87aeee0 --- /dev/null +++ b/chromium/base/memory/discardable_memory_allocator.h @@ -0,0 +1,32 @@ +// 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_DISCARDABLE_MEMORY_ALLOCATOR_H_ +#define BASE_MEMORY_DISCARDABLE_MEMORY_ALLOCATOR_H_ + +#include "base/base_export.h" +#include "base/memory/scoped_ptr.h" + +namespace base { +class DiscardableMemory; + +class BASE_EXPORT DiscardableMemoryAllocator { + public: + // Returns the allocator instance. + static DiscardableMemoryAllocator* GetInstance(); + + // Sets the allocator instance. Can only be called once, e.g. on startup. + // Ownership of |instance| remains with the caller. + static void SetInstance(DiscardableMemoryAllocator* allocator); + + virtual scoped_ptr<DiscardableMemory> AllocateLockedDiscardableMemory( + size_t size) = 0; + + protected: + virtual ~DiscardableMemoryAllocator() {} +}; + +} // namespace base + +#endif // BASE_MEMORY_DISCARDABLE_MEMORY_ALLOCATOR_H_ diff --git a/chromium/base/memory/discardable_memory_android.cc b/chromium/base/memory/discardable_memory_android.cc deleted file mode 100644 index de7112457de..00000000000 --- a/chromium/base/memory/discardable_memory_android.cc +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (c) 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. - -#include "base/memory/discardable_memory.h" - -#include "base/basictypes.h" -#include "base/compiler_specific.h" -#include "base/lazy_instance.h" -#include "base/logging.h" -#include "base/memory/discardable_memory_ashmem.h" -#include "base/memory/discardable_memory_ashmem_allocator.h" -#include "base/memory/discardable_memory_emulated.h" -#include "base/memory/discardable_memory_shmem.h" -#include "base/sys_info.h" - -namespace base { -namespace { - -const char kAshmemAllocatorName[] = "DiscardableMemoryAshmemAllocator"; - -// For Ashmem, have the DiscardableMemoryManager trigger userspace eviction -// when address space usage gets too high (e.g. 512 MBytes). -const size_t kAshmemMemoryLimit = 512 * 1024 * 1024; - -size_t GetOptimalAshmemRegionSizeForAllocator() { - // Note that this may do some I/O (without hitting the disk though) so it - // should not be called on the critical path. - return base::SysInfo::AmountOfPhysicalMemory() / 8; -} - -// Holds the shared state used for allocations. -struct SharedState { - SharedState() - : manager(kAshmemMemoryLimit, kAshmemMemoryLimit, TimeDelta::Max()), - allocator(kAshmemAllocatorName, - GetOptimalAshmemRegionSizeForAllocator()) {} - - internal::DiscardableMemoryManager manager; - internal::DiscardableMemoryAshmemAllocator allocator; -}; -LazyInstance<SharedState>::Leaky g_shared_state = LAZY_INSTANCE_INITIALIZER; - -} // namespace - -// static -void DiscardableMemory::ReleaseFreeMemory() { - internal::DiscardableMemoryShmem::ReleaseFreeMemory(); -} - -// static -bool DiscardableMemory::ReduceMemoryUsage() { - return internal::DiscardableMemoryEmulated::ReduceMemoryUsage(); -} - -// static -void DiscardableMemory::GetSupportedTypes( - std::vector<DiscardableMemoryType>* types) { - const DiscardableMemoryType supported_types[] = { - DISCARDABLE_MEMORY_TYPE_ASHMEM, - DISCARDABLE_MEMORY_TYPE_EMULATED, - DISCARDABLE_MEMORY_TYPE_SHMEM - }; - types->assign(supported_types, supported_types + arraysize(supported_types)); -} - -// static -scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemoryWithType( - DiscardableMemoryType type, size_t size) { - switch (type) { - case DISCARDABLE_MEMORY_TYPE_ASHMEM: { - SharedState* const shared_state = g_shared_state.Pointer(); - scoped_ptr<internal::DiscardableMemoryAshmem> memory( - new internal::DiscardableMemoryAshmem( - size, &shared_state->allocator, &shared_state->manager)); - if (!memory->Initialize()) - return nullptr; - - return memory.Pass(); - } - case DISCARDABLE_MEMORY_TYPE_EMULATED: { - scoped_ptr<internal::DiscardableMemoryEmulated> memory( - new internal::DiscardableMemoryEmulated(size)); - if (!memory->Initialize()) - return nullptr; - - return memory.Pass(); - } - case DISCARDABLE_MEMORY_TYPE_SHMEM: { - scoped_ptr<internal::DiscardableMemoryShmem> memory( - new internal::DiscardableMemoryShmem(size)); - if (!memory->Initialize()) - return nullptr; - - return memory.Pass(); - } - case DISCARDABLE_MEMORY_TYPE_NONE: - case DISCARDABLE_MEMORY_TYPE_MACH: - NOTREACHED(); - return nullptr; - } - - NOTREACHED(); - return nullptr; -} - -// static -void DiscardableMemory::PurgeForTesting() { - g_shared_state.Pointer()->manager.PurgeAll(); - internal::DiscardableMemoryEmulated::PurgeForTesting(); - internal::DiscardableMemoryShmem::PurgeForTesting(); -} - -} // namespace base diff --git a/chromium/base/memory/discardable_memory_ashmem.cc b/chromium/base/memory/discardable_memory_ashmem.cc deleted file mode 100644 index df0697c9ae9..00000000000 --- a/chromium/base/memory/discardable_memory_ashmem.cc +++ /dev/null @@ -1,79 +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. - -#include "base/memory/discardable_memory_ashmem.h" - -#include "base/memory/discardable_memory_ashmem_allocator.h" - -namespace base { -namespace internal { - -DiscardableMemoryAshmem::DiscardableMemoryAshmem( - size_t bytes, - DiscardableMemoryAshmemAllocator* allocator, - DiscardableMemoryManager* manager) - : bytes_(bytes), - allocator_(allocator), - manager_(manager), - is_locked_(false) { - manager_->Register(this, bytes_); -} - -DiscardableMemoryAshmem::~DiscardableMemoryAshmem() { - if (is_locked_) - Unlock(); - - manager_->Unregister(this); -} - -bool DiscardableMemoryAshmem::Initialize() { - return Lock() != DISCARDABLE_MEMORY_LOCK_STATUS_FAILED; -} - -DiscardableMemoryLockStatus DiscardableMemoryAshmem::Lock() { - DCHECK(!is_locked_); - - bool purged = false; - if (!manager_->AcquireLock(this, &purged)) - return DISCARDABLE_MEMORY_LOCK_STATUS_FAILED; - - is_locked_ = true; - return purged ? DISCARDABLE_MEMORY_LOCK_STATUS_PURGED - : DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS; -} - -void DiscardableMemoryAshmem::Unlock() { - DCHECK(is_locked_); - manager_->ReleaseLock(this); - is_locked_ = false; -} - -void* DiscardableMemoryAshmem::Memory() const { - DCHECK(is_locked_); - DCHECK(ashmem_chunk_); - return ashmem_chunk_->Memory(); -} - -bool DiscardableMemoryAshmem::AllocateAndAcquireLock() { - if (ashmem_chunk_) - return ashmem_chunk_->Lock(); - - ashmem_chunk_ = allocator_->Allocate(bytes_); - return false; -} - -void DiscardableMemoryAshmem::ReleaseLock() { - ashmem_chunk_->Unlock(); -} - -void DiscardableMemoryAshmem::Purge() { - ashmem_chunk_.reset(); -} - -bool DiscardableMemoryAshmem::IsMemoryResident() const { - return true; -} - -} // namespace internal -} // namespace base diff --git a/chromium/base/memory/discardable_memory_ashmem.h b/chromium/base/memory/discardable_memory_ashmem.h deleted file mode 100644 index c1cc077657a..00000000000 --- a/chromium/base/memory/discardable_memory_ashmem.h +++ /dev/null @@ -1,56 +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. - -#ifndef BASE_MEMORY_DISCARDABLE_MEMORY_ASHMEM_H_ -#define BASE_MEMORY_DISCARDABLE_MEMORY_ASHMEM_H_ - -#include "base/memory/discardable_memory.h" - -#include "base/macros.h" -#include "base/memory/discardable_memory_manager.h" - -namespace base { -namespace internal { - -class DiscardableAshmemChunk; -class DiscardableMemoryAshmemAllocator; -class DiscardableMemoryManager; - -class DiscardableMemoryAshmem - : public DiscardableMemory, - public internal::DiscardableMemoryManagerAllocation { - public: - explicit DiscardableMemoryAshmem(size_t bytes, - DiscardableMemoryAshmemAllocator* allocator, - DiscardableMemoryManager* manager); - - virtual ~DiscardableMemoryAshmem(); - - bool Initialize(); - - // Overridden from DiscardableMemory: - virtual DiscardableMemoryLockStatus Lock() override; - virtual void Unlock() override; - virtual void* Memory() const override; - - // Overridden from internal::DiscardableMemoryManagerAllocation: - virtual bool AllocateAndAcquireLock() override; - virtual void ReleaseLock() override; - virtual void Purge() override; - virtual bool IsMemoryResident() const override; - - private: - const size_t bytes_; - DiscardableMemoryAshmemAllocator* const allocator_; - DiscardableMemoryManager* const manager_; - bool is_locked_; - scoped_ptr<DiscardableAshmemChunk> ashmem_chunk_; - - DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryAshmem); -}; - -} // namespace internal -} // namespace base - -#endif // BASE_MEMORY_DISCARDABLE_MEMORY_ASHMEM_H_ diff --git a/chromium/base/memory/discardable_memory_ashmem_allocator.cc b/chromium/base/memory/discardable_memory_ashmem_allocator.cc deleted file mode 100644 index 3d4af925af3..00000000000 --- a/chromium/base/memory/discardable_memory_ashmem_allocator.cc +++ /dev/null @@ -1,528 +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. - -#include "base/memory/discardable_memory_ashmem_allocator.h" - -#include <sys/mman.h> -#include <unistd.h> - -#include <algorithm> -#include <cmath> -#include <limits> -#include <set> -#include <utility> - -#include "base/basictypes.h" -#include "base/containers/hash_tables.h" -#include "base/files/file_util.h" -#include "base/files/scoped_file.h" -#include "base/logging.h" -#include "base/memory/scoped_vector.h" -#include "third_party/ashmem/ashmem.h" - -// The allocator consists of three parts (classes): -// - DiscardableMemoryAshmemAllocator: entry point of all allocations (through -// its Allocate() method) that are dispatched to the AshmemRegion instances -// (which it owns). -// - AshmemRegion: manages allocations and destructions inside a single large -// (e.g. 32 MBytes) ashmem region. -// - DiscardableAshmemChunk: class mimicking the DiscardableMemory interface -// whose instances are returned to the client. - -namespace base { -namespace { - -// Only tolerate fragmentation in used chunks *caused by the client* (as opposed -// to the allocator when a free chunk is reused). The client can cause such -// fragmentation by e.g. requesting 4097 bytes. This size would be rounded up to -// 8192 by the allocator which would cause 4095 bytes of fragmentation (which is -// currently the maximum allowed). If the client requests 4096 bytes and a free -// chunk of 8192 bytes is available then the free chunk gets splitted into two -// pieces to minimize fragmentation (since 8192 - 4096 = 4096 which is greater -// than 4095). -// TODO(pliard): tune this if splitting chunks too often leads to performance -// issues. -const size_t kMaxChunkFragmentationBytes = 4096 - 1; - -const size_t kMinAshmemRegionSize = 32 * 1024 * 1024; - -// Returns 0 if the provided size is too high to be aligned. -size_t AlignToNextPage(size_t size) { - const size_t kPageSize = 4096; - DCHECK_EQ(static_cast<int>(kPageSize), getpagesize()); - if (size > std::numeric_limits<size_t>::max() - kPageSize + 1) - return 0; - const size_t mask = ~(kPageSize - 1); - return (size + kPageSize - 1) & mask; -} - -bool CreateAshmemRegion(const char* name, - size_t size, - int* out_fd, - uintptr_t* out_address) { - base::ScopedFD fd(ashmem_create_region(name, size)); - if (!fd.is_valid()) { - DLOG(ERROR) << "ashmem_create_region() failed"; - return false; - } - - const int err = ashmem_set_prot_region(fd.get(), PROT_READ | PROT_WRITE); - if (err < 0) { - DLOG(ERROR) << "Error " << err << " when setting protection of ashmem"; - return false; - } - - // There is a problem using MAP_PRIVATE here. As we are constantly calling - // Lock() and Unlock(), data could get lost if they are not written to the - // underlying file when Unlock() gets called. - void* const address = mmap( - NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0); - if (address == MAP_FAILED) { - DPLOG(ERROR) << "Failed to map memory."; - return false; - } - - *out_fd = fd.release(); - *out_address = reinterpret_cast<uintptr_t>(address); - return true; -} - -bool CloseAshmemRegion(int fd, size_t size, void* address) { - if (munmap(address, size) == -1) { - DPLOG(ERROR) << "Failed to unmap memory."; - close(fd); - return false; - } - return close(fd) == 0; -} - -bool LockAshmemRegion(int fd, size_t off, size_t size) { - return ashmem_pin_region(fd, off, size) != ASHMEM_WAS_PURGED; -} - -bool UnlockAshmemRegion(int fd, size_t off, size_t size) { - const int failed = ashmem_unpin_region(fd, off, size); - if (failed) - DLOG(ERROR) << "Failed to unpin memory."; - return !failed; -} - -} // namespace - -namespace internal { - -class AshmemRegion { - public: - // Note that |allocator| must outlive |this|. - static scoped_ptr<AshmemRegion> Create( - size_t size, - const std::string& name, - DiscardableMemoryAshmemAllocator* allocator) { - DCHECK_EQ(size, AlignToNextPage(size)); - int fd; - uintptr_t base; - if (!CreateAshmemRegion(name.c_str(), size, &fd, &base)) - return scoped_ptr<AshmemRegion>(); - return make_scoped_ptr(new AshmemRegion(fd, size, base, allocator)); - } - - ~AshmemRegion() { - const bool result = CloseAshmemRegion( - fd_, size_, reinterpret_cast<void*>(base_)); - DCHECK(result); - DCHECK(!highest_allocated_chunk_); - } - - // Returns a new instance of DiscardableAshmemChunk whose size is greater or - // equal than |actual_size| (which is expected to be greater or equal than - // |client_requested_size|). - // Allocation works as follows: - // 1) Reuse a previously freed chunk and return it if it succeeded. See - // ReuseFreeChunk_Locked() below for more information. - // 2) If no free chunk could be reused and the region is not big enough for - // the requested size then NULL is returned. - // 3) If there is enough room in the ashmem region then a new chunk is - // returned. This new chunk starts at |offset_| which is the end of the - // previously highest chunk in the region. - scoped_ptr<DiscardableAshmemChunk> Allocate_Locked( - size_t client_requested_size, - size_t actual_size) { - DCHECK_LE(client_requested_size, actual_size); - allocator_->lock_.AssertAcquired(); - - // Check that the |highest_allocated_chunk_| field doesn't contain a stale - // pointer. It should point to either a free chunk or a used chunk. - DCHECK(!highest_allocated_chunk_ || - address_to_free_chunk_map_.find(highest_allocated_chunk_) != - address_to_free_chunk_map_.end() || - used_to_previous_chunk_map_.find(highest_allocated_chunk_) != - used_to_previous_chunk_map_.end()); - - scoped_ptr<DiscardableAshmemChunk> memory = ReuseFreeChunk_Locked( - client_requested_size, actual_size); - if (memory) - return memory.Pass(); - - if (size_ - offset_ < actual_size) { - // This region does not have enough space left to hold the requested size. - return scoped_ptr<DiscardableAshmemChunk>(); - } - - uintptr_t const address = base_ + offset_; - memory.reset( - new DiscardableAshmemChunk(this, fd_, reinterpret_cast<void*>(address), - offset_, actual_size)); - - used_to_previous_chunk_map_.insert( - std::make_pair(address, highest_allocated_chunk_)); - highest_allocated_chunk_ = reinterpret_cast<uintptr_t>(address); - offset_ += actual_size; - DCHECK_LE(offset_, size_); - return memory.Pass(); - } - - void OnChunkDeletion(uintptr_t chunk, size_t size) { - AutoLock auto_lock(allocator_->lock_); - MergeAndAddFreeChunk_Locked(chunk, size); - // Note that |this| might be deleted beyond this point. - } - - private: - struct FreeChunk { - FreeChunk() : previous_chunk(0), start(0), size(0) {} - - explicit FreeChunk(size_t size) - : previous_chunk(0), - start(0), - size(size) { - } - - FreeChunk(uintptr_t previous_chunk, uintptr_t start, size_t size) - : previous_chunk(previous_chunk), - start(start), - size(size) { - DCHECK_LT(previous_chunk, start); - } - - uintptr_t const previous_chunk; - uintptr_t const start; - const size_t size; - - bool is_null() const { return !start; } - - bool operator<(const FreeChunk& other) const { - return size < other.size; - } - }; - - // Note that |allocator| must outlive |this|. - AshmemRegion(int fd, - size_t size, - uintptr_t base, - DiscardableMemoryAshmemAllocator* allocator) - : fd_(fd), - size_(size), - base_(base), - allocator_(allocator), - highest_allocated_chunk_(0), - offset_(0) { - DCHECK_GE(fd_, 0); - DCHECK_GE(size, kMinAshmemRegionSize); - DCHECK(base); - DCHECK(allocator); - } - - // Tries to reuse a previously freed chunk by doing a closest size match. - scoped_ptr<DiscardableAshmemChunk> ReuseFreeChunk_Locked( - size_t client_requested_size, - size_t actual_size) { - allocator_->lock_.AssertAcquired(); - const FreeChunk reused_chunk = RemoveFreeChunkFromIterator_Locked( - free_chunks_.lower_bound(FreeChunk(actual_size))); - if (reused_chunk.is_null()) - return scoped_ptr<DiscardableAshmemChunk>(); - - used_to_previous_chunk_map_.insert( - std::make_pair(reused_chunk.start, reused_chunk.previous_chunk)); - size_t reused_chunk_size = reused_chunk.size; - // |client_requested_size| is used below rather than |actual_size| to - // reflect the amount of bytes that would not be usable by the client (i.e. - // wasted). Using |actual_size| instead would not allow us to detect - // fragmentation caused by the client if he did misaligned allocations. - DCHECK_GE(reused_chunk.size, client_requested_size); - const size_t fragmentation_bytes = - reused_chunk.size - client_requested_size; - - if (fragmentation_bytes > kMaxChunkFragmentationBytes) { - // Split the free chunk being recycled so that its unused tail doesn't get - // reused (i.e. locked) which would prevent it from being evicted under - // memory pressure. - reused_chunk_size = actual_size; - uintptr_t const new_chunk_start = reused_chunk.start + actual_size; - if (reused_chunk.start == highest_allocated_chunk_) { - // We also need to update the pointer to the highest allocated chunk in - // case we are splitting the highest chunk. - highest_allocated_chunk_ = new_chunk_start; - } - DCHECK_GT(reused_chunk.size, actual_size); - const size_t new_chunk_size = reused_chunk.size - actual_size; - // Note that merging is not needed here since there can't be contiguous - // free chunks at this point. - AddFreeChunk_Locked( - FreeChunk(reused_chunk.start, new_chunk_start, new_chunk_size)); - } - - const size_t offset = reused_chunk.start - base_; - LockAshmemRegion(fd_, offset, reused_chunk_size); - scoped_ptr<DiscardableAshmemChunk> memory( - new DiscardableAshmemChunk(this, fd_, - reinterpret_cast<void*>(reused_chunk.start), - offset, reused_chunk_size)); - return memory.Pass(); - } - - // Makes the chunk identified with the provided arguments free and possibly - // merges this chunk with the previous and next contiguous ones. - // If the provided chunk is the only one used (and going to be freed) in the - // region then the internal ashmem region is closed so that the underlying - // physical pages are immediately released. - // Note that free chunks are unlocked therefore they can be reclaimed by the - // kernel if needed (under memory pressure) but they are not immediately - // released unfortunately since madvise(MADV_REMOVE) and - // fallocate(FALLOC_FL_PUNCH_HOLE) don't seem to work on ashmem. This might - // change in versions of kernel >=3.5 though. The fact that free chunks are - // not immediately released is the reason why we are trying to minimize - // fragmentation in order not to cause "artificial" memory pressure. - void MergeAndAddFreeChunk_Locked(uintptr_t chunk, size_t size) { - allocator_->lock_.AssertAcquired(); - size_t new_free_chunk_size = size; - // Merge with the previous chunk. - uintptr_t first_free_chunk = chunk; - DCHECK(!used_to_previous_chunk_map_.empty()); - const hash_map<uintptr_t, uintptr_t>::iterator previous_chunk_it = - used_to_previous_chunk_map_.find(chunk); - DCHECK(previous_chunk_it != used_to_previous_chunk_map_.end()); - uintptr_t previous_chunk = previous_chunk_it->second; - used_to_previous_chunk_map_.erase(previous_chunk_it); - - if (previous_chunk) { - const FreeChunk free_chunk = RemoveFreeChunk_Locked(previous_chunk); - if (!free_chunk.is_null()) { - new_free_chunk_size += free_chunk.size; - first_free_chunk = previous_chunk; - if (chunk == highest_allocated_chunk_) - highest_allocated_chunk_ = previous_chunk; - - // There should not be more contiguous previous free chunks. - previous_chunk = free_chunk.previous_chunk; - DCHECK(!address_to_free_chunk_map_.count(previous_chunk)); - } - } - - // Merge with the next chunk if free and present. - uintptr_t next_chunk = chunk + size; - const FreeChunk next_free_chunk = RemoveFreeChunk_Locked(next_chunk); - if (!next_free_chunk.is_null()) { - new_free_chunk_size += next_free_chunk.size; - if (next_free_chunk.start == highest_allocated_chunk_) - highest_allocated_chunk_ = first_free_chunk; - - // Same as above. - DCHECK( - !address_to_free_chunk_map_.count(next_chunk + next_free_chunk.size)); - } - - const bool whole_ashmem_region_is_free = - used_to_previous_chunk_map_.empty(); - if (!whole_ashmem_region_is_free) { - AddFreeChunk_Locked( - FreeChunk(previous_chunk, first_free_chunk, new_free_chunk_size)); - return; - } - - // The whole ashmem region is free thus it can be deleted. - DCHECK_EQ(base_, first_free_chunk); - DCHECK_EQ(base_, highest_allocated_chunk_); - DCHECK(free_chunks_.empty()); - DCHECK(address_to_free_chunk_map_.empty()); - DCHECK(used_to_previous_chunk_map_.empty()); - highest_allocated_chunk_ = 0; - allocator_->DeleteAshmemRegion_Locked(this); // Deletes |this|. - } - - void AddFreeChunk_Locked(const FreeChunk& free_chunk) { - allocator_->lock_.AssertAcquired(); - const std::multiset<FreeChunk>::iterator it = free_chunks_.insert( - free_chunk); - address_to_free_chunk_map_.insert(std::make_pair(free_chunk.start, it)); - // Update the next used contiguous chunk, if any, since its previous chunk - // may have changed due to free chunks merging/splitting. - uintptr_t const next_used_contiguous_chunk = - free_chunk.start + free_chunk.size; - hash_map<uintptr_t, uintptr_t>::iterator previous_it = - used_to_previous_chunk_map_.find(next_used_contiguous_chunk); - if (previous_it != used_to_previous_chunk_map_.end()) - previous_it->second = free_chunk.start; - } - - // Finds and removes the free chunk, if any, whose start address is - // |chunk_start|. Returns a copy of the unlinked free chunk or a free chunk - // whose content is null if it was not found. - FreeChunk RemoveFreeChunk_Locked(uintptr_t chunk_start) { - allocator_->lock_.AssertAcquired(); - const hash_map< - uintptr_t, std::multiset<FreeChunk>::iterator>::iterator it = - address_to_free_chunk_map_.find(chunk_start); - if (it == address_to_free_chunk_map_.end()) - return FreeChunk(); - return RemoveFreeChunkFromIterator_Locked(it->second); - } - - // Same as above but takes an iterator in. - FreeChunk RemoveFreeChunkFromIterator_Locked( - std::multiset<FreeChunk>::iterator free_chunk_it) { - allocator_->lock_.AssertAcquired(); - if (free_chunk_it == free_chunks_.end()) - return FreeChunk(); - DCHECK(free_chunk_it != free_chunks_.end()); - const FreeChunk free_chunk(*free_chunk_it); - address_to_free_chunk_map_.erase(free_chunk_it->start); - free_chunks_.erase(free_chunk_it); - return free_chunk; - } - - const int fd_; - const size_t size_; - uintptr_t const base_; - DiscardableMemoryAshmemAllocator* const allocator_; - // Points to the chunk with the highest address in the region. This pointer - // needs to be carefully updated when chunks are merged/split. - uintptr_t highest_allocated_chunk_; - // Points to the end of |highest_allocated_chunk_|. - size_t offset_; - // Allows free chunks recycling (lookup, insertion and removal) in O(log N). - // Note that FreeChunk values are indexed by their size and also note that - // multiple free chunks can have the same size (which is why multiset<> is - // used instead of e.g. set<>). - std::multiset<FreeChunk> free_chunks_; - // Used while merging free contiguous chunks to erase free chunks (from their - // start address) in constant time. Note that multiset<>::{insert,erase}() - // don't invalidate iterators (except the one for the element being removed - // obviously). - hash_map< - uintptr_t, std::multiset<FreeChunk>::iterator> address_to_free_chunk_map_; - // Maps the address of *used* chunks to the address of their previous - // contiguous chunk. - hash_map<uintptr_t, uintptr_t> used_to_previous_chunk_map_; - - DISALLOW_COPY_AND_ASSIGN(AshmemRegion); -}; - -DiscardableAshmemChunk::~DiscardableAshmemChunk() { - if (locked_) - UnlockAshmemRegion(fd_, offset_, size_); - ashmem_region_->OnChunkDeletion(reinterpret_cast<uintptr_t>(address_), size_); -} - -bool DiscardableAshmemChunk::Lock() { - DCHECK(!locked_); - locked_ = true; - return LockAshmemRegion(fd_, offset_, size_); -} - -void DiscardableAshmemChunk::Unlock() { - DCHECK(locked_); - locked_ = false; - UnlockAshmemRegion(fd_, offset_, size_); -} - -void* DiscardableAshmemChunk::Memory() const { - return address_; -} - -// Note that |ashmem_region| must outlive |this|. -DiscardableAshmemChunk::DiscardableAshmemChunk(AshmemRegion* ashmem_region, - int fd, - void* address, - size_t offset, - size_t size) - : ashmem_region_(ashmem_region), - fd_(fd), - address_(address), - offset_(offset), - size_(size), - locked_(true) { -} - -DiscardableMemoryAshmemAllocator::DiscardableMemoryAshmemAllocator( - const std::string& name, - size_t ashmem_region_size) - : name_(name), - ashmem_region_size_( - std::max(kMinAshmemRegionSize, AlignToNextPage(ashmem_region_size))), - last_ashmem_region_size_(0) { - DCHECK_GE(ashmem_region_size_, kMinAshmemRegionSize); -} - -DiscardableMemoryAshmemAllocator::~DiscardableMemoryAshmemAllocator() { - DCHECK(ashmem_regions_.empty()); -} - -scoped_ptr<DiscardableAshmemChunk> DiscardableMemoryAshmemAllocator::Allocate( - size_t size) { - const size_t aligned_size = AlignToNextPage(size); - if (!aligned_size) - return scoped_ptr<DiscardableAshmemChunk>(); - // TODO(pliard): make this function less naive by e.g. moving the free chunks - // multiset to the allocator itself in order to decrease even more - // fragmentation/speedup allocation. Note that there should not be more than a - // couple (=5) of AshmemRegion instances in practice though. - AutoLock auto_lock(lock_); - DCHECK_LE(ashmem_regions_.size(), 5U); - for (ScopedVector<AshmemRegion>::iterator it = ashmem_regions_.begin(); - it != ashmem_regions_.end(); ++it) { - scoped_ptr<DiscardableAshmemChunk> memory( - (*it)->Allocate_Locked(size, aligned_size)); - if (memory) - return memory.Pass(); - } - // The creation of the (large) ashmem region might fail if the address space - // is too fragmented. In case creation fails the allocator retries by - // repetitively dividing the size by 2. - const size_t min_region_size = std::max(kMinAshmemRegionSize, aligned_size); - for (size_t region_size = std::max(ashmem_region_size_, aligned_size); - region_size >= min_region_size; - region_size = AlignToNextPage(region_size / 2)) { - scoped_ptr<AshmemRegion> new_region( - AshmemRegion::Create(region_size, name_.c_str(), this)); - if (!new_region) - continue; - last_ashmem_region_size_ = region_size; - ashmem_regions_.push_back(new_region.release()); - return ashmem_regions_.back()->Allocate_Locked(size, aligned_size); - } - // TODO(pliard): consider adding an histogram to see how often this happens. - return scoped_ptr<DiscardableAshmemChunk>(); -} - -size_t DiscardableMemoryAshmemAllocator::last_ashmem_region_size() const { - AutoLock auto_lock(lock_); - return last_ashmem_region_size_; -} - -void DiscardableMemoryAshmemAllocator::DeleteAshmemRegion_Locked( - AshmemRegion* region) { - lock_.AssertAcquired(); - // Note that there should not be more than a couple of ashmem region instances - // in |ashmem_regions_|. - DCHECK_LE(ashmem_regions_.size(), 5U); - const ScopedVector<AshmemRegion>::iterator it = std::find( - ashmem_regions_.begin(), ashmem_regions_.end(), region); - DCHECK(ashmem_regions_.end() != it); - std::swap(*it, ashmem_regions_.back()); - ashmem_regions_.pop_back(); -} - -} // namespace internal -} // namespace base diff --git a/chromium/base/memory/discardable_memory_ashmem_allocator.h b/chromium/base/memory/discardable_memory_ashmem_allocator.h deleted file mode 100644 index 996dde92496..00000000000 --- a/chromium/base/memory/discardable_memory_ashmem_allocator.h +++ /dev/null @@ -1,93 +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. - -#ifndef BASE_MEMORY_DISCARDABLE_MEMORY_ASHMEM_ALLOCATOR_H_ -#define BASE_MEMORY_DISCARDABLE_MEMORY_ASHMEM_ALLOCATOR_H_ - -#include <string> - -#include "base/base_export.h" -#include "base/basictypes.h" -#include "base/memory/scoped_ptr.h" -#include "base/memory/scoped_vector.h" -#include "base/synchronization/lock.h" - -namespace base { -namespace internal { - -class AshmemRegion; - -// Internal class, whose instances are returned to the client of the allocator -// (e.g. DiscardableMemoryAshmem), that mimicks the DiscardableMemory interface. -class BASE_EXPORT_PRIVATE DiscardableAshmemChunk { - public: - ~DiscardableAshmemChunk(); - - // Returns whether the memory is still resident. - bool Lock(); - - void Unlock(); - - void* Memory() const; - - private: - friend class AshmemRegion; - - DiscardableAshmemChunk(AshmemRegion* ashmem_region, - int fd, - void* address, - size_t offset, - size_t size); - - AshmemRegion* const ashmem_region_; - const int fd_; - void* const address_; - const size_t offset_; - const size_t size_; - bool locked_; - - DISALLOW_COPY_AND_ASSIGN(DiscardableAshmemChunk); -}; - -// Ashmem regions are backed by a file (descriptor) therefore they are a limited -// resource. This allocator minimizes the problem by allocating large ashmem -// regions internally and returning smaller chunks to the client. -// Allocated chunks are systematically aligned on a page boundary therefore this -// allocator should not be used for small allocations. -class BASE_EXPORT_PRIVATE DiscardableMemoryAshmemAllocator { - public: - // Note that |name| is only used for debugging/measurement purposes. - // |ashmem_region_size| is the size that will be used to create the underlying - // ashmem regions and is expected to be greater or equal than 32 MBytes. - DiscardableMemoryAshmemAllocator(const std::string& name, - size_t ashmem_region_size); - - ~DiscardableMemoryAshmemAllocator(); - - // Note that the allocator must outlive the returned DiscardableAshmemChunk - // instance. - scoped_ptr<DiscardableAshmemChunk> Allocate(size_t size); - - // Returns the size of the last ashmem region which was created. This is used - // for testing only. - size_t last_ashmem_region_size() const; - - private: - friend class AshmemRegion; - - void DeleteAshmemRegion_Locked(AshmemRegion* region); - - const std::string name_; - const size_t ashmem_region_size_; - mutable Lock lock_; - size_t last_ashmem_region_size_; - ScopedVector<AshmemRegion> ashmem_regions_; - - DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryAshmemAllocator); -}; - -} // namespace internal -} // namespace base - -#endif // BASE_MEMORY_DISCARDABLE_MEMORY_ASHMEM_ALLOCATOR_H_ diff --git a/chromium/base/memory/discardable_memory_ashmem_allocator_unittest.cc b/chromium/base/memory/discardable_memory_ashmem_allocator_unittest.cc deleted file mode 100644 index e9f63ba3439..00000000000 --- a/chromium/base/memory/discardable_memory_ashmem_allocator_unittest.cc +++ /dev/null @@ -1,319 +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. - -#include "base/memory/discardable_memory_ashmem_allocator.h" - -#include <sys/types.h> -#include <unistd.h> - -#include "base/memory/discardable_memory.h" -#include "base/memory/scoped_ptr.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_split.h" -#include "base/strings/stringprintf.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace internal { - -const char kAllocatorName[] = "allocator-for-testing"; - -const size_t kAshmemRegionSizeForTesting = 32 * 1024 * 1024; -const size_t kPageSize = 4096; - -const size_t kMaxAllowedAllocationSize = - std::numeric_limits<size_t>::max() - kPageSize + 1; - -class DiscardableMemoryAshmemAllocatorTest : public testing::Test { - protected: - DiscardableMemoryAshmemAllocatorTest() - : allocator_(kAllocatorName, kAshmemRegionSizeForTesting) { - } - - DiscardableMemoryAshmemAllocator allocator_; -}; - -void WriteToDiscardableAshmemChunk(DiscardableAshmemChunk* memory, - size_t size) { - // Write to the first and the last pages only to avoid paging in up to 64 - // MBytes. - static_cast<char*>(memory->Memory())[0] = 'a'; - static_cast<char*>(memory->Memory())[size - 1] = 'a'; -} - -TEST_F(DiscardableMemoryAshmemAllocatorTest, Basic) { - const size_t size = 128; - scoped_ptr<DiscardableAshmemChunk> memory(allocator_.Allocate(size)); - ASSERT_TRUE(memory); - WriteToDiscardableAshmemChunk(memory.get(), size); -} - -TEST_F(DiscardableMemoryAshmemAllocatorTest, ZeroAllocationIsNotSupported) { - scoped_ptr<DiscardableAshmemChunk> memory(allocator_.Allocate(0)); - ASSERT_FALSE(memory); -} - -TEST_F(DiscardableMemoryAshmemAllocatorTest, TooLargeAllocationFails) { - scoped_ptr<DiscardableAshmemChunk> memory( - allocator_.Allocate(kMaxAllowedAllocationSize + 1)); - // Page-alignment would have caused an overflow resulting in a small - // allocation if the input size wasn't checked correctly. - ASSERT_FALSE(memory); -} - -TEST_F(DiscardableMemoryAshmemAllocatorTest, - AshmemRegionsAreNotSmallerThanRequestedSize) { - // The creation of the underlying ashmem region is expected to fail since - // there should not be enough room in the address space. When ashmem creation - // fails, the allocator repetitively retries by dividing the size by 2. This - // size should not be smaller than the size the user requested so the - // allocation here should just fail (and not succeed with the minimum ashmem - // region size). - scoped_ptr<DiscardableAshmemChunk> memory( - allocator_.Allocate(kMaxAllowedAllocationSize)); - ASSERT_FALSE(memory); -} - -TEST_F(DiscardableMemoryAshmemAllocatorTest, - AshmemRegionsAreAlwaysPageAligned) { - // Use a separate allocator here so that we can override the ashmem region - // size. - DiscardableMemoryAshmemAllocator allocator( - kAllocatorName, kMaxAllowedAllocationSize); - scoped_ptr<DiscardableAshmemChunk> memory(allocator.Allocate(kPageSize)); - ASSERT_TRUE(memory); - EXPECT_GT(kMaxAllowedAllocationSize, allocator.last_ashmem_region_size()); - ASSERT_TRUE(allocator.last_ashmem_region_size() % kPageSize == 0); -} - -TEST_F(DiscardableMemoryAshmemAllocatorTest, LargeAllocation) { - const size_t size = 64 * 1024 * 1024; - scoped_ptr<DiscardableAshmemChunk> memory(allocator_.Allocate(size)); - ASSERT_TRUE(memory); - WriteToDiscardableAshmemChunk(memory.get(), size); -} - -TEST_F(DiscardableMemoryAshmemAllocatorTest, ChunksArePageAligned) { - scoped_ptr<DiscardableAshmemChunk> memory(allocator_.Allocate(kPageSize)); - ASSERT_TRUE(memory); - EXPECT_EQ(0U, reinterpret_cast<uint64_t>(memory->Memory()) % kPageSize); - WriteToDiscardableAshmemChunk(memory.get(), kPageSize); -} - -TEST_F(DiscardableMemoryAshmemAllocatorTest, AllocateFreeAllocate) { - scoped_ptr<DiscardableAshmemChunk> memory(allocator_.Allocate(kPageSize)); - // Extra allocation that prevents the region from being deleted when |memory| - // gets deleted. - scoped_ptr<DiscardableAshmemChunk> memory_lock( - allocator_.Allocate(kPageSize)); - ASSERT_TRUE(memory); - void* const address = memory->Memory(); - memory->Unlock(); // Tests that the reused chunk is being locked correctly. - memory.reset(); - memory = allocator_.Allocate(kPageSize); - ASSERT_TRUE(memory); - // The previously freed chunk should be reused. - EXPECT_EQ(address, memory->Memory()); - WriteToDiscardableAshmemChunk(memory.get(), kPageSize); -} - -TEST_F(DiscardableMemoryAshmemAllocatorTest, - FreeingWholeAshmemRegionClosesAshmem) { - scoped_ptr<DiscardableAshmemChunk> memory(allocator_.Allocate(kPageSize)); - ASSERT_TRUE(memory); - const int kMagic = 0xdeadbeef; - *static_cast<int*>(memory->Memory()) = kMagic; - memory.reset(); - // The previous ashmem region should have been closed thus it should not be - // reused. - memory = allocator_.Allocate(kPageSize); - ASSERT_TRUE(memory); - EXPECT_NE(kMagic, *static_cast<const int*>(memory->Memory())); -} - -TEST_F(DiscardableMemoryAshmemAllocatorTest, AllocateUsesBestFitAlgorithm) { - scoped_ptr<DiscardableAshmemChunk> memory1( - allocator_.Allocate(3 * kPageSize)); - ASSERT_TRUE(memory1); - scoped_ptr<DiscardableAshmemChunk> memory2( - allocator_.Allocate(2 * kPageSize)); - ASSERT_TRUE(memory2); - scoped_ptr<DiscardableAshmemChunk> memory3( - allocator_.Allocate(1 * kPageSize)); - ASSERT_TRUE(memory3); - void* const address_3 = memory3->Memory(); - memory1.reset(); - // Don't free |memory2| to avoid merging the 3 blocks together. - memory3.reset(); - memory1 = allocator_.Allocate(1 * kPageSize); - ASSERT_TRUE(memory1); - // The chunk whose size is closest to the requested size should be reused. - EXPECT_EQ(address_3, memory1->Memory()); - WriteToDiscardableAshmemChunk(memory1.get(), kPageSize); -} - -TEST_F(DiscardableMemoryAshmemAllocatorTest, MergeFreeChunks) { - scoped_ptr<DiscardableAshmemChunk> memory1(allocator_.Allocate(kPageSize)); - ASSERT_TRUE(memory1); - scoped_ptr<DiscardableAshmemChunk> memory2(allocator_.Allocate(kPageSize)); - ASSERT_TRUE(memory2); - scoped_ptr<DiscardableAshmemChunk> memory3(allocator_.Allocate(kPageSize)); - ASSERT_TRUE(memory3); - scoped_ptr<DiscardableAshmemChunk> memory4(allocator_.Allocate(kPageSize)); - ASSERT_TRUE(memory4); - void* const memory1_address = memory1->Memory(); - memory1.reset(); - memory3.reset(); - // Freeing |memory2| (located between memory1 and memory3) should merge the - // three free blocks together. - memory2.reset(); - memory1 = allocator_.Allocate(3 * kPageSize); - EXPECT_EQ(memory1_address, memory1->Memory()); -} - -TEST_F(DiscardableMemoryAshmemAllocatorTest, MergeFreeChunksAdvanced) { - scoped_ptr<DiscardableAshmemChunk> memory1( - allocator_.Allocate(4 * kPageSize)); - ASSERT_TRUE(memory1); - scoped_ptr<DiscardableAshmemChunk> memory2( - allocator_.Allocate(4 * kPageSize)); - ASSERT_TRUE(memory2); - void* const memory1_address = memory1->Memory(); - memory1.reset(); - memory1 = allocator_.Allocate(2 * kPageSize); - memory2.reset(); - // At this point, the region should be in this state: - // 8 KBytes (used), 24 KBytes (free). - memory2 = allocator_.Allocate(6 * kPageSize); - EXPECT_EQ( - static_cast<const char*>(memory2->Memory()), - static_cast<const char*>(memory1_address) + 2 * kPageSize); -} - -TEST_F(DiscardableMemoryAshmemAllocatorTest, MergeFreeChunksAdvanced2) { - scoped_ptr<DiscardableAshmemChunk> memory1( - allocator_.Allocate(4 * kPageSize)); - ASSERT_TRUE(memory1); - scoped_ptr<DiscardableAshmemChunk> memory2( - allocator_.Allocate(4 * kPageSize)); - ASSERT_TRUE(memory2); - void* const memory1_address = memory1->Memory(); - memory1.reset(); - memory1 = allocator_.Allocate(2 * kPageSize); - scoped_ptr<DiscardableAshmemChunk> memory3( - allocator_.Allocate(2 * kPageSize)); - // At this point, the region should be in this state: - // 8 KBytes (used), 8 KBytes (used), 16 KBytes (used). - memory3.reset(); - memory2.reset(); - // At this point, the region should be in this state: - // 8 KBytes (used), 24 KBytes (free). - memory2 = allocator_.Allocate(6 * kPageSize); - EXPECT_EQ( - static_cast<const char*>(memory2->Memory()), - static_cast<const char*>(memory1_address) + 2 * kPageSize); -} - -TEST_F(DiscardableMemoryAshmemAllocatorTest, - MergeFreeChunksAndDeleteAshmemRegion) { - scoped_ptr<DiscardableAshmemChunk> memory1( - allocator_.Allocate(4 * kPageSize)); - ASSERT_TRUE(memory1); - scoped_ptr<DiscardableAshmemChunk> memory2( - allocator_.Allocate(4 * kPageSize)); - ASSERT_TRUE(memory2); - memory1.reset(); - memory1 = allocator_.Allocate(2 * kPageSize); - scoped_ptr<DiscardableAshmemChunk> memory3( - allocator_.Allocate(2 * kPageSize)); - // At this point, the region should be in this state: - // 8 KBytes (used), 8 KBytes (used), 16 KBytes (used). - memory1.reset(); - memory3.reset(); - // At this point, the region should be in this state: - // 8 KBytes (free), 8 KBytes (used), 8 KBytes (free). - const int kMagic = 0xdeadbeef; - *static_cast<int*>(memory2->Memory()) = kMagic; - memory2.reset(); - // The whole region should have been deleted. - memory2 = allocator_.Allocate(2 * kPageSize); - EXPECT_NE(kMagic, *static_cast<int*>(memory2->Memory())); -} - -TEST_F(DiscardableMemoryAshmemAllocatorTest, - TooLargeFreeChunksDontCauseTooMuchFragmentationWhenRecycled) { - // Keep |memory_1| below allocated so that the ashmem region doesn't get - // closed when |memory_2| is deleted. - scoped_ptr<DiscardableAshmemChunk> memory_1(allocator_.Allocate(64 * 1024)); - ASSERT_TRUE(memory_1); - scoped_ptr<DiscardableAshmemChunk> memory_2(allocator_.Allocate(32 * 1024)); - ASSERT_TRUE(memory_2); - void* const address = memory_2->Memory(); - memory_2.reset(); - const size_t size = 16 * 1024; - memory_2 = allocator_.Allocate(size); - ASSERT_TRUE(memory_2); - EXPECT_EQ(address, memory_2->Memory()); - WriteToDiscardableAshmemChunk(memory_2.get(), size); - scoped_ptr<DiscardableAshmemChunk> memory_3(allocator_.Allocate(size)); - // The unused tail (16 KBytes large) of the previously freed chunk should be - // reused. - EXPECT_EQ(static_cast<char*>(address) + size, memory_3->Memory()); - WriteToDiscardableAshmemChunk(memory_3.get(), size); -} - -TEST_F(DiscardableMemoryAshmemAllocatorTest, UseMultipleAshmemRegions) { - // Leave one page untouched at the end of the ashmem region. - const size_t size = kAshmemRegionSizeForTesting - kPageSize; - scoped_ptr<DiscardableAshmemChunk> memory1(allocator_.Allocate(size)); - ASSERT_TRUE(memory1); - WriteToDiscardableAshmemChunk(memory1.get(), size); - - scoped_ptr<DiscardableAshmemChunk> memory2( - allocator_.Allocate(kAshmemRegionSizeForTesting)); - ASSERT_TRUE(memory2); - WriteToDiscardableAshmemChunk(memory2.get(), kAshmemRegionSizeForTesting); - // The last page of the first ashmem region should be used for this - // allocation. - scoped_ptr<DiscardableAshmemChunk> memory3(allocator_.Allocate(kPageSize)); - ASSERT_TRUE(memory3); - WriteToDiscardableAshmemChunk(memory3.get(), kPageSize); - EXPECT_EQ(memory3->Memory(), static_cast<char*>(memory1->Memory()) + size); -} - -TEST_F(DiscardableMemoryAshmemAllocatorTest, - HighestAllocatedChunkPointerIsUpdatedWhenHighestChunkGetsSplit) { - // Prevents the ashmem region from getting closed when |memory2| gets freed. - scoped_ptr<DiscardableAshmemChunk> memory1(allocator_.Allocate(kPageSize)); - ASSERT_TRUE(memory1); - - scoped_ptr<DiscardableAshmemChunk> memory2( - allocator_.Allocate(4 * kPageSize)); - ASSERT_TRUE(memory2); - - memory2.reset(); - memory2 = allocator_.Allocate(kPageSize); - // There should now be a free chunk of size 3 * |kPageSize| starting at offset - // 2 * |kPageSize| and the pointer to the highest allocated chunk should have - // also been updated to |base_| + 2 * |kPageSize|. This pointer is used to - // maintain the container mapping a chunk address to its previous chunk and - // this map is in turn used while merging previous contiguous chunks. - - // Allocate more than 3 * |kPageSize| so that the free chunk of size 3 * - // |kPageSize| is not reused and |highest_allocated_chunk_| gets used instead. - scoped_ptr<DiscardableAshmemChunk> memory3( - allocator_.Allocate(4 * kPageSize)); - ASSERT_TRUE(memory3); - - // Deleting |memory3| (whose size is 4 * |kPageSize|) should result in a merge - // with its previous chunk which is the free chunk of size |3 * kPageSize|. - memory3.reset(); - memory3 = allocator_.Allocate((3 + 4) * kPageSize); - EXPECT_EQ(memory3->Memory(), - static_cast<const char*>(memory2->Memory()) + kPageSize); -} - -} // namespace internal -} // namespace base diff --git a/chromium/base/memory/discardable_memory_emulated.cc b/chromium/base/memory/discardable_memory_emulated.cc deleted file mode 100644 index 43034006319..00000000000 --- a/chromium/base/memory/discardable_memory_emulated.cc +++ /dev/null @@ -1,121 +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. - -#include "base/memory/discardable_memory_emulated.h" - -#include "base/lazy_instance.h" -#include "base/memory/discardable_memory_manager.h" - -namespace base { -namespace { - -// This is admittedly pretty magical. -const size_t kEmulatedMemoryLimit = 512 * 1024 * 1024; -const size_t kEmulatedSoftMemoryLimit = 32 * 1024 * 1024; -const size_t kEmulatedHardMemoryLimitExpirationTimeMs = 1000; - -// internal::DiscardableMemoryManager has an explicit constructor that takes -// a number of memory limit parameters. The LeakyLazyInstanceTraits doesn't -// handle the case. Thus, we need our own class here. -struct DiscardableMemoryManagerLazyInstanceTraits { - // Leaky as discardable memory clients can use this after the exit handler - // has been called. - static const bool kRegisterOnExit = false; -#ifndef NDEBUG - static const bool kAllowedToAccessOnNonjoinableThread = true; -#endif - - static internal::DiscardableMemoryManager* New(void* instance) { - return new (instance) internal::DiscardableMemoryManager( - kEmulatedMemoryLimit, - kEmulatedSoftMemoryLimit, - TimeDelta::FromMilliseconds(kEmulatedHardMemoryLimitExpirationTimeMs)); - } - static void Delete(internal::DiscardableMemoryManager* instance) { - instance->~DiscardableMemoryManager(); - } -}; - -LazyInstance<internal::DiscardableMemoryManager, - DiscardableMemoryManagerLazyInstanceTraits> - g_manager = LAZY_INSTANCE_INITIALIZER; - -} // namespace - -namespace internal { - -DiscardableMemoryEmulated::DiscardableMemoryEmulated(size_t bytes) - : bytes_(bytes), - is_locked_(false) { - g_manager.Pointer()->Register(this, bytes); -} - -DiscardableMemoryEmulated::~DiscardableMemoryEmulated() { - if (is_locked_) - Unlock(); - g_manager.Pointer()->Unregister(this); -} - -// static -bool DiscardableMemoryEmulated::ReduceMemoryUsage() { - return g_manager.Pointer()->ReduceMemoryUsage(); -} - -// static -void DiscardableMemoryEmulated::ReduceMemoryUsageUntilWithinLimit( - size_t bytes) { - g_manager.Pointer()->ReduceMemoryUsageUntilWithinLimit(bytes); -} - -// static -void DiscardableMemoryEmulated::PurgeForTesting() { - g_manager.Pointer()->PurgeAll(); -} - -bool DiscardableMemoryEmulated::Initialize() { - return Lock() != DISCARDABLE_MEMORY_LOCK_STATUS_FAILED; -} - -DiscardableMemoryLockStatus DiscardableMemoryEmulated::Lock() { - DCHECK(!is_locked_); - - bool purged = false; - if (!g_manager.Pointer()->AcquireLock(this, &purged)) - return DISCARDABLE_MEMORY_LOCK_STATUS_FAILED; - - is_locked_ = true; - return purged ? DISCARDABLE_MEMORY_LOCK_STATUS_PURGED - : DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS; -} - -void DiscardableMemoryEmulated::Unlock() { - DCHECK(is_locked_); - g_manager.Pointer()->ReleaseLock(this); - is_locked_ = false; -} - -void* DiscardableMemoryEmulated::Memory() const { - DCHECK(is_locked_); - DCHECK(memory_); - return memory_.get(); -} - -bool DiscardableMemoryEmulated::AllocateAndAcquireLock() { - if (memory_) - return true; - - memory_.reset(new uint8[bytes_]); - return false; -} - -void DiscardableMemoryEmulated::Purge() { - memory_.reset(); -} - -bool DiscardableMemoryEmulated::IsMemoryResident() const { - return true; -} - -} // namespace internal -} // namespace base diff --git a/chromium/base/memory/discardable_memory_emulated.h b/chromium/base/memory/discardable_memory_emulated.h deleted file mode 100644 index 150c1ab5669..00000000000 --- a/chromium/base/memory/discardable_memory_emulated.h +++ /dev/null @@ -1,55 +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. - -#ifndef BASE_MEMORY_DISCARDABLE_MEMORY_EMULATED_H_ -#define BASE_MEMORY_DISCARDABLE_MEMORY_EMULATED_H_ - -#include "base/memory/discardable_memory.h" - -#include "base/memory/discardable_memory_manager.h" - -namespace base { -namespace internal { - -class DiscardableMemoryEmulated - : public DiscardableMemory, - public internal::DiscardableMemoryManagerAllocation { - public: - explicit DiscardableMemoryEmulated(size_t bytes); - ~DiscardableMemoryEmulated() override; - - static bool ReduceMemoryUsage(); - - // TODO(reveman): Remove this as it is breaking the discardable memory design - // principle that implementations should not rely on information this is - // unavailable in kernel space. crbug.com/400423 - BASE_EXPORT static void ReduceMemoryUsageUntilWithinLimit(size_t bytes); - - static void PurgeForTesting(); - - bool Initialize(); - - // Overridden from DiscardableMemory: - DiscardableMemoryLockStatus Lock() override; - void Unlock() override; - void* Memory() const override; - - // Overridden from internal::DiscardableMemoryManagerAllocation: - bool AllocateAndAcquireLock() override; - void ReleaseLock() override {} - void Purge() override; - bool IsMemoryResident() const override; - - private: - const size_t bytes_; - scoped_ptr<uint8[]> memory_; - bool is_locked_; - - DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryEmulated); -}; - -} // namespace internal -} // namespace base - -#endif // BASE_MEMORY_DISCARDABLE_MEMORY_EMULATED_H_ diff --git a/chromium/base/memory/discardable_memory_linux.cc b/chromium/base/memory/discardable_memory_linux.cc deleted file mode 100644 index 9b4e9401ce4..00000000000 --- a/chromium/base/memory/discardable_memory_linux.cc +++ /dev/null @@ -1,70 +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. - -#include "base/memory/discardable_memory.h" - -#include "base/logging.h" -#include "base/memory/discardable_memory_emulated.h" -#include "base/memory/discardable_memory_shmem.h" - -namespace base { - -// static -void DiscardableMemory::ReleaseFreeMemory() { - internal::DiscardableMemoryShmem::ReleaseFreeMemory(); -} - -// static -bool DiscardableMemory::ReduceMemoryUsage() { - return internal::DiscardableMemoryEmulated::ReduceMemoryUsage(); -} - -// static -void DiscardableMemory::GetSupportedTypes( - std::vector<DiscardableMemoryType>* types) { - const DiscardableMemoryType supported_types[] = { - DISCARDABLE_MEMORY_TYPE_EMULATED, - DISCARDABLE_MEMORY_TYPE_SHMEM - }; - types->assign(supported_types, supported_types + arraysize(supported_types)); -} - -// static -scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemoryWithType( - DiscardableMemoryType type, size_t size) { - switch (type) { - case DISCARDABLE_MEMORY_TYPE_EMULATED: { - scoped_ptr<internal::DiscardableMemoryEmulated> memory( - new internal::DiscardableMemoryEmulated(size)); - if (!memory->Initialize()) - return nullptr; - - return memory.Pass(); - } - case DISCARDABLE_MEMORY_TYPE_SHMEM: { - scoped_ptr<internal::DiscardableMemoryShmem> memory( - new internal::DiscardableMemoryShmem(size)); - if (!memory->Initialize()) - return nullptr; - - return memory.Pass(); - } - case DISCARDABLE_MEMORY_TYPE_NONE: - case DISCARDABLE_MEMORY_TYPE_ASHMEM: - case DISCARDABLE_MEMORY_TYPE_MACH: - NOTREACHED(); - return nullptr; - } - - NOTREACHED(); - return nullptr; -} - -// static -void DiscardableMemory::PurgeForTesting() { - internal::DiscardableMemoryEmulated::PurgeForTesting(); - internal::DiscardableMemoryShmem::PurgeForTesting(); -} - -} // namespace base diff --git a/chromium/base/memory/discardable_memory_mac.cc b/chromium/base/memory/discardable_memory_mac.cc deleted file mode 100644 index 18cf80ac4aa..00000000000 --- a/chromium/base/memory/discardable_memory_mac.cc +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) 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. - -#include "base/memory/discardable_memory.h" - -#include "base/logging.h" -#include "base/memory/discardable_memory_emulated.h" -#include "base/memory/discardable_memory_mach.h" -#include "base/memory/discardable_memory_manager.h" -#include "base/memory/discardable_memory_shmem.h" -#include "base/memory/scoped_ptr.h" - -namespace base { - -// static -void DiscardableMemory::ReleaseFreeMemory() { - internal::DiscardableMemoryShmem::ReleaseFreeMemory(); -} - -// static -bool DiscardableMemory::ReduceMemoryUsage() { - return internal::DiscardableMemoryEmulated::ReduceMemoryUsage(); -} - -// static -void DiscardableMemory::GetSupportedTypes( - std::vector<DiscardableMemoryType>* types) { - const DiscardableMemoryType supported_types[] = { - DISCARDABLE_MEMORY_TYPE_MACH, - DISCARDABLE_MEMORY_TYPE_EMULATED, - DISCARDABLE_MEMORY_TYPE_SHMEM - }; - types->assign(supported_types, supported_types + arraysize(supported_types)); -} - -// static -scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemoryWithType( - DiscardableMemoryType type, size_t size) { - switch (type) { - case DISCARDABLE_MEMORY_TYPE_MACH: { - scoped_ptr<internal::DiscardableMemoryMach> memory( - new internal::DiscardableMemoryMach(size)); - if (!memory->Initialize()) - return nullptr; - - return memory.Pass(); - } - case DISCARDABLE_MEMORY_TYPE_EMULATED: { - scoped_ptr<internal::DiscardableMemoryEmulated> memory( - new internal::DiscardableMemoryEmulated(size)); - if (!memory->Initialize()) - return nullptr; - - return memory.Pass(); - } - case DISCARDABLE_MEMORY_TYPE_SHMEM: { - scoped_ptr<internal::DiscardableMemoryShmem> memory( - new internal::DiscardableMemoryShmem(size)); - if (!memory->Initialize()) - return nullptr; - - return memory.Pass(); - } - case DISCARDABLE_MEMORY_TYPE_NONE: - case DISCARDABLE_MEMORY_TYPE_ASHMEM: - NOTREACHED(); - return nullptr; - } - - NOTREACHED(); - return nullptr; -} - -// static -void DiscardableMemory::PurgeForTesting() { - internal::DiscardableMemoryMach::PurgeForTesting(); - internal::DiscardableMemoryEmulated::PurgeForTesting(); - internal::DiscardableMemoryShmem::PurgeForTesting(); -} - -} // namespace base diff --git a/chromium/base/memory/discardable_memory_mach.cc b/chromium/base/memory/discardable_memory_mach.cc deleted file mode 100644 index 5fc43f2a7d8..00000000000 --- a/chromium/base/memory/discardable_memory_mach.cc +++ /dev/null @@ -1,162 +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. - -#include "base/memory/discardable_memory_mach.h" - -#include <mach/mach.h> - -#include "base/basictypes.h" -#include "base/compiler_specific.h" -#include "base/lazy_instance.h" -#include "base/logging.h" -#include "base/mac/mach_logging.h" - -namespace base { -namespace { - -// For Mach, have the DiscardableMemoryManager trigger userspace eviction when -// address space usage gets too high (e.g. 512 MBytes). -const size_t kMachMemoryLimit = 512 * 1024 * 1024; - -// internal::DiscardableMemoryManager has an explicit constructor that takes -// a number of memory limit parameters. The LeakyLazyInstanceTraits doesn't -// handle the case. Thus, we need our own class here. -struct DiscardableMemoryManagerLazyInstanceTraits { - // Leaky as discardable memory clients can use this after the exit handler - // has been called. - static const bool kRegisterOnExit = false; -#ifndef NDEBUG - static const bool kAllowedToAccessOnNonjoinableThread = true; -#endif - - static internal::DiscardableMemoryManager* New(void* instance) { - return new (instance) internal::DiscardableMemoryManager( - kMachMemoryLimit, kMachMemoryLimit, TimeDelta::Max()); - } - static void Delete(internal::DiscardableMemoryManager* instance) { - instance->~DiscardableMemoryManager(); - } -}; - -LazyInstance<internal::DiscardableMemoryManager, - DiscardableMemoryManagerLazyInstanceTraits> - g_manager = LAZY_INSTANCE_INITIALIZER; - -// The VM subsystem allows tagging of memory and 240-255 is reserved for -// application use (see mach/vm_statistics.h). Pick 252 (after chromium's atomic -// weight of ~52). -const int kDiscardableMemoryTag = VM_MAKE_TAG(252); - -} // namespace - -namespace internal { - -DiscardableMemoryMach::DiscardableMemoryMach(size_t bytes) - : memory_(0, 0), bytes_(mach_vm_round_page(bytes)), is_locked_(false) { - g_manager.Pointer()->Register(this, bytes); -} - -DiscardableMemoryMach::~DiscardableMemoryMach() { - if (is_locked_) - Unlock(); - g_manager.Pointer()->Unregister(this); -} - -// static -void DiscardableMemoryMach::PurgeForTesting() { - int state = 0; - vm_purgable_control(mach_task_self(), 0, VM_PURGABLE_PURGE_ALL, &state); -} - -bool DiscardableMemoryMach::Initialize() { - return Lock() != DISCARDABLE_MEMORY_LOCK_STATUS_FAILED; -} - -DiscardableMemoryLockStatus DiscardableMemoryMach::Lock() { - DCHECK(!is_locked_); - - bool purged = false; - if (!g_manager.Pointer()->AcquireLock(this, &purged)) - return DISCARDABLE_MEMORY_LOCK_STATUS_FAILED; - - is_locked_ = true; - return purged ? DISCARDABLE_MEMORY_LOCK_STATUS_PURGED - : DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS; -} - -void DiscardableMemoryMach::Unlock() { - DCHECK(is_locked_); - g_manager.Pointer()->ReleaseLock(this); - is_locked_ = false; -} - -void* DiscardableMemoryMach::Memory() const { - DCHECK(is_locked_); - return reinterpret_cast<void*>(memory_.address()); -} - -bool DiscardableMemoryMach::AllocateAndAcquireLock() { - kern_return_t ret; - bool persistent; - if (!memory_.size()) { - vm_address_t address = 0; - ret = vm_allocate( - mach_task_self(), - &address, - bytes_, - VM_FLAGS_ANYWHERE | VM_FLAGS_PURGABLE | kDiscardableMemoryTag); - MACH_CHECK(ret == KERN_SUCCESS, ret) << "vm_allocate"; - memory_.reset(address, bytes_); - - // When making a fresh allocation, it's impossible for |persistent| to - // be true. - persistent = false; - } else { - // |persistent| will be reset to false below if appropriate, but when - // reusing an existing allocation, it's possible for it to be true. - persistent = true; - -#if !defined(NDEBUG) - ret = vm_protect(mach_task_self(), - memory_.address(), - memory_.size(), - FALSE, - VM_PROT_DEFAULT); - MACH_DCHECK(ret == KERN_SUCCESS, ret) << "vm_protect"; -#endif - } - - int state = VM_PURGABLE_NONVOLATILE; - ret = vm_purgable_control( - mach_task_self(), memory_.address(), VM_PURGABLE_SET_STATE, &state); - MACH_CHECK(ret == KERN_SUCCESS, ret) << "vm_purgable_control"; - if (state & VM_PURGABLE_EMPTY) - persistent = false; - - return persistent; -} - -void DiscardableMemoryMach::ReleaseLock() { - int state = VM_PURGABLE_VOLATILE | VM_VOLATILE_GROUP_DEFAULT; - kern_return_t ret = vm_purgable_control( - mach_task_self(), memory_.address(), VM_PURGABLE_SET_STATE, &state); - MACH_CHECK(ret == KERN_SUCCESS, ret) << "vm_purgable_control"; - -#if !defined(NDEBUG) - ret = vm_protect( - mach_task_self(), memory_.address(), memory_.size(), FALSE, VM_PROT_NONE); - MACH_DCHECK(ret == KERN_SUCCESS, ret) << "vm_protect"; -#endif -} - -void DiscardableMemoryMach::Purge() { - memory_.reset(); -} - -bool DiscardableMemoryMach::IsMemoryResident() const { - return true; -} - -} // namespace internal -} // namespace base diff --git a/chromium/base/memory/discardable_memory_mach.h b/chromium/base/memory/discardable_memory_mach.h deleted file mode 100644 index af2191f338c..00000000000 --- a/chromium/base/memory/discardable_memory_mach.h +++ /dev/null @@ -1,49 +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. - -#ifndef BASE_MEMORY_DISCARDABLE_MEMORY_MACH_H_ -#define BASE_MEMORY_DISCARDABLE_MEMORY_MACH_H_ - -#include "base/memory/discardable_memory.h" - -#include "base/mac/scoped_mach_vm.h" -#include "base/memory/discardable_memory_manager.h" - -namespace base { -namespace internal { - -class DiscardableMemoryMach - : public DiscardableMemory, - public internal::DiscardableMemoryManagerAllocation { - public: - explicit DiscardableMemoryMach(size_t bytes); - ~DiscardableMemoryMach() override; - - static void PurgeForTesting(); - - bool Initialize(); - - // Overridden from DiscardableMemory: - DiscardableMemoryLockStatus Lock() override; - void Unlock() override; - void* Memory() const override; - - // Overridden from internal::DiscardableMemoryManagerAllocation: - bool AllocateAndAcquireLock() override; - void ReleaseLock() override; - void Purge() override; - bool IsMemoryResident() const override; - - private: - mac::ScopedMachVM memory_; - const size_t bytes_; - bool is_locked_; - - DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryMach); -}; - -} // namespace internal -} // namespace base - -#endif // BASE_MEMORY_DISCARDABLE_MEMORY_MACH_H_ diff --git a/chromium/base/memory/discardable_memory_manager.cc b/chromium/base/memory/discardable_memory_manager.cc deleted file mode 100644 index faf583b485f..00000000000 --- a/chromium/base/memory/discardable_memory_manager.cc +++ /dev/null @@ -1,246 +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. - -#include "base/memory/discardable_memory_manager.h" - -#include "base/bind.h" -#include "base/containers/adapters.h" -#include "base/containers/hash_tables.h" -#include "base/containers/mru_cache.h" -#include "base/debug/crash_logging.h" -#include "base/debug/trace_event.h" -#include "base/strings/string_number_conversions.h" -#include "base/synchronization/lock.h" - -namespace base { -namespace internal { - -DiscardableMemoryManager::DiscardableMemoryManager( - size_t memory_limit, - size_t soft_memory_limit, - TimeDelta hard_memory_limit_expiration_time) - : allocations_(AllocationMap::NO_AUTO_EVICT), - bytes_allocated_(0u), - memory_limit_(memory_limit), - soft_memory_limit_(soft_memory_limit), - hard_memory_limit_expiration_time_(hard_memory_limit_expiration_time) { - BytesAllocatedChanged(bytes_allocated_); -} - -DiscardableMemoryManager::~DiscardableMemoryManager() { - DCHECK(allocations_.empty()); - DCHECK_EQ(0u, bytes_allocated_); -} - -void DiscardableMemoryManager::SetMemoryLimit(size_t bytes) { - AutoLock lock(lock_); - memory_limit_ = bytes; - PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired( - Now(), memory_limit_); -} - -void DiscardableMemoryManager::SetSoftMemoryLimit(size_t bytes) { - AutoLock lock(lock_); - soft_memory_limit_ = bytes; -} - -void DiscardableMemoryManager::SetHardMemoryLimitExpirationTime( - TimeDelta hard_memory_limit_expiration_time) { - AutoLock lock(lock_); - hard_memory_limit_expiration_time_ = hard_memory_limit_expiration_time; -} - -void DiscardableMemoryManager::ReleaseFreeMemory() { - TRACE_EVENT0("base", "DiscardableMemoryManager::ReleaseFreeMemory"); - - AutoLock lock(lock_); - size_t bytes_allocated_before_releasing_memory = bytes_allocated_; - for (auto& entry : allocations_) { - Allocation* allocation = entry.first; - AllocationInfo* info = &entry.second; - - if (!info->purgable) - continue; - - // Skip if memory is still resident, otherwise purge and adjust - // |bytes_allocated_|. - if (allocation->IsMemoryResident()) - continue; - - size_t bytes_purgable = info->bytes; - DCHECK_LE(bytes_purgable, bytes_allocated_); - bytes_allocated_ -= bytes_purgable; - info->purgable = false; - allocation->Purge(); - } - - if (bytes_allocated_ != bytes_allocated_before_releasing_memory) - BytesAllocatedChanged(bytes_allocated_); -} - -bool DiscardableMemoryManager::ReduceMemoryUsage() { - return PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit(); -} - -void DiscardableMemoryManager::ReduceMemoryUsageUntilWithinLimit(size_t bytes) { - AutoLock lock(lock_); - PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(Now(), - bytes); -} - -void DiscardableMemoryManager::Register(Allocation* allocation, size_t bytes) { - AutoLock lock(lock_); - DCHECK(allocations_.Peek(allocation) == allocations_.end()); - allocations_.Put(allocation, AllocationInfo(bytes)); -} - -void DiscardableMemoryManager::Unregister(Allocation* allocation) { - AutoLock lock(lock_); - AllocationMap::iterator it = allocations_.Peek(allocation); - DCHECK(it != allocations_.end()); - const AllocationInfo& info = it->second; - - if (info.purgable) { - size_t bytes_purgable = info.bytes; - DCHECK_LE(bytes_purgable, bytes_allocated_); - bytes_allocated_ -= bytes_purgable; - BytesAllocatedChanged(bytes_allocated_); - } - allocations_.Erase(it); -} - -bool DiscardableMemoryManager::AcquireLock(Allocation* allocation, - bool* purged) { - AutoLock lock(lock_); - // Note: |allocations_| is an MRU cache, and use of |Get| here updates that - // cache. - AllocationMap::iterator it = allocations_.Get(allocation); - DCHECK(it != allocations_.end()); - AllocationInfo* info = &it->second; - - if (!info->bytes) - return false; - - TimeTicks now = Now(); - size_t bytes_required = info->purgable ? 0u : info->bytes; - - if (memory_limit_) { - size_t limit = 0; - if (bytes_required < memory_limit_) - limit = memory_limit_ - bytes_required; - - PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(now, - limit); - } - - // Check for overflow. - if (std::numeric_limits<size_t>::max() - bytes_required < bytes_allocated_) - return false; - - *purged = !allocation->AllocateAndAcquireLock(); - info->purgable = false; - info->last_usage = now; - if (bytes_required) { - bytes_allocated_ += bytes_required; - BytesAllocatedChanged(bytes_allocated_); - } - return true; -} - -void DiscardableMemoryManager::ReleaseLock(Allocation* allocation) { - AutoLock lock(lock_); - // Note: |allocations_| is an MRU cache, and use of |Get| here updates that - // cache. - AllocationMap::iterator it = allocations_.Get(allocation); - DCHECK(it != allocations_.end()); - AllocationInfo* info = &it->second; - - TimeTicks now = Now(); - allocation->ReleaseLock(); - info->purgable = true; - info->last_usage = now; - - PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired( - now, memory_limit_); -} - -void DiscardableMemoryManager::PurgeAll() { - AutoLock lock(lock_); - PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(Now(), 0); -} - -bool DiscardableMemoryManager::IsRegisteredForTest( - Allocation* allocation) const { - AutoLock lock(lock_); - AllocationMap::const_iterator it = allocations_.Peek(allocation); - return it != allocations_.end(); -} - -bool DiscardableMemoryManager::CanBePurgedForTest( - Allocation* allocation) const { - AutoLock lock(lock_); - AllocationMap::const_iterator it = allocations_.Peek(allocation); - return it != allocations_.end() && it->second.purgable; -} - -size_t DiscardableMemoryManager::GetBytesAllocatedForTest() const { - AutoLock lock(lock_); - return bytes_allocated_; -} - -bool DiscardableMemoryManager:: - PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit() { - AutoLock lock(lock_); - - PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired( - Now() - hard_memory_limit_expiration_time_, soft_memory_limit_); - - return bytes_allocated_ <= soft_memory_limit_; -} - -void DiscardableMemoryManager:: - PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired( - TimeTicks timestamp, - size_t limit) { - lock_.AssertAcquired(); - - size_t bytes_allocated_before_purging = bytes_allocated_; - for (auto& entry : base::Reversed(allocations_)) { - Allocation* allocation = entry.first; - AllocationInfo* info = &entry.second; - - if (bytes_allocated_ <= limit) - break; - - bool purgable = info->purgable && info->last_usage <= timestamp; - if (!purgable) - continue; - - size_t bytes_purgable = info->bytes; - DCHECK_LE(bytes_purgable, bytes_allocated_); - bytes_allocated_ -= bytes_purgable; - info->purgable = false; - allocation->Purge(); - } - - if (bytes_allocated_ != bytes_allocated_before_purging) - BytesAllocatedChanged(bytes_allocated_); -} - -void DiscardableMemoryManager::BytesAllocatedChanged( - size_t new_bytes_allocated) const { - TRACE_COUNTER_ID1( - "base", "DiscardableMemoryUsage", this, new_bytes_allocated); - - static const char kDiscardableMemoryUsageKey[] = "dm-usage"; - base::debug::SetCrashKeyValue(kDiscardableMemoryUsageKey, - Uint64ToString(new_bytes_allocated)); -} - -TimeTicks DiscardableMemoryManager::Now() const { - return TimeTicks::Now(); -} - -} // namespace internal -} // namespace base diff --git a/chromium/base/memory/discardable_memory_manager.h b/chromium/base/memory/discardable_memory_manager.h deleted file mode 100644 index 8bf92891a3e..00000000000 --- a/chromium/base/memory/discardable_memory_manager.h +++ /dev/null @@ -1,173 +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. - -#ifndef BASE_MEMORY_DISCARDABLE_MEMORY_MANAGER_H_ -#define BASE_MEMORY_DISCARDABLE_MEMORY_MANAGER_H_ - -#include "base/base_export.h" -#include "base/containers/hash_tables.h" -#include "base/containers/mru_cache.h" -#include "base/synchronization/lock.h" -#include "base/time/time.h" - -namespace base { -namespace internal { - -// This interface is used by the DiscardableMemoryManager class to provide some -// level of userspace control over discardable memory allocations. -class DiscardableMemoryManagerAllocation { - public: - // Allocate and acquire a lock that prevents the allocation from being purged - // by the system. Returns true if memory was previously allocated and is still - // resident. - virtual bool AllocateAndAcquireLock() = 0; - - // Release a previously acquired lock on the allocation so that it can be - // purged by the system. - virtual void ReleaseLock() = 0; - - // Explicitly purge this allocation. It is illegal to call this while a lock - // is acquired on the allocation. - virtual void Purge() = 0; - - // Check if allocated memory is still resident. It is illegal to call this - // while a lock is acquired on the allocation. - virtual bool IsMemoryResident() const = 0; - - protected: - virtual ~DiscardableMemoryManagerAllocation() {} -}; - -} // namespace internal -} // namespace base - -namespace base { -namespace internal { - -// The DiscardableMemoryManager manages a collection of -// DiscardableMemoryManagerAllocation instances. It is used on platforms that -// need some level of userspace control over discardable memory. It keeps track -// of all allocation instances (in case they need to be purged), and the total -// amount of allocated memory (in case this forces a purge). When memory usage -// reaches the limit, the manager purges the LRU memory. -class BASE_EXPORT_PRIVATE DiscardableMemoryManager { - public: - typedef DiscardableMemoryManagerAllocation Allocation; - - DiscardableMemoryManager(size_t memory_limit, - size_t soft_memory_limit, - TimeDelta hard_memory_limit_expiration_time); - virtual ~DiscardableMemoryManager(); - - // The maximum number of bytes of memory that may be allocated before we force - // a purge. - void SetMemoryLimit(size_t bytes); - - // The number of bytes of memory that may be allocated but unused for the hard - // limit expiration time without getting purged. - void SetSoftMemoryLimit(size_t bytes); - - // Sets the memory usage cutoff time for hard memory limit. - void SetHardMemoryLimitExpirationTime( - TimeDelta hard_memory_limit_expiration_time); - - // This will make sure that all purged memory is released to the OS. - void ReleaseFreeMemory(); - - // This will attempt to reduce memory footprint until within soft memory - // limit. Returns true if there's no need to call this again until allocations - // have been used. - bool ReduceMemoryUsage(); - - // This can be called to attempt to reduce memory footprint until within - // limit for bytes to keep under moderate pressure. - void ReduceMemoryUsageUntilWithinLimit(size_t bytes); - - // Adds the given allocation to the manager's collection. - void Register(Allocation* allocation, size_t bytes); - - // Removes the given allocation from the manager's collection. - void Unregister(Allocation* allocation); - - // Returns false if an error occurred. Otherwise, returns true and sets - // |purged| to indicate whether or not allocation has been purged since last - // use. - bool AcquireLock(Allocation* allocation, bool* purged); - - // Release a previously acquired lock on allocation. This allows the manager - // to purge it if necessary. - void ReleaseLock(Allocation* allocation); - - // Purges all discardable memory. - void PurgeAll(); - - // Returns true if allocation has been added to the manager's collection. This - // should only be used by tests. - bool IsRegisteredForTest(Allocation* allocation) const; - - // Returns true if allocation can be purged. This should only be used by - // tests. - bool CanBePurgedForTest(Allocation* allocation) const; - - // Returns total amount of allocated discardable memory. This should only be - // used by tests. - size_t GetBytesAllocatedForTest() const; - - private: - struct AllocationInfo { - explicit AllocationInfo(size_t bytes) : bytes(bytes), purgable(false) {} - - const size_t bytes; - bool purgable; - TimeTicks last_usage; - }; - typedef HashingMRUCache<Allocation*, AllocationInfo> AllocationMap; - - // Purges memory not used since |hard_memory_limit_expiration_time_| before - // "right now" until usage is less or equal to |soft_memory_limit_|. - // Returns true if total amount of memory is less or equal to soft memory - // limit. - bool PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit(); - - // Purges memory that has not been used since |timestamp| until usage is less - // or equal to |limit|. - // Caller must acquire |lock_| prior to calling this function. - void PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired( - TimeTicks timestamp, - size_t limit); - - // Called when a change to |bytes_allocated_| has been made. - void BytesAllocatedChanged(size_t new_bytes_allocated) const; - - // Virtual for tests. - virtual TimeTicks Now() const; - - // Needs to be held when accessing members. - mutable Lock lock_; - - // A MRU cache of all allocated bits of memory. Used for purging. - AllocationMap allocations_; - - // The total amount of allocated memory. - size_t bytes_allocated_; - - // The maximum number of bytes of memory that may be allocated. - size_t memory_limit_; - - // The number of bytes of memory that may be allocated but not used for - // |hard_memory_limit_expiration_time_| amount of time when receiving an idle - // notification. - size_t soft_memory_limit_; - - // Amount of time it takes for an allocation to become affected by - // |soft_memory_limit_|. - TimeDelta hard_memory_limit_expiration_time_; - - DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryManager); -}; - -} // namespace internal -} // namespace base - -#endif // BASE_MEMORY_DISCARDABLE_MEMORY_MANAGER_H_ diff --git a/chromium/base/memory/discardable_memory_manager_unittest.cc b/chromium/base/memory/discardable_memory_manager_unittest.cc deleted file mode 100644 index 12f7f0816a5..00000000000 --- a/chromium/base/memory/discardable_memory_manager_unittest.cc +++ /dev/null @@ -1,494 +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. - -#include "base/memory/discardable_memory_manager.h" - -#include "base/bind.h" -#include "base/synchronization/waitable_event.h" -#include "base/threading/thread.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace { - -class TestAllocationImpl : public internal::DiscardableMemoryManagerAllocation { - public: - TestAllocationImpl() : is_allocated_(false), is_locked_(false) {} - ~TestAllocationImpl() override { DCHECK(!is_locked_); } - - // Overridden from internal::DiscardableMemoryManagerAllocation: - bool AllocateAndAcquireLock() override { - bool was_allocated = is_allocated_; - is_allocated_ = true; - DCHECK(!is_locked_); - is_locked_ = true; - return was_allocated; - } - void ReleaseLock() override { - DCHECK(is_locked_); - is_locked_ = false; - } - void Purge() override { - DCHECK(is_allocated_); - is_allocated_ = false; - } - virtual bool IsMemoryResident() const override { - DCHECK(is_allocated_); - return true; - } - - bool is_locked() const { return is_locked_; } - - private: - bool is_allocated_; - bool is_locked_; -}; - -// Tests can assume that the default limit is at least 1024. Tests that rely on -// something else needs to explicit set the limit. -const size_t kDefaultMemoryLimit = 1024; -const size_t kDefaultSoftMemoryLimit = kDefaultMemoryLimit; - -class TestDiscardableMemoryManagerImpl - : public internal::DiscardableMemoryManager { - public: - TestDiscardableMemoryManagerImpl() - : DiscardableMemoryManager(kDefaultMemoryLimit, - kDefaultSoftMemoryLimit, - TimeDelta::Max()) {} - - void SetNow(TimeTicks now) { now_ = now; } - - private: - // Overriden from internal::DiscardableMemoryManager: - TimeTicks Now() const override { return now_; } - - TimeTicks now_; -}; - -class DiscardableMemoryManagerTestBase { - public: - DiscardableMemoryManagerTestBase() {} - - protected: - enum LockStatus { - LOCK_STATUS_FAILED, - LOCK_STATUS_PURGED, - LOCK_STATUS_SUCCESS - }; - - size_t BytesAllocated() const { return manager_.GetBytesAllocatedForTest(); } - - void SetMemoryLimit(size_t bytes) { manager_.SetMemoryLimit(bytes); } - - void SetSoftMemoryLimit(size_t bytes) { manager_.SetSoftMemoryLimit(bytes); } - - void SetHardMemoryLimitExpirationTime(TimeDelta time) { - manager_.SetHardMemoryLimitExpirationTime(time); - } - - void Register(TestAllocationImpl* allocation, size_t bytes) { - manager_.Register(allocation, bytes); - } - - void Unregister(TestAllocationImpl* allocation) { - manager_.Unregister(allocation); - } - - bool IsRegistered(TestAllocationImpl* allocation) const { - return manager_.IsRegisteredForTest(allocation); - } - - LockStatus Lock(TestAllocationImpl* allocation) { - bool purged; - if (!manager_.AcquireLock(allocation, &purged)) - return LOCK_STATUS_FAILED; - return purged ? LOCK_STATUS_PURGED : LOCK_STATUS_SUCCESS; - } - - void Unlock(TestAllocationImpl* allocation) { - manager_.ReleaseLock(allocation); - } - - LockStatus RegisterAndLock(TestAllocationImpl* allocation, size_t bytes) { - manager_.Register(allocation, bytes); - return Lock(allocation); - } - - bool CanBePurged(TestAllocationImpl* allocation) const { - return manager_.CanBePurgedForTest(allocation); - } - - void SetNow(TimeTicks now) { manager_.SetNow(now); } - - void PurgeAll() { return manager_.PurgeAll(); } - - bool ReduceMemoryUsage() { return manager_.ReduceMemoryUsage(); } - - void ReduceMemoryUsageUntilWithinLimit(size_t bytes) { - manager_.ReduceMemoryUsageUntilWithinLimit(bytes); - } - - private: - TestDiscardableMemoryManagerImpl manager_; -}; - -class DiscardableMemoryManagerTest : public DiscardableMemoryManagerTestBase, - public testing::Test { - public: - DiscardableMemoryManagerTest() {} -}; - -TEST_F(DiscardableMemoryManagerTest, CreateAndLock) { - size_t size = 1024; - TestAllocationImpl allocation; - Register(&allocation, size); - EXPECT_TRUE(IsRegistered(&allocation)); - EXPECT_EQ(LOCK_STATUS_PURGED, Lock(&allocation)); - EXPECT_TRUE(allocation.is_locked()); - EXPECT_EQ(1024u, BytesAllocated()); - EXPECT_FALSE(CanBePurged(&allocation)); - Unlock(&allocation); - Unregister(&allocation); -} - -TEST_F(DiscardableMemoryManagerTest, CreateZeroSize) { - size_t size = 0; - TestAllocationImpl allocation; - Register(&allocation, size); - EXPECT_TRUE(IsRegistered(&allocation)); - EXPECT_EQ(LOCK_STATUS_FAILED, Lock(&allocation)); - EXPECT_EQ(0u, BytesAllocated()); - Unregister(&allocation); -} - -TEST_F(DiscardableMemoryManagerTest, LockAfterUnlock) { - size_t size = 1024; - TestAllocationImpl allocation; - RegisterAndLock(&allocation, size); - EXPECT_EQ(1024u, BytesAllocated()); - EXPECT_FALSE(CanBePurged(&allocation)); - - // Now unlock so we can lock later. - Unlock(&allocation); - EXPECT_TRUE(CanBePurged(&allocation)); - - EXPECT_EQ(LOCK_STATUS_SUCCESS, Lock(&allocation)); - EXPECT_FALSE(CanBePurged(&allocation)); - Unlock(&allocation); - Unregister(&allocation); -} - -TEST_F(DiscardableMemoryManagerTest, LockAfterPurge) { - size_t size = 1024; - TestAllocationImpl allocation; - RegisterAndLock(&allocation, size); - EXPECT_EQ(1024u, BytesAllocated()); - EXPECT_FALSE(CanBePurged(&allocation)); - - // Now unlock so we can lock later. - Unlock(&allocation); - EXPECT_TRUE(CanBePurged(&allocation)); - - // Force the system to purge. - PurgeAll(); - - EXPECT_EQ(LOCK_STATUS_PURGED, Lock(&allocation)); - EXPECT_FALSE(CanBePurged(&allocation)); - - Unlock(&allocation); - Unregister(&allocation); -} - -TEST_F(DiscardableMemoryManagerTest, LockAfterPurgeAndCannotReallocate) { - size_t size = 1024; - TestAllocationImpl allocation; - RegisterAndLock(&allocation, size); - EXPECT_EQ(1024u, BytesAllocated()); - EXPECT_FALSE(CanBePurged(&allocation)); - - // Now unlock so we can lock later. - Unlock(&allocation); - EXPECT_TRUE(CanBePurged(&allocation)); - - // Set max allowed allocation to 1 byte. This will cause the memory to be - // purged. - SetMemoryLimit(1); - - EXPECT_EQ(LOCK_STATUS_PURGED, Lock(&allocation)); - EXPECT_FALSE(CanBePurged(&allocation)); - - Unlock(&allocation); - Unregister(&allocation); -} - -TEST_F(DiscardableMemoryManagerTest, Overflow) { - size_t size = 1024; - { - TestAllocationImpl allocation; - RegisterAndLock(&allocation, size); - EXPECT_EQ(1024u, BytesAllocated()); - - size_t massive_size = std::numeric_limits<size_t>::max(); - TestAllocationImpl massive_allocation; - Register(&massive_allocation, massive_size); - EXPECT_EQ(LOCK_STATUS_FAILED, Lock(&massive_allocation)); - EXPECT_EQ(1024u, BytesAllocated()); - - Unlock(&allocation); - EXPECT_EQ(LOCK_STATUS_PURGED, Lock(&massive_allocation)); - Unlock(&massive_allocation); - Unregister(&massive_allocation); - Unregister(&allocation); - } - EXPECT_EQ(0u, BytesAllocated()); -} - -class PermutationTestData { - public: - PermutationTestData(unsigned d0, unsigned d1, unsigned d2) { - ordering_[0] = d0; - ordering_[1] = d1; - ordering_[2] = d2; - } - - const unsigned* ordering() const { return ordering_; } - - private: - unsigned ordering_[3]; -}; - -class DiscardableMemoryManagerPermutationTest - : public DiscardableMemoryManagerTestBase, - public testing::TestWithParam<PermutationTestData> { - public: - DiscardableMemoryManagerPermutationTest() {} - - protected: - // Use memory in order specified by ordering parameter. - void RegisterAndUseAllocations() { - for (int i = 0; i < 3; ++i) { - RegisterAndLock(&allocation_[i], 1024); - Unlock(&allocation_[i]); - } - for (int i = 0; i < 3; ++i) { - int index = GetParam().ordering()[i]; - EXPECT_NE(LOCK_STATUS_FAILED, Lock(&allocation_[index])); - // Leave i == 0 locked. - if (i > 0) - Unlock(&allocation_[index]); - } - } - - TestAllocationImpl* allocation(unsigned position) { - return &allocation_[GetParam().ordering()[position]]; - } - - void UnlockAndUnregisterAllocations() { - for (int i = 0; i < 3; ++i) { - if (allocation_[i].is_locked()) - Unlock(&allocation_[i]); - Unregister(&allocation_[i]); - } - } - - private: - TestAllocationImpl allocation_[3]; -}; - -// Verify that memory was discarded in the correct order after reducing usage to -// limit. -TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscarded) { - RegisterAndUseAllocations(); - - SetMemoryLimit(2048); - - ReduceMemoryUsageUntilWithinLimit(1024); - - EXPECT_NE(LOCK_STATUS_FAILED, Lock(allocation(2))); - EXPECT_EQ(LOCK_STATUS_PURGED, Lock(allocation(1))); - // 0 should still be locked. - EXPECT_TRUE(allocation(0)->is_locked()); - - UnlockAndUnregisterAllocations(); -} - -// Verify that memory was discarded in the correct order after changing -// memory limit. -TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscardedExceedLimit) { - RegisterAndUseAllocations(); - - SetMemoryLimit(2048); - - EXPECT_NE(LOCK_STATUS_FAILED, Lock(allocation(2))); - EXPECT_EQ(LOCK_STATUS_PURGED, Lock(allocation(1))); - // 0 should still be locked. - EXPECT_TRUE(allocation(0)->is_locked()); - - UnlockAndUnregisterAllocations(); -} - -// Verify that no more memory than necessary was discarded after changing -// memory limit. -TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscardedAmount) { - SetMemoryLimit(4096); - - RegisterAndUseAllocations(); - - SetMemoryLimit(2048); - - EXPECT_EQ(LOCK_STATUS_SUCCESS, Lock(allocation(2))); - EXPECT_EQ(LOCK_STATUS_PURGED, Lock(allocation(1))); - // 0 should still be locked. - EXPECT_TRUE(allocation(0)->is_locked()); - - UnlockAndUnregisterAllocations(); -} - -TEST_P(DiscardableMemoryManagerPermutationTest, PurgeFreesAllUnlocked) { - RegisterAndUseAllocations(); - - PurgeAll(); - - for (int i = 0; i < 3; ++i) { - if (i == 0) - EXPECT_TRUE(allocation(i)->is_locked()); - else - EXPECT_EQ(LOCK_STATUS_PURGED, Lock(allocation(i))); - } - - UnlockAndUnregisterAllocations(); -} - -INSTANTIATE_TEST_CASE_P(DiscardableMemoryManagerPermutationTests, - DiscardableMemoryManagerPermutationTest, - ::testing::Values(PermutationTestData(0, 1, 2), - PermutationTestData(0, 2, 1), - PermutationTestData(1, 0, 2), - PermutationTestData(1, 2, 0), - PermutationTestData(2, 0, 1), - PermutationTestData(2, 1, 0))); - -TEST_F(DiscardableMemoryManagerTest, NormalDestruction) { - { - size_t size = 1024; - TestAllocationImpl allocation; - Register(&allocation, size); - Unregister(&allocation); - } - EXPECT_EQ(0u, BytesAllocated()); -} - -TEST_F(DiscardableMemoryManagerTest, DestructionAfterLocked) { - { - size_t size = 1024; - TestAllocationImpl allocation; - RegisterAndLock(&allocation, size); - EXPECT_EQ(1024u, BytesAllocated()); - EXPECT_FALSE(CanBePurged(&allocation)); - Unlock(&allocation); - Unregister(&allocation); - } - EXPECT_EQ(0u, BytesAllocated()); -} - -TEST_F(DiscardableMemoryManagerTest, DestructionAfterPurged) { - { - size_t size = 1024; - TestAllocationImpl allocation; - RegisterAndLock(&allocation, size); - EXPECT_EQ(1024u, BytesAllocated()); - Unlock(&allocation); - EXPECT_TRUE(CanBePurged(&allocation)); - SetMemoryLimit(0); - EXPECT_EQ(0u, BytesAllocated()); - Unregister(&allocation); - } - EXPECT_EQ(0u, BytesAllocated()); -} - -TEST_F(DiscardableMemoryManagerTest, ReduceMemoryUsage) { - SetMemoryLimit(3072); - SetSoftMemoryLimit(1024); - SetHardMemoryLimitExpirationTime(TimeDelta::FromInternalValue(1)); - - size_t size = 1024; - TestAllocationImpl allocation[3]; - RegisterAndLock(&allocation[0], size); - RegisterAndLock(&allocation[1], size); - RegisterAndLock(&allocation[2], size); - EXPECT_EQ(3072u, BytesAllocated()); - - // Above soft limit but nothing that can be purged. - EXPECT_FALSE(ReduceMemoryUsage()); - - SetNow(TimeTicks::FromInternalValue(0)); - Unlock(&allocation[0]); - - // Above soft limit but still nothing that can be purged as all unlocked - // allocations are within the hard limit cutoff time. - EXPECT_FALSE(ReduceMemoryUsage()); - - SetNow(TimeTicks::FromInternalValue(1)); - Unlock(&allocation[1]); - - // One unlocked allocation is no longer within the hard limit cutoff time. It - // should be purged and ReduceMemoryUsage() should return false as we're not - // yet within the soft memory limit. - EXPECT_FALSE(ReduceMemoryUsage()); - EXPECT_EQ(2048u, BytesAllocated()); - - // One more unlocked allocation is no longer within the hard limit cutoff - // time. It should be purged and ReduceMemoryUsage() should return true as - // we're now within the soft memory limit. - SetNow(TimeTicks::FromInternalValue(2)); - EXPECT_TRUE(ReduceMemoryUsage()); - EXPECT_EQ(1024u, BytesAllocated()); - - Unlock(&allocation[2]); - - Unregister(&allocation[0]); - Unregister(&allocation[1]); - Unregister(&allocation[2]); -} - -class ThreadedDiscardableMemoryManagerTest - : public DiscardableMemoryManagerTest { - public: - ThreadedDiscardableMemoryManagerTest() - : memory_usage_thread_("memory_usage_thread"), - thread_sync_(true, false) {} - - virtual void SetUp() override { memory_usage_thread_.Start(); } - - virtual void TearDown() override { memory_usage_thread_.Stop(); } - - void UseMemoryHelper() { - size_t size = 1024; - TestAllocationImpl allocation; - RegisterAndLock(&allocation, size); - Unlock(&allocation); - Unregister(&allocation); - } - - void SignalHelper() { thread_sync_.Signal(); } - - Thread memory_usage_thread_; - WaitableEvent thread_sync_; -}; - -TEST_F(ThreadedDiscardableMemoryManagerTest, UseMemoryOnThread) { - memory_usage_thread_.message_loop()->PostTask( - FROM_HERE, - Bind(&ThreadedDiscardableMemoryManagerTest::UseMemoryHelper, - Unretained(this))); - memory_usage_thread_.message_loop()->PostTask( - FROM_HERE, - Bind(&ThreadedDiscardableMemoryManagerTest::SignalHelper, - Unretained(this))); - thread_sync_.Wait(); -} - -} // namespace -} // namespace base diff --git a/chromium/base/memory/discardable_memory_shmem.cc b/chromium/base/memory/discardable_memory_shmem.cc deleted file mode 100644 index 77699f0739f..00000000000 --- a/chromium/base/memory/discardable_memory_shmem.cc +++ /dev/null @@ -1,121 +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. - -#include "base/memory/discardable_memory_shmem.h" - -#include "base/lazy_instance.h" -#include "base/memory/discardable_memory_shmem_allocator.h" -#include "base/memory/discardable_shared_memory.h" - -namespace base { -namespace { - -// Have the DiscardableMemoryManager trigger in-process eviction -// when address space usage gets too high (e.g. 512 MBytes). -const size_t kMemoryLimit = 512 * 1024 * 1024; - -// internal::DiscardableMemoryManager has an explicit constructor that takes -// a number of memory limit parameters. The LeakyLazyInstanceTraits doesn't -// handle the case. Thus, we need our own class here. -struct DiscardableMemoryManagerLazyInstanceTraits { - // Leaky as discardable memory clients can use this after the exit handler - // has been called. - static const bool kRegisterOnExit = false; -#ifndef NDEBUG - static const bool kAllowedToAccessOnNonjoinableThread = true; -#endif - - static internal::DiscardableMemoryManager* New(void* instance) { - return new (instance) internal::DiscardableMemoryManager( - kMemoryLimit, kMemoryLimit, TimeDelta::Max()); - } - static void Delete(internal::DiscardableMemoryManager* instance) { - instance->~DiscardableMemoryManager(); - } -}; - -LazyInstance<internal::DiscardableMemoryManager, - DiscardableMemoryManagerLazyInstanceTraits> g_manager = - LAZY_INSTANCE_INITIALIZER; - -} // namespace - -namespace internal { - -DiscardableMemoryShmem::DiscardableMemoryShmem(size_t bytes) - : bytes_(bytes), is_locked_(false) { - g_manager.Pointer()->Register(this, bytes); -} - -DiscardableMemoryShmem::~DiscardableMemoryShmem() { - if (is_locked_) - Unlock(); - g_manager.Pointer()->Unregister(this); -} - -// static -void DiscardableMemoryShmem::ReleaseFreeMemory() { - g_manager.Pointer()->ReleaseFreeMemory(); -} - -// static -void DiscardableMemoryShmem::PurgeForTesting() { - g_manager.Pointer()->PurgeAll(); -} - -bool DiscardableMemoryShmem::Initialize() { - return Lock() != DISCARDABLE_MEMORY_LOCK_STATUS_FAILED; -} - -DiscardableMemoryLockStatus DiscardableMemoryShmem::Lock() { - DCHECK(!is_locked_); - - bool purged = false; - if (!g_manager.Pointer()->AcquireLock(this, &purged)) - return DISCARDABLE_MEMORY_LOCK_STATUS_FAILED; - - is_locked_ = true; - return purged ? DISCARDABLE_MEMORY_LOCK_STATUS_PURGED - : DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS; -} - -void DiscardableMemoryShmem::Unlock() { - DCHECK(is_locked_); - g_manager.Pointer()->ReleaseLock(this); - is_locked_ = false; -} - -void* DiscardableMemoryShmem::Memory() const { - DCHECK(is_locked_); - DCHECK(shared_memory_); - return shared_memory_->memory(); -} - -bool DiscardableMemoryShmem::AllocateAndAcquireLock() { - if (shared_memory_ && shared_memory_->Lock()) - return true; - - // TODO(reveman): Allocate fixed size memory segments and use a free list to - // improve performance and limit the number of file descriptors used. - shared_memory_ = DiscardableMemoryShmemAllocator::GetInstance() - ->AllocateLockedDiscardableSharedMemory(bytes_); - DCHECK(shared_memory_); - return false; -} - -void DiscardableMemoryShmem::ReleaseLock() { - shared_memory_->Unlock(); -} - -void DiscardableMemoryShmem::Purge() { - shared_memory_->Purge(Time()); - shared_memory_.reset(); -} - -bool DiscardableMemoryShmem::IsMemoryResident() const { - return shared_memory_->IsMemoryResident(); -} - -} // namespace internal -} // namespace base diff --git a/chromium/base/memory/discardable_memory_shmem.h b/chromium/base/memory/discardable_memory_shmem.h deleted file mode 100644 index d25f84cb46d..00000000000 --- a/chromium/base/memory/discardable_memory_shmem.h +++ /dev/null @@ -1,52 +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. - -#ifndef BASE_MEMORY_DISCARDABLE_MEMORY_SHMEM_H_ -#define BASE_MEMORY_DISCARDABLE_MEMORY_SHMEM_H_ - -#include "base/memory/discardable_memory.h" - -#include "base/memory/discardable_memory_manager.h" - -namespace base { -class DiscardableSharedMemory; - -namespace internal { - -class DiscardableMemoryShmem - : public DiscardableMemory, - public internal::DiscardableMemoryManagerAllocation { - public: - explicit DiscardableMemoryShmem(size_t bytes); - virtual ~DiscardableMemoryShmem(); - - static void ReleaseFreeMemory(); - - static void PurgeForTesting(); - - bool Initialize(); - - // Overridden from DiscardableMemory: - virtual DiscardableMemoryLockStatus Lock() override; - virtual void Unlock() override; - virtual void* Memory() const override; - - // Overridden from internal::DiscardableMemoryManagerAllocation: - virtual bool AllocateAndAcquireLock() override; - virtual void ReleaseLock() override; - virtual void Purge() override; - virtual bool IsMemoryResident() const override; - - private: - const size_t bytes_; - scoped_ptr<DiscardableSharedMemory> shared_memory_; - bool is_locked_; - - DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryShmem); -}; - -} // namespace internal -} // namespace base - -#endif // BASE_MEMORY_DISCARDABLE_MEMORY_SHMEM_H_ diff --git a/chromium/base/memory/discardable_memory_shmem_allocator.cc b/chromium/base/memory/discardable_memory_shmem_allocator.cc deleted file mode 100644 index 78d15c1031e..00000000000 --- a/chromium/base/memory/discardable_memory_shmem_allocator.cc +++ /dev/null @@ -1,58 +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. - -#include "base/memory/discardable_memory_shmem_allocator.h" - -#include "base/lazy_instance.h" -#include "base/logging.h" -#include "base/memory/discardable_shared_memory.h" - -namespace base { -namespace { - -// Default allocator implementation that allocates in-process -// DiscardableSharedMemory instances. -class DiscardableMemoryShmemAllocatorImpl - : public DiscardableMemoryShmemAllocator { - public: - // Overridden from DiscardableMemoryShmemAllocator: - virtual scoped_ptr<DiscardableSharedMemory> - AllocateLockedDiscardableSharedMemory(size_t size) override { - scoped_ptr<DiscardableSharedMemory> memory(new DiscardableSharedMemory); - if (!memory->CreateAndMap(size)) - return scoped_ptr<DiscardableSharedMemory>(); - - return memory.Pass(); - } -}; - -LazyInstance<DiscardableMemoryShmemAllocatorImpl>::Leaky g_default_allocator = - LAZY_INSTANCE_INITIALIZER; - -DiscardableMemoryShmemAllocator* g_allocator = nullptr; - -} // namespace - -// static -void DiscardableMemoryShmemAllocator::SetInstance( - DiscardableMemoryShmemAllocator* allocator) { - DCHECK(allocator); - - // Make sure this function is only called once before the first call - // to GetInstance(). - DCHECK(!g_allocator); - - g_allocator = allocator; -} - -// static -DiscardableMemoryShmemAllocator* -DiscardableMemoryShmemAllocator::GetInstance() { - if (!g_allocator) - g_allocator = g_default_allocator.Pointer(); - - return g_allocator; -} - -} // namespace base diff --git a/chromium/base/memory/discardable_memory_shmem_allocator.h b/chromium/base/memory/discardable_memory_shmem_allocator.h deleted file mode 100644 index 68624b3960b..00000000000 --- a/chromium/base/memory/discardable_memory_shmem_allocator.h +++ /dev/null @@ -1,32 +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. - -#ifndef BASE_MEMORY_DISCARDABLE_MEMORY_SHMEM_ALLOCATOR_H_ -#define BASE_MEMORY_DISCARDABLE_MEMORY_SHMEM_ALLOCATOR_H_ - -#include "base/base_export.h" -#include "base/memory/scoped_ptr.h" - -namespace base { -class DiscardableSharedMemory; - -class BASE_EXPORT DiscardableMemoryShmemAllocator { - public: - // Returns the allocator instance. - static DiscardableMemoryShmemAllocator* GetInstance(); - - // Sets the allocator instance. Can only be called once, e.g. on startup. - // Ownership of |instance| remains with the caller. - static void SetInstance(DiscardableMemoryShmemAllocator* allocator); - - virtual scoped_ptr<DiscardableSharedMemory> - AllocateLockedDiscardableSharedMemory(size_t size) = 0; - - protected: - virtual ~DiscardableMemoryShmemAllocator() {} -}; - -} // namespace base - -#endif // BASE_MEMORY_DISCARDABLE_MEMORY_SHMEM_ALLOCATOR_H_ diff --git a/chromium/base/memory/discardable_memory_unittest.cc b/chromium/base/memory/discardable_memory_unittest.cc deleted file mode 100644 index 600475ee06b..00000000000 --- a/chromium/base/memory/discardable_memory_unittest.cc +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright (c) 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. - -#include "base/memory/discardable_memory.h" - -#include <algorithm> - -#include "testing/gtest/include/gtest/gtest.h" - -#if defined(OS_ANDROID) -#include <limits> -#endif - -namespace base { -namespace { - -class DiscardableMemoryTest - : public testing::TestWithParam<DiscardableMemoryType> { - public: - DiscardableMemoryTest() {} - virtual ~DiscardableMemoryTest() { - } - - protected: - scoped_ptr<DiscardableMemory> CreateLockedMemory(size_t size) { - return DiscardableMemory::CreateLockedMemoryWithType( - GetParam(), size).Pass(); - } -}; - -const size_t kSize = 1024; - -TEST_P(DiscardableMemoryTest, IsNamed) { - std::string type_name(DiscardableMemory::GetTypeName(GetParam())); - EXPECT_NE("unknown", type_name); - EXPECT_EQ(GetParam(), DiscardableMemory::GetNamedType(type_name)); -} - -bool IsNativeType(DiscardableMemoryType type) { - return type == DISCARDABLE_MEMORY_TYPE_ASHMEM || - type == DISCARDABLE_MEMORY_TYPE_MACH; -} - -TEST_P(DiscardableMemoryTest, SupportedNatively) { - std::vector<DiscardableMemoryType> supported_types; - DiscardableMemory::GetSupportedTypes(&supported_types); -#if defined(DISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY) - EXPECT_NE(0, std::count_if(supported_types.begin(), - supported_types.end(), - IsNativeType)); -#else - // If we ever have a platform that decides at runtime if it can support - // discardable memory natively, then we'll have to add a 'never supported - // natively' define for this case. At present, if it's not always supported - // natively, it's never supported. - EXPECT_EQ(0, std::count_if(supported_types.begin(), - supported_types.end(), - IsNativeType)); -#endif -} - -// Test Lock() and Unlock() functionalities. -TEST_P(DiscardableMemoryTest, LockAndUnLock) { - const scoped_ptr<DiscardableMemory> memory(CreateLockedMemory(kSize)); - ASSERT_TRUE(memory); - void* addr = memory->Memory(); - ASSERT_NE(nullptr, addr); - - memory->Unlock(); - - EXPECT_NE(DISCARDABLE_MEMORY_LOCK_STATUS_FAILED, memory->Lock()); - addr = memory->Memory(); - ASSERT_NE(nullptr, addr); - - memory->Unlock(); -} - -// Test delete a discardable memory while it is locked. -TEST_P(DiscardableMemoryTest, DeleteWhileLocked) { - const scoped_ptr<DiscardableMemory> memory(CreateLockedMemory(kSize)); - ASSERT_TRUE(memory); -} - -// Test forced purging. -TEST_P(DiscardableMemoryTest, Purge) { - const scoped_ptr<DiscardableMemory> memory(CreateLockedMemory(kSize)); - ASSERT_TRUE(memory); - memory->Unlock(); - - DiscardableMemory::PurgeForTesting(); - EXPECT_EQ(DISCARDABLE_MEMORY_LOCK_STATUS_PURGED, memory->Lock()); -} - -#if !defined(NDEBUG) && !defined(OS_ANDROID) -// Death tests are not supported with Android APKs. -TEST_P(DiscardableMemoryTest, UnlockedMemoryAccessCrashesInDebugMode) { - const scoped_ptr<DiscardableMemory> memory(CreateLockedMemory(kSize)); - ASSERT_TRUE(memory); - memory->Unlock(); - ASSERT_DEATH_IF_SUPPORTED( - { *static_cast<int*>(memory->Memory()) = 0xdeadbeef; }, ".*"); -} -#endif - -// Test behavior when creating enough instances that could use up a 32-bit -// address space. -TEST_P(DiscardableMemoryTest, AddressSpace) { - const size_t kLargeSize = 4 * 1024 * 1024; // 4MiB. - const size_t kNumberOfInstances = 1024 + 1; // >4GiB total. - - scoped_ptr<DiscardableMemory> instances[kNumberOfInstances]; - for (auto& memory : instances) { - memory = CreateLockedMemory(kLargeSize); - ASSERT_TRUE(memory); - void* addr = memory->Memory(); - ASSERT_NE(nullptr, addr); - memory->Unlock(); - } -} - -std::vector<DiscardableMemoryType> GetSupportedDiscardableMemoryTypes() { - std::vector<DiscardableMemoryType> supported_types; - DiscardableMemory::GetSupportedTypes(&supported_types); - return supported_types; -} - -INSTANTIATE_TEST_CASE_P( - DiscardableMemoryTests, - DiscardableMemoryTest, - ::testing::ValuesIn(GetSupportedDiscardableMemoryTypes())); - -} // namespace -} // namespace base diff --git a/chromium/base/memory/discardable_memory_win.cc b/chromium/base/memory/discardable_memory_win.cc deleted file mode 100644 index 9b4e9401ce4..00000000000 --- a/chromium/base/memory/discardable_memory_win.cc +++ /dev/null @@ -1,70 +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. - -#include "base/memory/discardable_memory.h" - -#include "base/logging.h" -#include "base/memory/discardable_memory_emulated.h" -#include "base/memory/discardable_memory_shmem.h" - -namespace base { - -// static -void DiscardableMemory::ReleaseFreeMemory() { - internal::DiscardableMemoryShmem::ReleaseFreeMemory(); -} - -// static -bool DiscardableMemory::ReduceMemoryUsage() { - return internal::DiscardableMemoryEmulated::ReduceMemoryUsage(); -} - -// static -void DiscardableMemory::GetSupportedTypes( - std::vector<DiscardableMemoryType>* types) { - const DiscardableMemoryType supported_types[] = { - DISCARDABLE_MEMORY_TYPE_EMULATED, - DISCARDABLE_MEMORY_TYPE_SHMEM - }; - types->assign(supported_types, supported_types + arraysize(supported_types)); -} - -// static -scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemoryWithType( - DiscardableMemoryType type, size_t size) { - switch (type) { - case DISCARDABLE_MEMORY_TYPE_EMULATED: { - scoped_ptr<internal::DiscardableMemoryEmulated> memory( - new internal::DiscardableMemoryEmulated(size)); - if (!memory->Initialize()) - return nullptr; - - return memory.Pass(); - } - case DISCARDABLE_MEMORY_TYPE_SHMEM: { - scoped_ptr<internal::DiscardableMemoryShmem> memory( - new internal::DiscardableMemoryShmem(size)); - if (!memory->Initialize()) - return nullptr; - - return memory.Pass(); - } - case DISCARDABLE_MEMORY_TYPE_NONE: - case DISCARDABLE_MEMORY_TYPE_ASHMEM: - case DISCARDABLE_MEMORY_TYPE_MACH: - NOTREACHED(); - return nullptr; - } - - NOTREACHED(); - return nullptr; -} - -// static -void DiscardableMemory::PurgeForTesting() { - internal::DiscardableMemoryEmulated::PurgeForTesting(); - internal::DiscardableMemoryShmem::PurgeForTesting(); -} - -} // namespace base diff --git a/chromium/base/memory/discardable_shared_memory.cc b/chromium/base/memory/discardable_shared_memory.cc index 653304806ec..f8248e942cb 100644 --- a/chromium/base/memory/discardable_shared_memory.cc +++ b/chromium/base/memory/discardable_shared_memory.cc @@ -13,6 +13,11 @@ #include "base/atomicops.h" #include "base/logging.h" #include "base/numerics/safe_math.h" +#include "base/process/process_metrics.h" + +#if defined(OS_ANDROID) +#include "third_party/ashmem/ashmem.h" +#endif namespace base { namespace { @@ -59,7 +64,7 @@ struct SharedState { SharedState(LockState lock_state, Time timestamp) { int64 wire_timestamp = TimeToWireFormat<sizeof(AtomicType)>(timestamp); DCHECK_GE(wire_timestamp, 0); - DCHECK((lock_state & ~1) == 0); + DCHECK_EQ(lock_state & ~1, 0); value.u = (static_cast<UAtomicType>(wire_timestamp) << 1) | lock_state; } @@ -84,14 +89,28 @@ SharedState* SharedStateFromSharedMemory(const SharedMemory& shared_memory) { return static_cast<SharedState*>(shared_memory.memory()); } +// Round up |size| to a multiple of alignment, which must be a power of two. +size_t Align(size_t alignment, size_t size) { + DCHECK_EQ(alignment & (alignment - 1), 0u); + return (size + alignment - 1) & ~(alignment - 1); +} + +// Round up |size| to a multiple of page size. +size_t AlignToPageSize(size_t size) { + return Align(base::GetPageSize(), size); +} + } // namespace -DiscardableSharedMemory::DiscardableSharedMemory() { +DiscardableSharedMemory::DiscardableSharedMemory() + : mapped_size_(0), locked_page_count_(0) { } DiscardableSharedMemory::DiscardableSharedMemory( SharedMemoryHandle shared_memory_handle) - : shared_memory_(shared_memory_handle, false) { + : shared_memory_(shared_memory_handle, false), + mapped_size_(0), + locked_page_count_(0) { } DiscardableSharedMemory::~DiscardableSharedMemory() { @@ -99,13 +118,22 @@ DiscardableSharedMemory::~DiscardableSharedMemory() { bool DiscardableSharedMemory::CreateAndMap(size_t size) { CheckedNumeric<size_t> checked_size = size; - checked_size += sizeof(SharedState); + checked_size += AlignToPageSize(sizeof(SharedState)); if (!checked_size.IsValid()) return false; if (!shared_memory_.CreateAndMapAnonymous(checked_size.ValueOrDie())) return false; + mapped_size_ = + shared_memory_.mapped_size() - AlignToPageSize(sizeof(SharedState)); + + locked_page_count_ = AlignToPageSize(mapped_size_) / base::GetPageSize(); +#if DCHECK_IS_ON() + for (size_t page = 0; page < locked_page_count_; ++page) + locked_pages_.insert(page); +#endif + DCHECK(last_known_usage_.is_null()); SharedState new_state(SharedState::LOCKED, Time()); subtle::Release_Store(&SharedStateFromSharedMemory(shared_memory_)->value.i, @@ -114,35 +142,141 @@ bool DiscardableSharedMemory::CreateAndMap(size_t size) { } bool DiscardableSharedMemory::Map(size_t size) { - return shared_memory_.Map(sizeof(SharedState) + size); + if (!shared_memory_.Map(AlignToPageSize(sizeof(SharedState)) + size)) + return false; + + mapped_size_ = + shared_memory_.mapped_size() - AlignToPageSize(sizeof(SharedState)); + + locked_page_count_ = AlignToPageSize(mapped_size_) / base::GetPageSize(); +#if DCHECK_IS_ON() + for (size_t page = 0; page < locked_page_count_; ++page) + locked_pages_.insert(page); +#endif + + return true; +} + +bool DiscardableSharedMemory::Unmap() { + if (!shared_memory_.Unmap()) + return false; + + mapped_size_ = 0; + return true; } -bool DiscardableSharedMemory::Lock() { +DiscardableSharedMemory::LockResult DiscardableSharedMemory::Lock( + size_t offset, size_t length) { + DCHECK_EQ(AlignToPageSize(offset), offset); + DCHECK_EQ(AlignToPageSize(length), length); + + // Calls to this function must be synchronized properly. + DFAKE_SCOPED_LOCK(thread_collision_warner_); + DCHECK(shared_memory_.memory()); - // Return false when instance has been purged or not initialized properly by - // checking if |last_known_usage_| is NULL. - if (last_known_usage_.is_null()) - return false; + // We need to successfully acquire the platform independent lock before + // individual pages can be locked. + if (!locked_page_count_) { + // Return false when instance has been purged or not initialized properly + // by checking if |last_known_usage_| is NULL. + if (last_known_usage_.is_null()) + return FAILED; + + SharedState old_state(SharedState::UNLOCKED, last_known_usage_); + SharedState new_state(SharedState::LOCKED, Time()); + SharedState result(subtle::Acquire_CompareAndSwap( + &SharedStateFromSharedMemory(shared_memory_)->value.i, + old_state.value.i, + new_state.value.i)); + if (result.value.u != old_state.value.u) { + // Update |last_known_usage_| in case the above CAS failed because of + // an incorrect timestamp. + last_known_usage_ = result.GetTimestamp(); + return FAILED; + } + } - SharedState old_state(SharedState::UNLOCKED, last_known_usage_); - SharedState new_state(SharedState::LOCKED, Time()); - SharedState result(subtle::Acquire_CompareAndSwap( - &SharedStateFromSharedMemory(shared_memory_)->value.i, - old_state.value.i, - new_state.value.i)); - if (result.value.u == old_state.value.u) - return true; + // Zero for length means "everything onward". + if (!length) + length = AlignToPageSize(mapped_size_) - offset; + + size_t start = offset / base::GetPageSize(); + size_t end = start + length / base::GetPageSize(); + DCHECK_LT(start, end); + DCHECK_LE(end, AlignToPageSize(mapped_size_) / base::GetPageSize()); + + // Add pages to |locked_page_count_|. + // Note: Locking a page that is already locked is an error. + locked_page_count_ += end - start; +#if DCHECK_IS_ON() + // Detect incorrect usage by keeping track of exactly what pages are locked. + for (auto page = start; page < end; ++page) { + auto result = locked_pages_.insert(page); + DCHECK(result.second); + } + DCHECK_EQ(locked_pages_.size(), locked_page_count_); +#endif + +#if defined(OS_ANDROID) + SharedMemoryHandle handle = shared_memory_.handle(); + if (SharedMemory::IsHandleValid(handle)) { + if (ashmem_pin_region( + handle.fd, AlignToPageSize(sizeof(SharedState)) + offset, length)) { + return PURGED; + } + } +#endif - // Update |last_known_usage_| in case the above CAS failed because of - // an incorrect timestamp. - last_known_usage_ = result.GetTimestamp(); - return false; + return SUCCESS; } -void DiscardableSharedMemory::Unlock() { +void DiscardableSharedMemory::Unlock(size_t offset, size_t length) { + DCHECK_EQ(AlignToPageSize(offset), offset); + DCHECK_EQ(AlignToPageSize(length), length); + + // Calls to this function must be synchronized properly. + DFAKE_SCOPED_LOCK(thread_collision_warner_); + + // Zero for length means "everything onward". + if (!length) + length = AlignToPageSize(mapped_size_) - offset; + DCHECK(shared_memory_.memory()); +#if defined(OS_ANDROID) + SharedMemoryHandle handle = shared_memory_.handle(); + if (SharedMemory::IsHandleValid(handle)) { + if (ashmem_unpin_region( + handle.fd, AlignToPageSize(sizeof(SharedState)) + offset, length)) { + DPLOG(ERROR) << "ashmem_unpin_region() failed"; + } + } +#endif + + size_t start = offset / base::GetPageSize(); + size_t end = start + length / base::GetPageSize(); + DCHECK_LT(start, end); + DCHECK_LE(end, AlignToPageSize(mapped_size_) / base::GetPageSize()); + + // Remove pages from |locked_page_count_|. + // Note: Unlocking a page that is not locked is an error. + DCHECK_GE(locked_page_count_, end - start); + locked_page_count_ -= end - start; +#if DCHECK_IS_ON() + // Detect incorrect usage by keeping track of exactly what pages are locked. + for (auto page = start; page < end; ++page) { + auto erased_count = locked_pages_.erase(page); + DCHECK_EQ(1u, erased_count); + } + DCHECK_EQ(locked_pages_.size(), locked_page_count_); +#endif + + // Early out and avoid releasing the platform independent lock if some pages + // are still locked. + if (locked_page_count_) + return; + Time current_time = Now(); DCHECK(!current_time.is_null()); @@ -151,7 +285,7 @@ void DiscardableSharedMemory::Unlock() { // Note: timestamp cannot be NULL as that is a unique value used when // locked or purged. DCHECK(!new_state.GetTimestamp().is_null()); - // Timestamps precision should at least be accurate to the second. + // Timestamp precision should at least be accurate to the second. DCHECK_EQ((new_state.GetTimestamp() - Time::UnixEpoch()).InSeconds(), (current_time - Time::UnixEpoch()).InSeconds()); SharedState result(subtle::Release_CompareAndSwap( @@ -165,10 +299,14 @@ void DiscardableSharedMemory::Unlock() { } void* DiscardableSharedMemory::memory() const { - return SharedStateFromSharedMemory(shared_memory_) + 1; + return reinterpret_cast<uint8*>(shared_memory_.memory()) + + AlignToPageSize(sizeof(SharedState)); } bool DiscardableSharedMemory::Purge(Time current_time) { + // Calls to this function must be synchronized properly. + DFAKE_SCOPED_LOCK(thread_collision_warner_); + // Early out if not mapped. This can happen if the segment was previously // unmapped using a call to Close(). if (!shared_memory_.memory()) @@ -197,22 +335,6 @@ bool DiscardableSharedMemory::Purge(Time current_time) { return true; } -bool DiscardableSharedMemory::PurgeAndTruncate(Time current_time) { - if (!Purge(current_time)) - return false; - -#if defined(OS_POSIX) - // Truncate shared memory to size of SharedState. - SharedMemoryHandle handle = shared_memory_.handle(); - if (SharedMemory::IsHandleValid(handle)) { - if (HANDLE_EINTR(ftruncate(handle.fd, sizeof(SharedState))) != 0) - DPLOG(ERROR) << "ftruncate() failed"; - } -#endif - - return true; -} - bool DiscardableSharedMemory::IsMemoryResident() const { DCHECK(shared_memory_.memory()); @@ -227,6 +349,26 @@ void DiscardableSharedMemory::Close() { shared_memory_.Close(); } +#if defined(DISCARDABLE_SHARED_MEMORY_SHRINKING) +void DiscardableSharedMemory::Shrink() { +#if defined(OS_POSIX) + SharedMemoryHandle handle = shared_memory_.handle(); + if (!SharedMemory::IsHandleValid(handle)) + return; + + // Truncate shared memory to size of SharedState. + if (HANDLE_EINTR( + ftruncate(handle.fd, AlignToPageSize(sizeof(SharedState)))) != 0) { + DPLOG(ERROR) << "ftruncate() failed"; + return; + } + mapped_size_ = 0; +#else + NOTIMPLEMENTED(); +#endif +} +#endif + Time DiscardableSharedMemory::Now() const { return Time::Now(); } diff --git a/chromium/base/memory/discardable_shared_memory.h b/chromium/base/memory/discardable_shared_memory.h index ca2accf1a2e..74bbe8e95f4 100644 --- a/chromium/base/memory/discardable_shared_memory.h +++ b/chromium/base/memory/discardable_shared_memory.h @@ -6,18 +6,35 @@ #define BASE_MEMORY_DISCARDABLE_SHARED_MEMORY_H_ #include "base/base_export.h" +#include "base/logging.h" #include "base/memory/shared_memory.h" +#include "base/threading/thread_collision_warner.h" #include "base/time/time.h" +#if DCHECK_IS_ON() +#include <set> +#endif + +// Define DISCARDABLE_SHARED_MEMORY_SHRINKING if platform supports shrinking +// of discardable shared memory segments. +#if defined(OS_POSIX) && !defined(OS_ANDROID) +#define DISCARDABLE_SHARED_MEMORY_SHRINKING +#endif + namespace base { // Platform abstraction for discardable shared memory. +// +// This class is not thread-safe. Clients are responsible for synchronizing +// access to an instance of this class. class BASE_EXPORT DiscardableSharedMemory { public: + enum LockResult { SUCCESS, PURGED, FAILED }; + DiscardableSharedMemory(); // Create a new DiscardableSharedMemory object from an existing, open shared - // memory file. + // memory file. Memory must be locked. explicit DiscardableSharedMemory(SharedMemoryHandle handle); // Closes any open files. @@ -27,32 +44,48 @@ class BASE_EXPORT DiscardableSharedMemory { // Returns true on success and false on failure. bool CreateAndMap(size_t size); - // Maps the discardable memory into the caller's address space. + // Maps the locked discardable memory into the caller's address space. // Returns true on success, false otherwise. bool Map(size_t size); + // Unmaps the discardable shared memory from the caller's address space. + // Returns true if successful; returns false on error or if the memory is + // not mapped. + bool Unmap(); + // The actual size of the mapped memory (may be larger than requested). - size_t mapped_size() const { return shared_memory_.mapped_size(); } + size_t mapped_size() const { return mapped_size_; } // Returns a shared memory handle for this DiscardableSharedMemory object. SharedMemoryHandle handle() const { return shared_memory_.handle(); } - // Locks the memory so that it will not be purged by the system. Returns - // true if successful and the memory is still resident. Locking can fail - // for three reasons; object might have been purged, our last known usage - // timestamp might be out of date or memory might already be locked. Last - // know usage time is updated to the actual last usage timestamp if memory - // is still resident or 0 if not. - bool Lock(); - - // Unlock previously successfully locked memory. - void Unlock(); + // Locks a range of memory so that it will not be purged by the system. + // The range of memory must be unlocked. The result of trying to lock an + // already locked range is undefined. |offset| and |length| must both be + // a multiple of the page size as returned by GetPageSize(). + // Passing 0 for |length| means "everything onward". + // Returns SUCCESS if range was successfully locked and the memory is still + // resident, PURGED if range was successfully locked but has been purged + // since last time it was locked and FAILED if range could not be locked. + // Locking can fail for two reasons; object might have been purged, our + // last known usage timestamp might be out of date. Last known usage time + // is updated to the actual last usage timestamp if memory is still resident + // or 0 if not. + LockResult Lock(size_t offset, size_t length); + + // Unlock a previously successfully locked range of memory. The range of + // memory must be locked. The result of trying to unlock a not + // previously locked range is undefined. + // |offset| and |length| must both be a multiple of the page size as returned + // by GetPageSize(). + // Passing 0 for |length| means "everything onward". + void Unlock(size_t offset, size_t length); // Gets a pointer to the opened discardable memory space. Discardable memory // must have been mapped via Map(). void* memory() const; - // Returns the last know usage time for DiscardableSharedMemory object. This + // Returns the last known usage time for DiscardableSharedMemory object. This // may be earlier than the "true" usage time when memory has been used by a // different process. Returns NULL time if purged. Time last_known_usage() const { return last_known_usage_; } @@ -62,7 +95,7 @@ class BASE_EXPORT DiscardableSharedMemory { // for two reasons; object might be locked or our last known usage timestamp // might be out of date. Last known usage time is updated to |current_time| // if locked or the actual last usage timestamp if unlocked. It is often - // neccesary to call this function twice for the object to successfully be + // necessary to call this function twice for the object to successfully be // purged. First call, updates |last_known_usage_|. Second call, successfully // purges the object using the updated |last_known_usage_|. // Note: there is no guarantee that multiple calls to this function will @@ -71,12 +104,6 @@ class BASE_EXPORT DiscardableSharedMemory { // each call. bool Purge(Time current_time); - // Purge and release as much memory as possible to the OS. - // Note: The amount of memory that can be released to the OS is platform - // specific. Best case, all but one page is released. Worst case, nothing - // is released. - bool PurgeAndTruncate(Time current_time); - // Returns true if memory is still resident. bool IsMemoryResident() const; @@ -94,11 +121,25 @@ class BASE_EXPORT DiscardableSharedMemory { return shared_memory_.ShareToProcess(process_handle, new_handle); } +#if defined(DISCARDABLE_SHARED_MEMORY_SHRINKING) + // Release as much memory as possible to the OS. The change in size will + // be reflected by the return value of mapped_size(). + void Shrink(); +#endif + private: // Virtual for tests. virtual Time Now() const; SharedMemory shared_memory_; + size_t mapped_size_; + size_t locked_page_count_; +#if DCHECK_IS_ON() + std::set<size_t> locked_pages_; +#endif + // Implementation is not thread-safe but still usable if clients are + // synchronized somehow. Use a collision warner to detect incorrect usage. + DFAKE_MUTEX(thread_collision_warner_); Time last_known_usage_; DISALLOW_COPY_AND_ASSIGN(DiscardableSharedMemory); diff --git a/chromium/base/memory/discardable_shared_memory_unittest.cc b/chromium/base/memory/discardable_shared_memory_unittest.cc index e5174298bfd..91b0b68523a 100644 --- a/chromium/base/memory/discardable_shared_memory_unittest.cc +++ b/chromium/base/memory/discardable_shared_memory_unittest.cc @@ -4,6 +4,7 @@ #include "base/basictypes.h" #include "base/memory/discardable_shared_memory.h" +#include "base/process/process_metrics.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { @@ -20,7 +21,7 @@ class TestDiscardableSharedMemory : public DiscardableSharedMemory { private: // Overriden from DiscardableSharedMemory: - virtual Time Now() const override { return now_; } + Time Now() const override { return now_; } Time now_; }; @@ -60,13 +61,17 @@ TEST(DiscardableSharedMemoryTest, LockAndUnlock) { // Memory is initially locked. Unlock it. memory1.SetNow(Time::FromDoubleT(1)); - memory1.Unlock(); + memory1.Unlock(0, 0); // Lock and unlock memory. - rv = memory1.Lock(); - EXPECT_TRUE(rv); + auto lock_rv = memory1.Lock(0, 0); + EXPECT_EQ(DiscardableSharedMemory::SUCCESS, lock_rv); memory1.SetNow(Time::FromDoubleT(2)); - memory1.Unlock(); + memory1.Unlock(0, 0); + + // Lock again before duplicating and passing ownership to new instance. + lock_rv = memory1.Lock(0, 0); + EXPECT_EQ(DiscardableSharedMemory::SUCCESS, lock_rv); SharedMemoryHandle shared_handle; ASSERT_TRUE( @@ -77,35 +82,21 @@ TEST(DiscardableSharedMemoryTest, LockAndUnlock) { rv = memory2.Map(kDataSize); ASSERT_TRUE(rv); - // Lock first instance again. - rv = memory1.Lock(); - EXPECT_TRUE(rv); - // Unlock second instance. memory2.SetNow(Time::FromDoubleT(3)); - memory2.Unlock(); - - // Lock and unlock second instance. - rv = memory2.Lock(); - EXPECT_TRUE(rv); - memory2.SetNow(Time::FromDoubleT(4)); - memory2.Unlock(); + memory2.Unlock(0, 0); - // Try to lock first instance again. Should fail as first instance has an - // incorrect last know usage time. - rv = memory1.Lock(); - EXPECT_FALSE(rv); + // Lock second instance before passing ownership back to first instance. + lock_rv = memory2.Lock(0, 0); + EXPECT_EQ(DiscardableSharedMemory::SUCCESS, lock_rv); // Memory should still be resident. rv = memory1.IsMemoryResident(); EXPECT_TRUE(rv); - // Second attempt to lock first instance should succeed as last known usage - // time is now correct. - rv = memory1.Lock(); - EXPECT_TRUE(rv); - memory1.SetNow(Time::FromDoubleT(5)); - memory1.Unlock(); + // Unlock first instance. + memory1.SetNow(Time::FromDoubleT(4)); + memory1.Unlock(0, 0); } TEST(DiscardableSharedMemoryTest, Purge) { @@ -129,7 +120,7 @@ TEST(DiscardableSharedMemoryTest, Purge) { EXPECT_FALSE(rv); memory2.SetNow(Time::FromDoubleT(2)); - memory2.Unlock(); + memory2.Unlock(0, 0); ASSERT_TRUE(memory2.IsMemoryResident()); @@ -144,8 +135,8 @@ TEST(DiscardableSharedMemoryTest, Purge) { EXPECT_TRUE(rv); // Lock should fail as memory has been purged. - rv = memory2.Lock(); - EXPECT_FALSE(rv); + auto lock_rv = memory2.Lock(0, 0); + EXPECT_EQ(DiscardableSharedMemory::FAILED, lock_rv); ASSERT_FALSE(memory2.IsMemoryResident()); } @@ -167,12 +158,12 @@ TEST(DiscardableSharedMemoryTest, LastUsed) { ASSERT_TRUE(rv); memory2.SetNow(Time::FromDoubleT(1)); - memory2.Unlock(); + memory2.Unlock(0, 0); EXPECT_EQ(memory2.last_known_usage(), Time::FromDoubleT(1)); - rv = memory2.Lock(); - EXPECT_TRUE(rv); + auto lock_rv = memory2.Lock(0, 0); + EXPECT_EQ(DiscardableSharedMemory::SUCCESS, lock_rv); // This should fail as memory is locked. rv = memory1.Purge(Time::FromDoubleT(2)); @@ -182,7 +173,7 @@ TEST(DiscardableSharedMemoryTest, LastUsed) { EXPECT_EQ(memory1.last_known_usage(), Time::FromDoubleT(2)); memory2.SetNow(Time::FromDoubleT(3)); - memory2.Unlock(); + memory2.Unlock(0, 0); // Usage time should be correct for |memory2| instance. EXPECT_EQ(memory2.last_known_usage(), Time::FromDoubleT(3)); @@ -235,17 +226,129 @@ TEST(DiscardableSharedMemoryTest, LockShouldAlwaysFailAfterSuccessfulPurge) { ASSERT_TRUE(rv); memory2.SetNow(Time::FromDoubleT(1)); - memory2.Unlock(); + memory2.Unlock(0, 0); rv = memory2.Purge(Time::FromDoubleT(2)); EXPECT_TRUE(rv); // Lock should fail as memory has been purged. - rv = memory2.Lock(); + auto lock_rv = memory2.Lock(0, 0); + EXPECT_EQ(DiscardableSharedMemory::FAILED, lock_rv); +} + +TEST(DiscardableSharedMemoryTest, LockAndUnlockRange) { + const uint32 kDataSize = 32; + + uint32 data_size_in_bytes = kDataSize * base::GetPageSize(); + + TestDiscardableSharedMemory memory1; + bool rv = memory1.CreateAndMap(data_size_in_bytes); + ASSERT_TRUE(rv); + + SharedMemoryHandle shared_handle; + ASSERT_TRUE( + memory1.ShareToProcess(GetCurrentProcessHandle(), &shared_handle)); + ASSERT_TRUE(SharedMemory::IsHandleValid(shared_handle)); + + TestDiscardableSharedMemory memory2(shared_handle); + rv = memory2.Map(data_size_in_bytes); + ASSERT_TRUE(rv); + + // Unlock first page. + memory2.SetNow(Time::FromDoubleT(1)); + memory2.Unlock(0, base::GetPageSize()); + + rv = memory1.Purge(Time::FromDoubleT(2)); + EXPECT_FALSE(rv); + + // Lock first page again. + memory2.SetNow(Time::FromDoubleT(3)); + auto lock_rv = memory2.Lock(0, base::GetPageSize()); + EXPECT_NE(DiscardableSharedMemory::FAILED, lock_rv); + + // Unlock first page. + memory2.SetNow(Time::FromDoubleT(4)); + memory2.Unlock(0, base::GetPageSize()); + + rv = memory1.Purge(Time::FromDoubleT(5)); + EXPECT_FALSE(rv); + + // Unlock second page. + memory2.SetNow(Time::FromDoubleT(6)); + memory2.Unlock(base::GetPageSize(), base::GetPageSize()); + + rv = memory1.Purge(Time::FromDoubleT(7)); EXPECT_FALSE(rv); - rv = memory1.Lock(); + + // Unlock anything onwards. + memory2.SetNow(Time::FromDoubleT(8)); + memory2.Unlock(2 * base::GetPageSize(), 0); + + // Memory is unlocked, but our usage timestamp is incorrect. + rv = memory1.Purge(Time::FromDoubleT(9)); EXPECT_FALSE(rv); + + // The failed purge attempt should have updated usage time to the correct + // value. + EXPECT_EQ(Time::FromDoubleT(8), memory1.last_known_usage()); + + // Purge should now succeed. + rv = memory1.Purge(Time::FromDoubleT(10)); + EXPECT_TRUE(rv); +} + +TEST(DiscardableSharedMemoryTest, MappedSize) { + const uint32 kDataSize = 1024; + + TestDiscardableSharedMemory memory; + bool rv = memory.CreateAndMap(kDataSize); + ASSERT_TRUE(rv); + + EXPECT_LE(kDataSize, memory.mapped_size()); + + // Mapped size should be 0 after memory segment has been unmapped. + rv = memory.Unmap(); + EXPECT_TRUE(rv); + EXPECT_EQ(0u, memory.mapped_size()); +} + +TEST(DiscardableSharedMemoryTest, Close) { + const uint32 kDataSize = 1024; + + TestDiscardableSharedMemory memory; + bool rv = memory.CreateAndMap(kDataSize); + ASSERT_TRUE(rv); + + // Mapped size should be unchanged after memory segment has been closed. + memory.Close(); + EXPECT_LE(kDataSize, memory.mapped_size()); + + // Memory is initially locked. Unlock it. + memory.SetNow(Time::FromDoubleT(1)); + memory.Unlock(0, 0); + + // Lock and unlock memory. + auto lock_rv = memory.Lock(0, 0); + EXPECT_EQ(DiscardableSharedMemory::SUCCESS, lock_rv); + memory.SetNow(Time::FromDoubleT(2)); + memory.Unlock(0, 0); +} + +#if defined(DISCARDABLE_SHARED_MEMORY_SHRINKING) +TEST(DiscardableSharedMemoryTest, Shrink) { + const uint32 kDataSize = 1024; + + TestDiscardableSharedMemory memory; + bool rv = memory.CreateAndMap(kDataSize); + ASSERT_TRUE(rv); + + EXPECT_NE(0u, memory.mapped_size()); + + // Mapped size should be 0 after shrinking memory segment. + memory.Shrink(); + EXPECT_EQ(0u, memory.mapped_size()); } +#endif } // namespace } // namespace base diff --git a/chromium/base/memory/manual_constructor.h b/chromium/base/memory/manual_constructor.h index 9275f73b83b..56081a1d3bc 100644 --- a/chromium/base/memory/manual_constructor.h +++ b/chromium/base/memory/manual_constructor.h @@ -18,6 +18,7 @@ #include <stddef.h> +#include "base/compiler_specific.h" #include "base/memory/aligned_memory.h" namespace base { @@ -33,11 +34,7 @@ class ManualConstructor { // Support users creating arrays of ManualConstructor<>s. This ensures that // the array itself has the correct alignment. static void* operator new[](size_t size) { -#if defined(COMPILER_MSVC) - return AlignedAlloc(size, __alignof(Type)); -#else - return AlignedAlloc(size, __alignof__(Type)); -#endif + return AlignedAlloc(size, ALIGNOF(Type)); } static void operator delete[](void* mem) { AlignedFree(mem); @@ -56,56 +53,9 @@ class ManualConstructor { inline Type& operator*() { return *get(); } inline const Type& operator*() const { return *get(); } - // You can pass up to eight constructor arguments as arguments of Init(). - inline void Init() { - new(space_.void_data()) Type; - } - - template <typename T1> - inline void Init(const T1& p1) { - new(space_.void_data()) Type(p1); - } - - template <typename T1, typename T2> - inline void Init(const T1& p1, const T2& p2) { - new(space_.void_data()) Type(p1, p2); - } - - template <typename T1, typename T2, typename T3> - inline void Init(const T1& p1, const T2& p2, const T3& p3) { - new(space_.void_data()) Type(p1, p2, p3); - } - - template <typename T1, typename T2, typename T3, typename T4> - inline void Init(const T1& p1, const T2& p2, const T3& p3, const T4& p4) { - new(space_.void_data()) Type(p1, p2, p3, p4); - } - - template <typename T1, typename T2, typename T3, typename T4, typename T5> - inline void Init(const T1& p1, const T2& p2, const T3& p3, const T4& p4, - const T5& p5) { - new(space_.void_data()) Type(p1, p2, p3, p4, p5); - } - - template <typename T1, typename T2, typename T3, typename T4, typename T5, - typename T6> - inline void Init(const T1& p1, const T2& p2, const T3& p3, const T4& p4, - const T5& p5, const T6& p6) { - new(space_.void_data()) Type(p1, p2, p3, p4, p5, p6); - } - - template <typename T1, typename T2, typename T3, typename T4, typename T5, - typename T6, typename T7> - inline void Init(const T1& p1, const T2& p2, const T3& p3, const T4& p4, - const T5& p5, const T6& p6, const T7& p7) { - new(space_.void_data()) Type(p1, p2, p3, p4, p5, p6, p7); - } - - template <typename T1, typename T2, typename T3, typename T4, typename T5, - typename T6, typename T7, typename T8> - inline void Init(const T1& p1, const T2& p2, const T3& p3, const T4& p4, - const T5& p5, const T6& p6, const T7& p7, const T8& p8) { - new(space_.void_data()) Type(p1, p2, p3, p4, p5, p6, p7, p8); + template <typename... Ts> + inline void Init(const Ts&... params) { + new(space_.void_data()) Type(params...); } inline void Destroy() { @@ -113,11 +63,7 @@ class ManualConstructor { } private: -#if defined(COMPILER_MSVC) - AlignedMemory<sizeof(Type), __alignof(Type)> space_; -#else - AlignedMemory<sizeof(Type), __alignof__(Type)> space_; -#endif + AlignedMemory<sizeof(Type), ALIGNOF(Type)> space_; }; } // namespace base diff --git a/chromium/base/memory/memory_pressure_listener.cc b/chromium/base/memory/memory_pressure_listener.cc index 745c9859a08..6a8ed210a7d 100644 --- a/chromium/base/memory/memory_pressure_listener.cc +++ b/chromium/base/memory/memory_pressure_listener.cc @@ -4,9 +4,9 @@ #include "base/memory/memory_pressure_listener.h" -#include "base/debug/trace_event.h" #include "base/lazy_instance.h" #include "base/observer_list_threadsafe.h" +#include "base/trace_event/trace_event.h" namespace { @@ -51,9 +51,10 @@ void MemoryPressureListener::Notify(MemoryPressureLevel memory_pressure_level) { // static void MemoryPressureListener::NotifyMemoryPressure( MemoryPressureLevel memory_pressure_level) { + DCHECK_NE(memory_pressure_level, MEMORY_PRESSURE_LEVEL_NONE); TRACE_EVENT1("memory", "MemoryPressureListener::NotifyMemoryPressure", "level", memory_pressure_level); - g_observers.Get().Notify(&MemoryPressureListener::Notify, + g_observers.Get().Notify(FROM_HERE, &MemoryPressureListener::Notify, memory_pressure_level); } diff --git a/chromium/base/memory/memory_pressure_listener.h b/chromium/base/memory/memory_pressure_listener.h index f586209c5bc..6adaeeed7fd 100644 --- a/chromium/base/memory/memory_pressure_listener.h +++ b/chromium/base/memory/memory_pressure_listener.h @@ -3,15 +3,12 @@ // found in the LICENSE file. // MemoryPressure provides static APIs for handling memory pressure on -// platforms that have such signals, such as Android. +// platforms that have such signals, such as Android and ChromeOS. // The app will try to discard buffers that aren't deemed essential (individual // modules will implement their own policy). -// -// Refer to memory_pressure_level_list.h for information about what sorts of -// signals can be sent under what conditions. -#ifndef BASE_MEMORY_PRESSURE_LISTENER_H_ -#define BASE_MEMORY_PRESSURE_LISTENER_H_ +#ifndef BASE_MEMORY_MEMORY_PRESSURE_LISTENER_H_ +#define BASE_MEMORY_MEMORY_PRESSURE_LISTENER_H_ #include "base/base_export.h" #include "base/basictypes.h" @@ -26,12 +23,11 @@ namespace base { // the listener. // Note that even on the same thread, the callback is not guaranteed to be // called synchronously within the system memory pressure broadcast. -// Please see notes on memory_pressure_level_list.h: some levels are absolutely -// critical, and if not enough memory is returned to the system, it'll -// potentially kill the app, and then later the app will have to be +// Please see notes in MemoryPressureLevel enum below: some levels are +// absolutely critical, and if not enough memory is returned to the system, +// it'll potentially kill the app, and then later the app will have to be // cold-started. // -// // Example: // // void OnMemoryPressure(MemoryPressureLevel memory_pressure_level) { @@ -52,6 +48,11 @@ class BASE_EXPORT MemoryPressureListener { // A Java counterpart will be generated for this enum. // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.base enum MemoryPressureLevel { + // No problems, there is enough memory to use. This event is not sent via + // callback, but the enum is used in other places to find out the current + // state of the system. + MEMORY_PRESSURE_LEVEL_NONE = -1, + // Modules are advised to free buffers that are cheap to re-allocate and not // immediately needed. MEMORY_PRESSURE_LEVEL_MODERATE = 0, @@ -81,4 +82,4 @@ class BASE_EXPORT MemoryPressureListener { } // namespace base -#endif // BASE_MEMORY_PRESSURE_LISTENER_H_ +#endif // BASE_MEMORY_MEMORY_PRESSURE_LISTENER_H_ diff --git a/chromium/base/memory/memory_pressure_monitor.cc b/chromium/base/memory/memory_pressure_monitor.cc new file mode 100644 index 00000000000..00633f1dd61 --- /dev/null +++ b/chromium/base/memory/memory_pressure_monitor.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/memory/memory_pressure_monitor.h" + +#include "base/logging.h" + +namespace base { +namespace { + +MemoryPressureMonitor* g_monitor = nullptr; + +} // namespace + +MemoryPressureMonitor::MemoryPressureMonitor() { + DCHECK(!g_monitor); + g_monitor = this; +} + +MemoryPressureMonitor::~MemoryPressureMonitor() { + DCHECK(g_monitor); + g_monitor = nullptr; +} + +// static +MemoryPressureMonitor* MemoryPressureMonitor::Get() { + return g_monitor; +} + +} // namespace base diff --git a/chromium/base/memory/memory_pressure_monitor.h b/chromium/base/memory/memory_pressure_monitor.h new file mode 100644 index 00000000000..90c94209653 --- /dev/null +++ b/chromium/base/memory/memory_pressure_monitor.h @@ -0,0 +1,43 @@ +// 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_MEMORY_PRESSURE_MONITOR_H_ +#define BASE_MEMORY_MEMORY_PRESSURE_MONITOR_H_ + +#include "base/base_export.h" +#include "base/memory/memory_pressure_listener.h" + +namespace base { + +// TODO(chrisha): Make this a concrete class with per-OS implementations rather +// than an abstract base class. + +// Declares the interface for a MemoryPressureMonitor. There are multiple +// OS specific implementations of this class. An instance of the memory +// pressure observer is created at the process level, tracks memory usage, and +// pushes memory state change notifications to the static function +// base::MemoryPressureListener::NotifyMemoryPressure. This is turn notifies +// all MemoryPressureListener instances via a callback. +class BASE_EXPORT MemoryPressureMonitor { + public: + using MemoryPressureLevel = base::MemoryPressureListener::MemoryPressureLevel; + + virtual ~MemoryPressureMonitor(); + + // Return the singleton MemoryPressureMonitor. + static MemoryPressureMonitor* Get(); + + // Returns the currently observed memory pressure. + virtual MemoryPressureLevel GetCurrentPressureLevel() const = 0; + + protected: + MemoryPressureMonitor(); + + private: + DISALLOW_COPY_AND_ASSIGN(MemoryPressureMonitor); +}; + +} // namespace base + +#endif // BASE_MEMORY_MEMORY_PRESSURE_MONITOR_H_ diff --git a/chromium/base/memory/raw_scoped_refptr_mismatch_checker.h b/chromium/base/memory/raw_scoped_refptr_mismatch_checker.h index 7974f3047b2..01905588058 100644 --- a/chromium/base/memory/raw_scoped_refptr_mismatch_checker.h +++ b/chromium/base/memory/raw_scoped_refptr_mismatch_checker.h @@ -47,30 +47,30 @@ struct ParamsUseScopedRefptrCorrectly { }; template <> -struct ParamsUseScopedRefptrCorrectly<Tuple0> { +struct ParamsUseScopedRefptrCorrectly<Tuple<>> { enum { value = 1 }; }; template <typename A> -struct ParamsUseScopedRefptrCorrectly<Tuple1<A> > { +struct ParamsUseScopedRefptrCorrectly<Tuple<A>> { enum { value = !NeedsScopedRefptrButGetsRawPtr<A>::value }; }; template <typename A, typename B> -struct ParamsUseScopedRefptrCorrectly<Tuple2<A, B> > { +struct ParamsUseScopedRefptrCorrectly<Tuple<A, B>> { enum { value = !(NeedsScopedRefptrButGetsRawPtr<A>::value || NeedsScopedRefptrButGetsRawPtr<B>::value) }; }; template <typename A, typename B, typename C> -struct ParamsUseScopedRefptrCorrectly<Tuple3<A, B, C> > { +struct ParamsUseScopedRefptrCorrectly<Tuple<A, B, C>> { enum { value = !(NeedsScopedRefptrButGetsRawPtr<A>::value || NeedsScopedRefptrButGetsRawPtr<B>::value || NeedsScopedRefptrButGetsRawPtr<C>::value) }; }; template <typename A, typename B, typename C, typename D> -struct ParamsUseScopedRefptrCorrectly<Tuple4<A, B, C, D> > { +struct ParamsUseScopedRefptrCorrectly<Tuple<A, B, C, D>> { enum { value = !(NeedsScopedRefptrButGetsRawPtr<A>::value || NeedsScopedRefptrButGetsRawPtr<B>::value || NeedsScopedRefptrButGetsRawPtr<C>::value || @@ -78,7 +78,7 @@ struct ParamsUseScopedRefptrCorrectly<Tuple4<A, B, C, D> > { }; template <typename A, typename B, typename C, typename D, typename E> -struct ParamsUseScopedRefptrCorrectly<Tuple5<A, B, C, D, E> > { +struct ParamsUseScopedRefptrCorrectly<Tuple<A, B, C, D, E>> { enum { value = !(NeedsScopedRefptrButGetsRawPtr<A>::value || NeedsScopedRefptrButGetsRawPtr<B>::value || NeedsScopedRefptrButGetsRawPtr<C>::value || @@ -88,7 +88,7 @@ struct ParamsUseScopedRefptrCorrectly<Tuple5<A, B, C, D, E> > { template <typename A, typename B, typename C, typename D, typename E, typename F> -struct ParamsUseScopedRefptrCorrectly<Tuple6<A, B, C, D, E, F> > { +struct ParamsUseScopedRefptrCorrectly<Tuple<A, B, C, D, E, F>> { enum { value = !(NeedsScopedRefptrButGetsRawPtr<A>::value || NeedsScopedRefptrButGetsRawPtr<B>::value || NeedsScopedRefptrButGetsRawPtr<C>::value || @@ -99,7 +99,7 @@ struct ParamsUseScopedRefptrCorrectly<Tuple6<A, B, C, D, E, F> > { template <typename A, typename B, typename C, typename D, typename E, typename F, typename G> -struct ParamsUseScopedRefptrCorrectly<Tuple7<A, B, C, D, E, F, G> > { +struct ParamsUseScopedRefptrCorrectly<Tuple<A, B, C, D, E, F, G>> { enum { value = !(NeedsScopedRefptrButGetsRawPtr<A>::value || NeedsScopedRefptrButGetsRawPtr<B>::value || NeedsScopedRefptrButGetsRawPtr<C>::value || @@ -111,7 +111,7 @@ struct ParamsUseScopedRefptrCorrectly<Tuple7<A, B, C, D, E, F, G> > { template <typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H> -struct ParamsUseScopedRefptrCorrectly<Tuple8<A, B, C, D, E, F, G, H> > { +struct ParamsUseScopedRefptrCorrectly<Tuple<A, B, C, D, E, F, G, H>> { enum { value = !(NeedsScopedRefptrButGetsRawPtr<A>::value || NeedsScopedRefptrButGetsRawPtr<B>::value || NeedsScopedRefptrButGetsRawPtr<C>::value || diff --git a/chromium/base/memory/ref_counted.h b/chromium/base/memory/ref_counted.h index 7869e72d111..5f94b4c37ae 100644 --- a/chromium/base/memory/ref_counted.h +++ b/chromium/base/memory/ref_counted.h @@ -14,13 +14,10 @@ #ifndef NDEBUG #include "base/logging.h" #endif +#include "base/move.h" #include "base/threading/thread_collision_warner.h" #include "build/build_config.h" -#if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_IOS) || defined(OS_ANDROID) -#define DISABLE_SCOPED_REFPTR_CONVERSION_OPERATOR -#endif - namespace base { namespace subtle { @@ -268,6 +265,7 @@ class RefCountedData // template <class T> class scoped_refptr { + TYPE_WITH_MOVE_CONSTRUCTOR_FOR_CPP_03(scoped_refptr) public: typedef T element_type; @@ -290,6 +288,11 @@ class scoped_refptr { AddRef(ptr_); } + template <typename U> + scoped_refptr(scoped_refptr<U>&& r) : ptr_(r.get()) { + r.ptr_ = nullptr; + } + ~scoped_refptr() { if (ptr_) Release(ptr_); @@ -297,12 +300,6 @@ class scoped_refptr { T* get() const { return ptr_; } -#if !defined(DISABLE_SCOPED_REFPTR_CONVERSION_OPERATOR) - // Allow scoped_refptr<C> to be used in boolean expression - // and comparison operations. - operator T*() const { return ptr_; } -#endif - T& operator*() const { assert(ptr_ != NULL); return *ptr_; @@ -333,6 +330,17 @@ class scoped_refptr { return *this = r.get(); } + scoped_refptr<T>& operator=(scoped_refptr<T>&& r) { + scoped_refptr<T>(r.Pass()).swap(*this); + return *this; + } + + template <typename U> + scoped_refptr<T>& operator=(scoped_refptr<U>&& r) { + scoped_refptr<T>(r.Pass()).swap(*this); + return *this; + } + void swap(T** pp) { T* p = ptr_; ptr_ = *pp; @@ -343,7 +351,21 @@ class scoped_refptr { swap(&r.ptr_); } -#if defined(DISABLE_SCOPED_REFPTR_CONVERSION_OPERATOR) + private: + template <typename U> friend class scoped_refptr; + + // Allow scoped_refptr<T> to be used in boolean expressions, but not + // implicitly convertible to a real bool (which is dangerous). + // + // Note that this trick is only safe when the == and != operators + // are declared explicitly, as otherwise "refptr1 == refptr2" + // will compile but do the wrong thing (i.e., convert to Testable + // and then do the comparison). + typedef T* scoped_refptr::*Testable; + + public: + operator Testable() const { return ptr_ ? &scoped_refptr::ptr_ : nullptr; } + template <typename U> bool operator==(const scoped_refptr<U>& rhs) const { return ptr_ == rhs.get(); @@ -358,7 +380,6 @@ class scoped_refptr { bool operator<(const scoped_refptr<U>& rhs) const { return ptr_ < rhs.get(); } -#endif protected: T* ptr_; @@ -389,8 +410,8 @@ scoped_refptr<T> make_scoped_refptr(T* t) { return scoped_refptr<T>(t); } -#if defined(DISABLE_SCOPED_REFPTR_CONVERSION_OPERATOR) -// Temporary operator overloads to facilitate the transition... +// Temporary operator overloads to facilitate the transition. See +// https://crbug.com/110610. template <typename T, typename U> bool operator==(const scoped_refptr<T>& lhs, const U* rhs) { return lhs.get() == rhs; @@ -415,6 +436,5 @@ template <typename T> std::ostream& operator<<(std::ostream& out, const scoped_refptr<T>& p) { return out << p.get(); } -#endif // defined(DISABLE_SCOPED_REFPTR_CONVERSION_OPERATOR) #endif // BASE_MEMORY_REF_COUNTED_H_ diff --git a/chromium/base/memory/ref_counted_delete_on_message_loop.h b/chromium/base/memory/ref_counted_delete_on_message_loop.h index 7b898ac00dd..6a109e81e00 100644 --- a/chromium/base/memory/ref_counted_delete_on_message_loop.h +++ b/chromium/base/memory/ref_counted_delete_on_message_loop.h @@ -8,7 +8,9 @@ #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 { @@ -18,7 +20,7 @@ namespace base { // Sample usage: // class Foo : public RefCountedDeleteOnMessageLoop<Foo> { // -// Foo(const scoped_refptr<MessageLoopProxy>& loop) +// Foo(const scoped_refptr<SingleThreadTaskRunner>& loop) // : RefCountedDeleteOnMessageLoop<Foo>(loop) { // ... // } @@ -30,12 +32,18 @@ namespace base { // ~Foo(); // }; +// TODO(skyostil): Rename this to RefCountedDeleteOnTaskRunner. template <class T> class RefCountedDeleteOnMessageLoop : public subtle::RefCountedThreadSafeBase { public: + // This constructor will accept a MessageL00pProxy object, but new code should + // prefer a SingleThreadTaskRunner. A SingleThreadTaskRunner for the + // MessageLoop on the current thread can be acquired by calling + // MessageLoop::current()->task_runner(). RefCountedDeleteOnMessageLoop( - const scoped_refptr<MessageLoopProxy>& proxy) : proxy_(proxy) { - DCHECK(proxy_.get()); + const scoped_refptr<SingleThreadTaskRunner>& task_runner) + : task_runner_(task_runner) { + DCHECK(task_runner_); } void AddRef() const { @@ -53,13 +61,13 @@ class RefCountedDeleteOnMessageLoop : public subtle::RefCountedThreadSafeBase { void DestructOnMessageLoop() const { const T* t = static_cast<const T*>(this); - if (proxy_->BelongsToCurrentThread()) + if (task_runner_->BelongsToCurrentThread()) delete t; else - proxy_->DeleteSoon(FROM_HERE, t); + task_runner_->DeleteSoon(FROM_HERE, t); } - scoped_refptr<MessageLoopProxy> proxy_; + scoped_refptr<SingleThreadTaskRunner> task_runner_; private: DISALLOW_COPY_AND_ASSIGN(RefCountedDeleteOnMessageLoop); diff --git a/chromium/base/memory/ref_counted_unittest.cc b/chromium/base/memory/ref_counted_unittest.cc index 7e73bde1155..6f8e599cbdc 100644 --- a/chromium/base/memory/ref_counted_unittest.cc +++ b/chromium/base/memory/ref_counted_unittest.cc @@ -10,9 +10,19 @@ namespace { class SelfAssign : public base::RefCounted<SelfAssign> { + protected: + virtual ~SelfAssign() {} + + private: friend class base::RefCounted<SelfAssign>; +}; + +class Derived : public SelfAssign { + protected: + ~Derived() override {} - ~SelfAssign() {} + private: + friend class base::RefCounted<Derived>; }; class CheckDerivedMemberAccess : public scoped_refptr<SelfAssign> { @@ -30,19 +40,71 @@ class ScopedRefPtrToSelf : public base::RefCounted<ScopedRefPtrToSelf> { static bool was_destroyed() { return was_destroyed_; } - void SelfDestruct() { self_ptr_ = NULL; } + static void reset_was_destroyed() { was_destroyed_ = false; } + + scoped_refptr<ScopedRefPtrToSelf> self_ptr_; private: friend class base::RefCounted<ScopedRefPtrToSelf>; ~ScopedRefPtrToSelf() { was_destroyed_ = true; } static bool was_destroyed_; - - scoped_refptr<ScopedRefPtrToSelf> self_ptr_; }; bool ScopedRefPtrToSelf::was_destroyed_ = false; +class ScopedRefPtrCountBase : public base::RefCounted<ScopedRefPtrCountBase> { + public: + ScopedRefPtrCountBase() { ++constructor_count_; } + + static int constructor_count() { return constructor_count_; } + + static int destructor_count() { return destructor_count_; } + + static void reset_count() { + constructor_count_ = 0; + destructor_count_ = 0; + } + + protected: + virtual ~ScopedRefPtrCountBase() { ++destructor_count_; } + + private: + friend class base::RefCounted<ScopedRefPtrCountBase>; + + static int constructor_count_; + static int destructor_count_; +}; + +int ScopedRefPtrCountBase::constructor_count_ = 0; +int ScopedRefPtrCountBase::destructor_count_ = 0; + +class ScopedRefPtrCountDerived : public ScopedRefPtrCountBase { + public: + ScopedRefPtrCountDerived() { ++constructor_count_; } + + static int constructor_count() { return constructor_count_; } + + static int destructor_count() { return destructor_count_; } + + static void reset_count() { + constructor_count_ = 0; + destructor_count_ = 0; + } + + protected: + ~ScopedRefPtrCountDerived() override { ++destructor_count_; } + + private: + friend class base::RefCounted<ScopedRefPtrCountDerived>; + + static int constructor_count_; + static int destructor_count_; +}; + +int ScopedRefPtrCountDerived::constructor_count_ = 0; +int ScopedRefPtrCountDerived::destructor_count_ = 0; + } // end namespace TEST(RefCountedUnitTest, TestSelfAssignment) { @@ -56,10 +118,24 @@ TEST(RefCountedUnitTest, ScopedRefPtrMemberAccess) { CheckDerivedMemberAccess check; } -TEST(RefCountedUnitTest, ScopedRefPtrToSelf) { +TEST(RefCountedUnitTest, ScopedRefPtrToSelfPointerAssignment) { + ScopedRefPtrToSelf::reset_was_destroyed(); + ScopedRefPtrToSelf* check = new ScopedRefPtrToSelf(); EXPECT_FALSE(ScopedRefPtrToSelf::was_destroyed()); - check->SelfDestruct(); + check->self_ptr_ = nullptr; + EXPECT_TRUE(ScopedRefPtrToSelf::was_destroyed()); +} + +TEST(RefCountedUnitTest, ScopedRefPtrToSelfMoveAssignment) { + ScopedRefPtrToSelf::reset_was_destroyed(); + + ScopedRefPtrToSelf* check = new ScopedRefPtrToSelf(); + EXPECT_FALSE(ScopedRefPtrToSelf::was_destroyed()); + // Releasing |check->self_ptr_| will delete |check|. + // The move assignment operator must assign |check->self_ptr_| first then + // release |check->self_ptr_|. + check->self_ptr_ = scoped_refptr<ScopedRefPtrToSelf>(); EXPECT_TRUE(ScopedRefPtrToSelf::was_destroyed()); } @@ -72,3 +148,315 @@ TEST(RefCountedUnitTest, ScopedRefPtrToOpaque) { base::TestOpaqueRefCounted(p); base::TestOpaqueRefCounted(q); } + +TEST(RefCountedUnitTest, BooleanTesting) { + scoped_refptr<SelfAssign> p; + EXPECT_FALSE(p); + p = new SelfAssign; + EXPECT_TRUE(p); +} + +TEST(RefCountedUnitTest, Equality) { + scoped_refptr<SelfAssign> p1(new SelfAssign); + scoped_refptr<SelfAssign> p2(new SelfAssign); + + EXPECT_EQ(p1, p1); + EXPECT_EQ(p2, p2); + + EXPECT_NE(p1, p2); + EXPECT_NE(p2, p1); +} + +TEST(RefCountedUnitTest, ConvertibleEquality) { + scoped_refptr<Derived> p1(new Derived); + scoped_refptr<SelfAssign> p2; + + EXPECT_NE(p1, p2); + EXPECT_NE(p2, p1); + + p2 = p1; + + EXPECT_EQ(p1, p2); + EXPECT_EQ(p2, p1); +} + +TEST(RefCountedUnitTest, SelfMoveAssignment) { + ScopedRefPtrCountBase::reset_count(); + + { + ScopedRefPtrCountBase *raw = new ScopedRefPtrCountBase(); + scoped_refptr<ScopedRefPtrCountBase> p(raw); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + + p = p.Pass(); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + EXPECT_EQ(raw, p.get()); + + // p goes out of scope. + } + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); +} + +TEST(RefCountedUnitTest, MoveAssignment1) { + ScopedRefPtrCountBase::reset_count(); + + { + ScopedRefPtrCountBase *raw = new ScopedRefPtrCountBase(); + scoped_refptr<ScopedRefPtrCountBase> p1(raw); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + + { + scoped_refptr<ScopedRefPtrCountBase> p2; + + p2 = p1.Pass(); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + EXPECT_EQ(nullptr, p1.get()); + EXPECT_EQ(raw, p2.get()); + + // p2 goes out of scope. + } + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); + + // p1 goes out of scope. + } + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); +} + +TEST(RefCountedUnitTest, MoveAssignment2) { + ScopedRefPtrCountBase::reset_count(); + + { + ScopedRefPtrCountBase *raw = new ScopedRefPtrCountBase(); + scoped_refptr<ScopedRefPtrCountBase> p1; + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + + { + scoped_refptr<ScopedRefPtrCountBase> p2(raw); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + + p1 = p2.Pass(); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + EXPECT_EQ(raw, p1.get()); + EXPECT_EQ(nullptr, p2.get()); + + // p2 goes out of scope. + } + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + + // p1 goes out of scope. + } + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); +} + +TEST(RefCountedUnitTest, MoveAssignmentSameInstance1) { + ScopedRefPtrCountBase::reset_count(); + + { + ScopedRefPtrCountBase *raw = new ScopedRefPtrCountBase(); + scoped_refptr<ScopedRefPtrCountBase> p1(raw); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + + { + scoped_refptr<ScopedRefPtrCountBase> p2(p1); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + + p1 = p2.Pass(); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + EXPECT_EQ(raw, p1.get()); + EXPECT_EQ(nullptr, p2.get()); + + // p2 goes out of scope. + } + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + + // p1 goes out of scope. + } + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); +} + +TEST(RefCountedUnitTest, MoveAssignmentSameInstance2) { + ScopedRefPtrCountBase::reset_count(); + + { + ScopedRefPtrCountBase *raw = new ScopedRefPtrCountBase(); + scoped_refptr<ScopedRefPtrCountBase> p1(raw); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + + { + scoped_refptr<ScopedRefPtrCountBase> p2(p1); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + + p2 = p1.Pass(); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + EXPECT_EQ(nullptr, p1.get()); + EXPECT_EQ(raw, p2.get()); + + // p2 goes out of scope. + } + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); + + // p1 goes out of scope. + } + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); +} + +TEST(RefCountedUnitTest, MoveAssignmentDifferentInstances) { + ScopedRefPtrCountBase::reset_count(); + + { + ScopedRefPtrCountBase *raw1 = new ScopedRefPtrCountBase(); + scoped_refptr<ScopedRefPtrCountBase> p1(raw1); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + + { + ScopedRefPtrCountBase *raw2 = new ScopedRefPtrCountBase(); + scoped_refptr<ScopedRefPtrCountBase> p2(raw2); + EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + + p1 = p2.Pass(); + EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); + EXPECT_EQ(raw2, p1.get()); + EXPECT_EQ(nullptr, p2.get()); + + // p2 goes out of scope. + } + EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); + + // p1 goes out of scope. + } + EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(2, ScopedRefPtrCountBase::destructor_count()); +} + +TEST(RefCountedUnitTest, MoveAssignmentDerived) { + ScopedRefPtrCountBase::reset_count(); + ScopedRefPtrCountDerived::reset_count(); + + { + ScopedRefPtrCountBase *raw1 = new ScopedRefPtrCountBase(); + scoped_refptr<ScopedRefPtrCountBase> p1(raw1); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountDerived::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountDerived::destructor_count()); + + { + ScopedRefPtrCountDerived *raw2 = new ScopedRefPtrCountDerived(); + scoped_refptr<ScopedRefPtrCountDerived> p2(raw2); + EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountDerived::destructor_count()); + + p1 = p2.Pass(); + EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountDerived::destructor_count()); + EXPECT_EQ(raw2, p1.get()); + EXPECT_EQ(nullptr, p2.get()); + + // p2 goes out of scope. + } + EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountDerived::destructor_count()); + + // p1 goes out of scope. + } + EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(2, ScopedRefPtrCountBase::destructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountDerived::destructor_count()); +} + +TEST(RefCountedUnitTest, MoveConstructor) { + ScopedRefPtrCountBase::reset_count(); + + { + ScopedRefPtrCountBase *raw = new ScopedRefPtrCountBase(); + scoped_refptr<ScopedRefPtrCountBase> p1(raw); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + + { + scoped_refptr<ScopedRefPtrCountBase> p2(p1.Pass()); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + EXPECT_EQ(nullptr, p1.get()); + EXPECT_EQ(raw, p2.get()); + + // p2 goes out of scope. + } + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); + + // p1 goes out of scope. + } + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); +} + +TEST(RefCountedUnitTest, MoveConstructorDerived) { + ScopedRefPtrCountBase::reset_count(); + ScopedRefPtrCountDerived::reset_count(); + + { + ScopedRefPtrCountDerived *raw1 = new ScopedRefPtrCountDerived(); + scoped_refptr<ScopedRefPtrCountDerived> p1(raw1); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountDerived::destructor_count()); + + { + scoped_refptr<ScopedRefPtrCountBase> p2(p1.Pass()); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountDerived::destructor_count()); + EXPECT_EQ(nullptr, p1.get()); + EXPECT_EQ(raw1, p2.get()); + + // p2 goes out of scope. + } + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountDerived::destructor_count()); + + // p1 goes out of scope. + } + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountDerived::destructor_count()); +} + diff --git a/chromium/base/memory/scoped_open_process.h b/chromium/base/memory/scoped_open_process.h deleted file mode 100644 index 8bb19e241ec..00000000000 --- a/chromium/base/memory/scoped_open_process.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_MEMORY_SCOPED_OPEN_PROCESS_H_ -#define BASE_MEMORY_SCOPED_OPEN_PROCESS_H_ - -#include "base/process/process_handle.h" - -namespace base { - -// A class that opens a process from its process id and closes it when the -// instance goes out of scope. -class ScopedOpenProcess { - public: - ScopedOpenProcess() : handle_(kNullProcessHandle) { - } - - // Automatically close the process. - ~ScopedOpenProcess() { - Close(); - } - - // Open a new process by pid. Closes any previously opened process (even if - // opening the new one fails). - bool Open(ProcessId pid) { - Close(); - return OpenProcessHandle(pid, &handle_); - } - - // Close the previously opened process. - void Close() { - if (handle_ == kNullProcessHandle) - return; - - CloseProcessHandle(handle_); - handle_ = kNullProcessHandle; - } - - ProcessHandle handle() const { return handle_; } - - private: - ProcessHandle handle_; - DISALLOW_COPY_AND_ASSIGN(ScopedOpenProcess); -}; -} // namespace base - -#endif // BASE_MEMORY_SCOPED_OPEN_PROCESS_H_ diff --git a/chromium/base/memory/scoped_ptr.h b/chromium/base/memory/scoped_ptr.h index ae9eb0fee8b..987ccfa804e 100644 --- a/chromium/base/memory/scoped_ptr.h +++ b/chromium/base/memory/scoped_ptr.h @@ -85,6 +85,7 @@ #include <stdlib.h> #include <algorithm> // For std::swap(). +#include <iosfwd> #include "base/basictypes.h" #include "base/compiler_specific.h" @@ -585,4 +586,9 @@ scoped_ptr<T> make_scoped_ptr(T* ptr) { return scoped_ptr<T>(ptr); } +template <typename T> +std::ostream& operator<<(std::ostream& out, const scoped_ptr<T>& p) { + return out << p.get(); +} + #endif // BASE_MEMORY_SCOPED_PTR_H_ diff --git a/chromium/base/memory/scoped_ptr_unittest.cc b/chromium/base/memory/scoped_ptr_unittest.cc index 3f169a7fa2a..766f4444001 100644 --- a/chromium/base/memory/scoped_ptr_unittest.cc +++ b/chromium/base/memory/scoped_ptr_unittest.cc @@ -4,6 +4,8 @@ #include "base/memory/scoped_ptr.h" +#include <sstream> + #include "base/basictypes.h" #include "base/bind.h" #include "base/callback.h" @@ -669,10 +671,25 @@ TEST(ScopedPtrTest, SelfResetAbortsWithCustomDeleter) { TEST(ScopedPtrTest, SelfResetWithCustomDeleterOptOut) { // A custom deleter should be able to opt out of self-reset abort behavior. struct NoOpDeleter { +#if !defined(NDEBUG) typedef void AllowSelfReset; +#endif inline void operator()(int*) {} }; scoped_ptr<int> owner(new int); scoped_ptr<int, NoOpDeleter> x(owner.get()); x.reset(x.get()); } + +// Logging a scoped_ptr<T> to an ostream shouldn't convert it to a boolean +// value first. +TEST(ScopedPtrTest, LoggingDoesntConvertToBoolean) { + scoped_ptr<int> x(new int); + std::stringstream s1; + s1 << x; + + std::stringstream s2; + s2 << x.get(); + + EXPECT_EQ(s2.str(), s1.str()); +} diff --git a/chromium/base/memory/scoped_vector.h b/chromium/base/memory/scoped_vector.h index 1b30f635ca1..173ea5a1d63 100644 --- a/chromium/base/memory/scoped_vector.h +++ b/chromium/base/memory/scoped_vector.h @@ -9,6 +9,7 @@ #include "base/basictypes.h" #include "base/logging.h" +#include "base/memory/scoped_ptr.h" #include "base/move.h" #include "base/stl_util.h" @@ -64,6 +65,7 @@ class ScopedVector { reference back() { return v_.back(); } void push_back(T* elem) { v_.push_back(elem); } + void push_back(scoped_ptr<T> elem) { v_.push_back(elem.release()); } void pop_back() { DCHECK(!empty()); diff --git a/chromium/base/memory/scoped_vector_unittest.cc b/chromium/base/memory/scoped_vector_unittest.cc index b60ca14ab5c..220cfb04687 100644 --- a/chromium/base/memory/scoped_vector_unittest.cc +++ b/chromium/base/memory/scoped_vector_unittest.cc @@ -308,4 +308,17 @@ TEST(ScopedVectorTest, InsertRange) { EXPECT_EQ(LC_CONSTRUCTED, it->life_cycle_state()); } +// Assertions for push_back(scoped_ptr). +TEST(ScopedVectorTest, PushBackScopedPtr) { + int delete_counter = 0; + scoped_ptr<DeleteCounter> elem(new DeleteCounter(&delete_counter)); + EXPECT_EQ(0, delete_counter); + { + ScopedVector<DeleteCounter> v; + v.push_back(elem.Pass()); + EXPECT_EQ(0, delete_counter); + } + EXPECT_EQ(1, delete_counter); +} + } // namespace diff --git a/chromium/base/memory/shared_memory.h b/chromium/base/memory/shared_memory.h index 7254950feb0..d76e01c6e57 100644 --- a/chromium/base/memory/shared_memory.h +++ b/chromium/base/memory/shared_memory.h @@ -200,7 +200,8 @@ class BASE_EXPORT SharedMemory { SharedMemoryId id() const { return inode_; } #endif - // Closes the open shared memory segment. + // Closes the open shared memory segment. The memory will remain mapped if + // it was previously mapped. // It is safe to call Close repeatedly. void Close(); diff --git a/chromium/base/memory/shared_memory_nacl.cc b/chromium/base/memory/shared_memory_nacl.cc index 39625ee65b5..8435b2ba295 100644 --- a/chromium/base/memory/shared_memory_nacl.cc +++ b/chromium/base/memory/shared_memory_nacl.cc @@ -46,6 +46,7 @@ SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only, } SharedMemory::~SharedMemory() { + Unmap(); Close(); } @@ -125,7 +126,6 @@ SharedMemoryHandle SharedMemory::handle() const { } void SharedMemory::Close() { - Unmap(); if (mapped_file_ > 0) { if (close(mapped_file_) < 0) DPLOG(ERROR) << "close"; @@ -159,8 +159,10 @@ bool SharedMemory::ShareToProcessCommon(ProcessHandle process, new_handle->fd = new_fd; new_handle->auto_close = true; - if (close_self) + if (close_self) { + Unmap(); Close(); + } return true; } diff --git a/chromium/base/memory/shared_memory_posix.cc b/chromium/base/memory/shared_memory_posix.cc index 0358e63e338..d6c290fa016 100644 --- a/chromium/base/memory/shared_memory_posix.cc +++ b/chromium/base/memory/shared_memory_posix.cc @@ -16,6 +16,7 @@ #include "base/lazy_instance.h" #include "base/logging.h" #include "base/process/process_metrics.h" +#include "base/profiler/scoped_tracker.h" #include "base/safe_strerror_posix.h" #include "base/strings/utf_string_conversions.h" #include "base/synchronization/lock.h" @@ -80,6 +81,7 @@ SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only, } SharedMemory::~SharedMemory() { + Unmap(); Close(); } @@ -117,6 +119,11 @@ bool SharedMemory::CreateAndMapAnonymous(size_t size) { // 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; @@ -139,11 +146,22 @@ bool SharedMemory::Create(const SharedMemoryCreateOptions& options) { // Q: Why not use the shm_open() etc. APIs? // A: Because they're limited to 4mb on OS X. FFFFFFFUUUUUUUUUUU FilePath directory; - if (GetShmemTempDir(options.executable, &directory)) + if (GetShmemTempDir(options.executable, &directory)) { + // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466437 + // is fixed. + tracked_objects::ScopedTracker tracking_profile2( + FROM_HERE_WITH_EXPLICIT_FUNCTION( + "466437 SharedMemory::Create::OpenTemporaryFile")); fp.reset(CreateAndOpenTemporaryFileInDir(directory, &path)); + } if (fp) { if (options.share_read_only) { + // TODO(erikchen): Remove ScopedTracker below once + // http://crbug.com/466437 is fixed. + tracked_objects::ScopedTracker tracking_profile3( + 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()) { @@ -152,6 +170,12 @@ bool SharedMemory::Create(const SharedMemoryCreateOptions& options) { return false; } } + + // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466437 + // is fixed. + tracked_objects::ScopedTracker tracking_profile4( + FROM_HERE_WITH_EXPLICIT_FUNCTION( + "466437 SharedMemory::Create::Unlink")); // 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). @@ -331,8 +355,6 @@ SharedMemoryHandle SharedMemory::handle() const { } void SharedMemory::Close() { - Unmap(); - if (mapped_file_ > 0) { if (close(mapped_file_) < 0) PLOG(ERROR) << "close"; @@ -454,7 +476,7 @@ bool SharedMemory::ShareToProcessCommon(ProcessHandle process, 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(readonly_mapped_file_ >= 0); + CHECK_GE(readonly_mapped_file_, 0); handle_to_dup = readonly_mapped_file_; break; } @@ -468,8 +490,10 @@ bool SharedMemory::ShareToProcessCommon(ProcessHandle process, new_handle->fd = new_fd; new_handle->auto_close = true; - if (close_self) + if (close_self) { + Unmap(); Close(); + } return true; } diff --git a/chromium/base/memory/shared_memory_unittest.cc b/chromium/base/memory/shared_memory_unittest.cc index 0b3fd7b1dd7..6fe57060e6f 100644 --- a/chromium/base/memory/shared_memory_unittest.cc +++ b/chromium/base/memory/shared_memory_unittest.cc @@ -101,10 +101,10 @@ const char* const MultipleThreadMain::s_test_name_ = class MultipleLockThread : public PlatformThread::Delegate { public: explicit MultipleLockThread(int id) : id_(id) {} - virtual ~MultipleLockThread() {} + ~MultipleLockThread() override {} // PlatformThread::Delegate interface. - virtual void ThreadMain() override { + void ThreadMain() override { const uint32 kDataSize = sizeof(int); SharedMemoryHandle handle = NULL; { @@ -260,6 +260,29 @@ TEST(SharedMemoryTest, OpenExclusive) { } #endif +// Check that memory is still mapped after its closed. +TEST(SharedMemoryTest, CloseNoUnmap) { + const size_t kDataSize = 4096; + + SharedMemory memory; + ASSERT_TRUE(memory.CreateAndMapAnonymous(kDataSize)); + char* ptr = static_cast<char*>(memory.memory()); + ASSERT_NE(ptr, static_cast<void*>(NULL)); + memset(ptr, 'G', kDataSize); + + memory.Close(); + + EXPECT_EQ(ptr, memory.memory()); + EXPECT_EQ(SharedMemory::NULLHandle(), memory.handle()); + + for (size_t i = 0; i < kDataSize; i++) { + EXPECT_EQ('G', ptr[i]); + } + + memory.Unmap(); + EXPECT_EQ(nullptr, memory.memory()); +} + // Create a set of N threads to each open a shared memory segment and write to // it. Verify that they are always reading/writing consistent data. TEST(SharedMemoryTest, MultipleThreads) { @@ -676,15 +699,15 @@ const char* const SharedMemoryProcessTest::s_test_name_ = "MPMem"; TEST_F(SharedMemoryProcessTest, Tasks) { SharedMemoryProcessTest::CleanUp(); - ProcessHandle handles[kNumTasks]; + Process processes[kNumTasks]; for (int index = 0; index < kNumTasks; ++index) { - handles[index] = SpawnChild("SharedMemoryTestMain"); - ASSERT_TRUE(handles[index]); + processes[index] = SpawnChild("SharedMemoryTestMain"); + ASSERT_TRUE(processes[index].IsValid()); } int exit_code = 0; for (int index = 0; index < kNumTasks; ++index) { - EXPECT_TRUE(WaitForExitCode(handles[index], &exit_code)); + EXPECT_TRUE(processes[index].WaitForExit(&exit_code)); EXPECT_EQ(0, exit_code); } diff --git a/chromium/base/memory/shared_memory_win.cc b/chromium/base/memory/shared_memory_win.cc index eef7f037752..7e0cf0bf23e 100644 --- a/chromium/base/memory/shared_memory_win.cc +++ b/chromium/base/memory/shared_memory_win.cc @@ -71,6 +71,7 @@ SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only, } SharedMemory::~SharedMemory() { + Unmap(); Close(); if (lock_ != NULL) CloseHandle(lock_); @@ -118,8 +119,8 @@ bool SharedMemory::Create(const SharedMemoryCreateOptions& options) { return false; size_t rounded_size = (options.size + kSectionMask) & ~kSectionMask; - name_ = ASCIIToWide(options.name_deprecated == NULL ? "" : - *options.name_deprecated); + name_ = options.name_deprecated ? + ASCIIToUTF16(*options.name_deprecated) : L""; SECURITY_ATTRIBUTES sa = { sizeof(sa), NULL, FALSE }; SECURITY_DESCRIPTOR sd; ACL dacl; @@ -137,13 +138,14 @@ bool SharedMemory::Create(const SharedMemoryCreateOptions& options) { // Windows ignores DACLs on certain unnamed objects (like shared sections). // So, we generate a random name when we need to enforce read-only. uint64_t rand_values[4]; - base::RandBytes(&rand_values, sizeof(rand_values)); - name_ = base::StringPrintf(L"CrSharedMem_%016x%016x%016x%016x", - rand_values[0], rand_values[1], - rand_values[2], rand_values[3]); + RandBytes(&rand_values, sizeof(rand_values)); + name_ = StringPrintf(L"CrSharedMem_%016x%016x%016x%016x", + rand_values[0], rand_values[1], + rand_values[2], rand_values[3]); } mapped_file_ = CreateFileMapping(INVALID_HANDLE_VALUE, &sa, - PAGE_READWRITE, 0, static_cast<DWORD>(rounded_size), name_.c_str()); + PAGE_READWRITE, 0, static_cast<DWORD>(rounded_size), + name_.empty() ? nullptr : name_.c_str()); if (!mapped_file_) return false; @@ -171,7 +173,7 @@ bool SharedMemory::Delete(const std::string& name) { bool SharedMemory::Open(const std::string& name, bool read_only) { DCHECK(!mapped_file_); - name_ = ASCIIToWide(name); + name_ = ASCIIToUTF16(name); read_only_ = read_only; mapped_file_ = OpenFileMapping( read_only_ ? FILE_MAP_READ : FILE_MAP_READ | FILE_MAP_WRITE, @@ -218,7 +220,7 @@ bool SharedMemory::Unmap() { } bool SharedMemory::ShareToProcessCommon(ProcessHandle process, - SharedMemoryHandle *new_handle, + SharedMemoryHandle* new_handle, bool close_self, ShareMode share_mode) { *new_handle = 0; @@ -249,11 +251,6 @@ bool SharedMemory::ShareToProcessCommon(ProcessHandle process, void SharedMemory::Close() { - if (memory_ != NULL) { - UnmapViewOfFile(memory_); - memory_ = NULL; - } - if (mapped_file_ != NULL) { CloseHandle(mapped_file_); mapped_file_ = NULL; diff --git a/chromium/base/memory/singleton.h b/chromium/base/memory/singleton.h index e5e2e3efed0..e50bdc05f34 100644 --- a/chromium/base/memory/singleton.h +++ b/chromium/base/memory/singleton.h @@ -23,7 +23,6 @@ #include "base/atomicops.h" #include "base/base_export.h" #include "base/memory/aligned_memory.h" -#include "base/third_party/dynamic_annotations/dynamic_annotations.h" #include "base/threading/thread_restrictions.h" namespace base { @@ -237,8 +236,6 @@ class Singleton { // instance_ pointer must acquire visibility over the singleton data. base::subtle::AtomicWord value = base::subtle::Acquire_Load(&instance_); if (value != 0 && value != base::internal::kBeingCreatedMarker) { - // See the corresponding HAPPENS_BEFORE below. - ANNOTATE_HAPPENS_AFTER(&instance_); return reinterpret_cast<Type*>(value); } @@ -250,10 +247,6 @@ class Singleton { // stop right after we do this store. Type* newval = Traits::New(); - // This annotation helps race detectors recognize correct lock-less - // synchronization between different threads calling get(). - // See the corresponding HAPPENS_AFTER below and above. - ANNOTATE_HAPPENS_BEFORE(&instance_); // Releases the visibility over instance_ to the readers. base::subtle::Release_Store( &instance_, reinterpret_cast<base::subtle::AtomicWord>(newval)); @@ -267,8 +260,6 @@ class Singleton { // We hit a race. Wait for the other thread to complete it. value = base::internal::WaitForInstance(&instance_); - // See the corresponding HAPPENS_BEFORE above. - ANNOTATE_HAPPENS_AFTER(&instance_); return reinterpret_cast<Type*>(value); } diff --git a/chromium/base/memory/singleton_unittest.cc b/chromium/base/memory/singleton_unittest.cc index 0e9ecdc03aa..dbff007ada7 100644 --- a/chromium/base/memory/singleton_unittest.cc +++ b/chromium/base/memory/singleton_unittest.cc @@ -153,7 +153,7 @@ class SingletonTest : public testing::Test { public: SingletonTest() {} - virtual void SetUp() override { + void SetUp() override { non_leak_called_ = false; leaky_called_ = false; static_called_ = false; diff --git a/chromium/base/memory/weak_ptr_unittest.cc b/chromium/base/memory/weak_ptr_unittest.cc index d89a5c6bbda..20e5c7b05c2 100644 --- a/chromium/base/memory/weak_ptr_unittest.cc +++ b/chromium/base/memory/weak_ptr_unittest.cc @@ -8,8 +8,9 @@ #include "base/bind.h" #include "base/debug/leak_annotations.h" +#include "base/location.h" #include "base/memory/scoped_ptr.h" -#include "base/message_loop/message_loop.h" +#include "base/single_thread_task_runner.h" #include "base/synchronization/waitable_event.h" #include "base/threading/thread.h" #include "testing/gtest/include/gtest/gtest.h" @@ -25,9 +26,8 @@ class OffThreadObjectCreator { { Thread creator_thread("creator_thread"); creator_thread.Start(); - creator_thread.message_loop()->PostTask( - FROM_HERE, - base::Bind(OffThreadObjectCreator::CreateObject, &result)); + creator_thread.task_runner()->PostTask( + FROM_HERE, base::Bind(OffThreadObjectCreator::CreateObject, &result)); } DCHECK(result); // We synchronized on thread destruction above. return result; @@ -66,25 +66,23 @@ class BackgroundThread : public Thread { void CreateArrowFromTarget(Arrow** arrow, Target* target) { WaitableEvent completion(true, false); - message_loop()->PostTask( - FROM_HERE, - base::Bind(&BackgroundThread::DoCreateArrowFromTarget, - arrow, target, &completion)); + task_runner()->PostTask( + FROM_HERE, base::Bind(&BackgroundThread::DoCreateArrowFromTarget, arrow, + target, &completion)); completion.Wait(); } void CreateArrowFromArrow(Arrow** arrow, const Arrow* other) { WaitableEvent completion(true, false); - message_loop()->PostTask( - FROM_HERE, - base::Bind(&BackgroundThread::DoCreateArrowFromArrow, - arrow, other, &completion)); + task_runner()->PostTask( + FROM_HERE, base::Bind(&BackgroundThread::DoCreateArrowFromArrow, arrow, + other, &completion)); completion.Wait(); } void DeleteTarget(Target* object) { WaitableEvent completion(true, false); - message_loop()->PostTask( + task_runner()->PostTask( FROM_HERE, base::Bind(&BackgroundThread::DoDeleteTarget, object, &completion)); completion.Wait(); @@ -92,25 +90,23 @@ class BackgroundThread : public Thread { void CopyAndAssignArrow(Arrow* object) { WaitableEvent completion(true, false); - message_loop()->PostTask( - FROM_HERE, - base::Bind(&BackgroundThread::DoCopyAndAssignArrow, - object, &completion)); + task_runner()->PostTask( + FROM_HERE, base::Bind(&BackgroundThread::DoCopyAndAssignArrow, object, + &completion)); completion.Wait(); } void CopyAndAssignArrowBase(Arrow* object) { WaitableEvent completion(true, false); - message_loop()->PostTask( - FROM_HERE, - base::Bind(&BackgroundThread::DoCopyAndAssignArrowBase, - object, &completion)); + task_runner()->PostTask( + FROM_HERE, base::Bind(&BackgroundThread::DoCopyAndAssignArrowBase, + object, &completion)); completion.Wait(); } void DeleteArrow(Arrow* object) { WaitableEvent completion(true, false); - message_loop()->PostTask( + task_runner()->PostTask( FROM_HERE, base::Bind(&BackgroundThread::DoDeleteArrow, object, &completion)); completion.Wait(); @@ -119,9 +115,8 @@ class BackgroundThread : public Thread { Target* DeRef(const Arrow* arrow) { WaitableEvent completion(true, false); Target* result = NULL; - message_loop()->PostTask( - FROM_HERE, - base::Bind(&BackgroundThread::DoDeRef, arrow, &result, &completion)); + task_runner()->PostTask(FROM_HERE, base::Bind(&BackgroundThread::DoDeRef, + arrow, &result, &completion)); completion.Wait(); return result; } diff --git a/chromium/base/message_loop/incoming_task_queue.cc b/chromium/base/message_loop/incoming_task_queue.cc index 2ca32d6254b..c1ce939b0c4 100644 --- a/chromium/base/message_loop/incoming_task_queue.cc +++ b/chromium/base/message_loop/incoming_task_queue.cc @@ -4,18 +4,47 @@ #include "base/message_loop/incoming_task_queue.h" +#include <limits> + #include "base/location.h" #include "base/message_loop/message_loop.h" +#include "base/metrics/histogram.h" #include "base/synchronization/waitable_event.h" #include "base/time/time.h" namespace base { namespace internal { +namespace { + +#ifndef NDEBUG +// Delays larger than this are often bogus, and a warning should be emitted in +// debug builds to warn developers. http://crbug.com/450045 +const int kTaskDelayWarningThresholdInSeconds = + 14 * 24 * 60 * 60; // 14 days. +#endif + +// Returns true if MessagePump::ScheduleWork() must be called one +// time for every task that is added to the MessageLoop incoming queue. +bool AlwaysNotifyPump(MessageLoop::Type type) { +#if defined(OS_ANDROID) + // The Android UI message loop needs to get notified each time a task is + // added + // to the incoming queue. + return type == MessageLoop::TYPE_UI || type == MessageLoop::TYPE_JAVA; +#else + return false; +#endif +} + +} // namespace + IncomingTaskQueue::IncomingTaskQueue(MessageLoop* message_loop) : high_res_task_count_(0), message_loop_(message_loop), - next_sequence_num_(0) { + next_sequence_num_(0), + message_loop_scheduled_(false), + always_schedule_work_(AlwaysNotifyPump(message_loop_->type())) { } bool IncomingTaskQueue::AddToIncomingQueue( @@ -23,6 +52,11 @@ bool IncomingTaskQueue::AddToIncomingQueue( const Closure& task, TimeDelta delay, bool nestable) { + DLOG_IF(WARNING, + delay.InSeconds() > kTaskDelayWarningThresholdInSeconds) + << "Requesting super-long task delay period of " << delay.InSeconds() + << " seconds from here: " << from_here.ToString(); + AutoLock locked(incoming_queue_lock_); PendingTask pending_task( from_here, task, CalculateDelayedRuntime(delay), nestable); @@ -56,9 +90,14 @@ int IncomingTaskQueue::ReloadWorkQueue(TaskQueue* work_queue) { // Acquire all we can from the inter-thread queue with one lock acquisition. AutoLock lock(incoming_queue_lock_); - if (!incoming_queue_.empty()) + if (incoming_queue_.empty()) { + // If the loop attempts to reload but there are no tasks in the incoming + // queue, that means it will go to sleep waiting for more work. If the + // incoming queue becomes nonempty we need to schedule it again. + message_loop_scheduled_ = false; + } else { incoming_queue_.Swap(work_queue); - + } // Reset the count of high resolution tasks since our queue is now empty. int high_res_tasks = high_res_task_count_; high_res_task_count_ = 0; @@ -109,8 +148,16 @@ bool IncomingTaskQueue::PostPendingTask(PendingTask* pending_task) { incoming_queue_.push(*pending_task); pending_task->task.Reset(); - // Wake up the pump. - message_loop_->ScheduleWork(was_empty); + if (always_schedule_work_ || (!message_loop_scheduled_ && was_empty)) { + // Wake up the message loop. + message_loop_->ScheduleWork(); + // After we've scheduled the message loop, we do not need to do so again + // until we know it has processed all of the work in our queue and is + // waiting for more work again. The message loop will always attempt to + // reload from the incoming queue before waiting again so we clear this flag + // in ReloadWorkQueue(). + message_loop_scheduled_ = true; + } return true; } diff --git a/chromium/base/message_loop/incoming_task_queue.h b/chromium/base/message_loop/incoming_task_queue.h index 30f26033a2d..72e1f302829 100644 --- a/chromium/base/message_loop/incoming_task_queue.h +++ b/chromium/base/message_loop/incoming_task_queue.h @@ -84,6 +84,14 @@ class BASE_EXPORT IncomingTaskQueue // The next sequence number to use for delayed tasks. int next_sequence_num_; + // True if our message loop has already been scheduled and does not need to be + // scheduled again until an empty reload occurs. + bool message_loop_scheduled_; + + // True if we always need to call ScheduleWork when receiving a new task, even + // if the incoming queue was not empty. + const bool always_schedule_work_; + DISALLOW_COPY_AND_ASSIGN(IncomingTaskQueue); }; diff --git a/chromium/base/message_loop/message_loop.cc b/chromium/base/message_loop/message_loop.cc index 01402d070e3..eb0f9688494 100644 --- a/chromium/base/message_loop/message_loop.cc +++ b/chromium/base/message_loop/message_loop.cc @@ -86,18 +86,6 @@ bool enable_histogrammer_ = false; MessageLoop::MessagePumpFactory* message_pump_for_ui_factory_ = NULL; -// Returns true if MessagePump::ScheduleWork() must be called one -// time for every task that is added to the MessageLoop incoming queue. -bool AlwaysNotifyPump(MessageLoop::Type type) { -#if defined(OS_ANDROID) - // The Android UI message loop needs to get notified each time a task is added - // to the incoming queue. - return type == MessageLoop::TYPE_UI || type == MessageLoop::TYPE_JAVA; -#else - return false; -#endif -} - #if defined(OS_IOS) typedef MessagePumpIOSForIO MessagePumpForIO; #elif defined(OS_NACL_SFI) @@ -106,9 +94,11 @@ typedef MessagePumpDefault MessagePumpForIO; typedef MessagePumpLibevent MessagePumpForIO; #endif +#if !defined(OS_NACL_SFI) MessagePumpForIO* ToPumpIO(MessagePump* pump) { return static_cast<MessagePumpForIO*>(pump); } +#endif // !defined(OS_NACL_SFI) } // namespace @@ -127,8 +117,10 @@ MessageLoop::DestructionObserver::~DestructionObserver() { MessageLoop::MessageLoop(Type type) : type_(type), +#if defined(OS_WIN) pending_high_res_tasks_(0), in_high_res_mode_(false), +#endif nestable_tasks_allowed_(true), #if defined(OS_WIN) os_modal_loop_(false), @@ -143,8 +135,10 @@ MessageLoop::MessageLoop(Type type) MessageLoop::MessageLoop(scoped_ptr<MessagePump> pump) : pump_(pump.Pass()), type_(TYPE_CUSTOM), +#if defined(OS_WIN) pending_high_res_tasks_(0), in_high_res_mode_(false), +#endif nestable_tasks_allowed_(true), #if defined(OS_WIN) os_modal_loop_(false), @@ -158,7 +152,12 @@ MessageLoop::MessageLoop(scoped_ptr<MessagePump> pump) MessageLoop::~MessageLoop() { DCHECK_EQ(this, current()); + // iOS just attaches to the loop, it doesn't Run it. + // TODO(stuartmorgan): Consider wiring up a Detach(). +#if !defined(OS_IOS) DCHECK(!run_loop_); +#endif + #if defined(OS_WIN) if (in_high_res_mode_) Time::ActivateHighResolutionTimer(false); @@ -276,31 +275,27 @@ void MessageLoop::RemoveDestructionObserver( void MessageLoop::PostTask( const tracked_objects::Location& from_here, const Closure& task) { - DCHECK(!task.is_null()) << from_here.ToString(); - incoming_task_queue_->AddToIncomingQueue(from_here, task, TimeDelta(), true); + message_loop_proxy_->PostTask(from_here, task); } void MessageLoop::PostDelayedTask( const tracked_objects::Location& from_here, const Closure& task, TimeDelta delay) { - DCHECK(!task.is_null()) << from_here.ToString(); - incoming_task_queue_->AddToIncomingQueue(from_here, task, delay, true); + message_loop_proxy_->PostDelayedTask(from_here, task, delay); } void MessageLoop::PostNonNestableTask( const tracked_objects::Location& from_here, const Closure& task) { - DCHECK(!task.is_null()) << from_here.ToString(); - incoming_task_queue_->AddToIncomingQueue(from_here, task, TimeDelta(), false); + message_loop_proxy_->PostNonNestableTask(from_here, task); } void MessageLoop::PostNonNestableDelayedTask( const tracked_objects::Location& from_here, const Closure& task, TimeDelta delay) { - DCHECK(!task.is_null()) << from_here.ToString(); - incoming_task_queue_->AddToIncomingQueue(from_here, task, delay, false); + message_loop_proxy_->PostNonNestableDelayedTask(from_here, task, delay); } void MessageLoop::Run() { @@ -432,10 +427,13 @@ bool MessageLoop::ProcessNextDelayedNonNestableTask() { void MessageLoop::RunTask(const PendingTask& pending_task) { DCHECK(nestable_tasks_allowed_); +#if defined(OS_WIN) if (pending_task.is_high_res) { pending_high_res_tasks_--; - CHECK(pending_high_res_tasks_ >= 0); + CHECK_GE(pending_high_res_tasks_, 0); } +#endif + // Execute the task and assume the worst: It is probably not reentrant. nestable_tasks_allowed_ = false; @@ -505,14 +503,17 @@ void MessageLoop::ReloadWorkQueue() { // load. That reduces the number of locks-per-task significantly when our // queues get large. if (work_queue_.empty()) { +#if defined(OS_WIN) pending_high_res_tasks_ += incoming_task_queue_->ReloadWorkQueue(&work_queue_); +#else + incoming_task_queue_->ReloadWorkQueue(&work_queue_); +#endif } } -void MessageLoop::ScheduleWork(bool was_empty) { - if (was_empty || AlwaysNotifyPump(type_)) - pump_->ScheduleWork(); +void MessageLoop::ScheduleWork() { + pump_->ScheduleWork(); } //------------------------------------------------------------------------------ @@ -703,8 +704,8 @@ bool MessageLoopForIO::WaitForIOCompletion(DWORD timeout, IOHandler* filter) { bool MessageLoopForIO::WatchFileDescriptor(int fd, bool persistent, Mode mode, - FileDescriptorWatcher *controller, - Watcher *delegate) { + FileDescriptorWatcher* controller, + Watcher* delegate) { return ToPumpIO(pump_.get())->WatchFileDescriptor( fd, persistent, diff --git a/chromium/base/message_loop/message_loop.h b/chromium/base/message_loop/message_loop.h index 47df69a921b..fd7596a7920 100644 --- a/chromium/base/message_loop/message_loop.h +++ b/chromium/base/message_loop/message_loop.h @@ -106,7 +106,7 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate { TYPE_IO, #if defined(OS_ANDROID) TYPE_JAVA, -#endif // defined(OS_ANDROID) +#endif // defined(OS_ANDROID) }; // Normally, it is not necessary to instantiate a MessageLoop. Instead, it @@ -155,6 +155,9 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate { // DestructionObserver is receiving a notification callback. void RemoveDestructionObserver(DestructionObserver* destruction_observer); + // NOTE: Deprecated; prefer task_runner() and the TaskRunner interfaces. + // TODO(skyostil): Remove these functions (crbug.com/465354). + // // The "PostTask" family of methods call the task's Run method asynchronously // from within a message loop at some point in the future. // @@ -300,6 +303,8 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate { } // 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_; } @@ -391,7 +396,7 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate { // Wakes up the message pump. Can be called on any thread. The caller is // responsible for synchronizing ScheduleWork() calls. - void ScheduleWork(bool was_empty); + void ScheduleWork(); // Returns the TaskAnnotator which is used to add debug information to posted // tasks. @@ -452,6 +457,7 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate { // this queue is only accessed (push/pop) by our current thread. TaskQueue work_queue_; +#if defined(OS_WIN) // How many high resolution tasks are in the pending task queue. This value // increases by N every time we call ReloadWorkQueue() and decreases by 1 // every time we call RunTask() if the task needs a high resolution timer. @@ -459,6 +465,7 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate { // Tracks if we have requested high resolution timers. Its only use is to // turn off the high resolution timer upon loop destruction. bool in_high_res_mode_; +#endif // Contains delayed tasks, sorted by their 'delayed_run_time' property. DelayedTaskQueue delayed_work_queue_; @@ -639,8 +646,8 @@ class BASE_EXPORT MessageLoopForIO : public MessageLoop { bool WatchFileDescriptor(int fd, bool persistent, Mode mode, - FileDescriptorWatcher *controller, - Watcher *delegate); + FileDescriptorWatcher* controller, + Watcher* delegate); #endif // defined(OS_IOS) || defined(OS_POSIX) #endif // !defined(OS_NACL_SFI) }; diff --git a/chromium/base/message_loop/message_loop_proxy.h b/chromium/base/message_loop/message_loop_proxy.h index 88eeac4ee6d..d5ecc041b6f 100644 --- a/chromium/base/message_loop/message_loop_proxy.h +++ b/chromium/base/message_loop/message_loop_proxy.h @@ -10,6 +10,18 @@ #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 diff --git a/chromium/base/message_loop/message_loop_proxy_impl_unittest.cc b/chromium/base/message_loop/message_loop_proxy_impl_unittest.cc index 5f31140303f..fa253717654 100644 --- a/chromium/base/message_loop/message_loop_proxy_impl_unittest.cc +++ b/chromium/base/message_loop/message_loop_proxy_impl_unittest.cc @@ -38,14 +38,14 @@ class MessageLoopProxyImplTest : public testing::Test { } protected: - virtual void SetUp() override { + void SetUp() override { io_thread_.reset(new Thread("MessageLoopProxyImplTest_IO")); file_thread_.reset(new Thread("MessageLoopProxyImplTest_File")); io_thread_->Start(); file_thread_->Start(); } - virtual void TearDown() override { + void TearDown() override { io_thread_->Stop(); file_thread_->Stop(); } diff --git a/chromium/base/message_loop/message_loop_proxy_unittest.cc b/chromium/base/message_loop/message_loop_proxy_unittest.cc index 673ed884001..0b0d9f8ad00 100644 --- a/chromium/base/message_loop/message_loop_proxy_unittest.cc +++ b/chromium/base/message_loop/message_loop_proxy_unittest.cc @@ -31,7 +31,7 @@ class MessageLoopProxyTest : public testing::Test { } protected: - virtual void SetUp() override { + void SetUp() override { // Use SetUp() instead of the constructor to avoid posting a task to a // partialy constructed object. task_thread_.Start(); @@ -42,7 +42,7 @@ class MessageLoopProxyTest : public testing::Test { Bind(&MessageLoopProxyTest::BlockTaskThreadHelper, Unretained(this))); } - virtual void TearDown() override { + 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. diff --git a/chromium/base/message_loop/message_loop_unittest.cc b/chromium/base/message_loop/message_loop_unittest.cc index 733f5e5b510..ddde6bb170d 100644 --- a/chromium/base/message_loop/message_loop_unittest.cc +++ b/chromium/base/message_loop/message_loop_unittest.cc @@ -425,7 +425,7 @@ class DispatcherImpl : public MessagePumpDispatcher { public: DispatcherImpl() : dispatch_count_(0) {} - virtual uint32_t Dispatch(const NativeEvent& msg) override { + uint32_t Dispatch(const NativeEvent& msg) override { ::TranslateMessage(&msg); ::DispatchMessage(&msg); // Do not count WM_TIMER since it is not what we post and it will cause @@ -489,8 +489,9 @@ class TestIOHandler : public MessageLoopForIO::IOHandler { public: TestIOHandler(const wchar_t* name, HANDLE signal, bool wait); - virtual void OnIOCompleted(MessageLoopForIO::IOContext* context, - DWORD bytes_transfered, DWORD error); + void OnIOCompleted(MessageLoopForIO::IOContext* context, + DWORD bytes_transfered, + DWORD error) override; void Init(); void WaitForIO(); @@ -668,14 +669,12 @@ class DummyTaskObserver : public MessageLoop::TaskObserver { void WillProcessTask(const PendingTask& pending_task) override { num_tasks_started_++; - EXPECT_TRUE(pending_task.time_posted != TimeTicks()); EXPECT_LE(num_tasks_started_, num_tasks_); EXPECT_EQ(num_tasks_started_, num_tasks_processed_ + 1); } void DidProcessTask(const PendingTask& pending_task) override { num_tasks_processed_++; - EXPECT_TRUE(pending_task.time_posted != TimeTicks()); EXPECT_LE(num_tasks_started_, num_tasks_); EXPECT_EQ(num_tasks_started_, num_tasks_processed_); } diff --git a/chromium/base/message_loop/message_pump_android.h b/chromium/base/message_loop/message_pump_android.h index 9b4540f6ff9..d48050d21d1 100644 --- a/chromium/base/message_loop/message_pump_android.h +++ b/chromium/base/message_loop/message_pump_android.h @@ -22,12 +22,12 @@ class TimeTicks; class BASE_EXPORT MessagePumpForUI : public MessagePump { public: MessagePumpForUI(); - virtual ~MessagePumpForUI(); + ~MessagePumpForUI() override; - virtual void Run(Delegate* delegate) override; - virtual void Quit() override; - virtual void ScheduleWork() override; - virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override; + void Run(Delegate* delegate) override; + void Quit() override; + void ScheduleWork() override; + void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override; virtual void Start(Delegate* delegate); diff --git a/chromium/base/message_loop/message_pump_default.cc b/chromium/base/message_loop/message_pump_default.cc index 27c19e0227b..bb9d8ce9ffc 100644 --- a/chromium/base/message_loop/message_pump_default.cc +++ b/chromium/base/message_loop/message_pump_default.cc @@ -52,7 +52,8 @@ void MessagePumpDefault::Run(Delegate* delegate) { event_.Wait(); } else { TimeDelta delay = delayed_work_time_ - TimeTicks::Now(); - if (delay > TimeDelta()) { + // If the delay is under 1 ms we need to execute the task right away. + if (delay.InMilliseconds() >= 1) { event_.TimedWait(delay); } else { // It looks like delayed_work_time_ indicates a time in the past, so we diff --git a/chromium/base/message_loop/message_pump_default.h b/chromium/base/message_loop/message_pump_default.h index d63e8101a93..8aeaa62fcd2 100644 --- a/chromium/base/message_loop/message_pump_default.h +++ b/chromium/base/message_loop/message_pump_default.h @@ -38,4 +38,4 @@ class BASE_EXPORT MessagePumpDefault : public MessagePump { } // namespace base -#endif // BASE__MESSAGE_LOOPMESSAGE_PUMP_DEFAULT_H_ +#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_DEFAULT_H_ diff --git a/chromium/base/message_loop/message_pump_dispatcher.h b/chromium/base/message_loop/message_pump_dispatcher.h index 0dea22662f9..5b1bd55b5e7 100644 --- a/chromium/base/message_loop/message_pump_dispatcher.h +++ b/chromium/base/message_loop/message_pump_dispatcher.h @@ -2,8 +2,8 @@ // 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_PUMP_DISPATCHER_H -#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_DISPATCHER_H +#ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_DISPATCHER_H_ +#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_DISPATCHER_H_ #include <stdint.h> @@ -40,4 +40,4 @@ class BASE_EXPORT MessagePumpDispatcher { } // namespace base -#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_DISPATCHER_H +#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_DISPATCHER_H_ diff --git a/chromium/base/message_loop/message_pump_glib_unittest.cc b/chromium/base/message_loop/message_pump_glib_unittest.cc index ae4fefe2e75..7ddd4f08a04 100644 --- a/chromium/base/message_loop/message_pump_glib_unittest.cc +++ b/chromium/base/message_loop/message_pump_glib_unittest.cc @@ -161,11 +161,11 @@ class MessagePumpGLibTest : public testing::Test { MessagePumpGLibTest() : loop_(NULL), injector_(NULL) { } // Overridden from testing::Test: - virtual void SetUp() override { + void SetUp() override { loop_ = new MessageLoop(MessageLoop::TYPE_UI); injector_ = new EventInjector(); } - virtual void TearDown() override { + void TearDown() override { delete injector_; injector_ = NULL; delete loop_; diff --git a/chromium/base/message_loop/message_pump_io_ios.h b/chromium/base/message_loop/message_pump_io_ios.h index 18af4a8fe33..317a59c5d9a 100644 --- a/chromium/base/message_loop/message_pump_io_ios.h +++ b/chromium/base/message_loop/message_pump_io_ios.h @@ -97,7 +97,7 @@ class BASE_EXPORT MessagePumpIOSForIO : public MessagePumpNSRunLoop { }; MessagePumpIOSForIO(); - virtual ~MessagePumpIOSForIO(); + ~MessagePumpIOSForIO() override; // Have the current thread's message loop watch for a a situation in which // reading/writing to the FD can be performed without blocking. diff --git a/chromium/base/message_loop/message_pump_io_ios_unittest.cc b/chromium/base/message_loop/message_pump_io_ios_unittest.cc index 0bf8c082f36..ba96f83bbf0 100644 --- a/chromium/base/message_loop/message_pump_io_ios_unittest.cc +++ b/chromium/base/message_loop/message_pump_io_ios_unittest.cc @@ -18,9 +18,9 @@ class MessagePumpIOSForIOTest : public testing::Test { MessagePumpIOSForIOTest() : ui_loop_(MessageLoop::TYPE_UI), io_thread_("MessagePumpIOSForIOTestIOThread") {} - virtual ~MessagePumpIOSForIOTest() {} + ~MessagePumpIOSForIOTest() override {} - virtual void SetUp() override { + void SetUp() override { Thread::Options options(MessageLoop::TYPE_IO, 0); ASSERT_TRUE(io_thread_.StartWithOptions(options)); ASSERT_EQ(MessageLoop::TYPE_IO, io_thread_.message_loop()->type()); @@ -30,7 +30,7 @@ class MessagePumpIOSForIOTest : public testing::Test { ASSERT_EQ(0, ret); } - virtual void TearDown() override { + void TearDown() override { if (IGNORE_EINTR(close(pipefds_[0])) < 0) PLOG(ERROR) << "close"; if (IGNORE_EINTR(close(pipefds_[1])) < 0) @@ -64,11 +64,11 @@ namespace { // nothing useful. class StupidWatcher : public MessagePumpIOSForIO::Watcher { public: - virtual ~StupidWatcher() {} + ~StupidWatcher() override {} // base:MessagePumpIOSForIO::Watcher interface - virtual void OnFileCanReadWithoutBlocking(int fd) override {} - virtual void OnFileCanWriteWithoutBlocking(int fd) override {} + void OnFileCanReadWithoutBlocking(int fd) override {} + void OnFileCanWriteWithoutBlocking(int fd) override {} }; #if GTEST_HAS_DEATH_TEST && !defined(NDEBUG) @@ -93,16 +93,12 @@ class BaseWatcher : public MessagePumpIOSForIO::Watcher { : controller_(controller) { DCHECK(controller_); } - virtual ~BaseWatcher() {} + ~BaseWatcher() override {} // MessagePumpIOSForIO::Watcher interface - virtual void OnFileCanReadWithoutBlocking(int /* fd */) override { - NOTREACHED(); - } + void OnFileCanReadWithoutBlocking(int /* fd */) override { NOTREACHED(); } - virtual void OnFileCanWriteWithoutBlocking(int /* fd */) override { - NOTREACHED(); - } + void OnFileCanWriteWithoutBlocking(int /* fd */) override { NOTREACHED(); } protected: MessagePumpIOSForIO::FileDescriptorWatcher* controller_; @@ -114,11 +110,9 @@ class DeleteWatcher : public BaseWatcher { MessagePumpIOSForIO::FileDescriptorWatcher* controller) : BaseWatcher(controller) {} - virtual ~DeleteWatcher() { - DCHECK(!controller_); - } + ~DeleteWatcher() override { DCHECK(!controller_); } - virtual void OnFileCanWriteWithoutBlocking(int /* fd */) override { + void OnFileCanWriteWithoutBlocking(int /* fd */) override { DCHECK(controller_); delete controller_; controller_ = NULL; @@ -146,9 +140,9 @@ class StopWatcher : public BaseWatcher { pump_(pump), fd_to_start_watching_(fd_to_start_watching) {} - virtual ~StopWatcher() {} + ~StopWatcher() override {} - virtual void OnFileCanWriteWithoutBlocking(int /* fd */) override { + void OnFileCanWriteWithoutBlocking(int /* fd */) override { controller_->StopWatchingFileDescriptor(); if (fd_to_start_watching_ >= 0) { pump_->WatchFileDescriptor(fd_to_start_watching_, diff --git a/chromium/base/message_loop/message_pump_libevent.cc b/chromium/base/message_loop/message_pump_libevent.cc index d52025a1990..b5b1fb7e81e 100644 --- a/chromium/base/message_loop/message_pump_libevent.cc +++ b/chromium/base/message_loop/message_pump_libevent.cc @@ -15,6 +15,7 @@ #include "base/observer_list.h" #include "base/posix/eintr_wrapper.h" #include "base/time/time.h" +#include "base/trace_event/trace_event.h" #include "third_party/libevent/event.h" #if defined(OS_MACOSX) @@ -274,6 +275,9 @@ void MessagePumpLibevent::Run(Delegate* delegate) { delayed_work_time_ = TimeTicks(); } } + + if (!keep_running_) + break; } } @@ -341,6 +345,8 @@ void MessagePumpLibevent::OnLibeventNotification(int fd, short flags, WeakPtr<FileDescriptorWatcher> controller = static_cast<FileDescriptorWatcher*>(context)->weak_factory_.GetWeakPtr(); DCHECK(controller.get()); + TRACE_EVENT1("toplevel", "MessagePumpLibevent::OnLibeventNotification", + "fd", fd); MessagePumpLibevent* pump = controller->pump(); pump->processed_io_events_ = true; diff --git a/chromium/base/message_loop/message_pump_libevent_unittest.cc b/chromium/base/message_loop/message_pump_libevent_unittest.cc index f9b89c4230b..65d72172728 100644 --- a/chromium/base/message_loop/message_pump_libevent_unittest.cc +++ b/chromium/base/message_loop/message_pump_libevent_unittest.cc @@ -7,9 +7,14 @@ #include <unistd.h> #include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/files/file_util.h" +#include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/posix/eintr_wrapper.h" #include "base/run_loop.h" +#include "base/synchronization/waitable_event.h" +#include "base/synchronization/waitable_event_watcher.h" #include "base/threading/thread.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/libevent/event.h" @@ -19,11 +24,11 @@ namespace base { class MessagePumpLibeventTest : public testing::Test { protected: MessagePumpLibeventTest() - : ui_loop_(MessageLoop::TYPE_UI), + : ui_loop_(new MessageLoop(MessageLoop::TYPE_UI)), io_thread_("MessagePumpLibeventTestIOThread") {} - virtual ~MessagePumpLibeventTest() {} + ~MessagePumpLibeventTest() override {} - virtual void SetUp() override { + void SetUp() override { Thread::Options options(MessageLoop::TYPE_IO, 0); ASSERT_TRUE(io_thread_.StartWithOptions(options)); ASSERT_EQ(MessageLoop::TYPE_IO, io_thread_.message_loop()->type()); @@ -31,14 +36,13 @@ class MessagePumpLibeventTest : public testing::Test { ASSERT_EQ(0, ret); } - virtual void TearDown() override { + void TearDown() override { if (IGNORE_EINTR(close(pipefds_[0])) < 0) PLOG(ERROR) << "close"; if (IGNORE_EINTR(close(pipefds_[1])) < 0) PLOG(ERROR) << "close"; } - MessageLoop* ui_loop() { return &ui_loop_; } MessageLoopForIO* io_loop() const { return static_cast<MessageLoopForIO*>(io_thread_.message_loop()); } @@ -50,9 +54,9 @@ class MessagePumpLibeventTest : public testing::Test { } int pipefds_[2]; + scoped_ptr<MessageLoop> ui_loop_; private: - MessageLoop ui_loop_; Thread io_thread_; }; @@ -194,6 +198,69 @@ TEST_F(MessagePumpLibeventTest, NestedPumpWatcher) { OnLibeventNotification(pump.get(), &watcher); } +void FatalClosure() { + FAIL() << "Reached fatal closure."; +} + +class QuitWatcher : public BaseWatcher { + public: + QuitWatcher(MessagePumpLibevent::FileDescriptorWatcher* controller, + RunLoop* run_loop) + : BaseWatcher(controller), run_loop_(run_loop) {} + ~QuitWatcher() override {} + + void OnFileCanReadWithoutBlocking(int /* fd */) override { + // Post a fatal closure to the MessageLoop before we quit it. + MessageLoop::current()->PostTask(FROM_HERE, Bind(&FatalClosure)); + + // Now quit the MessageLoop. + run_loop_->Quit(); + } + + private: + RunLoop* run_loop_; // weak +}; + +void WriteFDWrapper(const int fd, + const char* buf, + int size, + WaitableEvent* event) { + ASSERT_TRUE(WriteFileDescriptor(fd, buf, size)); +} + +// Tests that MessagePumpLibevent quits immediately when it is quit from +// libevent's event_base_loop(). +TEST_F(MessagePumpLibeventTest, QuitWatcher) { + // Delete the old MessageLoop so that we can manage our own one here. + ui_loop_.reset(); + + MessagePumpLibevent* pump = new MessagePumpLibevent; // owned by |loop|. + MessageLoop loop(make_scoped_ptr(pump)); + RunLoop run_loop; + MessagePumpLibevent::FileDescriptorWatcher controller; + QuitWatcher delegate(&controller, &run_loop); + WaitableEvent event(false /* manual_reset */, false /* initially_signaled */); + WaitableEventWatcher watcher; + + // Tell the pump to watch the pipe. + pump->WatchFileDescriptor(pipefds_[0], false, MessagePumpLibevent::WATCH_READ, + &controller, &delegate); + + // Make the IO thread wait for |event| before writing to pipefds[1]. + const char buf = 0; + const WaitableEventWatcher::EventCallback write_fd_task = + Bind(&WriteFDWrapper, pipefds_[1], &buf, 1); + io_loop()->PostTask(FROM_HERE, + Bind(IgnoreResult(&WaitableEventWatcher::StartWatching), + Unretained(&watcher), &event, write_fd_task)); + + // Queue |event| to signal on |loop|. + loop.PostTask(FROM_HERE, Bind(&WaitableEvent::Signal, Unretained(&event))); + + // Now run the MessageLoop. + run_loop.Run(); +} + } // namespace } // namespace base diff --git a/chromium/base/message_loop/message_pump_mac.h b/chromium/base/message_loop/message_pump_mac.h index 55ab2c674d4..c8532020847 100644 --- a/chromium/base/message_loop/message_pump_mac.h +++ b/chromium/base/message_loop/message_pump_mac.h @@ -263,9 +263,9 @@ class BASE_EXPORT MessagePumpNSRunLoop : public MessagePumpCFRunLoopBase { class MessagePumpUIApplication : public MessagePumpCFRunLoopBase { public: MessagePumpUIApplication(); - virtual ~MessagePumpUIApplication(); - virtual void DoRun(Delegate* delegate) override; - virtual void Quit() override; + ~MessagePumpUIApplication() override; + void DoRun(Delegate* delegate) override; + void Quit() override; // This message pump can not spin the main message loop directly. Instead, // call |Attach()| to set up a delegate. It is an error to call |Run()|. diff --git a/chromium/base/message_loop/message_pump_perftest.cc b/chromium/base/message_loop/message_pump_perftest.cc index 9fca4653dbe..b3e5604c813 100644 --- a/chromium/base/message_loop/message_pump_perftest.cc +++ b/chromium/base/message_loop/message_pump_perftest.cc @@ -29,7 +29,7 @@ class ScheduleWorkTest : public testing::Test { void Increment(uint64_t amount) { counter_ += amount; } void Schedule(int index) { - base::TimeTicks start = base::TimeTicks::HighResNow(); + base::TimeTicks start = base::TimeTicks::Now(); base::TimeTicks thread_start; if (TimeTicks::IsThreadNowSupported()) thread_start = base::TimeTicks::ThreadNow(); @@ -39,10 +39,10 @@ class ScheduleWorkTest : public testing::Test { uint64_t schedule_calls = 0u; do { for (size_t i = 0; i < kBatchSize; ++i) { - target_message_loop()->ScheduleWork(true); + target_message_loop()->ScheduleWork(); schedule_calls++; } - now = base::TimeTicks::HighResNow(); + now = base::TimeTicks::Now(); base::TimeDelta laptime = now - lastnow; lastnow = now; minimum = std::min(minimum, laptime); @@ -242,7 +242,7 @@ class FakeMessagePump : public MessagePump { class PostTaskTest : public testing::Test { public: void Run(int batch_size, int tasks_per_reload) { - base::TimeTicks start = base::TimeTicks::HighResNow(); + base::TimeTicks start = base::TimeTicks::Now(); base::TimeTicks now; MessageLoop loop(scoped_ptr<MessagePump>(new FakeMessagePump)); scoped_refptr<internal::IncomingTaskQueue> queue( @@ -264,7 +264,7 @@ class PostTaskTest : public testing::Test { } } - now = base::TimeTicks::HighResNow(); + now = base::TimeTicks::Now(); } while (now - start < base::TimeDelta::FromSeconds(5)); std::string trace = StringPrintf("%d_tasks_per_reload", tasks_per_reload); perf_test::PrintResult( diff --git a/chromium/base/message_loop/message_pump_win.cc b/chromium/base/message_loop/message_pump_win.cc index ad89b7f6384..27b47e1a0f8 100644 --- a/chromium/base/message_loop/message_pump_win.cc +++ b/chromium/base/message_loop/message_pump_win.cc @@ -4,13 +4,15 @@ #include "base/message_loop/message_pump_win.h" +#include <limits> #include <math.h> -#include "base/debug/trace_event.h" #include "base/message_loop/message_loop.h" #include "base/metrics/histogram.h" #include "base/process/memory.h" +#include "base/profiler/scoped_tracker.h" #include "base/strings/stringprintf.h" +#include "base/trace_event/trace_event.h" #include "base/win/wrapped_window_proc.h" namespace base { @@ -51,6 +53,10 @@ void MessagePumpWin::RunWithDispatcher( state_ = previous_state; } +void MessagePumpWin::Run(Delegate* delegate) { + RunWithDispatcher(delegate, NULL); +} + void MessagePumpWin::Quit() { DCHECK(state_); state_->should_quit = true; @@ -69,12 +75,13 @@ int MessagePumpWin::GetCurrentDelay() const { double timeout = ceil((delayed_work_time_ - TimeTicks::Now()).InMillisecondsF()); - // If this value is negative, then we need to run delayed work soon. - int delay = static_cast<int>(timeout); - if (delay < 0) - delay = 0; - - return delay; + // Range check the |timeout| while converting to an integer. If the |timeout| + // is negative, then we need to run delayed work soon. If the |timeout| is + // "overflowingly" large, that means a delayed task was posted with a + // super-long delay. + return timeout < 0 ? 0 : + (timeout > std::numeric_limits<int>::max() ? + std::numeric_limits<int>::max() : static_cast<int>(timeout)); } //----------------------------------------------------------------------------- @@ -161,6 +168,11 @@ void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { // static LRESULT CALLBACK MessagePumpForUI::WndProcThunk( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { + // TODO(vadimt): Remove ScopedTracker below once crbug.com/440919 is fixed. + tracked_objects::ScopedTracker tracking_profile1( + FROM_HERE_WITH_EXPLICIT_FUNCTION( + "440919 MessagePumpForUI::WndProcThunk1")); + switch (message) { case kMsgHaveWork: reinterpret_cast<MessagePumpForUI*>(wparam)->HandleWorkMessage(); @@ -169,6 +181,12 @@ LRESULT CALLBACK MessagePumpForUI::WndProcThunk( reinterpret_cast<MessagePumpForUI*>(wparam)->HandleTimerMessage(); break; } + + // TODO(vadimt): Remove ScopedTracker below once crbug.com/440919 is fixed. + tracked_objects::ScopedTracker tracking_profile2( + FROM_HERE_WITH_EXPLICIT_FUNCTION( + "440919 MessagePumpForUI::WndProcThunk2")); + return DefWindowProc(hwnd, message, wparam, lparam); } @@ -319,6 +337,11 @@ void MessagePumpForUI::HandleTimerMessage() { } bool MessagePumpForUI::ProcessNextWindowsMessage() { + // TODO(vadimt): Remove ScopedTracker below once crbug.com/440919 is fixed. + tracked_objects::ScopedTracker tracking_profile1( + FROM_HERE_WITH_EXPLICIT_FUNCTION( + "440919 MessagePumpForUI::ProcessNextWindowsMessage1")); + // If there are sent messages in the queue then PeekMessage internally // dispatches the message and returns false. We return true in this // case to ensure that the message loop peeks again instead of calling @@ -328,6 +351,11 @@ bool MessagePumpForUI::ProcessNextWindowsMessage() { if (HIWORD(queue_status) & QS_SENDMESSAGE) sent_messages_in_queue = true; + // TODO(vadimt): Remove ScopedTracker below once crbug.com/440919 is fixed. + tracked_objects::ScopedTracker tracking_profile2( + FROM_HERE_WITH_EXPLICIT_FUNCTION( + "440919 MessagePumpForUI::ProcessNextWindowsMessage2")); + MSG msg; if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != FALSE) return ProcessMessageHelper(msg); @@ -336,6 +364,11 @@ bool MessagePumpForUI::ProcessNextWindowsMessage() { } bool MessagePumpForUI::ProcessMessageHelper(const MSG& msg) { + // TODO(vadimt): Remove ScopedTracker below once crbug.com/440919 is fixed. + tracked_objects::ScopedTracker tracking_profile1( + FROM_HERE_WITH_EXPLICIT_FUNCTION( + "440919 MessagePumpForUI::ProcessMessageHelper1")); + TRACE_EVENT1("base", "MessagePumpForUI::ProcessMessageHelper", "message", msg.message); if (WM_QUIT == msg.message) { @@ -350,16 +383,43 @@ bool MessagePumpForUI::ProcessMessageHelper(const MSG& msg) { if (msg.message == kMsgHaveWork && msg.hwnd == message_hwnd_) return ProcessPumpReplacementMessage(); + // TODO(vadimt): Remove ScopedTracker below once crbug.com/440919 is fixed. + tracked_objects::ScopedTracker tracking_profile2( + FROM_HERE_WITH_EXPLICIT_FUNCTION( + "440919 MessagePumpForUI::ProcessMessageHelper2")); + if (CallMsgFilter(const_cast<MSG*>(&msg), kMessageFilterCode)) return true; + // TODO(vadimt): Remove ScopedTracker below once crbug.com/440919 is fixed. + tracked_objects::ScopedTracker tracking_profile3( + FROM_HERE_WITH_EXPLICIT_FUNCTION( + "440919 MessagePumpForUI::ProcessMessageHelper3")); + uint32_t action = MessagePumpDispatcher::POST_DISPATCH_PERFORM_DEFAULT; - if (state_->dispatcher) + if (state_->dispatcher) { + // TODO(vadimt): Remove ScopedTracker below once crbug.com/440919 is fixed. + tracked_objects::ScopedTracker tracking_profile4( + FROM_HERE_WITH_EXPLICIT_FUNCTION( + "440919 MessagePumpForUI::ProcessMessageHelper4")); + action = state_->dispatcher->Dispatch(msg); + } if (action & MessagePumpDispatcher::POST_DISPATCH_QUIT_LOOP) state_->should_quit = true; if (action & MessagePumpDispatcher::POST_DISPATCH_PERFORM_DEFAULT) { + // TODO(vadimt): Remove ScopedTracker below once crbug.com/440919 is fixed. + tracked_objects::ScopedTracker tracking_profile5( + FROM_HERE_WITH_EXPLICIT_FUNCTION( + "440919 MessagePumpForUI::ProcessMessageHelper5")); + TranslateMessage(&msg); + + // TODO(vadimt): Remove ScopedTracker below once crbug.com/440919 is fixed. + tracked_objects::ScopedTracker tracking_profile6( + FROM_HERE_WITH_EXPLICIT_FUNCTION( + "440919 MessagePumpForUI::ProcessMessageHelper6")); + DispatchMessage(&msg); } @@ -416,6 +476,9 @@ MessagePumpForIO::MessagePumpForIO() { DCHECK(port_.IsValid()); } +MessagePumpForIO::~MessagePumpForIO() { +} + void MessagePumpForIO::ScheduleWork() { if (InterlockedExchange(&have_work_, 1)) return; // Someone else continued the pumping. @@ -617,7 +680,7 @@ ULONG_PTR MessagePumpForIO::HandlerToKey(IOHandler* handler, // |IOHandler| is at least pointer-size aligned, so the lowest two bits are // always cleared. We use the lowest bit to distinguish completion keys with // and without the associated |IOContext|. - DCHECK((key & 1) == 0); + DCHECK_EQ(key & 1, 0u); // Mark the completion key as context-less. if (!has_valid_io_context) diff --git a/chromium/base/message_loop/message_pump_win.h b/chromium/base/message_loop/message_pump_win.h index b2573174228..00f1287ce43 100644 --- a/chromium/base/message_loop/message_pump_win.h +++ b/chromium/base/message_loop/message_pump_win.h @@ -25,14 +25,13 @@ namespace base { class BASE_EXPORT MessagePumpWin : public MessagePump { public: MessagePumpWin() : have_work_(0), state_(NULL) {} - virtual ~MessagePumpWin() {} // Like MessagePump::Run, but MSG objects are routed through dispatcher. void RunWithDispatcher(Delegate* delegate, MessagePumpDispatcher* dispatcher); // MessagePump methods: - virtual void Run(Delegate* delegate) { RunWithDispatcher(delegate, NULL); } - virtual void Quit(); + void Run(Delegate* delegate) override; + void Quit() override; protected: struct RunState { @@ -115,18 +114,18 @@ class BASE_EXPORT MessagePumpForUI : public MessagePumpWin { static const int kMessageFilterCode = 0x5001; MessagePumpForUI(); - virtual ~MessagePumpForUI(); + ~MessagePumpForUI() override; // MessagePump methods: - virtual void ScheduleWork(); - virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time); + void ScheduleWork() override; + void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override; private: static LRESULT CALLBACK WndProcThunk(HWND window_handle, UINT message, WPARAM wparam, LPARAM lparam); - virtual void DoRunLoop(); + void DoRunLoop() override; void InitMessageWnd(); void WaitForWork(); void HandleWorkMessage(); @@ -267,11 +266,11 @@ class BASE_EXPORT MessagePumpForIO : public MessagePumpWin { }; MessagePumpForIO(); - virtual ~MessagePumpForIO() {} + ~MessagePumpForIO() override; // MessagePump methods: - virtual void ScheduleWork(); - virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time); + void ScheduleWork() override; + void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override; // Register the handler to be used when asynchronous IO for the given file // completes. The registration persists as long as |file_handle| is valid, so @@ -311,7 +310,7 @@ class BASE_EXPORT MessagePumpForIO : public MessagePumpWin { bool has_valid_io_context; }; - virtual void DoRunLoop(); + void DoRunLoop() override; void WaitForWork(); bool MatchCompletedIOItem(IOHandler* filter, IOItem* item); bool GetIOItem(DWORD timeout, IOItem* item); diff --git a/chromium/base/metrics/BUILD.gn b/chromium/base/metrics/BUILD.gn new file mode 100644 index 00000000000..845ac4f79c8 --- /dev/null +++ b/chromium/base/metrics/BUILD.gn @@ -0,0 +1,49 @@ +# Copyright (c) 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. + +source_set("metrics") { + sources = [ + "bucket_ranges.cc", + "bucket_ranges.h", + "field_trial.cc", + "field_trial.h", + "histogram.cc", + "histogram.h", + "histogram_base.cc", + "histogram_base.h", + "histogram_delta_serialization.cc", + "histogram_delta_serialization.h", + "histogram_flattener.h", + "histogram_macros.h", + "histogram_samples.cc", + "histogram_samples.h", + "histogram_snapshot_manager.cc", + "histogram_snapshot_manager.h", + "sample_map.cc", + "sample_map.h", + "sample_vector.cc", + "sample_vector.h", + "sparse_histogram.cc", + "sparse_histogram.h", + "statistics_recorder.cc", + "statistics_recorder.h", + "user_metrics.cc", + "user_metrics.h", + "user_metrics_action.h", + ] + + if (is_nacl) { + sources -= [ "field_trial.cc" ] + } + + configs += [ "//base:base_implementation" ] + + deps = [ + "//base/debug", + "//base/json", + "//base/memory", + ] + + visibility = [ "//base/*" ] +} diff --git a/chromium/base/metrics/field_trial.cc b/chromium/base/metrics/field_trial.cc index 7efca7a3ef5..639f6d38e24 100644 --- a/chromium/base/metrics/field_trial.cc +++ b/chromium/base/metrics/field_trial.cc @@ -223,6 +223,20 @@ bool FieldTrial::GetActiveGroup(ActiveGroup* active_group) const { return true; } +bool FieldTrial::GetState(FieldTrialState* field_trial_state) const { + if (!enable_field_trial_) + return false; + field_trial_state->trial_name = trial_name_; + // If the group name is empty (hasn't been finalized yet), use the default + // group name instead. + if (!group_name_.empty()) + field_trial_state->group_name = group_name_; + else + field_trial_state->group_name = default_group_name_; + field_trial_state->activated = group_reported_; + return true; +} + //------------------------------------------------------------------------------ // FieldTrialList methods and members. @@ -387,6 +401,29 @@ void FieldTrialList::StatesToString(std::string* output) { } // static +void FieldTrialList::AllStatesToString(std::string* output) { + if (!global_) + return; + AutoLock auto_lock(global_->lock_); + + for (const auto& registered : global_->registered_) { + FieldTrial::FieldTrialState trial; + if (!registered.second->GetState(&trial)) + continue; + DCHECK_EQ(std::string::npos, + trial.trial_name.find(kPersistentStringSeparator)); + DCHECK_EQ(std::string::npos, + trial.group_name.find(kPersistentStringSeparator)); + if (trial.activated) + output->append(1, kActivationMarker); + output->append(trial.trial_name); + output->append(1, kPersistentStringSeparator); + output->append(trial.group_name); + output->append(1, kPersistentStringSeparator); + } +} + +// static void FieldTrialList::GetActiveFieldTrialGroups( FieldTrial::ActiveGroups* active_groups) { DCHECK(active_groups->empty()); @@ -510,9 +547,8 @@ void FieldTrialList::NotifyFieldTrialGroupSelection(FieldTrial* field_trial) { return; global_->observer_list_->Notify( - &FieldTrialList::Observer::OnFieldTrialGroupFinalized, - field_trial->trial_name(), - field_trial->group_name_internal()); + FROM_HERE, &FieldTrialList::Observer::OnFieldTrialGroupFinalized, + field_trial->trial_name(), field_trial->group_name_internal()); } // static diff --git a/chromium/base/metrics/field_trial.h b/chromium/base/metrics/field_trial.h index e2e543947a1..26257ab4a89 100644 --- a/chromium/base/metrics/field_trial.h +++ b/chromium/base/metrics/field_trial.h @@ -106,6 +106,14 @@ class BASE_EXPORT FieldTrial : public RefCounted<FieldTrial> { std::string group_name; }; + // A triplet representing a FieldTrial, its selected group and whether it's + // active. + struct FieldTrialState { + std::string trial_name; + std::string group_name; + bool activated; + }; + typedef std::vector<ActiveGroup> ActiveGroups; // A return value to indicate that a given instance has not yet had a group @@ -180,8 +188,10 @@ class BASE_EXPORT FieldTrial : public RefCounted<FieldTrial> { FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, OneWinner); FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, DisableProbability); FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, ActiveGroups); + FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, AllGroups); FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, ActiveGroupsNotFinalized); FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, Save); + FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SaveAll); FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, DuplicateRestore); FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SetForcedTurnFeatureOff); FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SetForcedTurnFeatureOn); @@ -230,6 +240,13 @@ class BASE_EXPORT FieldTrial : public RefCounted<FieldTrial> { // untouched. bool GetActiveGroup(ActiveGroup* active_group) const; + // Returns the trial name and selected group name for this field trial via + // the output parameter |field_trial_state|, but only if the trial has not + // been disabled. In that case, true is returned and |field_trial_state| is + // filled in; otherwise, the result is false and |field_trial_state| is left + // untouched. + bool GetState(FieldTrialState* field_trial_state) const; + // Returns the group_name. A winner need not have been chosen. std::string group_name_internal() const { return group_name_; } @@ -404,6 +421,16 @@ class BASE_EXPORT FieldTrialList { // by |CreateTrialsFromString()|. static void StatesToString(std::string* output); + // Creates a persistent representation of all FieldTrial instances for + // resurrection in another process. This allows randomization to be done in + // one process, and secondary processes can be synchronized on the result. + // The resulting string contains the name and group name pairs of all + // registered FieldTrials which have not been disabled, with "/" used + // to separate all names and to terminate the string. All activated trials + // have their name prefixed with "*". This string is parsed by + // |CreateTrialsFromString()|. + static void AllStatesToString(std::string* output); + // Fills in the supplied vector |active_groups| (which must be empty when // called) with a snapshot of all registered FieldTrials for which the group // has been chosen and externally observed (via |group()|) and which have diff --git a/chromium/base/metrics/field_trial_unittest.cc b/chromium/base/metrics/field_trial_unittest.cc index ce95c2ae15b..f1a10425852 100644 --- a/chromium/base/metrics/field_trial_unittest.cc +++ b/chromium/base/metrics/field_trial_unittest.cc @@ -311,6 +311,36 @@ TEST_F(FieldTrialTest, ActiveGroups) { } } +TEST_F(FieldTrialTest, AllGroups) { + FieldTrial::FieldTrialState field_trial_state; + std::string one_winner("One Winner"); + scoped_refptr<FieldTrial> trial = + CreateFieldTrial(one_winner, 10, "Default", NULL); + std::string winner("Winner"); + trial->AppendGroup(winner, 10); + EXPECT_TRUE(trial->GetState(&field_trial_state)); + EXPECT_EQ(one_winner, field_trial_state.trial_name); + EXPECT_EQ(winner, field_trial_state.group_name); + trial->group(); + EXPECT_TRUE(trial->GetState(&field_trial_state)); + EXPECT_EQ(one_winner, field_trial_state.trial_name); + EXPECT_EQ(winner, field_trial_state.group_name); + + std::string multi_group("MultiGroup"); + scoped_refptr<FieldTrial> multi_group_trial = + CreateFieldTrial(multi_group, 9, "Default", NULL); + + multi_group_trial->AppendGroup("Me", 3); + multi_group_trial->AppendGroup("You", 3); + multi_group_trial->AppendGroup("Them", 3); + EXPECT_TRUE(multi_group_trial->GetState(&field_trial_state)); + // Finalize the group selection by accessing the selected group. + multi_group_trial->group(); + EXPECT_TRUE(multi_group_trial->GetState(&field_trial_state)); + EXPECT_EQ(multi_group, field_trial_state.trial_name); + EXPECT_EQ(multi_group_trial->group_name(), field_trial_state.group_name); +} + TEST_F(FieldTrialTest, ActiveGroupsNotFinalized) { const char kTrialName[] = "TestTrial"; const char kSecondaryGroupName[] = "SecondaryGroup"; @@ -388,6 +418,44 @@ TEST_F(FieldTrialTest, Save) { EXPECT_EQ("Some name/Winner/xxx/yyyy/zzz/default/", save_string); } +TEST_F(FieldTrialTest, SaveAll) { + std::string save_string; + + scoped_refptr<FieldTrial> trial = + CreateFieldTrial("Some name", 10, "Default some name", NULL); + EXPECT_EQ("", trial->group_name_internal()); + FieldTrialList::AllStatesToString(&save_string); + EXPECT_EQ("Some name/Default some name/", save_string); + save_string.clear(); + + // Create a winning group. + trial->AppendGroup("Winner", 10); + // Finalize the group selection by accessing the selected group. + trial->group(); + FieldTrialList::AllStatesToString(&save_string); + EXPECT_EQ("*Some name/Winner/", save_string); + save_string.clear(); + + // Create a second trial and winning group. + scoped_refptr<FieldTrial> trial2 = + CreateFieldTrial("xxx", 10, "Default xxx", NULL); + trial2->AppendGroup("yyyy", 10); + // Finalize the group selection by accessing the selected group. + trial2->group(); + + FieldTrialList::AllStatesToString(&save_string); + // We assume names are alphabetized... though this is not critical. + EXPECT_EQ("*Some name/Winner/*xxx/yyyy/", save_string); + save_string.clear(); + + // Create a third trial with only the default group. + scoped_refptr<FieldTrial> trial3 = + CreateFieldTrial("zzz", 10, "default", NULL); + + FieldTrialList::AllStatesToString(&save_string); + EXPECT_EQ("*Some name/Winner/*xxx/yyyy/zzz/default/", save_string); +} + TEST_F(FieldTrialTest, Restore) { ASSERT_FALSE(FieldTrialList::TrialExists("Some_name")); ASSERT_FALSE(FieldTrialList::TrialExists("xxx")); @@ -1014,6 +1082,43 @@ TEST_F(FieldTrialTest, CreateSimulatedFieldTrial) { } } +TEST(FieldTrialTestWithoutList, StatesStringFormat) { + std::string save_string; + + // Scoping the first FieldTrialList, as we need another one to test the + // importing function. + { + FieldTrialList field_trial_list(NULL); + scoped_refptr<FieldTrial> trial = + CreateFieldTrial("Abc", 10, "Default some name", NULL); + trial->AppendGroup("cba", 10); + trial->group(); + scoped_refptr<FieldTrial> trial2 = + CreateFieldTrial("Xyz", 10, "Default xxx", NULL); + trial2->AppendGroup("zyx", 10); + trial2->group(); + scoped_refptr<FieldTrial> trial3 = + CreateFieldTrial("zzz", 10, "default", NULL); + + FieldTrialList::AllStatesToString(&save_string); + } + + // Starting with a new blank FieldTrialList. + FieldTrialList field_trial_list(NULL); + ASSERT_TRUE(field_trial_list.CreateTrialsFromString( + save_string, FieldTrialList::DONT_ACTIVATE_TRIALS, + std::set<std::string>())); + + FieldTrial::ActiveGroups active_groups; + field_trial_list.GetActiveFieldTrialGroups(&active_groups); + ASSERT_EQ(2U, active_groups.size()); + EXPECT_EQ("Abc", active_groups[0].trial_name); + EXPECT_EQ("cba", active_groups[0].group_name); + EXPECT_EQ("Xyz", active_groups[1].trial_name); + EXPECT_EQ("zyx", active_groups[1].group_name); + EXPECT_TRUE(field_trial_list.TrialExists("zzz")); +} + #if GTEST_HAS_DEATH_TEST TEST(FieldTrialDeathTest, OneTimeRandomizedTrialWithoutFieldTrialList) { // Trying to instantiate a one-time randomized field trial before the diff --git a/chromium/base/metrics/histogram.cc b/chromium/base/metrics/histogram.cc index fe5b6e6117c..42ced3d0abc 100644 --- a/chromium/base/metrics/histogram.cc +++ b/chromium/base/metrics/histogram.cc @@ -369,7 +369,7 @@ HistogramBase* Histogram::DeserializeInfoImpl(PickleIterator* iter) { scoped_ptr<SampleVector> Histogram::SnapshotSampleVector() const { scoped_ptr<SampleVector> samples(new SampleVector(bucket_ranges())); samples->Add(*samples_); - return samples; + return samples.Pass(); } void Histogram::WriteAsciiImpl(bool graph_it, @@ -495,13 +495,13 @@ void Histogram::GetCountAndBucketData(Count* count, *sum = snapshot->sum(); size_t index = 0; for (size_t i = 0; i < bucket_count(); ++i) { - Sample count = snapshot->GetCountAtIndex(i); - if (count > 0) { + Sample count_at_index = snapshot->GetCountAtIndex(i); + if (count_at_index > 0) { scoped_ptr<DictionaryValue> bucket_value(new DictionaryValue()); bucket_value->SetInteger("low", ranges(i)); if (i != bucket_count() - 1) bucket_value->SetInteger("high", ranges(i + 1)); - bucket_value->SetInteger("count", count); + bucket_value->SetInteger("count", count_at_index); buckets->Set(index, bucket_value.release()); ++index; } diff --git a/chromium/base/metrics/histogram.h b/chromium/base/metrics/histogram.h index 5ed9d9eb9d6..9ee172e6cda 100644 --- a/chromium/base/metrics/histogram.h +++ b/chromium/base/metrics/histogram.h @@ -50,9 +50,12 @@ // at the low end of the histogram scale, but allows the histogram to cover a // gigantic range with the addition of very few buckets. -// Usually we use macros to define and use a histogram. These macros use a -// pattern involving a function static variable, that is a pointer to a -// histogram. This static is explicitly initialized on any thread +// Usually we use macros to define and use a histogram, which are defined in +// base/metrics/histogram_macros.h. Note: Callers should include that header +// directly if they only access the histogram APIs through macros. +// +// Macros use a pattern involving a function static variable, that is a pointer +// to a histogram. This static is explicitly initialized on any thread // that detects a uninitialized (NULL) pointer. The potentially racy // initialization is not a problem as it is always set to point to the same // value (i.e., the FactoryGet always returns the same value). FactoryGet @@ -67,7 +70,6 @@ #include <string> #include <vector> -#include "base/atomicops.h" #include "base/base_export.h" #include "base/basictypes.h" #include "base/compiler_specific.h" @@ -76,6 +78,8 @@ #include "base/memory/scoped_ptr.h" #include "base/metrics/bucket_ranges.h" #include "base/metrics/histogram_base.h" +// TODO(asvitkine): Migrate callers to to include this directly and remove this. +#include "base/metrics/histogram_macros.h" #include "base/metrics/histogram_samples.h" #include "base/time/time.h" @@ -84,234 +88,11 @@ class PickleIterator; namespace base { -class Lock; -//------------------------------------------------------------------------------ -// Histograms are often put in areas where they are called many many times, and -// performance is critical. As a result, they are designed to have a very low -// recurring cost of executing (adding additional samples). Toward that end, -// the macros declare a static pointer to the histogram in question, and only -// take a "slow path" to construct (or find) the histogram on the first run -// through the macro. We leak the histograms at shutdown time so that we don't -// have to validate using the pointers at any time during the running of the -// process. - -// The following code is generally what a thread-safe static pointer -// initialization looks like for a histogram (after a macro is expanded). This -// sample is an expansion (with comments) of the code for -// LOCAL_HISTOGRAM_CUSTOM_COUNTS(). - -/* - do { - // The pointer's presence indicates the initialization is complete. - // Initialization is idempotent, so it can safely be atomically repeated. - static base::subtle::AtomicWord atomic_histogram_pointer = 0; - - // Acquire_Load() ensures that we acquire visibility to the pointed-to data - // in the histogram. - base::Histogram* histogram_pointer(reinterpret_cast<base::Histogram*>( - base::subtle::Acquire_Load(&atomic_histogram_pointer))); - - if (!histogram_pointer) { - // This is the slow path, which will construct OR find the matching - // histogram. FactoryGet includes locks on a global histogram name map - // and is completely thread safe. - histogram_pointer = base::Histogram::FactoryGet( - name, min, max, bucket_count, base::HistogramBase::kNoFlags); - - // Use Release_Store to ensure that the histogram data is made available - // globally before we make the pointer visible. - // Several threads may perform this store, but the same value will be - // stored in all cases (for a given named/spec'ed histogram). - // We could do this without any barrier, since FactoryGet entered and - // exited a lock after construction, but this barrier makes things clear. - base::subtle::Release_Store(&atomic_histogram_pointer, - reinterpret_cast<base::subtle::AtomicWord>(histogram_pointer)); - } - - // Ensure calling contract is upheld, and the name does NOT vary. - DCHECK(histogram_pointer->histogram_name() == constant_histogram_name); - - histogram_pointer->Add(sample); - } while (0); -*/ - -// The above pattern is repeated in several macros. The only elements that -// vary are the invocation of the Add(sample) vs AddTime(sample), and the choice -// of which FactoryGet method to use. The different FactoryGet methods have -// various argument lists, so the function with its argument list is provided as -// a macro argument here. The name is only used in a DCHECK, to assure that -// callers don't try to vary the name of the histogram (which would tend to be -// ignored by the one-time initialization of the histogtram_pointer). -#define STATIC_HISTOGRAM_POINTER_BLOCK(constant_histogram_name, \ - histogram_add_method_invocation, \ - histogram_factory_get_invocation) \ - do { \ - static base::subtle::AtomicWord atomic_histogram_pointer = 0; \ - base::HistogramBase* histogram_pointer( \ - reinterpret_cast<base::HistogramBase*>( \ - base::subtle::Acquire_Load(&atomic_histogram_pointer))); \ - if (!histogram_pointer) { \ - histogram_pointer = histogram_factory_get_invocation; \ - base::subtle::Release_Store(&atomic_histogram_pointer, \ - reinterpret_cast<base::subtle::AtomicWord>(histogram_pointer)); \ - } \ - if (DCHECK_IS_ON) \ - histogram_pointer->CheckName(constant_histogram_name); \ - histogram_pointer->histogram_add_method_invocation; \ - } while (0) - - -//------------------------------------------------------------------------------ -// Provide easy general purpose histogram in a macro, just like stats counters. -// The first four macros use 50 buckets. - -#define LOCAL_HISTOGRAM_TIMES(name, sample) LOCAL_HISTOGRAM_CUSTOM_TIMES( \ - name, sample, base::TimeDelta::FromMilliseconds(1), \ - base::TimeDelta::FromSeconds(10), 50) - -// For folks that need real specific times, use this to select a precise range -// of times you want plotted, and the number of buckets you want used. -#define LOCAL_HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) \ - STATIC_HISTOGRAM_POINTER_BLOCK(name, AddTime(sample), \ - base::Histogram::FactoryTimeGet(name, min, max, bucket_count, \ - base::HistogramBase::kNoFlags)) - -#define LOCAL_HISTOGRAM_COUNTS(name, sample) LOCAL_HISTOGRAM_CUSTOM_COUNTS( \ - name, sample, 1, 1000000, 50) - -#define LOCAL_HISTOGRAM_COUNTS_100(name, sample) \ - LOCAL_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 100, 50) - -#define LOCAL_HISTOGRAM_COUNTS_10000(name, sample) \ - LOCAL_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 10000, 50) - -#define LOCAL_HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) \ - STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \ - base::Histogram::FactoryGet(name, min, max, bucket_count, \ - base::HistogramBase::kNoFlags)) - -// This is a helper macro used by other macros and shouldn't be used directly. -#define HISTOGRAM_ENUMERATION_WITH_FLAG(name, sample, boundary, flag) \ - STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \ - base::LinearHistogram::FactoryGet(name, 1, boundary, boundary + 1, \ - flag)) - -#define LOCAL_HISTOGRAM_PERCENTAGE(name, under_one_hundred) \ - LOCAL_HISTOGRAM_ENUMERATION(name, under_one_hundred, 101) - -#define LOCAL_HISTOGRAM_BOOLEAN(name, sample) \ - STATIC_HISTOGRAM_POINTER_BLOCK(name, AddBoolean(sample), \ - base::BooleanHistogram::FactoryGet(name, base::Histogram::kNoFlags)) - -// Support histograming of an enumerated value. The samples should always be -// strictly less than |boundary_value| -- this prevents you from running into -// problems down the line if you add additional buckets to the histogram. Note -// also that, despite explicitly setting the minimum bucket value to |1| below, -// it is fine for enumerated histograms to be 0-indexed -- this is because -// enumerated histograms should never have underflow. -#define LOCAL_HISTOGRAM_ENUMERATION(name, sample, boundary_value) \ - STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \ - base::LinearHistogram::FactoryGet(name, 1, boundary_value, \ - boundary_value + 1, base::HistogramBase::kNoFlags)) - -// Support histograming of an enumerated value. Samples should be one of the -// std::vector<int> list provided via |custom_ranges|. See comments above -// CustomRanges::FactoryGet about the requirement of |custom_ranges|. -// You can use the helper function CustomHistogram::ArrayToCustomRanges to -// transform a C-style array of valid sample values to a std::vector<int>. -#define LOCAL_HISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges) \ - STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \ - base::CustomHistogram::FactoryGet(name, custom_ranges, \ - base::HistogramBase::kNoFlags)) - -#define LOCAL_HISTOGRAM_MEMORY_KB(name, sample) LOCAL_HISTOGRAM_CUSTOM_COUNTS( \ - name, sample, 1000, 500000, 50) - -//------------------------------------------------------------------------------ -// The following macros provide typical usage scenarios for callers that wish -// to record histogram data, and have the data submitted/uploaded via UMA. -// Not all systems support such UMA, but if they do, the following macros -// should work with the service. - -#define UMA_HISTOGRAM_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \ - name, sample, base::TimeDelta::FromMilliseconds(1), \ - base::TimeDelta::FromSeconds(10), 50) - -#define UMA_HISTOGRAM_MEDIUM_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \ - name, sample, base::TimeDelta::FromMilliseconds(10), \ - base::TimeDelta::FromMinutes(3), 50) - -// Use this macro when times can routinely be much longer than 10 seconds. -#define UMA_HISTOGRAM_LONG_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \ - name, sample, base::TimeDelta::FromMilliseconds(1), \ - base::TimeDelta::FromHours(1), 50) - -// Use this macro when times can routinely be much longer than 10 seconds and -// you want 100 buckets. -#define UMA_HISTOGRAM_LONG_TIMES_100(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \ - name, sample, base::TimeDelta::FromMilliseconds(1), \ - base::TimeDelta::FromHours(1), 100) - -#define UMA_HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) \ - STATIC_HISTOGRAM_POINTER_BLOCK(name, AddTime(sample), \ - base::Histogram::FactoryTimeGet(name, min, max, bucket_count, \ - base::HistogramBase::kUmaTargetedHistogramFlag)) - -#define UMA_HISTOGRAM_COUNTS(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ - name, sample, 1, 1000000, 50) - -#define UMA_HISTOGRAM_COUNTS_100(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ - name, sample, 1, 100, 50) - -#define UMA_HISTOGRAM_COUNTS_10000(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ - name, sample, 1, 10000, 50) - -#define UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) \ - STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \ - base::Histogram::FactoryGet(name, min, max, bucket_count, \ - base::HistogramBase::kUmaTargetedHistogramFlag)) - -#define UMA_HISTOGRAM_MEMORY_KB(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ - name, sample, 1000, 500000, 50) - -#define UMA_HISTOGRAM_MEMORY_MB(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ - name, sample, 1, 1000, 50) - -#define UMA_HISTOGRAM_PERCENTAGE(name, under_one_hundred) \ - UMA_HISTOGRAM_ENUMERATION(name, under_one_hundred, 101) - -#define UMA_HISTOGRAM_BOOLEAN(name, sample) \ - STATIC_HISTOGRAM_POINTER_BLOCK(name, AddBoolean(sample), \ - base::BooleanHistogram::FactoryGet(name, \ - base::HistogramBase::kUmaTargetedHistogramFlag)) - -// The samples should always be strictly less than |boundary_value|. For more -// details, see the comment for the |HISTOGRAM_ENUMERATION| macro, above. -#define UMA_HISTOGRAM_ENUMERATION(name, sample, boundary_value) \ - HISTOGRAM_ENUMERATION_WITH_FLAG(name, sample, boundary_value, \ - base::HistogramBase::kUmaTargetedHistogramFlag) - -// Similar to UMA_HISTOGRAM_ENUMERATION, but used for recording stability -// histograms. Use this if recording a histogram that should be part of the -// initial stability log. -#define UMA_STABILITY_HISTOGRAM_ENUMERATION(name, sample, boundary_value) \ - HISTOGRAM_ENUMERATION_WITH_FLAG(name, sample, boundary_value, \ - base::HistogramBase::kUmaStabilityHistogramFlag) - -#define UMA_HISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges) \ - STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \ - base::CustomHistogram::FactoryGet(name, custom_ranges, \ - base::HistogramBase::kUmaTargetedHistogramFlag)) - -//------------------------------------------------------------------------------ - -class BucketRanges; -class SampleVector; - class BooleanHistogram; class CustomHistogram; class Histogram; class LinearHistogram; +class SampleVector; class BASE_EXPORT Histogram : public HistogramBase { public: diff --git a/chromium/base/metrics/histogram_base.cc b/chromium/base/metrics/histogram_base.cc index f09c84e20d0..de34c79d4b4 100644 --- a/chromium/base/metrics/histogram_base.cc +++ b/chromium/base/metrics/histogram_base.cc @@ -111,8 +111,8 @@ void HistogramBase::WriteJSON(std::string* output) const { root.SetInteger("count", count); root.SetDouble("sum", static_cast<double>(sum)); root.SetInteger("flags", flags()); - root.Set("params", parameters.release()); - root.Set("buckets", buckets.release()); + root.Set("params", parameters.Pass()); + root.Set("buckets", buckets.Pass()); root.SetInteger("pid", GetCurrentProcId()); serializer.Serialize(root); } diff --git a/chromium/base/metrics/histogram_base_unittest.cc b/chromium/base/metrics/histogram_base_unittest.cc index 4a2963aa6c4..2d6b6df0288 100644 --- a/chromium/base/metrics/histogram_base_unittest.cc +++ b/chromium/base/metrics/histogram_base_unittest.cc @@ -22,9 +22,7 @@ class HistogramBaseTest : public testing::Test { ResetStatisticsRecorder(); } - virtual ~HistogramBaseTest() { - delete statistics_recorder_; - } + ~HistogramBaseTest() override { delete statistics_recorder_; } void ResetStatisticsRecorder() { delete statistics_recorder_; diff --git a/chromium/base/metrics/histogram_macros.h b/chromium/base/metrics/histogram_macros.h new file mode 100644 index 00000000000..2aee1a5de44 --- /dev/null +++ b/chromium/base/metrics/histogram_macros.h @@ -0,0 +1,264 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_METRICS_HISTOGRAM_MACROS_H_ +#define BASE_METRICS_HISTOGRAM_MACROS_H_ + +#include "base/atomicops.h" +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/metrics/histogram.h" +#include "base/time/time.h" + +//------------------------------------------------------------------------------ +// Histograms are often put in areas where they are called many many times, and +// performance is critical. As a result, they are designed to have a very low +// recurring cost of executing (adding additional samples). Toward that end, +// the macros declare a static pointer to the histogram in question, and only +// take a "slow path" to construct (or find) the histogram on the first run +// through the macro. We leak the histograms at shutdown time so that we don't +// have to validate using the pointers at any time during the running of the +// process. + +// The following code is generally what a thread-safe static pointer +// initialization looks like for a histogram (after a macro is expanded). This +// sample is an expansion (with comments) of the code for +// LOCAL_HISTOGRAM_CUSTOM_COUNTS(). + +/* + do { + // The pointer's presence indicates the initialization is complete. + // Initialization is idempotent, so it can safely be atomically repeated. + static base::subtle::AtomicWord atomic_histogram_pointer = 0; + + // Acquire_Load() ensures that we acquire visibility to the pointed-to data + // in the histogram. + base::Histogram* histogram_pointer(reinterpret_cast<base::Histogram*>( + base::subtle::Acquire_Load(&atomic_histogram_pointer))); + + if (!histogram_pointer) { + // This is the slow path, which will construct OR find the matching + // histogram. FactoryGet includes locks on a global histogram name map + // and is completely thread safe. + histogram_pointer = base::Histogram::FactoryGet( + name, min, max, bucket_count, base::HistogramBase::kNoFlags); + + // Use Release_Store to ensure that the histogram data is made available + // globally before we make the pointer visible. + // Several threads may perform this store, but the same value will be + // stored in all cases (for a given named/spec'ed histogram). + // We could do this without any barrier, since FactoryGet entered and + // exited a lock after construction, but this barrier makes things clear. + base::subtle::Release_Store(&atomic_histogram_pointer, + reinterpret_cast<base::subtle::AtomicWord>(histogram_pointer)); + } + + // Ensure calling contract is upheld, and the name does NOT vary. + DCHECK(histogram_pointer->histogram_name() == constant_histogram_name); + + histogram_pointer->Add(sample); + } while (0); +*/ + +// The above pattern is repeated in several macros. The only elements that +// vary are the invocation of the Add(sample) vs AddTime(sample), and the choice +// of which FactoryGet method to use. The different FactoryGet methods have +// various argument lists, so the function with its argument list is provided as +// a macro argument here. The name is only used in a DCHECK, to assure that +// callers don't try to vary the name of the histogram (which would tend to be +// ignored by the one-time initialization of the histogtram_pointer). +#define STATIC_HISTOGRAM_POINTER_BLOCK(constant_histogram_name, \ + histogram_add_method_invocation, \ + histogram_factory_get_invocation) \ + do { \ + static base::subtle::AtomicWord atomic_histogram_pointer = 0; \ + base::HistogramBase* histogram_pointer( \ + reinterpret_cast<base::HistogramBase*>( \ + base::subtle::Acquire_Load(&atomic_histogram_pointer))); \ + if (!histogram_pointer) { \ + histogram_pointer = histogram_factory_get_invocation; \ + base::subtle::Release_Store( \ + &atomic_histogram_pointer, \ + reinterpret_cast<base::subtle::AtomicWord>(histogram_pointer)); \ + } \ + if (DCHECK_IS_ON()) \ + histogram_pointer->CheckName(constant_histogram_name); \ + histogram_pointer->histogram_add_method_invocation; \ + } while (0) + +//------------------------------------------------------------------------------ +// Provide easy general purpose histogram in a macro, just like stats counters. +// The first four macros use 50 buckets. + +#define LOCAL_HISTOGRAM_TIMES(name, sample) LOCAL_HISTOGRAM_CUSTOM_TIMES( \ + name, sample, base::TimeDelta::FromMilliseconds(1), \ + base::TimeDelta::FromSeconds(10), 50) + +// For folks that need real specific times, use this to select a precise range +// of times you want plotted, and the number of buckets you want used. +#define LOCAL_HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) \ + STATIC_HISTOGRAM_POINTER_BLOCK(name, AddTime(sample), \ + base::Histogram::FactoryTimeGet(name, min, max, bucket_count, \ + base::HistogramBase::kNoFlags)) + +#define LOCAL_HISTOGRAM_COUNTS(name, sample) LOCAL_HISTOGRAM_CUSTOM_COUNTS( \ + name, sample, 1, 1000000, 50) + +#define LOCAL_HISTOGRAM_COUNTS_100(name, sample) \ + LOCAL_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 100, 50) + +#define LOCAL_HISTOGRAM_COUNTS_10000(name, sample) \ + LOCAL_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 10000, 50) + +#define LOCAL_HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) \ + STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \ + base::Histogram::FactoryGet(name, min, max, bucket_count, \ + base::HistogramBase::kNoFlags)) + +// This is a helper macro used by other macros and shouldn't be used directly. +#define HISTOGRAM_ENUMERATION_WITH_FLAG(name, sample, boundary, flag) \ + STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \ + base::LinearHistogram::FactoryGet(name, 1, boundary, boundary + 1, \ + flag)) + +#define LOCAL_HISTOGRAM_PERCENTAGE(name, under_one_hundred) \ + LOCAL_HISTOGRAM_ENUMERATION(name, under_one_hundred, 101) + +#define LOCAL_HISTOGRAM_BOOLEAN(name, sample) \ + STATIC_HISTOGRAM_POINTER_BLOCK(name, AddBoolean(sample), \ + base::BooleanHistogram::FactoryGet(name, base::Histogram::kNoFlags)) + +// Support histograming of an enumerated value. The samples should always be +// strictly less than |boundary_value| -- this prevents you from running into +// problems down the line if you add additional buckets to the histogram. Note +// also that, despite explicitly setting the minimum bucket value to |1| below, +// it is fine for enumerated histograms to be 0-indexed -- this is because +// enumerated histograms should never have underflow. +#define LOCAL_HISTOGRAM_ENUMERATION(name, sample, boundary_value) \ + STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \ + base::LinearHistogram::FactoryGet(name, 1, boundary_value, \ + boundary_value + 1, base::HistogramBase::kNoFlags)) + +// Support histograming of an enumerated value. Samples should be one of the +// std::vector<int> list provided via |custom_ranges|. See comments above +// CustomRanges::FactoryGet about the requirement of |custom_ranges|. +// You can use the helper function CustomHistogram::ArrayToCustomRanges to +// transform a C-style array of valid sample values to a std::vector<int>. +#define LOCAL_HISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges) \ + STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \ + base::CustomHistogram::FactoryGet(name, custom_ranges, \ + base::HistogramBase::kNoFlags)) + +#define LOCAL_HISTOGRAM_MEMORY_KB(name, sample) LOCAL_HISTOGRAM_CUSTOM_COUNTS( \ + name, sample, 1000, 500000, 50) + +//------------------------------------------------------------------------------ +// The following macros provide typical usage scenarios for callers that wish +// to record histogram data, and have the data submitted/uploaded via UMA. +// Not all systems support such UMA, but if they do, the following macros +// should work with the service. + +#define UMA_HISTOGRAM_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \ + name, sample, base::TimeDelta::FromMilliseconds(1), \ + base::TimeDelta::FromSeconds(10), 50) + +#define UMA_HISTOGRAM_MEDIUM_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \ + name, sample, base::TimeDelta::FromMilliseconds(10), \ + base::TimeDelta::FromMinutes(3), 50) + +// Use this macro when times can routinely be much longer than 10 seconds. +#define UMA_HISTOGRAM_LONG_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \ + name, sample, base::TimeDelta::FromMilliseconds(1), \ + base::TimeDelta::FromHours(1), 50) + +// Use this macro when times can routinely be much longer than 10 seconds and +// you want 100 buckets. +#define UMA_HISTOGRAM_LONG_TIMES_100(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \ + name, sample, base::TimeDelta::FromMilliseconds(1), \ + base::TimeDelta::FromHours(1), 100) + +#define UMA_HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) \ + STATIC_HISTOGRAM_POINTER_BLOCK(name, AddTime(sample), \ + base::Histogram::FactoryTimeGet(name, min, max, bucket_count, \ + base::HistogramBase::kUmaTargetedHistogramFlag)) + +#define UMA_HISTOGRAM_COUNTS(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ + name, sample, 1, 1000000, 50) + +#define UMA_HISTOGRAM_COUNTS_100(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ + name, sample, 1, 100, 50) + +#define UMA_HISTOGRAM_COUNTS_10000(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ + name, sample, 1, 10000, 50) + +#define UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) \ + STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \ + base::Histogram::FactoryGet(name, min, max, bucket_count, \ + base::HistogramBase::kUmaTargetedHistogramFlag)) + +#define UMA_HISTOGRAM_MEMORY_KB(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ + name, sample, 1000, 500000, 50) + +#define UMA_HISTOGRAM_MEMORY_MB(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ + name, sample, 1, 1000, 50) + +#define UMA_HISTOGRAM_PERCENTAGE(name, under_one_hundred) \ + UMA_HISTOGRAM_ENUMERATION(name, under_one_hundred, 101) + +#define UMA_HISTOGRAM_BOOLEAN(name, sample) \ + STATIC_HISTOGRAM_POINTER_BLOCK(name, AddBoolean(sample), \ + base::BooleanHistogram::FactoryGet(name, \ + base::HistogramBase::kUmaTargetedHistogramFlag)) + +// The samples should always be strictly less than |boundary_value|. For more +// details, see the comment for the |LOCAL_HISTOGRAM_ENUMERATION| macro, above. +#define UMA_HISTOGRAM_ENUMERATION(name, sample, boundary_value) \ + HISTOGRAM_ENUMERATION_WITH_FLAG(name, sample, boundary_value, \ + base::HistogramBase::kUmaTargetedHistogramFlag) + +// Similar to UMA_HISTOGRAM_ENUMERATION, but used for recording stability +// histograms. Use this if recording a histogram that should be part of the +// initial stability log. +#define UMA_STABILITY_HISTOGRAM_ENUMERATION(name, sample, boundary_value) \ + HISTOGRAM_ENUMERATION_WITH_FLAG(name, sample, boundary_value, \ + base::HistogramBase::kUmaStabilityHistogramFlag) + +#define UMA_HISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges) \ + STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \ + base::CustomHistogram::FactoryGet(name, custom_ranges, \ + base::HistogramBase::kUmaTargetedHistogramFlag)) + +// Scoped class which logs its time on this earth as a UMA statistic. This is +// recommended for when you want a histogram which measures the time it takes +// for a method to execute. This measures up to 10 seconds. +#define SCOPED_UMA_HISTOGRAM_TIMER(name) \ + SCOPED_UMA_HISTOGRAM_TIMER_EXPANDER(name, false, __COUNTER__) + +// Similar scoped histogram timer, but this uses UMA_HISTOGRAM_LONG_TIMES_100, +// which measures up to an hour, and uses 100 buckets. This is more expensive +// to store, so only use if this often takes >10 seconds. +#define SCOPED_UMA_HISTOGRAM_LONG_TIMER(name) \ + SCOPED_UMA_HISTOGRAM_TIMER_EXPANDER(name, true, __COUNTER__) + +// This nested macro is necessary to expand __COUNTER__ to an actual value. +#define SCOPED_UMA_HISTOGRAM_TIMER_EXPANDER(name, is_long, key) \ + SCOPED_UMA_HISTOGRAM_TIMER_UNIQUE(name, is_long, key) + +#define SCOPED_UMA_HISTOGRAM_TIMER_UNIQUE(name, is_long, key) \ + class ScopedHistogramTimer##key { \ + public: \ + ScopedHistogramTimer##key() : constructed_(base::TimeTicks::Now()) {} \ + ~ScopedHistogramTimer##key() { \ + base::TimeDelta elapsed = base::TimeTicks::Now() - constructed_; \ + if (is_long) { \ + UMA_HISTOGRAM_LONG_TIMES_100(name, elapsed); \ + } else { \ + UMA_HISTOGRAM_TIMES(name, elapsed); \ + } \ + } \ + private: \ + base::TimeTicks constructed_; \ + } scoped_histogram_timer_##key + +#endif // BASE_METRICS_HISTOGRAM_MACROS_H_ diff --git a/chromium/base/metrics/histogram_macros_unittest.cc b/chromium/base/metrics/histogram_macros_unittest.cc new file mode 100644 index 00000000000..c5991619a0f --- /dev/null +++ b/chromium/base/metrics/histogram_macros_unittest.cc @@ -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. + +#include "base/metrics/histogram_macros.h" +#include "base/time/time.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { + +TEST(ScopedHistogramTimer, TwoTimersOneScope) { + SCOPED_UMA_HISTOGRAM_TIMER("TestTimer0"); + SCOPED_UMA_HISTOGRAM_TIMER("TestTimer1"); + SCOPED_UMA_HISTOGRAM_LONG_TIMER("TestLongTimer0"); + SCOPED_UMA_HISTOGRAM_LONG_TIMER("TestLongTimer1"); +} + +} // namespace base diff --git a/chromium/base/metrics/histogram_snapshot_manager_unittest.cc b/chromium/base/metrics/histogram_snapshot_manager_unittest.cc index 5dd72a7f1ce..3a1fd4044b2 100644 --- a/chromium/base/metrics/histogram_snapshot_manager_unittest.cc +++ b/chromium/base/metrics/histogram_snapshot_manager_unittest.cc @@ -51,7 +51,7 @@ class HistogramSnapshotManagerTest : public testing::Test { HistogramSnapshotManagerTest() : histogram_snapshot_manager_(&histogram_flattener_delta_recorder_) {} - virtual ~HistogramSnapshotManagerTest() {} + ~HistogramSnapshotManagerTest() override {} StatisticsRecorder statistics_recorder_; HistogramFlattenerDeltaRecorder histogram_flattener_delta_recorder_; diff --git a/chromium/base/metrics/histogram_unittest.cc b/chromium/base/metrics/histogram_unittest.cc index 41e36faa3c5..df43e65fdef 100644 --- a/chromium/base/metrics/histogram_unittest.cc +++ b/chromium/base/metrics/histogram_unittest.cc @@ -24,15 +24,13 @@ namespace base { class HistogramTest : public testing::Test { protected: - virtual void SetUp() { + void SetUp() override { // Each test will have a clean state (no Histogram / BucketRanges // registered). InitializeStatisticsRecorder(); } - virtual void TearDown() { - UninitializeStatisticsRecorder(); - } + void TearDown() override { UninitializeStatisticsRecorder(); } void InitializeStatisticsRecorder() { statistics_recorder_ = new StatisticsRecorder(); diff --git a/chromium/base/metrics/sparse_histogram_unittest.cc b/chromium/base/metrics/sparse_histogram_unittest.cc index 6a0efa4e12b..c29dd5e2254 100644 --- a/chromium/base/metrics/sparse_histogram_unittest.cc +++ b/chromium/base/metrics/sparse_histogram_unittest.cc @@ -18,15 +18,13 @@ namespace base { class SparseHistogramTest : public testing::Test { protected: - virtual void SetUp() { + void SetUp() override { // Each test will have a clean state (no Histogram / BucketRanges // registered). InitializeStatisticsRecorder(); } - virtual void TearDown() { - UninitializeStatisticsRecorder(); - } + void TearDown() override { UninitializeStatisticsRecorder(); } void InitializeStatisticsRecorder() { statistics_recorder_ = new StatisticsRecorder(); diff --git a/chromium/base/metrics/statistics_recorder.cc b/chromium/base/metrics/statistics_recorder.cc index 62617c5d804..fb069a5cdac 100644 --- a/chromium/base/metrics/statistics_recorder.cc +++ b/chromium/base/metrics/statistics_recorder.cc @@ -31,7 +31,6 @@ void StatisticsRecorder::Initialize() { g_statistics_recorder_.Get(); } - // static bool StatisticsRecorder::IsActive() { if (lock_ == NULL) @@ -287,11 +286,8 @@ StatisticsRecorder::StatisticsRecorder() { // static void StatisticsRecorder::DumpHistogramsToVlog(void* instance) { - DCHECK(VLOG_IS_ON(1)); - - StatisticsRecorder* me = reinterpret_cast<StatisticsRecorder*>(instance); string output; - me->WriteGraph(std::string(), &output); + StatisticsRecorder::WriteGraph(std::string(), &output); VLOG(1) << output; } diff --git a/chromium/base/metrics/statistics_recorder.h b/chromium/base/metrics/statistics_recorder.h index 3bef622b76f..b5230575963 100644 --- a/chromium/base/metrics/statistics_recorder.h +++ b/chromium/base/metrics/statistics_recorder.h @@ -88,6 +88,7 @@ class BASE_EXPORT StatisticsRecorder { friend class HistogramBaseTest; friend class HistogramSnapshotManagerTest; friend class HistogramTest; + friend class JsonPrefStoreTest; friend class SparseHistogramTest; friend class StatisticsRecorderTest; FRIEND_TEST_ALL_PREFIXES(HistogramDeltaSerializationTest, diff --git a/chromium/base/metrics/statistics_recorder_unittest.cc b/chromium/base/metrics/statistics_recorder_unittest.cc index cabeb714e5b..e653bf30dfe 100644 --- a/chromium/base/metrics/statistics_recorder_unittest.cc +++ b/chromium/base/metrics/statistics_recorder_unittest.cc @@ -15,15 +15,13 @@ namespace base { class StatisticsRecorderTest : public testing::Test { protected: - virtual void SetUp() { + void SetUp() override { // Each test will have a clean state (no Histogram / BucketRanges // registered). InitializeStatisticsRecorder(); } - virtual void TearDown() { - UninitializeStatisticsRecorder(); - } + void TearDown() override { UninitializeStatisticsRecorder(); } void InitializeStatisticsRecorder() { statistics_recorder_ = new StatisticsRecorder(); diff --git a/chromium/base/metrics/stats_counters.cc b/chromium/base/metrics/stats_counters.cc deleted file mode 100644 index 12416d9f0f5..00000000000 --- a/chromium/base/metrics/stats_counters.cc +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright (c) 2010 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/metrics/stats_counters.h" - -namespace base { - -StatsCounter::StatsCounter(const std::string& name) - : counter_id_(-1) { - // We prepend the name with 'c:' to indicate that it is a counter. - if (StatsTable::current()) { - // TODO(mbelshe): name_ construction is racy and it may corrupt memory for - // static. - name_ = "c:"; - name_.append(name); - } -} - -StatsCounter::~StatsCounter() { -} - -void StatsCounter::Set(int value) { - int* loc = GetPtr(); - if (loc) - *loc = value; -} - -void StatsCounter::Add(int value) { - int* loc = GetPtr(); - if (loc) - (*loc) += value; -} - -StatsCounter::StatsCounter() - : counter_id_(-1) { -} - -int* StatsCounter::GetPtr() { - StatsTable* table = StatsTable::current(); - if (!table) - return NULL; - - // If counter_id_ is -1, then we haven't looked it up yet. - if (counter_id_ == -1) { - counter_id_ = table->FindCounter(name_); - if (table->GetSlot() == 0) { - if (!table->RegisterThread(std::string())) { - // There is no room for this thread. This thread - // cannot use counters. - counter_id_ = 0; - return NULL; - } - } - } - - // If counter_id_ is > 0, then we have a valid counter. - if (counter_id_ > 0) - return table->GetLocation(counter_id_, table->GetSlot()); - - // counter_id_ was zero, which means the table is full. - return NULL; -} - - -StatsCounterTimer::StatsCounterTimer(const std::string& name) { - // we prepend the name with 't:' to indicate that it is a timer. - if (StatsTable::current()) { - // TODO(mbelshe): name_ construction is racy and it may corrupt memory for - // static. - name_ = "t:"; - name_.append(name); - } -} - -StatsCounterTimer::~StatsCounterTimer() { -} - -void StatsCounterTimer::Start() { - if (!Enabled()) - return; - start_time_ = TimeTicks::Now(); - stop_time_ = TimeTicks(); -} - -// Stop the timer and record the results. -void StatsCounterTimer::Stop() { - if (!Enabled() || !Running()) - return; - stop_time_ = TimeTicks::Now(); - Record(); -} - -// Returns true if the timer is running. -bool StatsCounterTimer::Running() { - return Enabled() && !start_time_.is_null() && stop_time_.is_null(); -} - -// Accept a TimeDelta to increment. -void StatsCounterTimer::AddTime(TimeDelta time) { - Add(static_cast<int>(time.InMilliseconds())); -} - -void StatsCounterTimer::Record() { - AddTime(stop_time_ - start_time_); -} - - -StatsRate::StatsRate(const std::string& name) - : StatsCounterTimer(name), - counter_(name), - largest_add_(std::string(" ").append(name).append("MAX")) { -} - -StatsRate::~StatsRate() { -} - -void StatsRate::Add(int value) { - counter_.Increment(); - StatsCounterTimer::Add(value); - if (value > largest_add_.value()) - largest_add_.Set(value); -} - -} // namespace base diff --git a/chromium/base/metrics/stats_counters.h b/chromium/base/metrics/stats_counters.h deleted file mode 100644 index 0f8354f26a4..00000000000 --- a/chromium/base/metrics/stats_counters.h +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_METRICS_STATS_COUNTERS_H_ -#define BASE_METRICS_STATS_COUNTERS_H_ - -#include <string> - -#include "base/base_export.h" -#include "base/compiler_specific.h" -#include "base/metrics/stats_table.h" -#include "base/time/time.h" - -namespace base { - -// StatsCounters are dynamically created values which can be tracked in -// the StatsTable. They are designed to be lightweight to create and -// easy to use. -// -// Since StatsCounters can be created dynamically by name, there is -// a hash table lookup to find the counter in the table. A StatsCounter -// object can be created once and used across multiple threads safely. -// -// Example usage: -// { -// StatsCounter request_count("RequestCount"); -// request_count.Increment(); -// } -// -// Note that creating counters on the stack does work, however creating -// the counter object requires a hash table lookup. For inner loops, it -// may be better to create the counter either as a member of another object -// (or otherwise outside of the loop) for maximum performance. -// -// Internally, a counter represents a value in a row of a StatsTable. -// The row has a 32bit value for each process/thread in the table and also -// a name (stored in the table metadata). -// -// NOTE: In order to make stats_counters usable in lots of different code, -// avoid any dependencies inside this header file. -// - -//------------------------------------------------------------------------------ -// Define macros for ease of use. They also allow us to change definitions -// as the implementation varies, or depending on compile options. -//------------------------------------------------------------------------------ -// First provide generic macros, which exist in production as well as debug. -#define STATS_COUNTER(name, delta) do { \ - base::StatsCounter counter(name); \ - counter.Add(delta); \ -} while (0) - -#define SIMPLE_STATS_COUNTER(name) STATS_COUNTER(name, 1) - -#define RATE_COUNTER(name, duration) do { \ - base::StatsRate hit_count(name); \ - hit_count.AddTime(duration); \ -} while (0) - -// Define Debug vs non-debug flavors of macros. -#ifndef NDEBUG - -#define DSTATS_COUNTER(name, delta) STATS_COUNTER(name, delta) -#define DSIMPLE_STATS_COUNTER(name) SIMPLE_STATS_COUNTER(name) -#define DRATE_COUNTER(name, duration) RATE_COUNTER(name, duration) - -#else // NDEBUG - -#define DSTATS_COUNTER(name, delta) do {} while (0) -#define DSIMPLE_STATS_COUNTER(name) do {} while (0) -#define DRATE_COUNTER(name, duration) do {} while (0) - -#endif // NDEBUG - -//------------------------------------------------------------------------------ -// StatsCounter represents a counter in the StatsTable class. -class BASE_EXPORT StatsCounter { - public: - // Create a StatsCounter object. - explicit StatsCounter(const std::string& name); - virtual ~StatsCounter(); - - // Sets the counter to a specific value. - void Set(int value); - - // Increments the counter. - void Increment() { - Add(1); - } - - virtual void Add(int value); - - // Decrements the counter. - void Decrement() { - Add(-1); - } - - void Subtract(int value) { - Add(-value); - } - - // Is this counter enabled? - // Returns false if table is full. - bool Enabled() { - return GetPtr() != NULL; - } - - int value() { - int* loc = GetPtr(); - if (loc) return *loc; - return 0; - } - - protected: - StatsCounter(); - - // Returns the cached address of this counter location. - int* GetPtr(); - - std::string name_; - // The counter id in the table. We initialize to -1 (an invalid value) - // and then cache it once it has been looked up. The counter_id is - // valid across all threads and processes. - int32 counter_id_; -}; - - -// A StatsCounterTimer is a StatsCounter which keeps a timer during -// the scope of the StatsCounterTimer. On destruction, it will record -// its time measurement. -class BASE_EXPORT StatsCounterTimer : protected StatsCounter { - public: - // Constructs and starts the timer. - explicit StatsCounterTimer(const std::string& name); - ~StatsCounterTimer() override; - - // Start the timer. - void Start(); - - // Stop the timer and record the results. - void Stop(); - - // Returns true if the timer is running. - bool Running(); - - // Accept a TimeDelta to increment. - virtual void AddTime(TimeDelta time); - - protected: - // Compute the delta between start and stop, in milliseconds. - void Record(); - - TimeTicks start_time_; - TimeTicks stop_time_; -}; - -// A StatsRate is a timer that keeps a count of the number of intervals added so -// that several statistics can be produced: -// min, max, avg, count, total -class BASE_EXPORT StatsRate : public StatsCounterTimer { - public: - // Constructs and starts the timer. - explicit StatsRate(const std::string& name); - ~StatsRate() override; - - void Add(int value) override; - - private: - StatsCounter counter_; - StatsCounter largest_add_; -}; - - -// Helper class for scoping a timer or rate. -template<class T> class StatsScope { - public: - explicit StatsScope<T>(T& timer) - : timer_(timer) { - timer_.Start(); - } - - ~StatsScope() { - timer_.Stop(); - } - - void Stop() { - timer_.Stop(); - } - - private: - T& timer_; -}; - -} // namespace base - -#endif // BASE_METRICS_STATS_COUNTERS_H_ diff --git a/chromium/base/metrics/stats_table.cc b/chromium/base/metrics/stats_table.cc deleted file mode 100644 index 0986395dd98..00000000000 --- a/chromium/base/metrics/stats_table.cc +++ /dev/null @@ -1,617 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/metrics/stats_table.h" - -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "base/memory/shared_memory.h" -#include "base/process/process_handle.h" -#include "base/strings/string_piece.h" -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" -#include "base/threading/platform_thread.h" -#include "base/threading/thread_local_storage.h" - -namespace base { - -// The StatsTable uses a shared memory segment that is laid out as follows -// -// +-------------------------------------------+ -// | Version | Size | MaxCounters | MaxThreads | -// +-------------------------------------------+ -// | Thread names table | -// +-------------------------------------------+ -// | Thread TID table | -// +-------------------------------------------+ -// | Thread PID table | -// +-------------------------------------------+ -// | Counter names table | -// +-------------------------------------------+ -// | Data | -// +-------------------------------------------+ -// -// The data layout is a grid, where the columns are the thread_ids and the -// rows are the counter_ids. -// -// If the first character of the thread_name is '\0', then that column is -// empty. -// If the first character of the counter_name is '\0', then that row is -// empty. -// -// About Locking: -// This class is designed to be both multi-thread and multi-process safe. -// Aside from initialization, this is done by partitioning the data which -// each thread uses so that no locking is required. However, to allocate -// the rows and columns of the table to particular threads, locking is -// required. -// -// At the shared-memory level, we have a lock. This lock protects the -// shared-memory table only, and is used when we create new counters (e.g. -// use rows) or when we register new threads (e.g. use columns). Reading -// data from the table does not require any locking at the shared memory -// level. -// -// Each process which accesses the table will create a StatsTable object. -// The StatsTable maintains a hash table of the existing counters in the -// table for faster lookup. Since the hash table is process specific, -// each process maintains its own cache. We avoid complexity here by never -// de-allocating from the hash table. (Counters are dynamically added, -// but not dynamically removed). - -// In order for external viewers to be able to read our shared memory, -// we all need to use the same size ints. -COMPILE_ASSERT(sizeof(int)==4, expect_4_byte_ints); - -namespace { - -// An internal version in case we ever change the format of this -// file, and so that we can identify our table. -const int kTableVersion = 0x13131313; - -// The name for un-named counters and threads in the table. -const char kUnknownName[] = "<unknown>"; - -// Calculates delta to align an offset to the size of an int -inline int AlignOffset(int offset) { - return (sizeof(int) - (offset % sizeof(int))) % sizeof(int); -} - -inline int AlignedSize(int size) { - return size + AlignOffset(size); -} - -} // namespace - -// The StatsTable::Internal maintains convenience pointers into the -// shared memory segment. Use this class to keep the data structure -// clean and accessible. -class StatsTable::Internal { - public: - // Various header information contained in the memory mapped segment. - struct TableHeader { - int version; - int size; - int max_counters; - int max_threads; - }; - - // Construct a new Internal based on expected size parameters, or - // return NULL on failure. - static Internal* New(const StatsTable::TableIdentifier& table, - int size, - int max_threads, - int max_counters); - - SharedMemory* shared_memory() { return shared_memory_.get(); } - - // Accessors for our header pointers - TableHeader* table_header() const { return table_header_; } - int version() const { return table_header_->version; } - int size() const { return table_header_->size; } - int max_counters() const { return table_header_->max_counters; } - int max_threads() const { return table_header_->max_threads; } - - // Accessors for our tables - char* thread_name(int slot_id) const { - return &thread_names_table_[ - (slot_id-1) * (StatsTable::kMaxThreadNameLength)]; - } - PlatformThreadId* thread_tid(int slot_id) const { - return &(thread_tid_table_[slot_id-1]); - } - int* thread_pid(int slot_id) const { - return &(thread_pid_table_[slot_id-1]); - } - char* counter_name(int counter_id) const { - return &counter_names_table_[ - (counter_id-1) * (StatsTable::kMaxCounterNameLength)]; - } - int* row(int counter_id) const { - return &data_table_[(counter_id-1) * max_threads()]; - } - - private: - // Constructor is private because you should use New() instead. - explicit Internal(SharedMemory* shared_memory) - : shared_memory_(shared_memory), - table_header_(NULL), - thread_names_table_(NULL), - thread_tid_table_(NULL), - thread_pid_table_(NULL), - counter_names_table_(NULL), - data_table_(NULL) { - } - - // Create or open the SharedMemory used by the stats table. - static SharedMemory* CreateSharedMemory( - const StatsTable::TableIdentifier& table, - int size); - - // Initializes the table on first access. Sets header values - // appropriately and zeroes all counters. - void InitializeTable(void* memory, int size, int max_counters, - int max_threads); - - // Initializes our in-memory pointers into a pre-created StatsTable. - void ComputeMappedPointers(void* memory); - - scoped_ptr<SharedMemory> shared_memory_; - TableHeader* table_header_; - char* thread_names_table_; - PlatformThreadId* thread_tid_table_; - int* thread_pid_table_; - char* counter_names_table_; - int* data_table_; - - DISALLOW_COPY_AND_ASSIGN(Internal); -}; - -// static -StatsTable::Internal* StatsTable::Internal::New( - const StatsTable::TableIdentifier& table, - int size, - int max_threads, - int max_counters) { - scoped_ptr<SharedMemory> shared_memory(CreateSharedMemory(table, size)); - if (!shared_memory.get()) - return NULL; - if (!shared_memory->Map(size)) - return NULL; - void* memory = shared_memory->memory(); - - scoped_ptr<Internal> internal(new Internal(shared_memory.release())); - TableHeader* header = static_cast<TableHeader*>(memory); - - // If the version does not match, then assume the table needs - // to be initialized. - if (header->version != kTableVersion) - internal->InitializeTable(memory, size, max_counters, max_threads); - - // We have a valid table, so compute our pointers. - internal->ComputeMappedPointers(memory); - - return internal.release(); -} - -// static -SharedMemory* StatsTable::Internal::CreateSharedMemory( - const StatsTable::TableIdentifier& table, - int size) { -#if defined(OS_POSIX) - // Check for existing table. - if (table.fd != -1) - return new SharedMemory(table, false); - - // Otherwise we need to create it. - scoped_ptr<SharedMemory> shared_memory(new SharedMemory()); - if (!shared_memory->CreateAnonymous(size)) - return NULL; - return shared_memory.release(); -#elif defined(OS_WIN) - scoped_ptr<SharedMemory> shared_memory(new SharedMemory()); - if (table.empty()) { - // Create an anonymous table. - if (!shared_memory->CreateAnonymous(size)) - return NULL; - } else { - // Create a named table for sharing between processes. - if (!shared_memory->CreateNamedDeprecated(table, true, size)) - return NULL; - } - return shared_memory.release(); -#endif -} - -void StatsTable::Internal::InitializeTable(void* memory, int size, - int max_counters, - int max_threads) { - // Zero everything. - memset(memory, 0, size); - - // Initialize the header. - TableHeader* header = static_cast<TableHeader*>(memory); - header->version = kTableVersion; - header->size = size; - header->max_counters = max_counters; - header->max_threads = max_threads; -} - -void StatsTable::Internal::ComputeMappedPointers(void* memory) { - char* data = static_cast<char*>(memory); - int offset = 0; - - table_header_ = reinterpret_cast<TableHeader*>(data); - offset += sizeof(*table_header_); - offset += AlignOffset(offset); - - // Verify we're looking at a valid StatsTable. - DCHECK_EQ(table_header_->version, kTableVersion); - - thread_names_table_ = reinterpret_cast<char*>(data + offset); - offset += sizeof(char) * - max_threads() * StatsTable::kMaxThreadNameLength; - offset += AlignOffset(offset); - - thread_tid_table_ = reinterpret_cast<PlatformThreadId*>(data + offset); - offset += sizeof(int) * max_threads(); - offset += AlignOffset(offset); - - thread_pid_table_ = reinterpret_cast<int*>(data + offset); - offset += sizeof(int) * max_threads(); - offset += AlignOffset(offset); - - counter_names_table_ = reinterpret_cast<char*>(data + offset); - offset += sizeof(char) * - max_counters() * StatsTable::kMaxCounterNameLength; - offset += AlignOffset(offset); - - data_table_ = reinterpret_cast<int*>(data + offset); - offset += sizeof(int) * max_threads() * max_counters(); - - DCHECK_EQ(offset, size()); -} - -// TLSData carries the data stored in the TLS slots for the -// StatsTable. This is used so that we can properly cleanup when the -// thread exits and return the table slot. -// -// Each thread that calls RegisterThread in the StatsTable will have -// a TLSData stored in its TLS. -struct StatsTable::TLSData { - StatsTable* table; - int slot; -}; - -// We keep a singleton table which can be easily accessed. -StatsTable* global_table = NULL; - -StatsTable::StatsTable(const TableIdentifier& table, - int max_threads, - int max_counters) - : internal_(NULL), - tls_index_(SlotReturnFunction) { - int table_size = - AlignedSize(sizeof(Internal::TableHeader)) + - AlignedSize((max_counters * sizeof(char) * kMaxCounterNameLength)) + - AlignedSize((max_threads * sizeof(char) * kMaxThreadNameLength)) + - AlignedSize(max_threads * sizeof(int)) + - AlignedSize(max_threads * sizeof(int)) + - AlignedSize((sizeof(int) * (max_counters * max_threads))); - - internal_ = Internal::New(table, table_size, max_threads, max_counters); - - if (!internal_) - DPLOG(ERROR) << "StatsTable did not initialize"; -} - -StatsTable::~StatsTable() { - // Before we tear down our copy of the table, be sure to - // unregister our thread. - UnregisterThread(); - - // Return ThreadLocalStorage. At this point, if any registered threads - // still exist, they cannot Unregister. - tls_index_.Free(); - - // Cleanup our shared memory. - delete internal_; - - // If we are the global table, unregister ourselves. - if (global_table == this) - global_table = NULL; -} - -StatsTable* StatsTable::current() { - return global_table; -} - -void StatsTable::set_current(StatsTable* value) { - global_table = value; -} - -int StatsTable::GetSlot() const { - TLSData* data = GetTLSData(); - if (!data) - return 0; - return data->slot; -} - -int StatsTable::RegisterThread(const std::string& name) { - int slot = 0; - if (!internal_) - return 0; - - // Registering a thread requires that we lock the shared memory - // so that two threads don't grab the same slot. Fortunately, - // thread creation shouldn't happen in inner loops. - // TODO(viettrungluu): crbug.com/345734: Use a different locking mechanism. - { - SharedMemoryAutoLockDeprecated lock(internal_->shared_memory()); - slot = FindEmptyThread(); - if (!slot) { - return 0; - } - - // We have space, so consume a column in the table. - std::string thread_name = name; - if (name.empty()) - thread_name = kUnknownName; - strlcpy(internal_->thread_name(slot), thread_name.c_str(), - kMaxThreadNameLength); - *(internal_->thread_tid(slot)) = PlatformThread::CurrentId(); - *(internal_->thread_pid(slot)) = GetCurrentProcId(); - } - - // Set our thread local storage. - TLSData* data = new TLSData; - data->table = this; - data->slot = slot; - tls_index_.Set(data); - return slot; -} - -int StatsTable::CountThreadsRegistered() const { - if (!internal_) - return 0; - - // Loop through the shared memory and count the threads that are active. - // We intentionally do not lock the table during the operation. - int count = 0; - for (int index = 1; index <= internal_->max_threads(); index++) { - char* name = internal_->thread_name(index); - if (*name != '\0') - count++; - } - return count; -} - -int StatsTable::FindCounter(const std::string& name) { - // Note: the API returns counters numbered from 1..N, although - // internally, the array is 0..N-1. This is so that we can return - // zero as "not found". - if (!internal_) - return 0; - - // Create a scope for our auto-lock. - { - AutoLock scoped_lock(counters_lock_); - - // Attempt to find the counter. - CountersMap::const_iterator iter; - iter = counters_.find(name); - if (iter != counters_.end()) - return iter->second; - } - - // Counter does not exist, so add it. - return AddCounter(name); -} - -int* StatsTable::GetLocation(int counter_id, int slot_id) const { - if (!internal_) - return NULL; - if (slot_id > internal_->max_threads()) - return NULL; - - int* row = internal_->row(counter_id); - return &(row[slot_id-1]); -} - -const char* StatsTable::GetRowName(int index) const { - if (!internal_) - return NULL; - - return internal_->counter_name(index); -} - -int StatsTable::GetRowValue(int index) const { - return GetRowValue(index, 0); -} - -int StatsTable::GetRowValue(int index, int pid) const { - if (!internal_) - return 0; - - int rv = 0; - int* row = internal_->row(index); - for (int slot_id = 1; slot_id <= internal_->max_threads(); slot_id++) { - if (pid == 0 || *internal_->thread_pid(slot_id) == pid) - rv += row[slot_id-1]; - } - return rv; -} - -int StatsTable::GetCounterValue(const std::string& name) { - return GetCounterValue(name, 0); -} - -int StatsTable::GetCounterValue(const std::string& name, int pid) { - if (!internal_) - return 0; - - int row = FindCounter(name); - if (!row) - return 0; - return GetRowValue(row, pid); -} - -int StatsTable::GetMaxCounters() const { - if (!internal_) - return 0; - return internal_->max_counters(); -} - -int StatsTable::GetMaxThreads() const { - if (!internal_) - return 0; - return internal_->max_threads(); -} - -int* StatsTable::FindLocation(const char* name) { - // Get the static StatsTable - StatsTable *table = StatsTable::current(); - if (!table) - return NULL; - - // Get the slot for this thread. Try to register - // it if none exists. - int slot = table->GetSlot(); - if (!slot) - slot = table->RegisterThread(std::string()); - if (!slot) - return NULL; - - // Find the counter id for the counter. - std::string str_name(name); - int counter = table->FindCounter(str_name); - - // Now we can find the location in the table. - return table->GetLocation(counter, slot); -} - -void StatsTable::UnregisterThread() { - UnregisterThread(GetTLSData()); -} - -void StatsTable::UnregisterThread(TLSData* data) { - if (!data) - return; - DCHECK(internal_); - - // Mark the slot free by zeroing out the thread name. - char* name = internal_->thread_name(data->slot); - *name = '\0'; - - // Remove the calling thread's TLS so that it cannot use the slot. - tls_index_.Set(NULL); - delete data; -} - -void StatsTable::SlotReturnFunction(void* data) { - // This is called by the TLS destructor, which on some platforms has - // already cleared the TLS info, so use the tls_data argument - // rather than trying to fetch it ourselves. - TLSData* tls_data = static_cast<TLSData*>(data); - if (tls_data) { - DCHECK(tls_data->table); - tls_data->table->UnregisterThread(tls_data); - } -} - -int StatsTable::FindEmptyThread() const { - // Note: the API returns slots numbered from 1..N, although - // internally, the array is 0..N-1. This is so that we can return - // zero as "not found". - // - // The reason for doing this is because the thread 'slot' is stored - // in TLS, which is always initialized to zero, not -1. If 0 were - // returned as a valid slot number, it would be confused with the - // uninitialized state. - if (!internal_) - return 0; - - int index = 1; - for (; index <= internal_->max_threads(); index++) { - char* name = internal_->thread_name(index); - if (!*name) - break; - } - if (index > internal_->max_threads()) - return 0; // The table is full. - return index; -} - -int StatsTable::FindCounterOrEmptyRow(const std::string& name) const { - // Note: the API returns slots numbered from 1..N, although - // internally, the array is 0..N-1. This is so that we can return - // zero as "not found". - // - // There isn't much reason for this other than to be consistent - // with the way we track columns for thread slots. (See comments - // in FindEmptyThread for why it is done this way). - if (!internal_) - return 0; - - int free_slot = 0; - for (int index = 1; index <= internal_->max_counters(); index++) { - char* row_name = internal_->counter_name(index); - if (!*row_name && !free_slot) - free_slot = index; // save that we found a free slot - else if (!strncmp(row_name, name.c_str(), kMaxCounterNameLength)) - return index; - } - return free_slot; -} - -int StatsTable::AddCounter(const std::string& name) { - if (!internal_) - return 0; - - int counter_id = 0; - { - // To add a counter to the shared memory, we need the - // shared memory lock. - SharedMemoryAutoLockDeprecated lock(internal_->shared_memory()); - - // We have space, so create a new counter. - counter_id = FindCounterOrEmptyRow(name); - if (!counter_id) - return 0; - - std::string counter_name = name; - if (name.empty()) - counter_name = kUnknownName; - strlcpy(internal_->counter_name(counter_id), counter_name.c_str(), - kMaxCounterNameLength); - } - - // now add to our in-memory cache - { - AutoLock lock(counters_lock_); - counters_[name] = counter_id; - } - return counter_id; -} - -StatsTable::TLSData* StatsTable::GetTLSData() const { - TLSData* data = - static_cast<TLSData*>(tls_index_.Get()); - if (!data) - return NULL; - - DCHECK(data->slot); - DCHECK_EQ(data->table, this); - return data; -} - -#if defined(OS_POSIX) -SharedMemoryHandle StatsTable::GetSharedMemoryHandle() const { - if (!internal_) - return SharedMemory::NULLHandle(); - return internal_->shared_memory()->handle(); -} -#endif - -} // namespace base diff --git a/chromium/base/metrics/stats_table.h b/chromium/base/metrics/stats_table.h deleted file mode 100644 index 719e6304813..00000000000 --- a/chromium/base/metrics/stats_table.h +++ /dev/null @@ -1,220 +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. -// -// A StatsTable is a table of statistics. It can be used across multiple -// processes and threads, maintaining cheap statistics counters without -// locking. -// -// The goal is to make it very cheap and easy for developers to add -// counters to code, without having to build one-off utilities or mechanisms -// to track the counters, and also to allow a single "view" to display -// the contents of all counters. -// -// To achieve this, StatsTable creates a shared memory segment to store -// the data for the counters. Upon creation, it has a specific size -// which governs the maximum number of counters and concurrent -// threads/processes which can use it. -// - -#ifndef BASE_METRICS_STATS_TABLE_H_ -#define BASE_METRICS_STATS_TABLE_H_ - -#include <string> - -#include "base/base_export.h" -#include "base/basictypes.h" -#include "base/containers/hash_tables.h" -#include "base/memory/shared_memory.h" -#include "base/synchronization/lock.h" -#include "base/threading/thread_local_storage.h" -#include "build/build_config.h" - -#if defined(OS_POSIX) -#include "base/file_descriptor_posix.h" -#endif - -namespace base { - -class BASE_EXPORT StatsTable { - public: - // Identifies a StatsTable. We often want to share these between processes. - // - // On Windows, we use a named shared memory segment so the table identifier - // should be a relatively unique string identifying the table to use. An - // empty string can be used to use an anonymous shared memory segment for - // cases where the table does not need to be shared between processes. - // - // Posix does not support named memory so we explicitly share file - // descriptors. On Posix, pass a default-constructed file descriptor if a - // handle doesn't already exist, and a new one will be created. - // - // If a table doesn't already exist with the given identifier, a new one will - // be created with zeroed counters. -#if defined(OS_POSIX) - typedef FileDescriptor TableIdentifier; -#elif defined(OS_WIN) - typedef std::string TableIdentifier; -#endif - - // Create a new StatsTable. - // - // max_threads is the maximum number of threads the table will support. - // If the StatsTable already exists, this number is ignored. - // - // max_counters is the maximum number of counters the table will support. - // If the StatsTable already exists, this number is ignored. - StatsTable(const TableIdentifier& table, - int max_threads, - int max_counters); - - // Destroys the StatsTable. When the last StatsTable is destroyed - // (across all processes), the StatsTable is removed from disk. - ~StatsTable(); - - // For convenience, we create a static table. This is generally - // used automatically by the counters. - static StatsTable* current(); - - // Set the global table for use in this process. - static void set_current(StatsTable* value); - - // Get the slot id for the calling thread. Returns 0 if no - // slot is assigned. - int GetSlot() const; - - // All threads that contribute data to the table must register with the - // table first. This function will set thread local storage for the - // thread containing the location in the table where this thread will - // write its counter data. - // - // name is just a debugging tag to label the thread, and it does not - // need to be unique. It will be truncated to kMaxThreadNameLength-1 - // characters. - // - // On success, returns the slot id for this thread. On failure, - // returns 0. - int RegisterThread(const std::string& name); - - // Returns the number of threads currently registered. This is really not - // useful except for diagnostics and debugging. - int CountThreadsRegistered() const; - - // Find a counter in the StatsTable. - // - // Returns an id for the counter which can be used to call GetLocation(). - // If the counter does not exist, attempts to create a row for the new - // counter. If there is no space in the table for the new counter, - // returns 0. - int FindCounter(const std::string& name); - - // TODO(mbelshe): implement RemoveCounter. - - // Gets the location of a particular value in the table based on - // the counter id and slot id. - int* GetLocation(int counter_id, int slot_id) const; - - // Gets the counter name at a particular row. If the row is empty, - // returns NULL. - const char* GetRowName(int index) const; - - // Gets the sum of the values for a particular row. - int GetRowValue(int index) const; - - // Gets the sum of the values for a particular row for a given pid. - int GetRowValue(int index, int pid) const; - - // Gets the sum of the values for a particular counter. If the counter - // does not exist, creates the counter. - int GetCounterValue(const std::string& name); - - // Gets the sum of the values for a particular counter for a given pid. - // If the counter does not exist, creates the counter. - int GetCounterValue(const std::string& name, int pid); - - // The maxinum number of counters/rows in the table. - int GetMaxCounters() const; - - // The maxinum number of threads/columns in the table. - int GetMaxThreads() const; - -#if defined(OS_POSIX) - // Get the underlying shared memory handle for the table. - base::SharedMemoryHandle GetSharedMemoryHandle() const; -#endif - - // The maximum length (in characters) of a Thread's name including - // null terminator, as stored in the shared memory. - static const int kMaxThreadNameLength = 32; - - // The maximum length (in characters) of a Counter's name including - // null terminator, as stored in the shared memory. - static const int kMaxCounterNameLength = 64; - - // Convenience function to lookup a counter location for a - // counter by name for the calling thread. Will register - // the thread if it is not already registered. - static int* FindLocation(const char *name); - - private: - class Internal; - struct TLSData; - typedef hash_map<std::string, int> CountersMap; - - // Returns the space occupied by a thread in the table. Generally used - // if a thread terminates but the process continues. This function - // does not zero out the thread's counters. - // Cannot be used inside a posix tls destructor. - void UnregisterThread(); - - // This variant expects the tls data to be passed in, so it is safe to - // call from inside a posix tls destructor (see doc for pthread_key_create). - void UnregisterThread(TLSData* tls_data); - - // The SlotReturnFunction is called at thread exit for each thread - // which used the StatsTable. - static void SlotReturnFunction(void* data); - - // Locates a free slot in the table. Returns a number > 0 on success, - // or 0 on failure. The caller must hold the shared_memory lock when - // calling this function. - int FindEmptyThread() const; - - // Locates a counter in the table or finds an empty row. Returns a - // number > 0 on success, or 0 on failure. The caller must hold the - // shared_memory_lock when calling this function. - int FindCounterOrEmptyRow(const std::string& name) const; - - // Internal function to add a counter to the StatsTable. Assumes that - // the counter does not already exist in the table. - // - // name is a unique identifier for this counter, and will be truncated - // to kMaxCounterNameLength-1 characters. - // - // On success, returns the counter_id for the newly added counter. - // On failure, returns 0. - int AddCounter(const std::string& name); - - // Get the TLS data for the calling thread. Returns NULL if none is - // initialized. - TLSData* GetTLSData() const; - - Internal* internal_; - - // The counters_lock_ protects the counters_ hash table. - base::Lock counters_lock_; - - // The counters_ hash map is an in-memory hash of the counters. - // It is used for quick lookup of counters, but is cannot be used - // as a substitute for what is in the shared memory. Even though - // we don't have a counter in our hash table, another process may - // have created it. - CountersMap counters_; - ThreadLocalStorage::Slot tls_index_; - - DISALLOW_COPY_AND_ASSIGN(StatsTable); -}; - -} // namespace base - -#endif // BASE_METRICS_STATS_TABLE_H_ diff --git a/chromium/base/metrics/stats_table_unittest.cc b/chromium/base/metrics/stats_table_unittest.cc deleted file mode 100644 index 45b0a4397bc..00000000000 --- a/chromium/base/metrics/stats_table_unittest.cc +++ /dev/null @@ -1,402 +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/memory/shared_memory.h" -#include "base/metrics/stats_counters.h" -#include "base/metrics/stats_table.h" -#include "base/process/kill.h" -#include "base/strings/string_piece.h" -#include "base/strings/stringprintf.h" -#include "base/strings/utf_string_conversions.h" -#include "base/test/multiprocess_test.h" -#include "base/threading/platform_thread.h" -#include "base/threading/simple_thread.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/multiprocess_func_list.h" - -namespace base { - -class StatsTableTest : public MultiProcessTest { -}; - -// Open a StatsTable and verify that we can write to each of the -// locations in the table. -TEST_F(StatsTableTest, VerifySlots) { - const int kMaxThreads = 1; - const int kMaxCounter = 5; - StatsTable table(StatsTable::TableIdentifier(), kMaxThreads, kMaxCounter); - - // Register a single thread. - std::string thread_name = "mainThread"; - int slot_id = table.RegisterThread(thread_name); - EXPECT_NE(slot_id, 0); - - // Fill up the table with counters. - std::string counter_base_name = "counter"; - for (int index = 0; index < kMaxCounter; index++) { - std::string counter_name = counter_base_name; - base::StringAppendF(&counter_name, "counter.ctr%d", index); - int counter_id = table.FindCounter(counter_name); - EXPECT_GT(counter_id, 0); - } - - // Try to allocate an additional thread. Verify it fails. - slot_id = table.RegisterThread("too many threads"); - EXPECT_EQ(slot_id, 0); - - // Try to allocate an additional counter. Verify it fails. - int counter_id = table.FindCounter(counter_base_name); - EXPECT_EQ(counter_id, 0); -} - -// CounterZero will continually be set to 0. -const std::string kCounterZero = "CounterZero"; -// Counter1313 will continually be set to 1313. -const std::string kCounter1313 = "Counter1313"; -// CounterIncrement will be incremented each time. -const std::string kCounterIncrement = "CounterIncrement"; -// CounterDecrement will be decremented each time. -const std::string kCounterDecrement = "CounterDecrement"; -// CounterMixed will be incremented by odd numbered threads and -// decremented by even threads. -const std::string kCounterMixed = "CounterMixed"; -// The number of thread loops that we will do. -const int kThreadLoops = 100; - -class StatsTableThread : public SimpleThread { - public: - StatsTableThread(std::string name, int id) - : SimpleThread(name), - id_(id) {} - - void Run() override; - - private: - int id_; -}; - -void StatsTableThread::Run() { - // Each thread will open the shared memory and set counters - // concurrently in a loop. We'll use some pauses to - // mixup the thread scheduling. - - StatsCounter zero_counter(kCounterZero); - StatsCounter lucky13_counter(kCounter1313); - StatsCounter increment_counter(kCounterIncrement); - StatsCounter decrement_counter(kCounterDecrement); - for (int index = 0; index < kThreadLoops; index++) { - StatsCounter mixed_counter(kCounterMixed); // create this one in the loop - zero_counter.Set(0); - lucky13_counter.Set(1313); - increment_counter.Increment(); - decrement_counter.Decrement(); - if (id_ % 2) - mixed_counter.Decrement(); - else - mixed_counter.Increment(); - PlatformThread::Sleep(TimeDelta::FromMilliseconds(index % 10)); - } -} - -// Create a few threads and have them poke on their counters. -// See http://crbug.com/10611 for more information. -// It is disabled on Win x64 incremental linking pending resolution of -// http://crbug.com/251251. -#if defined(OS_MACOSX) || defined(THREAD_SANITIZER) || \ - (defined(OS_WIN) && defined(ARCH_CPU_X86_64) && \ - defined(INCREMENTAL_LINKING)) -#define MAYBE_MultipleThreads DISABLED_MultipleThreads -#else -#define MAYBE_MultipleThreads MultipleThreads -#endif -TEST_F(StatsTableTest, MAYBE_MultipleThreads) { - // Create a stats table. - const int kMaxThreads = 20; - const int kMaxCounter = 5; - StatsTable table(StatsTable::TableIdentifier(), kMaxThreads, kMaxCounter); - StatsTable::set_current(&table); - - EXPECT_EQ(0, table.CountThreadsRegistered()); - - // Spin up a set of threads to go bang on the various counters. - // After we join the threads, we'll make sure the counters - // contain the values we expected. - StatsTableThread* threads[kMaxThreads]; - - // Spawn the threads. - for (int index = 0; index < kMaxThreads; index++) { - threads[index] = new StatsTableThread("MultipleThreadsTest", index); - threads[index]->Start(); - } - - // Wait for the threads to finish. - for (int index = 0; index < kMaxThreads; index++) { - threads[index]->Join(); - delete threads[index]; - } - - StatsCounter zero_counter(kCounterZero); - StatsCounter lucky13_counter(kCounter1313); - StatsCounter increment_counter(kCounterIncrement); - StatsCounter decrement_counter(kCounterDecrement); - StatsCounter mixed_counter(kCounterMixed); - - // Verify the various counters are correct. - std::string name; - name = "c:" + kCounterZero; - EXPECT_EQ(0, table.GetCounterValue(name)); - name = "c:" + kCounter1313; - EXPECT_EQ(1313 * kMaxThreads, - table.GetCounterValue(name)); - name = "c:" + kCounterIncrement; - EXPECT_EQ(kMaxThreads * kThreadLoops, - table.GetCounterValue(name)); - name = "c:" + kCounterDecrement; - EXPECT_EQ(-kMaxThreads * kThreadLoops, - table.GetCounterValue(name)); - name = "c:" + kCounterMixed; - EXPECT_EQ((kMaxThreads % 2) * kThreadLoops, - table.GetCounterValue(name)); - EXPECT_EQ(0, table.CountThreadsRegistered()); -} - -// This multiprocess test only runs on Windows. On Posix, the shared memory -// handle is not sent between the processes properly. -#if defined(OS_WIN) -const std::string kMPTableName = "MultipleProcessStatTable"; - -MULTIPROCESS_TEST_MAIN(StatsTableMultipleProcessMain) { - // Each process will open the shared memory and set counters - // concurrently in a loop. We'll use some pauses to - // mixup the scheduling. - - StatsTable table(kMPTableName, 0, 0); - StatsTable::set_current(&table); - StatsCounter zero_counter(kCounterZero); - StatsCounter lucky13_counter(kCounter1313); - StatsCounter increment_counter(kCounterIncrement); - StatsCounter decrement_counter(kCounterDecrement); - for (int index = 0; index < kThreadLoops; index++) { - zero_counter.Set(0); - lucky13_counter.Set(1313); - increment_counter.Increment(); - decrement_counter.Decrement(); - PlatformThread::Sleep(TimeDelta::FromMilliseconds(index % 10)); - } - return 0; -} - -// Create a few processes and have them poke on their counters. -// This test is slow and flaky http://crbug.com/10611 -TEST_F(StatsTableTest, DISABLED_MultipleProcesses) { - // Create a stats table. - const int kMaxProcs = 20; - const int kMaxCounter = 5; - StatsTable table(kMPTableName, kMaxProcs, kMaxCounter); - StatsTable::set_current(&table); - EXPECT_EQ(0, table.CountThreadsRegistered()); - - // Spin up a set of processes to go bang on the various counters. - // After we join the processes, we'll make sure the counters - // contain the values we expected. - ProcessHandle procs[kMaxProcs]; - - // Spawn the processes. - for (int16 index = 0; index < kMaxProcs; index++) { - procs[index] = SpawnChild("StatsTableMultipleProcessMain"); - EXPECT_NE(kNullProcessHandle, procs[index]); - } - - // Wait for the processes to finish. - for (int index = 0; index < kMaxProcs; index++) { - EXPECT_TRUE(WaitForSingleProcess( - procs[index], base::TimeDelta::FromMinutes(1))); - CloseProcessHandle(procs[index]); - } - - StatsCounter zero_counter(kCounterZero); - StatsCounter lucky13_counter(kCounter1313); - StatsCounter increment_counter(kCounterIncrement); - StatsCounter decrement_counter(kCounterDecrement); - - // Verify the various counters are correct. - std::string name; - name = "c:" + kCounterZero; - EXPECT_EQ(0, table.GetCounterValue(name)); - name = "c:" + kCounter1313; - EXPECT_EQ(1313 * kMaxProcs, - table.GetCounterValue(name)); - name = "c:" + kCounterIncrement; - EXPECT_EQ(kMaxProcs * kThreadLoops, - table.GetCounterValue(name)); - name = "c:" + kCounterDecrement; - EXPECT_EQ(-kMaxProcs * kThreadLoops, - table.GetCounterValue(name)); - EXPECT_EQ(0, table.CountThreadsRegistered()); -} -#endif - -class MockStatsCounter : public StatsCounter { - public: - explicit MockStatsCounter(const std::string& name) - : StatsCounter(name) {} - int* Pointer() { return GetPtr(); } -}; - -// Test some basic StatsCounter operations -TEST_F(StatsTableTest, StatsCounter) { - // Create a stats table. - const int kMaxThreads = 20; - const int kMaxCounter = 5; - StatsTable table(StatsTable::TableIdentifier(), kMaxThreads, kMaxCounter); - StatsTable::set_current(&table); - - MockStatsCounter foo("foo"); - - // Test initial state. - EXPECT_TRUE(foo.Enabled()); - ASSERT_NE(foo.Pointer(), static_cast<int*>(0)); - EXPECT_EQ(0, *(foo.Pointer())); - EXPECT_EQ(0, table.GetCounterValue("c:foo")); - - // Test Increment. - while (*(foo.Pointer()) < 123) foo.Increment(); - EXPECT_EQ(123, table.GetCounterValue("c:foo")); - foo.Add(0); - EXPECT_EQ(123, table.GetCounterValue("c:foo")); - foo.Add(-1); - EXPECT_EQ(122, table.GetCounterValue("c:foo")); - - // Test Set. - foo.Set(0); - EXPECT_EQ(0, table.GetCounterValue("c:foo")); - foo.Set(100); - EXPECT_EQ(100, table.GetCounterValue("c:foo")); - foo.Set(-1); - EXPECT_EQ(-1, table.GetCounterValue("c:foo")); - foo.Set(0); - EXPECT_EQ(0, table.GetCounterValue("c:foo")); - - // Test Decrement. - foo.Subtract(1); - EXPECT_EQ(-1, table.GetCounterValue("c:foo")); - foo.Subtract(0); - EXPECT_EQ(-1, table.GetCounterValue("c:foo")); - foo.Subtract(-1); - EXPECT_EQ(0, table.GetCounterValue("c:foo")); -} - -class MockStatsCounterTimer : public StatsCounterTimer { - public: - explicit MockStatsCounterTimer(const std::string& name) - : StatsCounterTimer(name) {} - - TimeTicks start_time() { return start_time_; } - TimeTicks stop_time() { return stop_time_; } -}; - -// Test some basic StatsCounterTimer operations -TEST_F(StatsTableTest, StatsCounterTimer) { - // Create a stats table. - const int kMaxThreads = 20; - const int kMaxCounter = 5; - StatsTable table(StatsTable::TableIdentifier(), kMaxThreads, kMaxCounter); - StatsTable::set_current(&table); - - MockStatsCounterTimer bar("bar"); - - // Test initial state. - EXPECT_FALSE(bar.Running()); - EXPECT_TRUE(bar.start_time().is_null()); - EXPECT_TRUE(bar.stop_time().is_null()); - - const TimeDelta kDuration = TimeDelta::FromMilliseconds(100); - - // Do some timing. - bar.Start(); - PlatformThread::Sleep(kDuration); - bar.Stop(); - EXPECT_GT(table.GetCounterValue("t:bar"), 0); - EXPECT_LE(kDuration.InMilliseconds(), table.GetCounterValue("t:bar")); - - // Verify that timing again is additive. - bar.Start(); - PlatformThread::Sleep(kDuration); - bar.Stop(); - EXPECT_GT(table.GetCounterValue("t:bar"), 0); - EXPECT_LE(kDuration.InMilliseconds() * 2, table.GetCounterValue("t:bar")); -} - -// Test some basic StatsRate operations -TEST_F(StatsTableTest, StatsRate) { - // Create a stats table. - const int kMaxThreads = 20; - const int kMaxCounter = 5; - StatsTable table(StatsTable::TableIdentifier(), kMaxThreads, kMaxCounter); - StatsTable::set_current(&table); - - StatsRate baz("baz"); - - // Test initial state. - EXPECT_FALSE(baz.Running()); - EXPECT_EQ(0, table.GetCounterValue("c:baz")); - EXPECT_EQ(0, table.GetCounterValue("t:baz")); - - const TimeDelta kDuration = TimeDelta::FromMilliseconds(100); - - // Do some timing. - baz.Start(); - PlatformThread::Sleep(kDuration); - baz.Stop(); - EXPECT_EQ(1, table.GetCounterValue("c:baz")); - EXPECT_LE(kDuration.InMilliseconds(), table.GetCounterValue("t:baz")); - - // Verify that timing again is additive. - baz.Start(); - PlatformThread::Sleep(kDuration); - baz.Stop(); - EXPECT_EQ(2, table.GetCounterValue("c:baz")); - EXPECT_LE(kDuration.InMilliseconds() * 2, table.GetCounterValue("t:baz")); -} - -// Test some basic StatsScope operations -TEST_F(StatsTableTest, StatsScope) { - // Create a stats table. - const int kMaxThreads = 20; - const int kMaxCounter = 5; - StatsTable table(StatsTable::TableIdentifier(), kMaxThreads, kMaxCounter); - StatsTable::set_current(&table); - - StatsCounterTimer foo("foo"); - StatsRate bar("bar"); - - // Test initial state. - EXPECT_EQ(0, table.GetCounterValue("t:foo")); - EXPECT_EQ(0, table.GetCounterValue("t:bar")); - EXPECT_EQ(0, table.GetCounterValue("c:bar")); - - const TimeDelta kDuration = TimeDelta::FromMilliseconds(100); - - // Try a scope. - { - StatsScope<StatsCounterTimer> timer(foo); - StatsScope<StatsRate> timer2(bar); - PlatformThread::Sleep(kDuration); - } - EXPECT_LE(kDuration.InMilliseconds(), table.GetCounterValue("t:foo")); - EXPECT_LE(kDuration.InMilliseconds(), table.GetCounterValue("t:bar")); - EXPECT_EQ(1, table.GetCounterValue("c:bar")); - - // Try a second scope. - { - StatsScope<StatsCounterTimer> timer(foo); - StatsScope<StatsRate> timer2(bar); - PlatformThread::Sleep(kDuration); - } - EXPECT_LE(kDuration.InMilliseconds() * 2, table.GetCounterValue("t:foo")); - EXPECT_LE(kDuration.InMilliseconds() * 2, table.GetCounterValue("t:bar")); - EXPECT_EQ(2, table.GetCounterValue("c:bar")); -} - -} // namespace base diff --git a/chromium/base/move.h b/chromium/base/move.h index 06f3f323723..87dc52d16c5 100644 --- a/chromium/base/move.h +++ b/chromium/base/move.h @@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/compiler_specific.h" - #ifndef BASE_MOVE_H_ #define BASE_MOVE_H_ +#include "base/compiler_specific.h" + // Macro with the boilerplate that makes a type move-only in C++03. // // USAGE @@ -219,11 +219,16 @@ #define MOVE_ONLY_TYPE_WITH_MOVE_CONSTRUCTOR_FOR_CPP_03(type) \ private: \ - type(type&); \ - void operator=(type&); \ + type(const type&); \ + void operator=(const type&); \ public: \ type&& Pass() WARN_UNUSED_RESULT { return static_cast<type&&>(*this); } \ typedef void MoveOnlyTypeForCPP03; \ private: +#define TYPE_WITH_MOVE_CONSTRUCTOR_FOR_CPP_03(type) \ + public: \ + type&& Pass() WARN_UNUSED_RESULT { return static_cast<type&&>(*this); } \ + private: + #endif // BASE_MOVE_H_ diff --git a/chromium/base/move_unittest.cc b/chromium/base/move_unittest.cc new file mode 100644 index 00000000000..1f4ce84cb6a --- /dev/null +++ b/chromium/base/move_unittest.cc @@ -0,0 +1,49 @@ +// Copyright (c) 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/move.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +class MoveOnly { + MOVE_ONLY_TYPE_WITH_MOVE_CONSTRUCTOR_FOR_CPP_03(MoveOnly) + + public: + MoveOnly() {} + + MoveOnly(MoveOnly&& other) {} + MoveOnly& operator=(MoveOnly&& other) { return *this; } +}; + +class Container { + public: + Container() = default; + Container(const Container& other) = default; + Container& operator=(const Container& other) = default; + + Container(Container&& other) { value_ = other.value_.Pass(); } + + Container& operator=(Container&& other) { + value_ = other.value_.Pass(); + return *this; + } + + private: + MoveOnly value_; +}; + +Container GetContainerRvalue() { + Container x; + return x; +} + +TEST(MoveTest, CopyableContainerCanBeMoved) { + // Container should be move-constructible and move-assignable. + Container y = GetContainerRvalue(); + y = GetContainerRvalue(); +} + +} // namespace diff --git a/chromium/base/native_library_ios.mm b/chromium/base/native_library_ios.mm new file mode 100644 index 00000000000..030c171eceb --- /dev/null +++ b/chromium/base/native_library_ios.mm @@ -0,0 +1,40 @@ +// Copyright (c) 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/native_library.h" + +#include "base/logging.h" + +namespace base { + +std::string NativeLibraryLoadError::ToString() const { + return message; +} + +// static +NativeLibrary LoadNativeLibrary(const base::FilePath& library_path, + NativeLibraryLoadError* error) { + NOTIMPLEMENTED(); + return nullptr; +} + +// static +void UnloadNativeLibrary(NativeLibrary library) { + NOTIMPLEMENTED(); + DCHECK(!library); +} + +// static +void* GetFunctionPointerFromNativeLibrary(NativeLibrary library, + const char* name) { + NOTIMPLEMENTED(); + return nullptr; +} + +// static +string16 GetNativeLibraryName(const string16& name) { + return name; +} + +} // namespace base diff --git a/chromium/base/nix/xdg_util.cc b/chromium/base/nix/xdg_util.cc index e2a48d4b938..ef045617761 100644 --- a/chromium/base/nix/xdg_util.cc +++ b/chromium/base/nix/xdg_util.cc @@ -33,7 +33,7 @@ FilePath GetXDGDirectory(Environment* env, const char* env_name, if (env->GetVar(env_name, &env_value) && !env_value.empty()) { path = FilePath(env_value); } else { - PathService::Get(base::DIR_HOME, &path); + PathService::Get(DIR_HOME, &path); path = path.Append(fallback_dir); } return path.StripTrailingSeparators(); @@ -46,7 +46,7 @@ FilePath GetXDGUserDirectory(const char* dir_name, const char* fallback_dir) { path = FilePath(xdg_dir); free(xdg_dir); } else { - PathService::Get(base::DIR_HOME, &path); + PathService::Get(DIR_HOME, &path); path = path.Append(fallback_dir); } return path.StripTrailingSeparators(); @@ -68,15 +68,17 @@ DesktopEnvironment GetDesktopEnvironment(Environment* env) { return DESKTOP_ENVIRONMENT_UNITY; } else if (xdg_current_desktop == "GNOME") { return DESKTOP_ENVIRONMENT_GNOME; + } else if (xdg_current_desktop == "KDE") { + return DESKTOP_ENVIRONMENT_KDE4; } } // DESKTOP_SESSION was what everyone used in 2010. std::string desktop_session; if (env->GetVar("DESKTOP_SESSION", &desktop_session)) { - if (desktop_session == "gnome") { + if (desktop_session == "gnome" || desktop_session =="mate") { return DESKTOP_ENVIRONMENT_GNOME; - } else if (desktop_session == "kde4") { + } else if (desktop_session == "kde4" || desktop_session == "kde-plasma") { return DESKTOP_ENVIRONMENT_KDE4; } else if (desktop_session == "kde") { // This may mean KDE4 on newer systems, so we have to check. diff --git a/chromium/base/nix/xdg_util_unittest.cc b/chromium/base/nix/xdg_util_unittest.cc index 6d10d3e3f00..136eb5d745d 100644 --- a/chromium/base/nix/xdg_util_unittest.cc +++ b/chromium/base/nix/xdg_util_unittest.cc @@ -25,51 +25,104 @@ class MockEnvironment : public Environment { MOCK_METHOD1(UnSetVar, bool(const char*)); }; -const char* const kGnome = "gnome"; -const char* const kKDE4 = "kde4"; -const char* const kKDE = "kde"; -const char* const kXFCE = "xfce"; +// Needs to be const char* to make gmock happy. +const char* const kDesktopGnome = "gnome"; +const char* const kDesktopGnomeFallback = "gnome-fallback"; +const char* const kDesktopMATE = "mate"; +const char* const kDesktopKDE4 = "kde4"; +const char* const kDesktopKDE = "kde"; +const char* const kDesktopXFCE = "xfce"; +const char* const kXdgDesktopGNOME = "GNOME"; +const char* const kXdgDesktopKDE = "KDE"; +const char* const kXdgDesktopUnity = "Unity"; + +const char kDesktopSession[] = "DESKTOP_SESSION"; +const char kXdgDesktop[] = "XDG_CURRENT_DESKTOP"; } // namespace TEST(XDGUtilTest, GetDesktopEnvironmentGnome) { MockEnvironment getter; EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false)); - EXPECT_CALL(getter, GetVar(StrEq("DESKTOP_SESSION"), _)) - .WillOnce(DoAll(SetArgumentPointee<1>(kGnome), Return(true))); + EXPECT_CALL(getter, GetVar(StrEq(kDesktopSession), _)) + .WillOnce(DoAll(SetArgumentPointee<1>(kDesktopGnome), Return(true))); - EXPECT_EQ(DESKTOP_ENVIRONMENT_GNOME, - GetDesktopEnvironment(&getter)); + EXPECT_EQ(DESKTOP_ENVIRONMENT_GNOME, GetDesktopEnvironment(&getter)); +} + +TEST(XDGUtilTest, GetDesktopEnvironmentMATE) { + MockEnvironment getter; + EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false)); + EXPECT_CALL(getter, GetVar(StrEq(kDesktopSession), _)) + .WillOnce(DoAll(SetArgumentPointee<1>(kDesktopMATE), Return(true))); + + EXPECT_EQ(DESKTOP_ENVIRONMENT_GNOME, GetDesktopEnvironment(&getter)); } TEST(XDGUtilTest, GetDesktopEnvironmentKDE4) { MockEnvironment getter; EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false)); - EXPECT_CALL(getter, GetVar(StrEq("DESKTOP_SESSION"), _)) - .WillOnce(DoAll(SetArgumentPointee<1>(kKDE4), Return(true))); + EXPECT_CALL(getter, GetVar(StrEq(kDesktopSession), _)) + .WillOnce(DoAll(SetArgumentPointee<1>(kDesktopKDE4), Return(true))); - EXPECT_EQ(DESKTOP_ENVIRONMENT_KDE4, - GetDesktopEnvironment(&getter)); + EXPECT_EQ(DESKTOP_ENVIRONMENT_KDE4, GetDesktopEnvironment(&getter)); } TEST(XDGUtilTest, GetDesktopEnvironmentKDE3) { MockEnvironment getter; EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false)); - EXPECT_CALL(getter, GetVar(StrEq("DESKTOP_SESSION"), _)) - .WillOnce(DoAll(SetArgumentPointee<1>(kKDE), Return(true))); + EXPECT_CALL(getter, GetVar(StrEq(kDesktopSession), _)) + .WillOnce(DoAll(SetArgumentPointee<1>(kDesktopKDE), Return(true))); - EXPECT_EQ(DESKTOP_ENVIRONMENT_KDE3, - GetDesktopEnvironment(&getter)); + EXPECT_EQ(DESKTOP_ENVIRONMENT_KDE3, GetDesktopEnvironment(&getter)); } TEST(XDGUtilTest, GetDesktopEnvironmentXFCE) { MockEnvironment getter; EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false)); - EXPECT_CALL(getter, GetVar(StrEq("DESKTOP_SESSION"), _)) - .WillOnce(DoAll(SetArgumentPointee<1>(kXFCE), Return(true))); + EXPECT_CALL(getter, GetVar(StrEq(kDesktopSession), _)) + .WillOnce(DoAll(SetArgumentPointee<1>(kDesktopXFCE), Return(true))); + + EXPECT_EQ(DESKTOP_ENVIRONMENT_XFCE, GetDesktopEnvironment(&getter)); +} + +TEST(XDGUtilTest, GetXdgDesktopGnome) { + MockEnvironment getter; + EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false)); + EXPECT_CALL(getter, GetVar(StrEq(kXdgDesktop), _)) + .WillOnce(DoAll(SetArgumentPointee<1>(kXdgDesktopGNOME), Return(true))); + + EXPECT_EQ(DESKTOP_ENVIRONMENT_GNOME, GetDesktopEnvironment(&getter)); +} + +TEST(XDGUtilTest, GetXdgDesktopGnomeFallback) { + MockEnvironment getter; + EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false)); + EXPECT_CALL(getter, GetVar(StrEq(kXdgDesktop), _)) + .WillOnce(DoAll(SetArgumentPointee<1>(kXdgDesktopUnity), Return(true))); + EXPECT_CALL(getter, GetVar(StrEq(kDesktopSession), _)) + .WillOnce(DoAll(SetArgumentPointee<1>(kDesktopGnomeFallback), + Return(true))); + + EXPECT_EQ(DESKTOP_ENVIRONMENT_GNOME, GetDesktopEnvironment(&getter)); +} + +TEST(XDGUtilTest, GetXdgDesktopKDE4) { + MockEnvironment getter; + EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false)); + EXPECT_CALL(getter, GetVar(StrEq(kXdgDesktop), _)) + .WillOnce(DoAll(SetArgumentPointee<1>(kXdgDesktopKDE), Return(true))); + + EXPECT_EQ(DESKTOP_ENVIRONMENT_KDE4, GetDesktopEnvironment(&getter)); +} + +TEST(XDGUtilTest, GetXdgDesktopUnity) { + MockEnvironment getter; + EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false)); + EXPECT_CALL(getter, GetVar(StrEq(kXdgDesktop), _)) + .WillOnce(DoAll(SetArgumentPointee<1>(kXdgDesktopUnity), Return(true))); - EXPECT_EQ(DESKTOP_ENVIRONMENT_XFCE, - GetDesktopEnvironment(&getter)); + EXPECT_EQ(DESKTOP_ENVIRONMENT_UNITY, GetDesktopEnvironment(&getter)); } } // namespace nix diff --git a/chromium/base/numerics/safe_conversions.h b/chromium/base/numerics/safe_conversions.h index fe85fc6f52d..d9b77f76d50 100644 --- a/chromium/base/numerics/safe_conversions.h +++ b/chromium/base/numerics/safe_conversions.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_SAFE_CONVERSIONS_H_ -#define BASE_SAFE_CONVERSIONS_H_ +#ifndef BASE_NUMERICS_SAFE_CONVERSIONS_H_ +#define BASE_NUMERICS_SAFE_CONVERSIONS_H_ #include <limits> @@ -60,5 +60,4 @@ inline Dst saturated_cast(Src value) { } // namespace base -#endif // BASE_SAFE_CONVERSIONS_H_ - +#endif // BASE_NUMERICS_SAFE_CONVERSIONS_H_ diff --git a/chromium/base/numerics/safe_conversions_impl.h b/chromium/base/numerics/safe_conversions_impl.h index c26757a4b3c..504ce7ef297 100644 --- a/chromium/base/numerics/safe_conversions_impl.h +++ b/chromium/base/numerics/safe_conversions_impl.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_SAFE_CONVERSIONS_IMPL_H_ -#define BASE_SAFE_CONVERSIONS_IMPL_H_ +#ifndef BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ +#define BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ #include <limits> @@ -212,5 +212,4 @@ inline RangeConstraint DstRangeRelationToSrcRange(Src value) { } // namespace internal } // namespace base -#endif // BASE_SAFE_CONVERSIONS_IMPL_H_ - +#endif // BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ diff --git a/chromium/base/numerics/safe_math.h b/chromium/base/numerics/safe_math.h index ccda1c873e6..1309446e195 100644 --- a/chromium/base/numerics/safe_math.h +++ b/chromium/base/numerics/safe_math.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_SAFE_MATH_H_ -#define BASE_SAFE_MATH_H_ +#ifndef BASE_NUMERICS_SAFE_MATH_H_ +#define BASE_NUMERICS_SAFE_MATH_H_ #include "base/numerics/safe_math_impl.h" @@ -269,4 +269,4 @@ using internal::CheckedNumeric; } // namespace base -#endif // BASE_SAFE_MATH_H_ +#endif // BASE_NUMERICS_SAFE_MATH_H_ diff --git a/chromium/base/numerics/safe_math_impl.h b/chromium/base/numerics/safe_math_impl.h index 663f393665d..08f2e88345f 100644 --- a/chromium/base/numerics/safe_math_impl.h +++ b/chromium/base/numerics/safe_math_impl.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SAFE_MATH_IMPL_H_ -#define SAFE_MATH_IMPL_H_ +#ifndef BASE_NUMERICS_SAFE_MATH_IMPL_H_ +#define BASE_NUMERICS_SAFE_MATH_IMPL_H_ #include <stdint.h> @@ -176,8 +176,8 @@ typename enable_if<std::numeric_limits<T>::is_integer&& std::numeric_limits< T>::is_signed&&(sizeof(T) * 2 > sizeof(uintmax_t)), T>::type CheckedMul(T x, T y, RangeConstraint* validity) { - // if either side is zero then the result will be zero. - if (!(x || y)) { + // If either side is zero then the result will be zero. + if (!x || !y) { return RANGE_VALID; } else if (x > 0) { @@ -498,4 +498,4 @@ struct IsIntegerArithmeticSafe { } // namespace internal } // namespace base -#endif // SAFE_MATH_IMPL_H_ +#endif // BASE_NUMERICS_SAFE_MATH_IMPL_H_ diff --git a/chromium/base/numerics/safe_numerics_unittest.cc b/chromium/base/numerics/safe_numerics_unittest.cc index 0402cef200a..3185b06faf0 100644 --- a/chromium/base/numerics/safe_numerics_unittest.cc +++ b/chromium/base/numerics/safe_numerics_unittest.cc @@ -26,14 +26,6 @@ using base::internal::RANGE_OVERFLOW; using base::internal::RANGE_UNDERFLOW; using base::enable_if; -// MSVS 2013 ia32 may not reset the FPU between calculations, and the test -// framework masks the exceptions. So we just force a manual reset after NaN. -inline void ResetFloatingPointUnit() { -#if defined(COMPILER_MSVC) && defined(ARCH_CPU_32_BITS) - _mm_empty(); -#endif -} - // These tests deliberately cause arithmetic overflows. If the compiler is // aggressive enough, it can const fold these overflows. Disable warnings about // overflows for const expressions. @@ -247,6 +239,9 @@ static void TestArithmetic(const char* dst, int line) { TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>() * 1)); TEST_EXPECTED_VALUE(1, (CheckedNumeric<Dst>(1) * 1)); TEST_EXPECTED_VALUE(-2, (CheckedNumeric<Dst>(-1) * 2)); + TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>(0) * 0)); + TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>(-1) * 0)); + TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>(0) * -1)); TEST_EXPECTED_VALIDITY( RANGE_OVERFLOW, CheckedNumeric<Dst>(DstLimits::max()) * DstLimits::max()); @@ -341,7 +336,6 @@ struct TestNumericConversion<Dst, Src, SIGN_PRESERVING_VALUE_PRESERVING> { TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::infinity()); TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::infinity() * -1); TEST_EXPECTED_RANGE(RANGE_INVALID, SrcLimits::quiet_NaN()); - ResetFloatingPointUnit(); } else if (numeric_limits<Src>::is_signed) { TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(-1)); TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::min()); @@ -373,7 +367,6 @@ struct TestNumericConversion<Dst, Src, SIGN_PRESERVING_NARROW> { TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::infinity()); TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::infinity() * -1); TEST_EXPECTED_RANGE(RANGE_INVALID, SrcLimits::quiet_NaN()); - ResetFloatingPointUnit(); } else if (SrcLimits::is_signed) { TEST_EXPECTED_VALUE(-1, checked_dst - static_cast<Src>(1)); TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::min()); @@ -432,7 +425,6 @@ struct TestNumericConversion<Dst, Src, SIGN_TO_UNSIGN_NARROW> { TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::infinity()); TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::infinity() * -1); TEST_EXPECTED_RANGE(RANGE_INVALID, SrcLimits::quiet_NaN()); - ResetFloatingPointUnit(); } else { TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::min()); } diff --git a/chromium/base/observer_list.h b/chromium/base/observer_list.h index c77ec15b1db..fb6f0262dfd 100644 --- a/chromium/base/observer_list.h +++ b/chromium/base/observer_list.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_OBSERVER_LIST_H__ -#define BASE_OBSERVER_LIST_H__ +#ifndef BASE_OBSERVER_LIST_H_ +#define BASE_OBSERVER_LIST_H_ #include <algorithm> #include <limits> @@ -79,7 +79,7 @@ class ObserverListBase // also the FOR_EACH_OBSERVER macro defined below. class Iterator { public: - Iterator(ObserverListBase<ObserverType>& list); + explicit Iterator(ObserverListBase<ObserverType>* list); ~Iterator(); ObserverType* GetNext(); @@ -100,7 +100,8 @@ class ObserverListBase // Remove an observer from the list if it is in the list. void RemoveObserver(ObserverType* obs); - bool HasObserver(ObserverType* observer) const; + // Determine whether a particular observer is in the list. + bool HasObserver(const ObserverType* observer) const; void Clear(); @@ -125,12 +126,11 @@ class ObserverListBase template <class ObserverType> ObserverListBase<ObserverType>::Iterator::Iterator( - ObserverListBase<ObserverType>& list) - : list_(list.AsWeakPtr()), + ObserverListBase<ObserverType>* list) + : list_(list->AsWeakPtr()), index_(0), - max_index_(list.type_ == NOTIFY_ALL ? - std::numeric_limits<size_t>::max() : - list.observers_.size()) { + max_index_(list->type_ == NOTIFY_ALL ? std::numeric_limits<size_t>::max() + : list->observers_.size()) { ++list_->notify_depth_; } @@ -143,17 +143,18 @@ ObserverListBase<ObserverType>::Iterator::~Iterator() { template <class ObserverType> ObserverType* ObserverListBase<ObserverType>::Iterator::GetNext() { if (!list_.get()) - return NULL; + return nullptr; ListType& observers = list_->observers_; // Advance if the current element is null size_t max_index = std::min(max_index_, observers.size()); while (index_ < max_index && !observers[index_]) ++index_; - return index_ < max_index ? observers[index_++] : NULL; + return index_ < max_index ? observers[index_++] : nullptr; } template <class ObserverType> void ObserverListBase<ObserverType>::AddObserver(ObserverType* obs) { + DCHECK(obs); if (std::find(observers_.begin(), observers_.end(), obs) != observers_.end()) { NOTREACHED() << "Observers can only be added once!"; @@ -164,11 +165,12 @@ void ObserverListBase<ObserverType>::AddObserver(ObserverType* obs) { template <class ObserverType> void ObserverListBase<ObserverType>::RemoveObserver(ObserverType* obs) { + DCHECK(obs); typename ListType::iterator it = std::find(observers_.begin(), observers_.end(), obs); if (it != observers_.end()) { if (notify_depth_) { - *it = 0; + *it = nullptr; } else { observers_.erase(it); } @@ -176,7 +178,8 @@ void ObserverListBase<ObserverType>::RemoveObserver(ObserverType* obs) { } template <class ObserverType> -bool ObserverListBase<ObserverType>::HasObserver(ObserverType* observer) const { +bool ObserverListBase<ObserverType>::HasObserver( + const ObserverType* observer) const { for (size_t i = 0; i < observers_.size(); ++i) { if (observers_[i] == observer) return true; @@ -189,7 +192,7 @@ void ObserverListBase<ObserverType>::Clear() { if (notify_depth_) { for (typename ListType::iterator it = observers_.begin(); it != observers_.end(); ++it) { - *it = 0; + *it = nullptr; } } else { observers_.clear(); @@ -199,8 +202,8 @@ void ObserverListBase<ObserverType>::Clear() { template <class ObserverType> void ObserverListBase<ObserverType>::Compact() { observers_.erase( - std::remove(observers_.begin(), observers_.end(), - static_cast<ObserverType*>(NULL)), observers_.end()); + std::remove(observers_.begin(), observers_.end(), nullptr), + observers_.end()); } template <class ObserverType, bool check_empty = false> @@ -226,15 +229,15 @@ class ObserverList : public ObserverListBase<ObserverType> { } }; -#define FOR_EACH_OBSERVER(ObserverType, observer_list, func) \ - do { \ - if ((observer_list).might_have_observers()) { \ - ObserverListBase<ObserverType>::Iterator \ - it_inside_observer_macro(observer_list); \ - ObserverType* obs; \ - while ((obs = it_inside_observer_macro.GetNext()) != NULL) \ - obs->func; \ - } \ +#define FOR_EACH_OBSERVER(ObserverType, observer_list, func) \ + do { \ + if ((observer_list).might_have_observers()) { \ + ObserverListBase<ObserverType>::Iterator it_inside_observer_macro( \ + &observer_list); \ + ObserverType* obs; \ + while ((obs = it_inside_observer_macro.GetNext()) != nullptr) \ + obs->func; \ + } \ } while (0) -#endif // BASE_OBSERVER_LIST_H__ +#endif // BASE_OBSERVER_LIST_H_ diff --git a/chromium/base/observer_list_threadsafe.h b/chromium/base/observer_list_threadsafe.h index 70b4f11ffa6..e0ce0daa90f 100644 --- a/chromium/base/observer_list_threadsafe.h +++ b/chromium/base/observer_list_threadsafe.h @@ -14,9 +14,10 @@ #include "base/logging.h" #include "base/memory/ref_counted.h" #include "base/message_loop/message_loop.h" -#include "base/message_loop/message_loop_proxy.h" #include "base/observer_list.h" +#include "base/single_thread_task_runner.h" #include "base/stl_util.h" +#include "base/thread_task_runner_handle.h" #include "base/threading/platform_thread.h" /////////////////////////////////////////////////////////////////////////////// @@ -111,7 +112,7 @@ class ObserverListThreadSafe if (!base::MessageLoop::current()) return; - ObserverList<ObserverType>* list = NULL; + ObserverList<ObserverType>* list = nullptr; base::PlatformThreadId thread_id = base::PlatformThread::CurrentId(); { base::AutoLock lock(list_lock_); @@ -128,8 +129,8 @@ class ObserverListThreadSafe // If the observer to be removed is in the list, RemoveObserver MUST // be called from the same thread which called AddObserver. void RemoveObserver(ObserverType* obs) { - ObserverListContext* context = NULL; - ObserverList<ObserverType>* list = NULL; + ObserverListContext* context = nullptr; + ObserverList<ObserverType>* list = nullptr; base::PlatformThreadId thread_id = base::PlatformThread::CurrentId(); { base::AutoLock lock(list_lock_); @@ -167,54 +168,37 @@ class ObserverListThreadSafe // Note, these calls are effectively asynchronous. You cannot assume // that at the completion of the Notify call that all Observers have // been Notified. The notification may still be pending delivery. - template <class Method> - void Notify(Method m) { - UnboundMethod<ObserverType, Method, Tuple0> method(m, MakeTuple()); - Notify<Method, Tuple0>(method); - } - - template <class Method, class A> - void Notify(Method m, const A& a) { - UnboundMethod<ObserverType, Method, Tuple1<A> > method(m, MakeTuple(a)); - Notify<Method, Tuple1<A> >(method); - } - - template <class Method, class A, class B> - void Notify(Method m, const A& a, const B& b) { - UnboundMethod<ObserverType, Method, Tuple2<A, B> > method( - m, MakeTuple(a, b)); - Notify<Method, Tuple2<A, B> >(method); - } + template <class Method, class... Params> + void Notify(const tracked_objects::Location& from_here, + Method m, + const Params&... params) { + UnboundMethod<ObserverType, Method, Tuple<Params...>> method( + m, MakeTuple(params...)); - template <class Method, class A, class B, class C> - void Notify(Method m, const A& a, const B& b, const C& c) { - UnboundMethod<ObserverType, Method, Tuple3<A, B, C> > method( - m, MakeTuple(a, b, c)); - Notify<Method, Tuple3<A, B, C> >(method); - } - - template <class Method, class A, class B, class C, class D> - void Notify(Method m, const A& a, const B& b, const C& c, const D& d) { - UnboundMethod<ObserverType, Method, Tuple4<A, B, C, D> > method( - m, MakeTuple(a, b, c, d)); - Notify<Method, Tuple4<A, B, C, D> >(method); + base::AutoLock lock(list_lock_); + for (const auto& entry : observer_lists_) { + ObserverListContext* context = entry.second; + context->task_runner->PostTask( + from_here, + base::Bind( + &ObserverListThreadSafe<ObserverType>::template NotifyWrapper< + Method, Tuple<Params...>>, + this, context, method)); + } } - // TODO(mbelshe): Add more wrappers for Notify() with more arguments. - private: // See comment above ObserverListThreadSafeTraits' definition. friend struct ObserverListThreadSafeTraits<ObserverType>; struct ObserverListContext { explicit ObserverListContext(NotificationType type) - : loop(base::MessageLoopProxy::current()), - list(type) { - } + : task_runner(base::ThreadTaskRunnerHandle::Get()), list(type) {} - scoped_refptr<base::MessageLoopProxy> loop; + scoped_refptr<base::SingleThreadTaskRunner> task_runner; ObserverList<ObserverType> list; + private: DISALLOW_COPY_AND_ASSIGN(ObserverListContext); }; @@ -222,26 +206,12 @@ class ObserverListThreadSafe STLDeleteValues(&observer_lists_); } - template <class Method, class Params> - void Notify(const UnboundMethod<ObserverType, Method, Params>& method) { - base::AutoLock lock(list_lock_); - typename ObserversListMap::iterator it; - for (it = observer_lists_.begin(); it != observer_lists_.end(); ++it) { - ObserverListContext* context = (*it).second; - context->loop->PostTask( - FROM_HERE, - base::Bind(&ObserverListThreadSafe<ObserverType>:: - template NotifyWrapper<Method, Params>, this, context, method)); - } - } - // Wrapper which is called to fire the notifications for each thread's // ObserverList. This function MUST be called on the thread which owns // the unsafe ObserverList. template <class Method, class Params> void NotifyWrapper(ObserverListContext* context, const UnboundMethod<ObserverType, Method, Params>& method) { - // Check that this list still needs notifications. { base::AutoLock lock(list_lock_); @@ -257,9 +227,9 @@ class ObserverListThreadSafe } { - typename ObserverList<ObserverType>::Iterator it(context->list); + typename ObserverList<ObserverType>::Iterator it(&context->list); ObserverType* obs; - while ((obs = it.GetNext()) != NULL) + while ((obs = it.GetNext()) != nullptr) method.Run(obs); } diff --git a/chromium/base/observer_list_unittest.cc b/chromium/base/observer_list_unittest.cc index 11f59be5065..2e51e455216 100644 --- a/chromium/base/observer_list_unittest.cc +++ b/chromium/base/observer_list_unittest.cc @@ -8,9 +8,10 @@ #include <vector> #include "base/compiler_specific.h" +#include "base/location.h" #include "base/memory/weak_ptr.h" -#include "base/message_loop/message_loop.h" #include "base/run_loop.h" +#include "base/single_thread_task_runner.h" #include "base/threading/platform_thread.h" #include "testing/gtest/include/gtest/gtest.h" @@ -71,7 +72,7 @@ class AddInObserve : public Foo { adder(1) { } - virtual void Observe(int x) override { + void Observe(int x) override { if (!added) { added = true; observer_list->AddObserver(&adder); @@ -94,7 +95,7 @@ class AddRemoveThread : public PlatformThread::Delegate, public: AddRemoveThread(ObserverListThreadSafe<Foo>* list, bool notify) : list_(list), - loop_(NULL), + loop_(nullptr), in_list_(false), start_(Time::Now()), count_observes_(0), @@ -107,7 +108,7 @@ class AddRemoveThread : public PlatformThread::Delegate, void ThreadMain() override { loop_ = new MessageLoop(); // Fire up a message loop. - loop_->PostTask( + loop_->task_runner()->PostTask( FROM_HERE, base::Bind(&AddRemoveThread::AddTask, weak_factory_.GetWeakPtr())); loop_->Run(); @@ -134,16 +135,17 @@ class AddRemoveThread : public PlatformThread::Delegate, } if (do_notifies_) { - list_->Notify(&Foo::Observe, 10); + list_->Notify(FROM_HERE, &Foo::Observe, 10); } - loop_->PostTask( + loop_->task_runner()->PostTask( FROM_HERE, base::Bind(&AddRemoveThread::AddTask, weak_factory_.GetWeakPtr())); } void Quit() { - loop_->PostTask(FROM_HERE, MessageLoop::QuitWhenIdleClosure()); + loop_->task_runner()->PostTask(FROM_HERE, + MessageLoop::QuitWhenIdleClosure()); } void Observe(int x) override { @@ -182,6 +184,9 @@ TEST(ObserverListTest, BasicTest) { observer_list.AddObserver(&a); observer_list.AddObserver(&b); + EXPECT_TRUE(observer_list.HasObserver(&a)); + EXPECT_FALSE(observer_list.HasObserver(&c)); + FOR_EACH_OBSERVER(Foo, observer_list, Observe(10)); observer_list.AddObserver(&evil); @@ -214,14 +219,14 @@ TEST(ObserverListThreadSafeTest, BasicTest) { observer_list->AddObserver(&a); observer_list->AddObserver(&b); - observer_list->Notify(&Foo::Observe, 10); + observer_list->Notify(FROM_HERE, &Foo::Observe, 10); RunLoop().RunUntilIdle(); observer_list->AddObserver(&evil); observer_list->AddObserver(&c); observer_list->AddObserver(&d); - observer_list->Notify(&Foo::Observe, 10); + observer_list->Notify(FROM_HERE, &Foo::Observe, 10); RunLoop().RunUntilIdle(); EXPECT_EQ(20, a.total); @@ -244,7 +249,7 @@ TEST(ObserverListThreadSafeTest, RemoveObserver) { observer_list->RemoveObserver(&a); observer_list->RemoveObserver(&b); - observer_list->Notify(&Foo::Observe, 10); + observer_list->Notify(FROM_HERE, &Foo::Observe, 10); RunLoop().RunUntilIdle(); EXPECT_EQ(0, a.total); @@ -255,7 +260,7 @@ TEST(ObserverListThreadSafeTest, RemoveObserver) { // Should also do nothing. observer_list->RemoveObserver(&b); - observer_list->Notify(&Foo::Observe, 10); + observer_list->Notify(FROM_HERE, &Foo::Observe, 10); RunLoop().RunUntilIdle(); EXPECT_EQ(10, a.total); @@ -277,7 +282,7 @@ TEST(ObserverListThreadSafeTest, WithoutMessageLoop) { MessageLoop loop; observer_list->AddObserver(&c); - observer_list->Notify(&Foo::Observe, 10); + observer_list->Notify(FROM_HERE, &Foo::Observe, 10); RunLoop().RunUntilIdle(); EXPECT_EQ(0, a.total); @@ -291,7 +296,7 @@ TEST(ObserverListThreadSafeTest, WithoutMessageLoop) { observer_list->RemoveObserver(&c); // Notify again. - observer_list->Notify(&Foo::Observe, 20); + observer_list->Notify(FROM_HERE, &Foo::Observe, 20); RunLoop().RunUntilIdle(); EXPECT_EQ(20, a.total); @@ -305,7 +310,7 @@ TEST(ObserverListThreadSafeTest, WithoutMessageLoop) { // Notifying should not fail but should also be a no-op. MessageLoop loop; observer_list->AddObserver(&b); - observer_list->Notify(&Foo::Observe, 30); + observer_list->Notify(FROM_HERE, &Foo::Observe, 30); RunLoop().RunUntilIdle(); EXPECT_EQ(20, a.total); @@ -350,7 +355,7 @@ TEST(ObserverListThreadSafeTest, RemoveMultipleObservers) { a.AddFooToRemove(&a); a.AddFooToRemove(&b); - observer_list->Notify(&Foo::Observe, 1); + observer_list->Notify(FROM_HERE, &Foo::Observe, 1); RunLoop().RunUntilIdle(); } @@ -389,7 +394,7 @@ static void ThreadSafeObserverHarness(int num_threads, if ((Time::Now() - start).InMilliseconds() > kThreadRunTime) break; - observer_list->Notify(&Foo::Observe, 10); + observer_list->Notify(FROM_HERE, &Foo::Observe, 10); RunLoop().RunUntilIdle(); } @@ -421,7 +426,7 @@ TEST(ObserverListThreadSafeTest, OutlivesMessageLoop) { observer_list->AddObserver(&a); delete loop; // Test passes if we don't crash here. - observer_list->Notify(&Foo::Observe, 1); + observer_list->Notify(FROM_HERE, &Foo::Observe, 1); } TEST(ObserverListTest, Existing) { @@ -455,7 +460,7 @@ TEST(ObserverListThreadSafeTest, Existing) { observer_list->AddObserver(&a); observer_list->AddObserver(&b); - observer_list->Notify(&Foo::Observe, 1); + observer_list->Notify(FROM_HERE, &Foo::Observe, 1); RunLoop().RunUntilIdle(); EXPECT_TRUE(b.added); @@ -464,7 +469,7 @@ TEST(ObserverListThreadSafeTest, Existing) { EXPECT_EQ(0, b.adder.total); // Notify again to make sure b's adder is notified. - observer_list->Notify(&Foo::Observe, 1); + observer_list->Notify(FROM_HERE, &Foo::Observe, 1); RunLoop().RunUntilIdle(); EXPECT_EQ(1, b.adder.total); } diff --git a/chromium/base/path_service.cc b/chromium/base/path_service.cc index ce4966ed709..3c437ee7493 100644 --- a/chromium/base/path_service.cc +++ b/chromium/base/path_service.cc @@ -33,7 +33,7 @@ namespace base { // Mac and Android. bool PathProviderPosix(int key, FilePath* result); #endif -} +} // namespace base namespace { diff --git a/chromium/base/path_service.h b/chromium/base/path_service.h index 554eb9e3464..025550f2ad1 100644 --- a/chromium/base/path_service.h +++ b/chromium/base/path_service.h @@ -15,7 +15,7 @@ namespace base { class FilePath; class ScopedPathOverride; -} // namespace +} // namespace base // The path service is a global table mapping keys to file system paths. It is // OK to use this service from multiple threads. diff --git a/chromium/base/path_service_unittest.cc b/chromium/base/path_service_unittest.cc index 543deb60a70..7551d676416 100644 --- a/chromium/base/path_service_unittest.cc +++ b/chromium/base/path_service_unittest.cc @@ -220,3 +220,55 @@ TEST_F(PathServiceTest, RemoveOverride) { EXPECT_TRUE(PathService::Get(base::DIR_TEMP, &new_user_data_dir)); EXPECT_EQ(original_user_data_dir, new_user_data_dir); } + +#if defined(OS_WIN) +TEST_F(PathServiceTest, GetProgramFiles) { + base::FilePath programfiles_dir; +#if defined(_WIN64) + // 64-bit on 64-bit. + EXPECT_TRUE(PathService::Get(base::DIR_PROGRAM_FILES, + &programfiles_dir)); + EXPECT_EQ(programfiles_dir.value(), + FILE_PATH_LITERAL("C:\\Program Files")); + EXPECT_TRUE(PathService::Get(base::DIR_PROGRAM_FILESX86, + &programfiles_dir)); + EXPECT_EQ(programfiles_dir.value(), + FILE_PATH_LITERAL("C:\\Program Files (x86)")); + EXPECT_TRUE(PathService::Get(base::DIR_PROGRAM_FILES6432, + &programfiles_dir)); + EXPECT_EQ(programfiles_dir.value(), + FILE_PATH_LITERAL("C:\\Program Files")); +#else + if (base::win::OSInfo::GetInstance()->wow64_status() == + base::win::OSInfo::WOW64_ENABLED) { + // 32-bit on 64-bit. + EXPECT_TRUE(PathService::Get(base::DIR_PROGRAM_FILES, + &programfiles_dir)); + EXPECT_EQ(programfiles_dir.value(), + FILE_PATH_LITERAL("C:\\Program Files (x86)")); + EXPECT_TRUE(PathService::Get(base::DIR_PROGRAM_FILESX86, + &programfiles_dir)); + EXPECT_EQ(programfiles_dir.value(), + FILE_PATH_LITERAL("C:\\Program Files (x86)")); + EXPECT_TRUE(PathService::Get(base::DIR_PROGRAM_FILES6432, + &programfiles_dir)); + EXPECT_EQ(programfiles_dir.value(), + FILE_PATH_LITERAL("C:\\Program Files")); + } else { + // 32-bit on 32-bit. + EXPECT_TRUE(PathService::Get(base::DIR_PROGRAM_FILES, + &programfiles_dir)); + EXPECT_EQ(programfiles_dir.value(), + FILE_PATH_LITERAL("C:\\Program Files")); + EXPECT_TRUE(PathService::Get(base::DIR_PROGRAM_FILESX86, + &programfiles_dir)); + EXPECT_EQ(programfiles_dir.value(), + FILE_PATH_LITERAL("C:\\Program Files")); + EXPECT_TRUE(PathService::Get(base::DIR_PROGRAM_FILES6432, + &programfiles_dir)); + EXPECT_EQ(programfiles_dir.value(), + FILE_PATH_LITERAL("C:\\Program Files")); + } +#endif +} +#endif diff --git a/chromium/base/pending_task.h b/chromium/base/pending_task.h index a2edc6947d0..fddfc868fe7 100644 --- a/chromium/base/pending_task.h +++ b/chromium/base/pending_task.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef PENDING_TASK_H_ -#define PENDING_TASK_H_ +#ifndef BASE_PENDING_TASK_H_ +#define BASE_PENDING_TASK_H_ #include <queue> @@ -57,4 +57,4 @@ typedef std::priority_queue<base::PendingTask> DelayedTaskQueue; } // namespace base -#endif // PENDING_TASK_H_ +#endif // BASE_PENDING_TASK_H_ diff --git a/chromium/base/pickle.cc b/chromium/base/pickle.cc index d461f413dfd..112ddc33e7b 100644 --- a/chromium/base/pickle.cc +++ b/chromium/base/pickle.cc @@ -152,15 +152,15 @@ bool PickleIterator::ReadString(std::string* result) { return true; } -bool PickleIterator::ReadWString(std::wstring* result) { +bool PickleIterator::ReadStringPiece(base::StringPiece* result) { int len; if (!ReadInt(&len)) return false; - const char* read_from = GetReadPointerAndAdvance(len, sizeof(wchar_t)); + const char* read_from = GetReadPointerAndAdvance(len); if (!read_from) return false; - result->assign(reinterpret_cast<const wchar_t*>(read_from), len); + *result = base::StringPiece(read_from, len); return true; } @@ -176,6 +176,19 @@ bool PickleIterator::ReadString16(string16* result) { return true; } +bool PickleIterator::ReadStringPiece16(base::StringPiece16* result) { + int len; + if (!ReadInt(&len)) + return false; + const char* read_from = GetReadPointerAndAdvance(len, sizeof(char16)); + if (!read_from) + return false; + + *result = base::StringPiece16(reinterpret_cast<const char16*>(read_from), + len); + return true; +} + bool PickleIterator::ReadData(const char** data, int* length) { *length = 0; *data = 0; @@ -271,22 +284,14 @@ Pickle& Pickle::operator=(const Pickle& other) { return *this; } -bool Pickle::WriteString(const std::string& value) { +bool Pickle::WriteString(const base::StringPiece& value) { if (!WriteInt(static_cast<int>(value.size()))) return false; return WriteBytes(value.data(), static_cast<int>(value.size())); } -bool Pickle::WriteWString(const std::wstring& value) { - if (!WriteInt(static_cast<int>(value.size()))) - return false; - - return WriteBytes(value.data(), - static_cast<int>(value.size() * sizeof(wchar_t))); -} - -bool Pickle::WriteString16(const string16& value) { +bool Pickle::WriteString16(const base::StringPiece16& value) { if (!WriteInt(static_cast<int>(value.size()))) return false; @@ -353,6 +358,7 @@ template void Pickle::WriteBytesStatic<8>(const void* data); inline void Pickle::WriteBytesCommon(const void* data, size_t length) { DCHECK_NE(kCapacityReadOnly, capacity_after_header_) << "oops: pickle is readonly"; + MSAN_CHECK_MEM_IS_INITIALIZED(data, length); size_t data_len = AlignInt(length, sizeof(uint32)); DCHECK_GE(data_len, length); #ifdef ARCH_CPU_64_BITS diff --git a/chromium/base/pickle.h b/chromium/base/pickle.h index 11cf4841f67..9589e2aadad 100644 --- a/chromium/base/pickle.h +++ b/chromium/base/pickle.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_PICKLE_H__ -#define BASE_PICKLE_H__ +#ifndef BASE_PICKLE_H_ +#define BASE_PICKLE_H_ #include <string> @@ -13,6 +13,7 @@ #include "base/gtest_prod_util.h" #include "base/logging.h" #include "base/strings/string16.h" +#include "base/strings/string_piece.h" class Pickle; @@ -26,7 +27,7 @@ class BASE_EXPORT PickleIterator { // Methods for reading the payload of the Pickle. To read from the start of // the Pickle, create a PickleIterator from a Pickle. If successful, these // methods return true. Otherwise, false is returned to indicate that the - // result could not be extracted. It is not possible to read from iterator + // result could not be extracted. It is not possible to read from the iterator // after that. bool ReadBool(bool* result) WARN_UNUSED_RESULT; bool ReadInt(int* result) WARN_UNUSED_RESULT; @@ -39,12 +40,26 @@ class BASE_EXPORT PickleIterator { bool ReadFloat(float* result) WARN_UNUSED_RESULT; bool ReadDouble(double* result) WARN_UNUSED_RESULT; bool ReadString(std::string* result) WARN_UNUSED_RESULT; - bool ReadWString(std::wstring* result) WARN_UNUSED_RESULT; + // The StringPiece data will only be valid for the lifetime of the message. + bool ReadStringPiece(base::StringPiece* result) WARN_UNUSED_RESULT; bool ReadString16(base::string16* result) WARN_UNUSED_RESULT; + // The StringPiece16 data will only be valid for the lifetime of the message. + bool ReadStringPiece16(base::StringPiece16* result) WARN_UNUSED_RESULT; + + // A pointer to the data will be placed in |*data|, and the length will be + // placed in |*length|. The pointer placed into |*data| points into the + // message's buffer so it will be scoped to the lifetime of the message (or + // until the message data is mutated). Do not keep the pointer around! bool ReadData(const char** data, int* length) WARN_UNUSED_RESULT; + + // A pointer to the data will be placed in |*data|. The caller specifies the + // number of bytes to read, and ReadBytes will validate this length. The + // pointer placed into |*data| points into the message's buffer so it will be + // scoped to the lifetime of the message (or until the message data is + // mutated). Do not keep the pointer around! bool ReadBytes(const char** data, int length) WARN_UNUSED_RESULT; - // Safer version of ReadInt() checks for the result not being negative. + // A safer version of ReadInt() that checks for the result not being negative. // Use it for reading the object sizes. bool ReadLength(int* result) WARN_UNUSED_RESULT { return ReadInt(result) && *result >= 0; @@ -57,7 +72,7 @@ class BASE_EXPORT PickleIterator { } private: - // Aligns 'i' by rounding it up to the next multiple of 'alignment' + // Aligns 'i' by rounding it up to the next multiple of 'alignment'. static size_t AlignInt(size_t i, int alignment) { return i + (alignment - (i % alignment)) % alignment; } @@ -142,91 +157,11 @@ class BASE_EXPORT Pickle { // Returns the data for this Pickle. const void* data() const { return header_; } - // For compatibility, these older style read methods pass through to the - // PickleIterator methods. - // TODO(jbates) Remove these methods. - bool ReadBool(PickleIterator* iter, - bool* result) const WARN_UNUSED_RESULT { - return iter->ReadBool(result); - } - bool ReadInt(PickleIterator* iter, - int* result) const WARN_UNUSED_RESULT { - return iter->ReadInt(result); - } - bool ReadLong(PickleIterator* iter, - long* result) const WARN_UNUSED_RESULT { - return iter->ReadLong(result); - } - bool ReadUInt16(PickleIterator* iter, - uint16* result) const WARN_UNUSED_RESULT { - return iter->ReadUInt16(result); - } - bool ReadUInt32(PickleIterator* iter, - uint32* result) const WARN_UNUSED_RESULT { - return iter->ReadUInt32(result); - } - bool ReadInt64(PickleIterator* iter, - int64* result) const WARN_UNUSED_RESULT { - return iter->ReadInt64(result); - } - bool ReadUInt64(PickleIterator* iter, - uint64* result) const WARN_UNUSED_RESULT { - return iter->ReadUInt64(result); - } - bool ReadSizeT(PickleIterator* iter, - size_t* result) const WARN_UNUSED_RESULT { - return iter->ReadSizeT(result); - } - bool ReadFloat(PickleIterator* iter, - float* result) const WARN_UNUSED_RESULT { - return iter->ReadFloat(result); - } - bool ReadDouble(PickleIterator* iter, - double* result) const WARN_UNUSED_RESULT { - return iter->ReadDouble(result); - } - bool ReadString(PickleIterator* iter, - std::string* result) const WARN_UNUSED_RESULT { - return iter->ReadString(result); - } - bool ReadWString(PickleIterator* iter, - std::wstring* result) const WARN_UNUSED_RESULT { - return iter->ReadWString(result); - } - bool ReadString16(PickleIterator* iter, - base::string16* result) const WARN_UNUSED_RESULT { - return iter->ReadString16(result); - } - // A pointer to the data will be placed in *data, and the length will be - // placed in *length. This buffer will be into the message's buffer so will - // be scoped to the lifetime of the message (or until the message data is - // mutated). - bool ReadData(PickleIterator* iter, - const char** data, - int* length) const WARN_UNUSED_RESULT { - return iter->ReadData(data, length); - } - // A pointer to the data will be placed in *data. The caller specifies the - // number of bytes to read, and ReadBytes will validate this length. The - // returned buffer will be into the message's buffer so will be scoped to the - // lifetime of the message (or until the message data is mutated). - bool ReadBytes(PickleIterator* iter, - const char** data, - int length) const WARN_UNUSED_RESULT { - return iter->ReadBytes(data, length); - } - - // Safer version of ReadInt() checks for the result not being negative. - // Use it for reading the object sizes. - bool ReadLength(PickleIterator* iter, - int* result) const WARN_UNUSED_RESULT { - return iter->ReadLength(result); - } - // 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 // to the Pickle. + bool WriteBool(bool value) { return WriteInt(value ? 1 : 0); } @@ -264,9 +199,8 @@ class BASE_EXPORT Pickle { bool WriteDouble(double value) { return WritePOD(value); } - bool WriteString(const std::string& value); - bool WriteWString(const std::wstring& value); - bool WriteString16(const base::string16& value); + bool WriteString(const base::StringPiece& value); + bool WriteString16(const base::StringPiece16& value); // "Data" is a blob with a length. When you read it out you will be given the // length. See also WriteBytes. bool WriteData(const char* data, int length); @@ -370,4 +304,4 @@ class BASE_EXPORT Pickle { FRIEND_TEST_ALL_PREFIXES(PickleTest, FindNextOverflow); }; -#endif // BASE_PICKLE_H__ +#endif // BASE_PICKLE_H_ diff --git a/chromium/base/pickle_unittest.cc b/chromium/base/pickle_unittest.cc index 20a8d674c1f..a2c405ce918 100644 --- a/chromium/base/pickle_unittest.cc +++ b/chromium/base/pickle_unittest.cc @@ -30,75 +30,82 @@ const double testdouble = 2.71828182845904523; const std::string teststring("Hello world"); // note non-aligned string length const std::wstring testwstring(L"Hello, world"); const base::string16 teststring16(base::ASCIIToUTF16("Hello, world")); +const char testrawstring[] = "Hello new world"; // Test raw string writing +// Test raw char16 writing, assumes UTF16 encoding is ANSI for alpha chars. +const base::char16 testrawstring16[] = {'A', 'l', 'o', 'h', 'a', 0}; const char testdata[] = "AAA\0BBB\0"; const int testdatalen = arraysize(testdata) - 1; -// checks that the result +// checks that the results can be read correctly from the Pickle void VerifyResult(const Pickle& pickle) { PickleIterator iter(pickle); bool outbool; - EXPECT_TRUE(pickle.ReadBool(&iter, &outbool)); + EXPECT_TRUE(iter.ReadBool(&outbool)); EXPECT_FALSE(outbool); - EXPECT_TRUE(pickle.ReadBool(&iter, &outbool)); + EXPECT_TRUE(iter.ReadBool(&outbool)); EXPECT_TRUE(outbool); int outint; - EXPECT_TRUE(pickle.ReadInt(&iter, &outint)); + EXPECT_TRUE(iter.ReadInt(&outint)); EXPECT_EQ(testint, outint); long outlong; - EXPECT_TRUE(pickle.ReadLong(&iter, &outlong)); + EXPECT_TRUE(iter.ReadLong(&outlong)); EXPECT_EQ(testlong, outlong); uint16 outuint16; - EXPECT_TRUE(pickle.ReadUInt16(&iter, &outuint16)); + EXPECT_TRUE(iter.ReadUInt16(&outuint16)); EXPECT_EQ(testuint16, outuint16); uint32 outuint32; - EXPECT_TRUE(pickle.ReadUInt32(&iter, &outuint32)); + EXPECT_TRUE(iter.ReadUInt32(&outuint32)); EXPECT_EQ(testuint32, outuint32); int64 outint64; - EXPECT_TRUE(pickle.ReadInt64(&iter, &outint64)); + EXPECT_TRUE(iter.ReadInt64(&outint64)); EXPECT_EQ(testint64, outint64); uint64 outuint64; - EXPECT_TRUE(pickle.ReadUInt64(&iter, &outuint64)); + EXPECT_TRUE(iter.ReadUInt64(&outuint64)); EXPECT_EQ(testuint64, outuint64); size_t outsizet; - EXPECT_TRUE(pickle.ReadSizeT(&iter, &outsizet)); + EXPECT_TRUE(iter.ReadSizeT(&outsizet)); EXPECT_EQ(testsizet, outsizet); float outfloat; - EXPECT_TRUE(pickle.ReadFloat(&iter, &outfloat)); + EXPECT_TRUE(iter.ReadFloat(&outfloat)); EXPECT_EQ(testfloat, outfloat); double outdouble; - EXPECT_TRUE(pickle.ReadDouble(&iter, &outdouble)); + EXPECT_TRUE(iter.ReadDouble(&outdouble)); EXPECT_EQ(testdouble, outdouble); std::string outstring; - EXPECT_TRUE(pickle.ReadString(&iter, &outstring)); + EXPECT_TRUE(iter.ReadString(&outstring)); EXPECT_EQ(teststring, outstring); - std::wstring outwstring; - EXPECT_TRUE(pickle.ReadWString(&iter, &outwstring)); - EXPECT_EQ(testwstring, outwstring); - base::string16 outstring16; - EXPECT_TRUE(pickle.ReadString16(&iter, &outstring16)); + EXPECT_TRUE(iter.ReadString16(&outstring16)); EXPECT_EQ(teststring16, outstring16); + base::StringPiece outstringpiece; + EXPECT_TRUE(iter.ReadStringPiece(&outstringpiece)); + EXPECT_EQ(testrawstring, outstringpiece); + + base::StringPiece16 outstringpiece16; + EXPECT_TRUE(iter.ReadStringPiece16(&outstringpiece16)); + EXPECT_EQ(testrawstring16, outstringpiece16); + const char* outdata; int outdatalen; - EXPECT_TRUE(pickle.ReadData(&iter, &outdata, &outdatalen)); + EXPECT_TRUE(iter.ReadData(&outdata, &outdatalen)); EXPECT_EQ(testdatalen, outdatalen); EXPECT_EQ(memcmp(testdata, outdata, outdatalen), 0); // reads past the end should fail - EXPECT_FALSE(pickle.ReadInt(&iter, &outint)); + EXPECT_FALSE(iter.ReadInt(&outint)); } } // namespace @@ -119,8 +126,9 @@ TEST(PickleTest, EncodeDecode) { EXPECT_TRUE(pickle.WriteFloat(testfloat)); EXPECT_TRUE(pickle.WriteDouble(testdouble)); EXPECT_TRUE(pickle.WriteString(teststring)); - EXPECT_TRUE(pickle.WriteWString(testwstring)); EXPECT_TRUE(pickle.WriteString16(teststring16)); + EXPECT_TRUE(pickle.WriteString(testrawstring)); + EXPECT_TRUE(pickle.WriteString16(testrawstring16)); EXPECT_TRUE(pickle.WriteData(testdata, testdatalen)); VerifyResult(pickle); @@ -148,9 +156,9 @@ TEST(PickleTest, SizeTFrom64Bit) { if (sizeof(size_t) < sizeof(uint64)) { // ReadSizeT() should return false when the original written value can't be // represented as a size_t. - EXPECT_FALSE(pickle.ReadSizeT(&iter, &outsizet)); + EXPECT_FALSE(iter.ReadSizeT(&outsizet)); } else { - EXPECT_TRUE(pickle.ReadSizeT(&iter, &outsizet)); + EXPECT_TRUE(iter.ReadSizeT(&outsizet)); EXPECT_EQ(testuint64, outsizet); } } @@ -164,7 +172,7 @@ TEST(PickleTest, SmallBuffer) { PickleIterator iter(pickle); int data; - EXPECT_FALSE(pickle.ReadInt(&iter, &data)); + EXPECT_FALSE(iter.ReadInt(&data)); } // Tests that we can handle improper headers. @@ -175,7 +183,7 @@ TEST(PickleTest, BigSize) { PickleIterator iter(pickle); int data; - EXPECT_FALSE(pickle.ReadInt(&iter, &data)); + EXPECT_FALSE(iter.ReadInt(&data)); } TEST(PickleTest, UnalignedSize) { @@ -185,7 +193,7 @@ TEST(PickleTest, UnalignedSize) { PickleIterator iter(pickle); int data; - EXPECT_FALSE(pickle.ReadInt(&iter, &data)); + EXPECT_FALSE(iter.ReadInt(&data)); } TEST(PickleTest, ZeroLenStr) { @@ -194,17 +202,17 @@ TEST(PickleTest, ZeroLenStr) { PickleIterator iter(pickle); std::string outstr; - EXPECT_TRUE(pickle.ReadString(&iter, &outstr)); + EXPECT_TRUE(iter.ReadString(&outstr)); EXPECT_EQ("", outstr); } -TEST(PickleTest, ZeroLenWStr) { +TEST(PickleTest, ZeroLenStr16) { Pickle pickle; - EXPECT_TRUE(pickle.WriteWString(std::wstring())); + EXPECT_TRUE(pickle.WriteString16(base::string16())); PickleIterator iter(pickle); std::string outstr; - EXPECT_TRUE(pickle.ReadString(&iter, &outstr)); + EXPECT_TRUE(iter.ReadString(&outstr)); EXPECT_EQ("", outstr); } @@ -214,16 +222,16 @@ TEST(PickleTest, BadLenStr) { PickleIterator iter(pickle); std::string outstr; - EXPECT_FALSE(pickle.ReadString(&iter, &outstr)); + EXPECT_FALSE(iter.ReadString(&outstr)); } -TEST(PickleTest, BadLenWStr) { +TEST(PickleTest, BadLenStr16) { Pickle pickle; EXPECT_TRUE(pickle.WriteInt(-1)); PickleIterator iter(pickle); - std::wstring woutstr; - EXPECT_FALSE(pickle.ReadWString(&iter, &woutstr)); + base::string16 outstr; + EXPECT_FALSE(iter.ReadString16(&outstr)); } TEST(PickleTest, FindNext) { @@ -351,7 +359,7 @@ TEST(PickleTest, HeaderPadding) { PickleIterator iter(pickle); int result; - ASSERT_TRUE(pickle.ReadInt(&iter, &result)); + ASSERT_TRUE(iter.ReadInt(&result)); EXPECT_EQ(static_cast<uint32>(result), kMagic); } @@ -375,14 +383,14 @@ TEST(PickleTest, EvilLengths) { // to out-of-bounds reading. PickleIterator iter(source); string16 str16; - EXPECT_FALSE(source.ReadString16(&iter, &str16)); + EXPECT_FALSE(iter.ReadString16(&str16)); // And check we didn't break ReadString16. str16 = (wchar_t) 'A'; Pickle str16_pickle; EXPECT_TRUE(str16_pickle.WriteString16(str16)); iter = PickleIterator(str16_pickle); - EXPECT_TRUE(str16_pickle.ReadString16(&iter, &str16)); + EXPECT_TRUE(iter.ReadString16(&str16)); EXPECT_EQ(1U, str16.length()); // Check we don't fail in a length check with invalid String16 size. @@ -390,14 +398,7 @@ TEST(PickleTest, EvilLengths) { Pickle bad_len; EXPECT_TRUE(bad_len.WriteInt(1 << 31)); iter = PickleIterator(bad_len); - EXPECT_FALSE(bad_len.ReadString16(&iter, &str16)); - - // Check we don't fail in a length check with large WStrings. - Pickle big_len; - EXPECT_TRUE(big_len.WriteInt(1 << 30)); - iter = PickleIterator(big_len); - std::wstring wstr; - EXPECT_FALSE(big_len.ReadWString(&iter, &wstr)); + EXPECT_FALSE(iter.ReadString16(&str16)); } // Check we can write zero bytes of data and 'data' can be NULL. @@ -408,7 +409,7 @@ TEST(PickleTest, ZeroLength) { PickleIterator iter(pickle); const char* outdata; int outdatalen; - EXPECT_TRUE(pickle.ReadData(&iter, &outdata, &outdatalen)); + EXPECT_TRUE(iter.ReadData(&outdata, &outdatalen)); EXPECT_EQ(0, outdatalen); // We can't assert that outdata is NULL. } @@ -421,7 +422,7 @@ TEST(PickleTest, ReadBytes) { PickleIterator iter(pickle); const char* outdata_char = NULL; - EXPECT_TRUE(pickle.ReadBytes(&iter, &outdata_char, sizeof(data))); + EXPECT_TRUE(iter.ReadBytes(&outdata_char, sizeof(data))); int outdata; memcpy(&outdata, outdata_char, sizeof(outdata)); diff --git a/chromium/base/port.h b/chromium/base/port.h index 0a04d55f0f6..56c4d4e109f 100644 --- a/chromium/base/port.h +++ b/chromium/base/port.h @@ -26,18 +26,6 @@ #define GG_INT64_C(x) GG_LONGLONG(x) #define GG_UINT64_C(x) GG_ULONGLONG(x) -// It's possible for functions that use a va_list, such as StringPrintf, to -// invalidate the data in it upon use. The fix is to make a copy of the -// structure before using it and use that copy instead. va_copy is provided -// for this purpose. MSVC does not provide va_copy, so define an -// implementation here. It is not guaranteed that assignment is a copy, so the -// StringUtil.VariableArgsFunc unit test tests this capability. -#if defined(COMPILER_GCC) -#define GG_VA_COPY(a, b) (va_copy(a, b)) -#elif defined(COMPILER_MSVC) -#define GG_VA_COPY(a, b) (a = b) -#endif - // Define an OS-neutral wrapper for shared library entry points #if defined(OS_WIN) #define API_CALL __stdcall diff --git a/chromium/base/posix/global_descriptors.cc b/chromium/base/posix/global_descriptors.cc index bcca443acc1..6c187838ad6 100644 --- a/chromium/base/posix/global_descriptors.cc +++ b/chromium/base/posix/global_descriptors.cc @@ -11,6 +11,16 @@ namespace base { +GlobalDescriptors::Descriptor::Descriptor(Key key, int fd) + : key(key), fd(fd), region(base::MemoryMappedFile::Region::kWholeFile) { +} + +GlobalDescriptors::Descriptor::Descriptor(Key key, + int fd, + base::MemoryMappedFile::Region region) + : key(key), fd(fd), region(region) { +} + // static GlobalDescriptors* GlobalDescriptors::GetInstance() { typedef Singleton<base::GlobalDescriptors, @@ -30,23 +40,38 @@ int GlobalDescriptors::Get(Key key) const { int GlobalDescriptors::MaybeGet(Key key) const { for (Mapping::const_iterator i = descriptors_.begin(); i != descriptors_.end(); ++i) { - if (i->first == key) - return i->second; + if (i->key == key) + return i->fd; } return -1; } void GlobalDescriptors::Set(Key key, int fd) { - for (Mapping::iterator - i = descriptors_.begin(); i != descriptors_.end(); ++i) { - if (i->first == key) { - i->second = fd; + Set(key, fd, base::MemoryMappedFile::Region::kWholeFile); +} + +void GlobalDescriptors::Set(Key key, + int fd, + base::MemoryMappedFile::Region region) { + for (auto& i : descriptors_) { + if (i.key == key) { + i.fd = fd; + i.region = region; return; } } - descriptors_.push_back(std::make_pair(key, fd)); + descriptors_.push_back(Descriptor(key, fd, region)); +} + +base::MemoryMappedFile::Region GlobalDescriptors::GetRegion(Key key) const { + for (const auto& i : descriptors_) { + if (i.key == key) + return i.region; + } + DLOG(FATAL) << "Unknown global descriptor: " << key; + return base::MemoryMappedFile::Region::kWholeFile; } void GlobalDescriptors::Reset(const Mapping& mapping) { diff --git a/chromium/base/posix/global_descriptors.h b/chromium/base/posix/global_descriptors.h index 3d7369c3174..c774634f59b 100644 --- a/chromium/base/posix/global_descriptors.h +++ b/chromium/base/posix/global_descriptors.h @@ -12,6 +12,7 @@ #include <stdint.h> +#include "base/files/memory_mapped_file.h" #include "base/memory/singleton.h" namespace base { @@ -36,8 +37,18 @@ namespace base { class BASE_EXPORT GlobalDescriptors { public: typedef uint32_t Key; - typedef std::pair<Key, int> KeyFDPair; - typedef std::vector<KeyFDPair> Mapping; + struct Descriptor { + Descriptor(Key key, int fd); + Descriptor(Key key, int fd, base::MemoryMappedFile::Region region); + + // Globally unique key. + Key key; + // Actual FD. + int fd; + // Optional region, defaults to kWholeFile. + base::MemoryMappedFile::Region region; + }; + typedef std::vector<Descriptor> Mapping; // Often we want a canonical descriptor for a given Key. In this case, we add // the following constant to the key value: @@ -53,12 +64,19 @@ class BASE_EXPORT GlobalDescriptors { // Get a descriptor given a key. It is a fatal error if the key is not known. int Get(Key key) const; - // Get a descriptor give a key. Returns -1 on error. + // Get a descriptor given a key. Returns -1 on error. int MaybeGet(Key key) const; - // Set the descriptor for the given key. + // Get a region given a key. It is a fatal error if the key is not known. + base::MemoryMappedFile::Region GetRegion(Key key) const; + + // Set the descriptor for the given |key|. This sets the region associated + // with |key| to kWholeFile. void Set(Key key, int fd); + // Set the descriptor and |region| for the given |key|. + void Set(Key key, int fd, base::MemoryMappedFile::Region region); + void Reset(const Mapping& mapping); private: diff --git a/chromium/base/posix/unix_domain_socket_linux.cc b/chromium/base/posix/unix_domain_socket_linux.cc index 203285b9626..16d8eaad241 100644 --- a/chromium/base/posix/unix_domain_socket_linux.cc +++ b/chromium/base/posix/unix_domain_socket_linux.cc @@ -136,8 +136,8 @@ ssize_t UnixDomainSocket::RecvMsgWithFlags(int fd, const unsigned payload_len = cmsg->cmsg_len - CMSG_LEN(0); if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { - DCHECK(payload_len % sizeof(int) == 0); - DCHECK(wire_fds == NULL); + DCHECK_EQ(payload_len % sizeof(int), 0u); + DCHECK_EQ(wire_fds, static_cast<void*>(nullptr)); wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg)); wire_fds_len = payload_len / sizeof(int); } @@ -146,8 +146,8 @@ ssize_t UnixDomainSocket::RecvMsgWithFlags(int fd, // SCM_CREDENTIALS. if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS) { - DCHECK(payload_len == sizeof(struct ucred)); - DCHECK(pid == -1); + DCHECK_EQ(payload_len, sizeof(struct ucred)); + DCHECK_EQ(pid, -1); pid = reinterpret_cast<struct ucred*>(CMSG_DATA(cmsg))->pid; } #endif diff --git a/chromium/base/posix/unix_domain_socket_linux_unittest.cc b/chromium/base/posix/unix_domain_socket_linux_unittest.cc index 60b2bb8a470..c05141cdcf1 100644 --- a/chromium/base/posix/unix_domain_socket_linux_unittest.cc +++ b/chromium/base/posix/unix_domain_socket_linux_unittest.cc @@ -10,9 +10,11 @@ #include "base/bind_helpers.h" #include "base/files/file_util.h" #include "base/files/scoped_file.h" +#include "base/location.h" #include "base/memory/scoped_vector.h" #include "base/pickle.h" #include "base/posix/unix_domain_socket_linux.h" +#include "base/single_thread_task_runner.h" #include "base/synchronization/waitable_event.h" #include "base/threading/thread.h" #include "testing/gtest/include/gtest/gtest.h" @@ -32,11 +34,10 @@ TEST(UnixDomainSocketTest, SendRecvMsgAbortOnReplyFDClose) { // Have the thread send a synchronous message via the socket. Pickle request; - message_thread.message_loop()->PostTask( + message_thread.task_runner()->PostTask( FROM_HERE, - Bind(IgnoreResult(&UnixDomainSocket::SendRecvMsg), - fds[1], static_cast<uint8_t*>(NULL), 0U, static_cast<int*>(NULL), - request)); + Bind(IgnoreResult(&UnixDomainSocket::SendRecvMsg), fds[1], + static_cast<uint8_t*>(NULL), 0U, static_cast<int*>(NULL), request)); // Receive the message. ScopedVector<base::ScopedFD> message_fds; @@ -51,9 +52,8 @@ TEST(UnixDomainSocketTest, SendRecvMsgAbortOnReplyFDClose) { // Check that the thread didn't get blocked. WaitableEvent event(false, false); - message_thread.message_loop()->PostTask( - FROM_HERE, - Bind(&WaitableEvent::Signal, Unretained(&event))); + message_thread.task_runner()->PostTask( + FROM_HERE, Bind(&WaitableEvent::Signal, Unretained(&event))); ASSERT_TRUE(event.TimedWait(TimeDelta::FromMilliseconds(5000))); } diff --git a/chromium/base/power_monitor/power_monitor.cc b/chromium/base/power_monitor/power_monitor.cc index 14dc4b51783..98c9c68c1df 100644 --- a/chromium/base/power_monitor/power_monitor.cc +++ b/chromium/base/power_monitor/power_monitor.cc @@ -45,17 +45,18 @@ bool PowerMonitor::IsOnBatteryPower() { void PowerMonitor::NotifyPowerStateChange(bool battery_in_use) { DVLOG(1) << "PowerStateChange: " << (battery_in_use ? "On" : "Off") << " battery"; - observers_->Notify(&PowerObserver::OnPowerStateChange, battery_in_use); + observers_->Notify(FROM_HERE, &PowerObserver::OnPowerStateChange, + battery_in_use); } void PowerMonitor::NotifySuspend() { DVLOG(1) << "Power Suspending"; - observers_->Notify(&PowerObserver::OnSuspend); + observers_->Notify(FROM_HERE, &PowerObserver::OnSuspend); } void PowerMonitor::NotifyResume() { DVLOG(1) << "Power Resuming"; - observers_->Notify(&PowerObserver::OnResume); + observers_->Notify(FROM_HERE, &PowerObserver::OnResume); } } // namespace base diff --git a/chromium/base/power_monitor/power_monitor_device_source_win.cc b/chromium/base/power_monitor/power_monitor_device_source_win.cc index b8b16e1d344..0e199dcddbb 100644 --- a/chromium/base/power_monitor/power_monitor_device_source_win.cc +++ b/chromium/base/power_monitor/power_monitor_device_source_win.cc @@ -5,6 +5,7 @@ #include "base/power_monitor/power_monitor.h" #include "base/power_monitor/power_monitor_device_source.h" #include "base/power_monitor/power_monitor_source.h" +#include "base/profiler/scoped_tracker.h" #include "base/win/wrapped_window_proc.h" namespace base { @@ -98,6 +99,11 @@ LRESULT CALLBACK PowerMonitorDeviceSource::PowerMessageWindow::WndProcThunk( UINT message, WPARAM wparam, LPARAM lparam) { + // TODO(vadimt): Remove ScopedTracker below once crbug.com/440919 is fixed. + tracked_objects::ScopedTracker tracking_profile( + FROM_HERE_WITH_EXPLICIT_FUNCTION( + "440919 PowerMonitorDeviceSource::PowerMessageWindow::WndProcThunk")); + switch (message) { case WM_POWERBROADCAST: ProcessWmPowerBroadcastMessage(wparam); diff --git a/chromium/base/power_monitor/power_monitor_unittest.cc b/chromium/base/power_monitor/power_monitor_unittest.cc index 5f7b531206f..2df82618f4c 100644 --- a/chromium/base/power_monitor/power_monitor_unittest.cc +++ b/chromium/base/power_monitor/power_monitor_unittest.cc @@ -15,7 +15,7 @@ class PowerMonitorTest : public testing::Test { power_monitor_.reset(new PowerMonitor( scoped_ptr<PowerMonitorSource>(power_monitor_source_))); } - virtual ~PowerMonitorTest() {}; + ~PowerMonitorTest() override{}; PowerMonitorTestSource* source() { return power_monitor_source_; } PowerMonitor* monitor() { return power_monitor_.get(); } diff --git a/chromium/base/prefs/json_pref_store.cc b/chromium/base/prefs/json_pref_store.cc index d4714037c01..da545b8634c 100644 --- a/chromium/base/prefs/json_pref_store.cc +++ b/chromium/base/prefs/json_pref_store.cc @@ -16,9 +16,11 @@ #include "base/metrics/histogram.h" #include "base/prefs/pref_filter.h" #include "base/sequenced_task_runner.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/task_runner_util.h" #include "base/threading/sequenced_worker_pool.h" +#include "base/time/default_clock.h" #include "base/values.h" // Result returned from internal read tasks. @@ -56,16 +58,16 @@ PersistentPrefStore::PrefReadError HandleReadErrors( DVLOG(1) << "Error while loading JSON file: " << error_msg << ", file: " << path.value(); switch (error_code) { - case JSONFileValueSerializer::JSON_ACCESS_DENIED: + case JSONFileValueDeserializer::JSON_ACCESS_DENIED: return PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED; break; - case JSONFileValueSerializer::JSON_CANNOT_READ_FILE: + case JSONFileValueDeserializer::JSON_CANNOT_READ_FILE: return PersistentPrefStore::PREF_READ_ERROR_FILE_OTHER; break; - case JSONFileValueSerializer::JSON_FILE_LOCKED: + case JSONFileValueDeserializer::JSON_FILE_LOCKED: return PersistentPrefStore::PREF_READ_ERROR_FILE_LOCKED; break; - case JSONFileValueSerializer::JSON_NO_SUCH_FILE: + case JSONFileValueDeserializer::JSON_NO_SUCH_FILE: return PersistentPrefStore::PREF_READ_ERROR_NO_FILE; break; default: @@ -91,6 +93,22 @@ PersistentPrefStore::PrefReadError HandleReadErrors( return PersistentPrefStore::PREF_READ_ERROR_NONE; } +// Records a sample for |size| in the Settings.JsonDataReadSizeKilobytes +// histogram suffixed with the base name of the JSON file under |path|. +void RecordJsonDataSizeHistogram(const base::FilePath& path, size_t size) { + std::string spaceless_basename; + base::ReplaceChars(path.BaseName().MaybeAsASCII(), " ", "_", + &spaceless_basename); + + // The histogram below is an expansion of the UMA_HISTOGRAM_CUSTOM_COUNTS + // macro adapted to allow for a dynamically suffixed histogram name. + // Note: The factory creates and owns the histogram. + base::HistogramBase* histogram = base::Histogram::FactoryGet( + "Settings.JsonDataReadSizeKilobytes." + spaceless_basename, 1, 10000, 50, + base::HistogramBase::kUmaTargetedHistogramFlag); + histogram->Add(static_cast<int>(size) / 1024); +} + scoped_ptr<JsonPrefStore::ReadResult> ReadPrefsFromDisk( const base::FilePath& path, const base::FilePath& alternate_path) { @@ -103,11 +121,15 @@ scoped_ptr<JsonPrefStore::ReadResult> ReadPrefsFromDisk( std::string error_msg; scoped_ptr<JsonPrefStore::ReadResult> read_result( new JsonPrefStore::ReadResult); - JSONFileValueSerializer serializer(path); - read_result->value.reset(serializer.Deserialize(&error_code, &error_msg)); + JSONFileValueDeserializer deserializer(path); + read_result->value.reset(deserializer.Deserialize(&error_code, &error_msg)); read_result->error = HandleReadErrors(read_result->value.get(), path, error_code, error_msg); read_result->no_dir = !base::PathExists(path.DirName()); + + if (read_result->error == PersistentPrefStore::PREF_READ_ERROR_NONE) + RecordJsonDataSizeHistogram(path, deserializer.get_last_read_size()); + return read_result.Pass(); } @@ -128,15 +150,10 @@ JsonPrefStore::JsonPrefStore( const base::FilePath& filename, const scoped_refptr<base::SequencedTaskRunner>& sequenced_task_runner, scoped_ptr<PrefFilter> pref_filter) - : path_(filename), - sequenced_task_runner_(sequenced_task_runner), - prefs_(new base::DictionaryValue()), - read_only_(false), - writer_(filename, sequenced_task_runner), - pref_filter_(pref_filter.Pass()), - initialized_(false), - filtering_in_progress_(false), - read_error_(PREF_READ_ERROR_NONE) { + : JsonPrefStore(filename, + base::FilePath(), + sequenced_task_runner, + pref_filter.Pass()) { } JsonPrefStore::JsonPrefStore( @@ -153,7 +170,10 @@ JsonPrefStore::JsonPrefStore( pref_filter_(pref_filter.Pass()), initialized_(false), filtering_in_progress_(false), - read_error_(PREF_READ_ERROR_NONE) { + pending_lossy_write_(false), + read_error_(PREF_READ_ERROR_NONE), + write_count_histogram_(writer_.commit_interval(), path_) { + DCHECK(!path_.empty()); } bool JsonPrefStore::GetValue(const std::string& key, @@ -200,7 +220,9 @@ bool JsonPrefStore::GetMutableValue(const std::string& key, return prefs_->Get(key, result); } -void JsonPrefStore::SetValue(const std::string& key, base::Value* value) { +void JsonPrefStore::SetValue(const std::string& key, + base::Value* value, + uint32 flags) { DCHECK(CalledOnValidThread()); DCHECK(value); @@ -208,13 +230,14 @@ void JsonPrefStore::SetValue(const std::string& key, base::Value* value) { base::Value* old_value = NULL; prefs_->Get(key, &old_value); if (!old_value || !value->Equals(old_value)) { - prefs_->Set(key, new_value.release()); - ReportValueChanged(key); + prefs_->Set(key, new_value.Pass()); + ReportValueChanged(key, flags); } } void JsonPrefStore::SetValueSilently(const std::string& key, - base::Value* value) { + base::Value* value, + uint32 flags) { DCHECK(CalledOnValidThread()); DCHECK(value); @@ -222,25 +245,23 @@ void JsonPrefStore::SetValueSilently(const std::string& key, base::Value* old_value = NULL; prefs_->Get(key, &old_value); if (!old_value || !value->Equals(old_value)) { - prefs_->Set(key, new_value.release()); - if (!read_only_) - writer_.ScheduleWrite(this); + prefs_->Set(key, new_value.Pass()); + ScheduleWrite(flags); } } -void JsonPrefStore::RemoveValue(const std::string& key) { +void JsonPrefStore::RemoveValue(const std::string& key, uint32 flags) { DCHECK(CalledOnValidThread()); if (prefs_->RemovePath(key, NULL)) - ReportValueChanged(key); + ReportValueChanged(key, flags); } -void JsonPrefStore::RemoveValueSilently(const std::string& key) { +void JsonPrefStore::RemoveValueSilently(const std::string& key, uint32 flags) { DCHECK(CalledOnValidThread()); prefs_->RemovePath(key, NULL); - if (!read_only_) - writer_.ScheduleWrite(this); + ScheduleWrite(flags); } bool JsonPrefStore::ReadOnly() const { @@ -258,13 +279,6 @@ PersistentPrefStore::PrefReadError JsonPrefStore::GetReadError() const { PersistentPrefStore::PrefReadError JsonPrefStore::ReadPrefs() { DCHECK(CalledOnValidThread()); - if (path_.empty()) { - scoped_ptr<ReadResult> no_file_result; - no_file_result->error = PREF_READ_ERROR_FILE_NOT_SPECIFIED; - OnFileRead(no_file_result.Pass()); - return PREF_READ_ERROR_FILE_NOT_SPECIFIED; - } - OnFileRead(ReadPrefsFromDisk(path_, alternate_path_)); return filtering_in_progress_ ? PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE : read_error_; @@ -275,12 +289,6 @@ void JsonPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) { initialized_ = false; error_delegate_.reset(error_delegate); - if (path_.empty()) { - scoped_ptr<ReadResult> no_file_result; - no_file_result->error = PREF_READ_ERROR_FILE_NOT_SPECIFIED; - OnFileRead(no_file_result.Pass()); - return; - } // Weakly binds the read task so that it doesn't kick in during shutdown. base::PostTaskAndReplyWithResult( @@ -293,11 +301,16 @@ void JsonPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) { void JsonPrefStore::CommitPendingWrite() { DCHECK(CalledOnValidThread()); + // Schedule a write for any lossy writes that are outstanding to ensure that + // they get flushed when this function is called. + if (pending_lossy_write_) + writer_.ScheduleWrite(this); + if (writer_.HasPendingWrite() && !read_only_) writer_.DoScheduledWrite(); } -void JsonPrefStore::ReportValueChanged(const std::string& key) { +void JsonPrefStore::ReportValueChanged(const std::string& key, uint32 flags) { DCHECK(CalledOnValidThread()); if (pref_filter_) @@ -305,8 +318,7 @@ void JsonPrefStore::ReportValueChanged(const std::string& key) { FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key)); - if (!read_only_) - writer_.ScheduleWrite(this); + ScheduleWrite(flags); } void JsonPrefStore::RegisterOnNextSuccessfulWriteCallback( @@ -385,32 +397,19 @@ JsonPrefStore::~JsonPrefStore() { bool JsonPrefStore::SerializeData(std::string* output) { DCHECK(CalledOnValidThread()); + pending_lossy_write_ = false; + + write_count_histogram_.RecordWriteOccured(); + if (pref_filter_) pref_filter_->FilterSerializeData(prefs_.get()); JSONStringValueSerializer serializer(output); - serializer.set_pretty_print(true); - bool result = serializer.Serialize(*prefs_); - - if (result) { - std::string spaceless_basename; - base::ReplaceChars(path_.BaseName().MaybeAsASCII(), " ", "_", - &spaceless_basename); - - // The histogram below is an expansion of the UMA_HISTOGRAM_COUNTS_10000 - // macro adapted to allow for a dynamically suffixed histogram name. - // Note: The factory creates and owns the histogram. - base::HistogramBase* histogram = - base::LinearHistogram::FactoryGet( - "Settings.JsonDataSizeKilobytes." + spaceless_basename, - 1, - 10000, - 50, - base::HistogramBase::kUmaTargetedHistogramFlag); - histogram->Add(static_cast<int>(output->size()) / 1024); - } - - return result; + // Not pretty-printing prefs shrinks pref file size by ~30%. To obtain + // readable prefs for debugging purposes, you can dump your prefs into any + // command-line or online JSON pretty printing tool. + serializer.set_pretty_print(false); + return serializer.Serialize(*prefs_); } void JsonPrefStore::FinalizeFileRead(bool initialization_successful, @@ -431,8 +430,8 @@ void JsonPrefStore::FinalizeFileRead(bool initialization_successful, initialized_ = true; - if (schedule_write && !read_only_) - writer_.ScheduleWrite(this); + if (schedule_write) + ScheduleWrite(DEFAULT_PREF_WRITE_FLAGS); if (error_delegate_ && read_error_ != PREF_READ_ERROR_NONE) error_delegate_->OnError(read_error_); @@ -443,3 +442,101 @@ void JsonPrefStore::FinalizeFileRead(bool initialization_successful, return; } + +void JsonPrefStore::ScheduleWrite(uint32 flags) { + if (read_only_) + return; + + if (flags & LOSSY_PREF_WRITE_FLAG) + pending_lossy_write_ = true; + else + writer_.ScheduleWrite(this); +} + +// NOTE: This value should NOT be changed without renaming the histogram +// otherwise it will create incompatible buckets. +const int32_t + JsonPrefStore::WriteCountHistogram::kHistogramWriteReportIntervalMins = 5; + +JsonPrefStore::WriteCountHistogram::WriteCountHistogram( + const base::TimeDelta& commit_interval, + const base::FilePath& path) + : WriteCountHistogram(commit_interval, + path, + scoped_ptr<base::Clock>(new base::DefaultClock)) { +} + +JsonPrefStore::WriteCountHistogram::WriteCountHistogram( + const base::TimeDelta& commit_interval, + const base::FilePath& path, + scoped_ptr<base::Clock> clock) + : commit_interval_(commit_interval), + path_(path), + clock_(clock.release()), + report_interval_( + base::TimeDelta::FromMinutes(kHistogramWriteReportIntervalMins)), + last_report_time_(clock_->Now()), + writes_since_last_report_(0) { +} + +JsonPrefStore::WriteCountHistogram::~WriteCountHistogram() { + ReportOutstandingWrites(); +} + +void JsonPrefStore::WriteCountHistogram::RecordWriteOccured() { + ReportOutstandingWrites(); + + ++writes_since_last_report_; +} + +void JsonPrefStore::WriteCountHistogram::ReportOutstandingWrites() { + base::Time current_time = clock_->Now(); + base::TimeDelta time_since_last_report = current_time - last_report_time_; + + if (time_since_last_report <= report_interval_) + return; + + // If the time since the last report exceeds the report interval, report all + // the writes since the last report. They must have all occurred in the same + // report interval. + base::HistogramBase* histogram = GetHistogram(); + histogram->Add(writes_since_last_report_); + + // There may be several report intervals that elapsed that don't have any + // writes in them. Report these too. + int64 total_num_intervals_elapsed = + (time_since_last_report / report_interval_); + for (int64 i = 0; i < total_num_intervals_elapsed - 1; ++i) + histogram->Add(0); + + writes_since_last_report_ = 0; + last_report_time_ += total_num_intervals_elapsed * report_interval_; +} + +base::HistogramBase* JsonPrefStore::WriteCountHistogram::GetHistogram() { + std::string spaceless_basename; + base::ReplaceChars(path_.BaseName().MaybeAsASCII(), " ", "_", + &spaceless_basename); + std::string histogram_name = + "Settings.JsonDataWriteCount." + spaceless_basename; + + // The min value for a histogram is 1. The max value is the maximum number of + // writes that can occur in the window being recorded. The number of buckets + // used is the max value (plus the underflow/overflow buckets). + int32_t min_value = 1; + int32_t max_value = report_interval_ / commit_interval_; + int32_t num_buckets = max_value + 1; + + // NOTE: These values should NOT be changed without renaming the histogram + // otherwise it will create incompatible buckets. + DCHECK_EQ(30, max_value); + DCHECK_EQ(31, num_buckets); + + // The histogram below is an expansion of the UMA_HISTOGRAM_CUSTOM_COUNTS + // macro adapted to allow for a dynamically suffixed histogram name. + // Note: The factory creates and owns the histogram. + base::HistogramBase* histogram = base::Histogram::FactoryGet( + histogram_name, min_value, max_value, num_buckets, + base::HistogramBase::kUmaTargetedHistogramFlag); + return histogram; +} diff --git a/chromium/base/prefs/json_pref_store.h b/chromium/base/prefs/json_pref_store.h index 16e431b3c92..ef260eb47da 100644 --- a/chromium/base/prefs/json_pref_store.h +++ b/chromium/base/prefs/json_pref_store.h @@ -13,9 +13,10 @@ #include "base/compiler_specific.h" #include "base/files/file_path.h" #include "base/files/important_file_writer.h" +#include "base/gtest_prod_util.h" #include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" #include "base/memory/weak_ptr.h" -#include "base/message_loop/message_loop_proxy.h" #include "base/observer_list.h" #include "base/prefs/base_prefs_export.h" #include "base/prefs/persistent_pref_store.h" @@ -24,11 +25,18 @@ class PrefFilter; namespace base { +class Clock; class DictionaryValue; class FilePath; +class HistogramBase; +class JsonPrefStoreLossyWriteTest; class SequencedTaskRunner; class SequencedWorkerPool; class Value; +FORWARD_DECLARE_TEST(JsonPrefStoreTest, WriteCountHistogramTestBasic); +FORWARD_DECLARE_TEST(JsonPrefStoreTest, WriteCountHistogramTestSinglePeriod); +FORWARD_DECLARE_TEST(JsonPrefStoreTest, WriteCountHistogramTestMultiplePeriods); +FORWARD_DECLARE_TEST(JsonPrefStoreTest, WriteCountHistogramTestPeriodWithGaps); } // A writable PrefStore implementation that is used for user preferences. @@ -75,9 +83,13 @@ class BASE_PREFS_EXPORT JsonPrefStore // PersistentPrefStore overrides: bool GetMutableValue(const std::string& key, base::Value** result) override; - void SetValue(const std::string& key, base::Value* value) override; - void SetValueSilently(const std::string& key, base::Value* value) override; - void RemoveValue(const std::string& key) override; + void SetValue(const std::string& key, + base::Value* value, + uint32 flags) override; + void SetValueSilently(const std::string& key, + base::Value* value, + uint32 flags) override; + void RemoveValue(const std::string& key, uint32 flags) override; bool ReadOnly() const override; PrefReadError GetReadError() const override; // Note this method may be asynchronous if this instance has a |pref_filter_| @@ -86,11 +98,11 @@ class BASE_PREFS_EXPORT JsonPrefStore PrefReadError ReadPrefs() override; void ReadPrefsAsync(ReadErrorDelegate* error_delegate) override; void CommitPendingWrite() override; - void ReportValueChanged(const std::string& key) override; + void ReportValueChanged(const std::string& key, uint32 flags) override; // Just like RemoveValue(), but doesn't notify observers. Used when doing some // cleanup that shouldn't otherwise alert observers. - void RemoveValueSilently(const std::string& key); + void RemoveValueSilently(const std::string& key, uint32 flags); // Registers |on_next_successful_write| to be called once, on the next // successful write event of |writer_|. @@ -98,6 +110,64 @@ class BASE_PREFS_EXPORT JsonPrefStore const base::Closure& on_next_successful_write); private: + // Represents a histogram for recording the number of writes to the pref file + // that occur every kHistogramWriteReportIntervalInMins minutes. + class BASE_PREFS_EXPORT WriteCountHistogram { + public: + static const int32_t kHistogramWriteReportIntervalMins; + + WriteCountHistogram(const base::TimeDelta& commit_interval, + const base::FilePath& path); + // Constructor for testing. |clock| is a test Clock that is used to retrieve + // the time. + WriteCountHistogram(const base::TimeDelta& commit_interval, + const base::FilePath& path, + scoped_ptr<base::Clock> clock); + ~WriteCountHistogram(); + + // Record that a write has occured. + void RecordWriteOccured(); + + // Reports writes (that have not yet been reported) in all of the recorded + // intervals that have elapsed up until current time. + void ReportOutstandingWrites(); + + base::HistogramBase* GetHistogram(); + + private: + // The minimum interval at which writes can occur. + const base::TimeDelta commit_interval_; + + // The path to the file. + const base::FilePath path_; + + // Clock which is used to retrieve the current time. + scoped_ptr<base::Clock> clock_; + + // The interval at which to report write counts. + const base::TimeDelta report_interval_; + + // The time at which the last histogram value was reported for the number + // of write counts. + base::Time last_report_time_; + + // The number of writes that have occured since the last write count was + // reported. + uint32_t writes_since_last_report_; + + DISALLOW_COPY_AND_ASSIGN(WriteCountHistogram); + }; + + FRIEND_TEST_ALL_PREFIXES(base::JsonPrefStoreTest, + WriteCountHistogramTestBasic); + FRIEND_TEST_ALL_PREFIXES(base::JsonPrefStoreTest, + WriteCountHistogramTestSinglePeriod); + FRIEND_TEST_ALL_PREFIXES(base::JsonPrefStoreTest, + WriteCountHistogramTestMultiplePeriods); + FRIEND_TEST_ALL_PREFIXES(base::JsonPrefStoreTest, + WriteCountHistogramTestPeriodWithGaps); + friend class base::JsonPrefStoreLossyWriteTest; + ~JsonPrefStore() override; // This method is called after the JSON file has been read. It then hands @@ -122,6 +192,10 @@ class BASE_PREFS_EXPORT JsonPrefStore scoped_ptr<base::DictionaryValue> prefs, bool schedule_write); + // Schedule a write with the file writer as long as |flags| doesn't contain + // WriteablePrefStore::LOSSY_PREF_WRITE_FLAG. + void ScheduleWrite(uint32 flags); + const base::FilePath path_; const base::FilePath alternate_path_; const scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_; @@ -140,10 +214,13 @@ class BASE_PREFS_EXPORT JsonPrefStore bool initialized_; bool filtering_in_progress_; + bool pending_lossy_write_; PrefReadError read_error_; std::set<std::string> keys_need_empty_value_; + WriteCountHistogram write_count_histogram_; + DISALLOW_COPY_AND_ASSIGN(JsonPrefStore); }; diff --git a/chromium/base/prefs/json_pref_store_unittest.cc b/chromium/base/prefs/json_pref_store_unittest.cc index dc4043e32be..67a8adbb4d4 100644 --- a/chromium/base/prefs/json_pref_store_unittest.cc +++ b/chromium/base/prefs/json_pref_store_unittest.cc @@ -7,15 +7,20 @@ #include "base/bind.h" #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" +#include "base/location.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" +#include "base/metrics/histogram_samples.h" +#include "base/metrics/statistics_recorder.h" #include "base/path_service.h" #include "base/prefs/pref_filter.h" #include "base/run_loop.h" +#include "base/single_thread_task_runner.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/simple_test_clock.h" #include "base/threading/sequenced_worker_pool.h" #include "base/threading/thread.h" #include "base/values.h" @@ -27,6 +32,12 @@ namespace { const char kHomePage[] = "homepage"; +// Set the time on the given SimpleTestClock to the given time in minutes. +void SetCurrentTimeInMinutes(double minutes, base::SimpleTestClock* clock) { + const int32_t kBaseTimeMins = 100; + clock->SetNow(base::Time::FromDoubleT((kBaseTimeMins + minutes) * 60)); +} + // A PrefFilter that will intercept all calls to FilterOnLoad() and hold on // to the |prefs| until explicitly asked to release them. class InterceptingPrefFilter : public PrefFilter { @@ -86,7 +97,7 @@ class MockReadErrorDelegate : public PersistentPrefStore::ReadErrorDelegate { class JsonPrefStoreTest : public testing::Test { protected: - virtual void SetUp() override { + void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); ASSERT_TRUE(PathService::Get(base::DIR_TEST_DATA, &data_dir_)); @@ -94,11 +105,10 @@ class JsonPrefStoreTest : public testing::Test { ASSERT_TRUE(PathExists(data_dir_)); } - virtual void TearDown() override { + void TearDown() override { // Make sure all pending tasks have been processed (e.g., deleting the // JsonPrefStore may post write tasks). - message_loop_.PostTask(FROM_HERE, MessageLoop::QuitWhenIdleClosure()); - message_loop_.Run(); + RunLoop().RunUntilIdle(); } // The path to temporary directory used to contain the test operations. @@ -107,16 +117,18 @@ class JsonPrefStoreTest : public testing::Test { base::FilePath data_dir_; // A message loop that we can use as the file thread message loop. MessageLoop message_loop_; + + private: + // Ensure histograms are reset for each test. + StatisticsRecorder statistics_recorder_; }; // Test fallback behavior for a nonexistent file. TEST_F(JsonPrefStoreTest, NonExistentFile) { - base::FilePath bogus_input_file = data_dir_.AppendASCII("read.txt"); + base::FilePath bogus_input_file = temp_dir_.path().AppendASCII("read.txt"); ASSERT_FALSE(PathExists(bogus_input_file)); scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore( - bogus_input_file, - message_loop_.message_loop_proxy().get(), - scoped_ptr<PrefFilter>()); + bogus_input_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>()); EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE, pref_store->ReadPrefs()); EXPECT_FALSE(pref_store->ReadOnly()); @@ -124,16 +136,14 @@ TEST_F(JsonPrefStoreTest, NonExistentFile) { // Test fallback behavior for a nonexistent file and alternate file. TEST_F(JsonPrefStoreTest, NonExistentFileAndAlternateFile) { - base::FilePath bogus_input_file = data_dir_.AppendASCII("read.txt"); + base::FilePath bogus_input_file = temp_dir_.path().AppendASCII("read.txt"); base::FilePath bogus_alternate_input_file = - data_dir_.AppendASCII("read_alternate.txt"); + temp_dir_.path().AppendASCII("read_alternate.txt"); ASSERT_FALSE(PathExists(bogus_input_file)); ASSERT_FALSE(PathExists(bogus_alternate_input_file)); - scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore( - bogus_input_file, - bogus_alternate_input_file, - message_loop_.message_loop_proxy().get(), - scoped_ptr<PrefFilter>()); + scoped_refptr<JsonPrefStore> pref_store = + new JsonPrefStore(bogus_input_file, bogus_alternate_input_file, + message_loop_.task_runner(), scoped_ptr<PrefFilter>()); EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE, pref_store->ReadPrefs()); EXPECT_FALSE(pref_store->ReadOnly()); @@ -144,10 +154,8 @@ TEST_F(JsonPrefStoreTest, InvalidFile) { base::FilePath invalid_file_original = data_dir_.AppendASCII("invalid.json"); base::FilePath invalid_file = temp_dir_.path().AppendASCII("invalid.json"); ASSERT_TRUE(base::CopyFile(invalid_file_original, invalid_file)); - scoped_refptr<JsonPrefStore> pref_store = - new JsonPrefStore(invalid_file, - message_loop_.message_loop_proxy().get(), - scoped_ptr<PrefFilter>()); + scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore( + invalid_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>()); EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE, pref_store->ReadPrefs()); EXPECT_FALSE(pref_store->ReadOnly()); @@ -184,7 +192,8 @@ void RunBasicJsonPrefStoreTest(JsonPrefStore* pref_store, 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, new StringValue(some_path.value()), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); EXPECT_TRUE(pref_store->GetValue(kSomeDirectory, &actual)); EXPECT_TRUE(actual->GetAsString(&path)); EXPECT_EQ(some_path.value(), path); @@ -195,7 +204,8 @@ void RunBasicJsonPrefStoreTest(JsonPrefStore* pref_store, EXPECT_TRUE(actual->GetAsBoolean(&boolean)); EXPECT_TRUE(boolean); - pref_store->SetValue(kNewWindowsInTabs, new FundamentalValue(false)); + pref_store->SetValue(kNewWindowsInTabs, new FundamentalValue(false), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); EXPECT_TRUE(pref_store->GetValue(kNewWindowsInTabs, &actual)); EXPECT_TRUE(actual->GetAsBoolean(&boolean)); EXPECT_FALSE(boolean); @@ -204,13 +214,15 @@ void RunBasicJsonPrefStoreTest(JsonPrefStore* pref_store, int integer = 0; EXPECT_TRUE(actual->GetAsInteger(&integer)); EXPECT_EQ(20, integer); - pref_store->SetValue(kMaxTabs, new FundamentalValue(10)); + pref_store->SetValue(kMaxTabs, 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))); + 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; @@ -233,9 +245,7 @@ TEST_F(JsonPrefStoreTest, Basic) { base::FilePath input_file = temp_dir_.path().AppendASCII("write.json"); ASSERT_TRUE(PathExists(input_file)); scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore( - input_file, - message_loop_.message_loop_proxy().get(), - scoped_ptr<PrefFilter>()); + input_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>()); ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs()); EXPECT_FALSE(pref_store->ReadOnly()); EXPECT_TRUE(pref_store->IsInitializationComplete()); @@ -262,9 +272,7 @@ TEST_F(JsonPrefStoreTest, BasicAsync) { base::FilePath input_file = temp_dir_.path().AppendASCII("write.json"); ASSERT_TRUE(PathExists(input_file)); scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore( - input_file, - message_loop_.message_loop_proxy().get(), - scoped_ptr<PrefFilter>()); + input_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>()); { MockPrefStoreObserver mock_observer; @@ -301,23 +309,21 @@ TEST_F(JsonPrefStoreTest, PreserveEmptyValues) { FilePath pref_file = temp_dir_.path().AppendASCII("empty_values.json"); scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore( - pref_file, - message_loop_.message_loop_proxy(), - scoped_ptr<PrefFilter>()); + 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("dict", new base::DictionaryValue); + pref_store->SetValue("list", new base::ListValue, + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + pref_store->SetValue("dict", new base::DictionaryValue, + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); // Write to file. pref_store->CommitPendingWrite(); - MessageLoop::current()->RunUntilIdle(); + RunLoop().RunUntilIdle(); // Reload. - pref_store = new JsonPrefStore( - pref_file, - message_loop_.message_loop_proxy(), - scoped_ptr<PrefFilter>()); + pref_store = new JsonPrefStore(pref_file, message_loop_.task_runner(), + scoped_ptr<PrefFilter>()); ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs()); ASSERT_FALSE(pref_store->ReadOnly()); @@ -335,15 +341,15 @@ TEST_F(JsonPrefStoreTest, RemoveClearsEmptyParent) { FilePath pref_file = temp_dir_.path().AppendASCII("empty_values.json"); scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore( - pref_file, - message_loop_.message_loop_proxy(), - scoped_ptr<PrefFilter>()); + pref_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>()); base::DictionaryValue* dict = new base::DictionaryValue; dict->SetString("key", "value"); - pref_store->SetValue("dict", dict); + pref_store->SetValue("dict", dict, + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); - pref_store->RemoveValue("dict.key"); + pref_store->RemoveValue("dict.key", + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); const base::Value* retrieved_dict = NULL; bool has_dict = pref_store->GetValue("dict", &retrieved_dict); @@ -352,12 +358,10 @@ TEST_F(JsonPrefStoreTest, RemoveClearsEmptyParent) { // Tests asynchronous reading of the file when there is no file. TEST_F(JsonPrefStoreTest, AsyncNonExistingFile) { - base::FilePath bogus_input_file = data_dir_.AppendASCII("read.txt"); + base::FilePath bogus_input_file = temp_dir_.path().AppendASCII("read.txt"); ASSERT_FALSE(PathExists(bogus_input_file)); scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore( - bogus_input_file, - message_loop_.message_loop_proxy().get(), - scoped_ptr<PrefFilter>()); + bogus_input_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>()); MockPrefStoreObserver mock_observer; pref_store->AddObserver(&mock_observer); @@ -385,10 +389,8 @@ TEST_F(JsonPrefStoreTest, ReadWithInterceptor) { new InterceptingPrefFilter()); InterceptingPrefFilter* raw_intercepting_pref_filter_ = intercepting_pref_filter.get(); - scoped_refptr<JsonPrefStore> pref_store = - new JsonPrefStore(input_file, - message_loop_.message_loop_proxy().get(), - intercepting_pref_filter.Pass()); + scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore( + input_file, message_loop_.task_runner(), intercepting_pref_filter.Pass()); ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE, pref_store->ReadPrefs()); @@ -432,10 +434,8 @@ TEST_F(JsonPrefStoreTest, ReadAsyncWithInterceptor) { new InterceptingPrefFilter()); InterceptingPrefFilter* raw_intercepting_pref_filter_ = intercepting_pref_filter.get(); - scoped_refptr<JsonPrefStore> pref_store = - new JsonPrefStore(input_file, - message_loop_.message_loop_proxy().get(), - intercepting_pref_filter.Pass()); + scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore( + input_file, message_loop_.task_runner(), intercepting_pref_filter.Pass()); MockPrefStoreObserver mock_observer; pref_store->AddObserver(&mock_observer); @@ -498,11 +498,9 @@ TEST_F(JsonPrefStoreTest, AlternateFile) { temp_dir_.path().AppendASCII("alternate.json"); ASSERT_FALSE(PathExists(input_file)); ASSERT_TRUE(PathExists(alternate_input_file)); - scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore( - input_file, - alternate_input_file, - message_loop_.message_loop_proxy().get(), - scoped_ptr<PrefFilter>()); + scoped_refptr<JsonPrefStore> pref_store = + new JsonPrefStore(input_file, alternate_input_file, + message_loop_.task_runner(), scoped_ptr<PrefFilter>()); ASSERT_FALSE(PathExists(input_file)); ASSERT_TRUE(PathExists(alternate_input_file)); @@ -544,11 +542,9 @@ TEST_F(JsonPrefStoreTest, AlternateFileIgnoredWhenMainFileExists) { temp_dir_.path().AppendASCII("alternate.json"); ASSERT_TRUE(PathExists(input_file)); ASSERT_TRUE(PathExists(alternate_input_file)); - scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore( - input_file, - alternate_input_file, - message_loop_.message_loop_proxy().get(), - scoped_ptr<PrefFilter>()); + scoped_refptr<JsonPrefStore> pref_store = + new JsonPrefStore(input_file, alternate_input_file, + message_loop_.task_runner(), scoped_ptr<PrefFilter>()); ASSERT_TRUE(PathExists(input_file)); ASSERT_TRUE(PathExists(alternate_input_file)); @@ -586,11 +582,9 @@ TEST_F(JsonPrefStoreTest, AlternateFileDNE) { temp_dir_.path().AppendASCII("alternate.json"); ASSERT_TRUE(PathExists(input_file)); ASSERT_FALSE(PathExists(alternate_input_file)); - scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore( - input_file, - alternate_input_file, - message_loop_.message_loop_proxy().get(), - scoped_ptr<PrefFilter>()); + scoped_refptr<JsonPrefStore> pref_store = + new JsonPrefStore(input_file, alternate_input_file, + message_loop_.task_runner(), scoped_ptr<PrefFilter>()); ASSERT_TRUE(PathExists(input_file)); ASSERT_FALSE(PathExists(alternate_input_file)); @@ -628,11 +622,9 @@ TEST_F(JsonPrefStoreTest, BasicAsyncWithAlternateFile) { temp_dir_.path().AppendASCII("alternate.json"); ASSERT_FALSE(PathExists(input_file)); ASSERT_TRUE(PathExists(alternate_input_file)); - scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore( - input_file, - alternate_input_file, - message_loop_.message_loop_proxy().get(), - scoped_ptr<PrefFilter>()); + scoped_refptr<JsonPrefStore> pref_store = + new JsonPrefStore(input_file, alternate_input_file, + message_loop_.task_runner(), scoped_ptr<PrefFilter>()); ASSERT_FALSE(PathExists(input_file)); ASSERT_TRUE(PathExists(alternate_input_file)); @@ -671,4 +663,269 @@ TEST_F(JsonPrefStoreTest, BasicAsyncWithAlternateFile) { pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json")); } +TEST_F(JsonPrefStoreTest, WriteCountHistogramTestBasic) { + SimpleTestClock* test_clock = new SimpleTestClock; + SetCurrentTimeInMinutes(0, test_clock); + JsonPrefStore::WriteCountHistogram histogram( + base::TimeDelta::FromSeconds(10), + base::FilePath(FILE_PATH_LITERAL("/tmp/Local State")), + scoped_ptr<base::Clock>(test_clock)); + int32 report_interval = + JsonPrefStore::WriteCountHistogram::kHistogramWriteReportIntervalMins; + + histogram.RecordWriteOccured(); + + SetCurrentTimeInMinutes(1.5 * report_interval, test_clock); + histogram.ReportOutstandingWrites(); + scoped_ptr<HistogramSamples> samples = + histogram.GetHistogram()->SnapshotSamples(); + ASSERT_EQ(1, samples->GetCount(1)); + ASSERT_EQ(1, samples->TotalCount()); + + ASSERT_EQ("Settings.JsonDataWriteCount.Local_State", + histogram.GetHistogram()->histogram_name()); + ASSERT_TRUE(histogram.GetHistogram()->HasConstructionArguments(1, 30, 31)); +} + +TEST_F(JsonPrefStoreTest, WriteCountHistogramTestSinglePeriod) { + SimpleTestClock* test_clock = new SimpleTestClock; + SetCurrentTimeInMinutes(0, test_clock); + JsonPrefStore::WriteCountHistogram histogram( + base::TimeDelta::FromSeconds(10), + base::FilePath(FILE_PATH_LITERAL("/tmp/Local State")), + scoped_ptr<base::Clock>(test_clock)); + int32 report_interval = + JsonPrefStore::WriteCountHistogram::kHistogramWriteReportIntervalMins; + + histogram.RecordWriteOccured(); + SetCurrentTimeInMinutes(0.5 * report_interval, test_clock); + histogram.RecordWriteOccured(); + SetCurrentTimeInMinutes(0.7 * report_interval, test_clock); + histogram.RecordWriteOccured(); + + // Nothing should be recorded until the report period has elapsed. + scoped_ptr<HistogramSamples> samples = + histogram.GetHistogram()->SnapshotSamples(); + ASSERT_EQ(0, samples->TotalCount()); + + SetCurrentTimeInMinutes(1.3 * report_interval, test_clock); + histogram.RecordWriteOccured(); + + // Now the report period has elapsed. + samples = histogram.GetHistogram()->SnapshotSamples(); + ASSERT_EQ(1, samples->GetCount(3)); + ASSERT_EQ(1, samples->TotalCount()); + + // The last write won't be recorded because the second count period hasn't + // fully elapsed. + SetCurrentTimeInMinutes(1.5 * report_interval, test_clock); + histogram.ReportOutstandingWrites(); + + samples = histogram.GetHistogram()->SnapshotSamples(); + ASSERT_EQ(1, samples->GetCount(3)); + ASSERT_EQ(1, samples->TotalCount()); +} + +TEST_F(JsonPrefStoreTest, WriteCountHistogramTestMultiplePeriods) { + SimpleTestClock* test_clock = new SimpleTestClock; + SetCurrentTimeInMinutes(0, test_clock); + JsonPrefStore::WriteCountHistogram histogram( + base::TimeDelta::FromSeconds(10), + base::FilePath(FILE_PATH_LITERAL("/tmp/Local State")), + scoped_ptr<base::Clock>(test_clock)); + int32 report_interval = + JsonPrefStore::WriteCountHistogram::kHistogramWriteReportIntervalMins; + + histogram.RecordWriteOccured(); + SetCurrentTimeInMinutes(0.5 * report_interval, test_clock); + histogram.RecordWriteOccured(); + SetCurrentTimeInMinutes(0.7 * report_interval, test_clock); + histogram.RecordWriteOccured(); + SetCurrentTimeInMinutes(1.3 * report_interval, test_clock); + histogram.RecordWriteOccured(); + SetCurrentTimeInMinutes(1.5 * report_interval, test_clock); + histogram.RecordWriteOccured(); + SetCurrentTimeInMinutes(2.1 * report_interval, test_clock); + histogram.RecordWriteOccured(); + SetCurrentTimeInMinutes(2.5 * report_interval, test_clock); + histogram.RecordWriteOccured(); + SetCurrentTimeInMinutes(2.7 * report_interval, test_clock); + histogram.RecordWriteOccured(); + SetCurrentTimeInMinutes(3.3 * report_interval, test_clock); + histogram.RecordWriteOccured(); + + // The last write won't be recorded because the second count period hasn't + // fully elapsed + SetCurrentTimeInMinutes(3.5 * report_interval, test_clock); + histogram.ReportOutstandingWrites(); + scoped_ptr<HistogramSamples> samples = + histogram.GetHistogram()->SnapshotSamples(); + ASSERT_EQ(2, samples->GetCount(3)); + ASSERT_EQ(1, samples->GetCount(2)); + ASSERT_EQ(3, samples->TotalCount()); +} + +TEST_F(JsonPrefStoreTest, WriteCountHistogramTestPeriodWithGaps) { + SimpleTestClock* test_clock = new SimpleTestClock; + SetCurrentTimeInMinutes(0, test_clock); + JsonPrefStore::WriteCountHistogram histogram( + base::TimeDelta::FromSeconds(10), + base::FilePath(FILE_PATH_LITERAL("/tmp/Local State")), + scoped_ptr<base::Clock>(test_clock)); + int32 report_interval = + JsonPrefStore::WriteCountHistogram::kHistogramWriteReportIntervalMins; + + // 1 write in the first period. + histogram.RecordWriteOccured(); + + // No writes in the second and third periods. + + // 2 writes in the fourth period. + SetCurrentTimeInMinutes(3.1 * report_interval, test_clock); + histogram.RecordWriteOccured(); + SetCurrentTimeInMinutes(3.3 * report_interval, test_clock); + histogram.RecordWriteOccured(); + + // No writes in the fifth period. + + // 3 writes in the sixth period. + SetCurrentTimeInMinutes(5.1 * report_interval, test_clock); + histogram.RecordWriteOccured(); + SetCurrentTimeInMinutes(5.3 * report_interval, test_clock); + histogram.RecordWriteOccured(); + SetCurrentTimeInMinutes(5.5 * report_interval, test_clock); + histogram.RecordWriteOccured(); + + SetCurrentTimeInMinutes(6.1 * report_interval, test_clock); + histogram.ReportOutstandingWrites(); + scoped_ptr<HistogramSamples> samples = + histogram.GetHistogram()->SnapshotSamples(); + ASSERT_EQ(3, samples->GetCount(0)); + ASSERT_EQ(1, samples->GetCount(1)); + ASSERT_EQ(1, samples->GetCount(2)); + ASSERT_EQ(1, samples->GetCount(3)); + ASSERT_EQ(6, samples->TotalCount()); +} + +class JsonPrefStoreLossyWriteTest : public JsonPrefStoreTest { + protected: + void SetUp() override { + JsonPrefStoreTest::SetUp(); + test_file_ = temp_dir_.path().AppendASCII("test.json"); + } + + // Creates a JsonPrefStore with the given |file_writer|. + scoped_refptr<JsonPrefStore> CreatePrefStore() { + return new JsonPrefStore(test_file_, message_loop_.task_runner(), + scoped_ptr<PrefFilter>()); + } + + // Return the ImportantFileWriter for a given JsonPrefStore. + ImportantFileWriter* GetImportantFileWriter( + scoped_refptr<JsonPrefStore> pref_store) { + return &(pref_store->writer_); + } + + // Get the contents of kTestFile. Pumps the message loop before returning the + // result. + std::string GetTestFileContents() { + RunLoop().RunUntilIdle(); + std::string file_contents; + ReadFileToString(test_file_, &file_contents); + return file_contents; + } + + private: + base::FilePath test_file_; +}; + +TEST_F(JsonPrefStoreLossyWriteTest, LossyWriteBasic) { + scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore(); + ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store); + + // 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"), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + ASSERT_TRUE(file_writer->HasPendingWrite()); + file_writer->DoScheduledWrite(); + ASSERT_EQ("{\"normal\":\"normal\"}", GetTestFileContents()); + ASSERT_FALSE(file_writer->HasPendingWrite()); + + // Set a lossy pref and check that it is not scheduled to be written. + // SetValue/RemoveValue. + pref_store->SetValue("lossy", 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"), + WriteablePrefStore::LOSSY_PREF_WRITE_FLAG); + ASSERT_FALSE(file_writer->HasPendingWrite()); + pref_store->RemoveValueSilently("lossy", + WriteablePrefStore::LOSSY_PREF_WRITE_FLAG); + ASSERT_FALSE(file_writer->HasPendingWrite()); + + // ReportValueChanged. + pref_store->SetValue("lossy", new base::StringValue("lossy"), + WriteablePrefStore::LOSSY_PREF_WRITE_FLAG); + ASSERT_FALSE(file_writer->HasPendingWrite()); + pref_store->ReportValueChanged("lossy", + WriteablePrefStore::LOSSY_PREF_WRITE_FLAG); + ASSERT_FALSE(file_writer->HasPendingWrite()); + + // Call CommitPendingWrite and check that the lossy pref and the normal pref + // are there with the last values set above. + pref_store->CommitPendingWrite(); + ASSERT_FALSE(file_writer->HasPendingWrite()); + ASSERT_EQ("{\"lossy\":\"lossy\",\"normal\":\"normal\"}", + GetTestFileContents()); +} + +TEST_F(JsonPrefStoreLossyWriteTest, LossyWriteMixedLossyFirst) { + scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore(); + ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store); + + // 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"), + 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"), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + ASSERT_TRUE(file_writer->HasPendingWrite()); + + // Call DoScheduledWrite and check both prefs get written. + file_writer->DoScheduledWrite(); + ASSERT_EQ("{\"lossy\":\"lossy\",\"normal\":\"normal\"}", + GetTestFileContents()); + ASSERT_FALSE(file_writer->HasPendingWrite()); +} + +TEST_F(JsonPrefStoreLossyWriteTest, LossyWriteMixedLossySecond) { + scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore(); + ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store); + + // 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"), + 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"), + WriteablePrefStore::LOSSY_PREF_WRITE_FLAG); + ASSERT_TRUE(file_writer->HasPendingWrite()); + + // Call DoScheduledWrite and check both prefs get written. + file_writer->DoScheduledWrite(); + ASSERT_EQ("{\"lossy\":\"lossy\",\"normal\":\"normal\"}", + GetTestFileContents()); + ASSERT_FALSE(file_writer->HasPendingWrite()); +} + } // namespace base diff --git a/chromium/base/prefs/mock_pref_change_callback.h b/chromium/base/prefs/mock_pref_change_callback.h index 422754afd6e..3030fab17c2 100644 --- a/chromium/base/prefs/mock_pref_change_callback.h +++ b/chromium/base/prefs/mock_pref_change_callback.h @@ -19,8 +19,7 @@ using testing::Truly; // |pref_name| in |prefs| matches |value|. If |value| is NULL, the matcher // checks that the value is not set. MATCHER_P3(PrefValueMatches, prefs, pref_name, value, "") { - const PrefService::Preference* pref = - prefs->FindPreference(pref_name.c_str()); + const PrefService::Preference* pref = prefs->FindPreference(pref_name); if (!pref) return false; diff --git a/chromium/base/prefs/overlay_user_pref_store.cc b/chromium/base/prefs/overlay_user_pref_store.cc index a708bb68363..e93dffd961a 100644 --- a/chromium/base/prefs/overlay_user_pref_store.cc +++ b/chromium/base/prefs/overlay_user_pref_store.cc @@ -63,34 +63,36 @@ bool OverlayUserPrefStore::GetMutableValue(const std::string& key, } void OverlayUserPrefStore::SetValue(const std::string& key, - base::Value* value) { + base::Value* value, + uint32 flags) { if (!ShallBeStoredInOverlay(key)) { - underlay_->SetValue(GetUnderlayKey(key), value); + underlay_->SetValue(GetUnderlayKey(key), value, flags); return; } if (overlay_.SetValue(key, value)) - ReportValueChanged(key); + ReportValueChanged(key, flags); } void OverlayUserPrefStore::SetValueSilently(const std::string& key, - base::Value* value) { + base::Value* value, + uint32 flags) { if (!ShallBeStoredInOverlay(key)) { - underlay_->SetValueSilently(GetUnderlayKey(key), value); + underlay_->SetValueSilently(GetUnderlayKey(key), value, flags); return; } overlay_.SetValue(key, value); } -void OverlayUserPrefStore::RemoveValue(const std::string& key) { +void OverlayUserPrefStore::RemoveValue(const std::string& key, uint32 flags) { if (!ShallBeStoredInOverlay(key)) { - underlay_->RemoveValue(GetUnderlayKey(key)); + underlay_->RemoveValue(GetUnderlayKey(key), flags); return; } if (overlay_.RemoveValue(key)) - ReportValueChanged(key); + ReportValueChanged(key, flags); } bool OverlayUserPrefStore::ReadOnly() const { @@ -119,13 +121,14 @@ void OverlayUserPrefStore::CommitPendingWrite() { // We do not write our content intentionally. } -void OverlayUserPrefStore::ReportValueChanged(const std::string& key) { +void OverlayUserPrefStore::ReportValueChanged(const std::string& key, + uint32 flags) { FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key)); } void OverlayUserPrefStore::OnPrefValueChanged(const std::string& key) { if (!overlay_.GetValue(GetOverlayKey(key), NULL)) - ReportValueChanged(GetOverlayKey(key)); + ReportValueChanged(GetOverlayKey(key), DEFAULT_PREF_WRITE_FLAGS); } void OverlayUserPrefStore::OnInitializationCompleted(bool succeeded) { diff --git a/chromium/base/prefs/overlay_user_pref_store.h b/chromium/base/prefs/overlay_user_pref_store.h index 5194a7bebd0..04c309d8a10 100644 --- a/chromium/base/prefs/overlay_user_pref_store.h +++ b/chromium/base/prefs/overlay_user_pref_store.h @@ -39,15 +39,19 @@ class BASE_PREFS_EXPORT OverlayUserPrefStore : public PersistentPrefStore, // Methods of PersistentPrefStore. bool GetMutableValue(const std::string& key, base::Value** result) override; - void SetValue(const std::string& key, base::Value* value) override; - void SetValueSilently(const std::string& key, base::Value* value) override; - void RemoveValue(const std::string& key) override; + void SetValue(const std::string& key, + base::Value* value, + uint32 flags) override; + void SetValueSilently(const std::string& key, + base::Value* value, + uint32 flags) override; + void RemoveValue(const std::string& key, uint32 flags) override; bool ReadOnly() const override; PrefReadError GetReadError() const override; PrefReadError ReadPrefs() override; void ReadPrefsAsync(ReadErrorDelegate* delegate) override; void CommitPendingWrite() override; - void ReportValueChanged(const std::string& key) override; + void ReportValueChanged(const std::string& key, uint32 flags) override; // Methods of PrefStore::Observer. void OnPrefValueChanged(const std::string& key) override; diff --git a/chromium/base/prefs/overlay_user_pref_store_unittest.cc b/chromium/base/prefs/overlay_user_pref_store_unittest.cc index 66a7b3bd1a8..06b4ec989a6 100644 --- a/chromium/base/prefs/overlay_user_pref_store_unittest.cc +++ b/chromium/base/prefs/overlay_user_pref_store_unittest.cc @@ -37,7 +37,7 @@ class OverlayUserPrefStoreTest : public testing::Test { overlay_->RegisterOverlayPref(mapped_overlay_key, mapped_underlay_key); } - virtual ~OverlayUserPrefStoreTest() {} + ~OverlayUserPrefStoreTest() override {} scoped_refptr<TestingPrefStore> underlay_; scoped_refptr<OverlayUserPrefStore> overlay_; @@ -48,38 +48,47 @@ TEST_F(OverlayUserPrefStoreTest, Observer) { overlay_->AddObserver(&obs); // Check that underlay first value is reported. - underlay_->SetValue(overlay_key, new FundamentalValue(42)); + underlay_->SetValue(overlay_key, 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, 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, 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, new FundamentalValue(45), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); EXPECT_TRUE(obs.changed_keys.empty()); // Check that overlay remove is reported. - overlay_->RemoveValue(overlay_key); + overlay_->RemoveValue(overlay_key, + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); obs.VerifyAndResetChangedKey(overlay_key); // Check that underlay remove is reported. - underlay_->RemoveValue(overlay_key); + underlay_->RemoveValue(overlay_key, + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); obs.VerifyAndResetChangedKey(overlay_key); // Check respecting of silence. - overlay_->SetValueSilently(overlay_key, new FundamentalValue(46)); + overlay_->SetValueSilently(overlay_key, 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)); - overlay_->SetValue(overlay_key, new FundamentalValue(48)); + underlay_->SetValue(overlay_key, new FundamentalValue(47), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + overlay_->SetValue(overlay_key, new FundamentalValue(48), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); EXPECT_TRUE(obs.changed_keys.empty()); } @@ -88,7 +97,8 @@ TEST_F(OverlayUserPrefStoreTest, GetAndSet) { 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, new FundamentalValue(42), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); // Value shines through: EXPECT_TRUE(overlay_->GetValue(overlay_key, &value)); @@ -97,7 +107,8 @@ TEST_F(OverlayUserPrefStoreTest, GetAndSet) { 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, new FundamentalValue(43), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); EXPECT_TRUE(overlay_->GetValue(overlay_key, &value)); EXPECT_TRUE(base::FundamentalValue(43).Equals(value)); @@ -105,7 +116,8 @@ TEST_F(OverlayUserPrefStoreTest, GetAndSet) { EXPECT_TRUE(underlay_->GetValue(overlay_key, &value)); EXPECT_TRUE(base::FundamentalValue(42).Equals(value)); - overlay_->RemoveValue(overlay_key); + overlay_->RemoveValue(overlay_key, + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); // Value shines through: EXPECT_TRUE(overlay_->GetValue(overlay_key, &value)); @@ -117,7 +129,8 @@ TEST_F(OverlayUserPrefStoreTest, GetAndSet) { // 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, new DictionaryValue, + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); Value* modify = NULL; EXPECT_TRUE(overlay_->GetMutableValue(overlay_key, &modify)); @@ -146,11 +159,13 @@ TEST_F(OverlayUserPrefStoreTest, GlobalPref) { const Value* value = NULL; // Check that underlay first value is reported. - underlay_->SetValue(regular_key, new FundamentalValue(42)); + underlay_->SetValue(regular_key, 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, new FundamentalValue(43), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); obs.VerifyAndResetChangedKey(regular_key); // Check that we get this value from the overlay @@ -158,7 +173,8 @@ TEST_F(OverlayUserPrefStoreTest, GlobalPref) { 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, new FundamentalValue(44), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); obs.VerifyAndResetChangedKey(regular_key); // Check that we get this value from the overlay and the underlay. @@ -168,7 +184,8 @@ TEST_F(OverlayUserPrefStoreTest, GlobalPref) { EXPECT_TRUE(base::FundamentalValue(44).Equals(value)); // Check that overlay remove is reported. - overlay_->RemoveValue(regular_key); + overlay_->RemoveValue(regular_key, + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); obs.VerifyAndResetChangedKey(regular_key); // Check that value was removed from overlay and underlay @@ -176,14 +193,17 @@ TEST_F(OverlayUserPrefStoreTest, GlobalPref) { EXPECT_FALSE(underlay_->GetValue(regular_key, &value)); // Check respecting of silence. - overlay_->SetValueSilently(regular_key, new FundamentalValue(46)); + overlay_->SetValueSilently(regular_key, 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)); - overlay_->SetValue(regular_key, new FundamentalValue(48)); + underlay_->SetValue(regular_key, new FundamentalValue(47), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + overlay_->SetValue(regular_key, new FundamentalValue(48), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); EXPECT_TRUE(obs.changed_keys.empty()); } @@ -196,11 +216,13 @@ TEST_F(OverlayUserPrefStoreTest, NamesMapping) { // 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, 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, new FundamentalValue(43), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); obs.VerifyAndResetChangedKey(mapped_overlay_key); // Check that we get this value from the overlay with both keys @@ -211,7 +233,8 @@ TEST_F(OverlayUserPrefStoreTest, NamesMapping) { 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, new FundamentalValue(44), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); obs.VerifyAndResetChangedKey(mapped_overlay_key); // Check that we get an overriden value from overlay, while reading the @@ -224,15 +247,18 @@ TEST_F(OverlayUserPrefStoreTest, NamesMapping) { 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, new FundamentalValue(45), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); EXPECT_TRUE(obs.changed_keys.empty()); // Check that overlay remove is reported. - overlay_->RemoveValue(mapped_overlay_key); + overlay_->RemoveValue(mapped_overlay_key, + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); obs.VerifyAndResetChangedKey(mapped_overlay_key); // Check that underlay remove is reported. - underlay_->RemoveValue(mapped_underlay_key); + underlay_->RemoveValue(mapped_underlay_key, + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); obs.VerifyAndResetChangedKey(mapped_overlay_key); // Check that value was removed. @@ -240,14 +266,17 @@ TEST_F(OverlayUserPrefStoreTest, NamesMapping) { 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, 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)); - overlay_->SetValue(mapped_overlay_key, new FundamentalValue(48)); + underlay_->SetValue(mapped_underlay_key, new FundamentalValue(47), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + overlay_->SetValue(mapped_overlay_key, new FundamentalValue(48), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); EXPECT_TRUE(obs.changed_keys.empty()); } diff --git a/chromium/base/prefs/pref_change_registrar.cc b/chromium/base/prefs/pref_change_registrar.cc index 28ac3740343..13193484ff4 100644 --- a/chromium/base/prefs/pref_change_registrar.cc +++ b/chromium/base/prefs/pref_change_registrar.cc @@ -23,12 +23,12 @@ void PrefChangeRegistrar::Init(PrefService* service) { service_ = service; } -void PrefChangeRegistrar::Add(const char* path, +void PrefChangeRegistrar::Add(const std::string& path, const base::Closure& obs) { Add(path, base::Bind(&PrefChangeRegistrar::InvokeUnnamedCallback, obs)); } -void PrefChangeRegistrar::Add(const char* path, +void PrefChangeRegistrar::Add(const std::string& path, const NamedChangeCallback& obs) { if (!service_) { NOTREACHED(); @@ -40,7 +40,7 @@ void PrefChangeRegistrar::Add(const char* path, observers_[path] = obs; } -void PrefChangeRegistrar::Remove(const char* path) { +void PrefChangeRegistrar::Remove(const std::string& path) { DCHECK(IsObserved(path)); observers_.erase(path); @@ -50,7 +50,7 @@ void PrefChangeRegistrar::Remove(const char* path) { void PrefChangeRegistrar::RemoveAll() { for (ObserverMap::const_iterator it = observers_.begin(); it != observers_.end(); ++it) { - service_->RemovePrefObserver(it->first.c_str(), this); + service_->RemovePrefObserver(it->first, this); } observers_.clear(); @@ -67,8 +67,7 @@ bool PrefChangeRegistrar::IsObserved(const std::string& pref) { bool PrefChangeRegistrar::IsManaged() { for (ObserverMap::const_iterator it = observers_.begin(); it != observers_.end(); ++it) { - const PrefService::Preference* pref = - service_->FindPreference(it->first.c_str()); + const PrefService::Preference* pref = service_->FindPreference(it->first); if (pref && pref->IsManaged()) return true; } diff --git a/chromium/base/prefs/pref_change_registrar.h b/chromium/base/prefs/pref_change_registrar.h index 70c22fe3f43..acf0a684962 100644 --- a/chromium/base/prefs/pref_change_registrar.h +++ b/chromium/base/prefs/pref_change_registrar.h @@ -40,11 +40,11 @@ class BASE_PREFS_EXPORT PrefChangeRegistrar : public PrefObserver { // the preference that is changing as its parameter. // // Only one observer may be registered per path. - void Add(const char* path, const base::Closure& obs); - void Add(const char* path, const NamedChangeCallback& obs); + void Add(const std::string& path, const base::Closure& obs); + void Add(const std::string& path, const NamedChangeCallback& obs); // Removes the pref observer registered for |path|. - void Remove(const char* path); + void Remove(const std::string& path); // Removes all observers that have been previously added with a call to Add. void RemoveAll(); diff --git a/chromium/base/prefs/pref_change_registrar_unittest.cc b/chromium/base/prefs/pref_change_registrar_unittest.cc index e9255a0ec63..da425cfad28 100644 --- a/chromium/base/prefs/pref_change_registrar_unittest.cc +++ b/chromium/base/prefs/pref_change_registrar_unittest.cc @@ -27,10 +27,8 @@ class MockPrefService : public TestingPrefServiceSimple { MockPrefService() {} virtual ~MockPrefService() {} - MOCK_METHOD2(AddPrefObserver, - void(const char*, PrefObserver*)); - MOCK_METHOD2(RemovePrefObserver, - void(const char*, PrefObserver*)); + MOCK_METHOD2(AddPrefObserver, void(const std::string&, PrefObserver*)); + MOCK_METHOD2(RemovePrefObserver, void(const std::string&, PrefObserver*)); }; } // namespace @@ -38,10 +36,10 @@ class MockPrefService : public TestingPrefServiceSimple { class PrefChangeRegistrarTest : public testing::Test { public: PrefChangeRegistrarTest() {} - virtual ~PrefChangeRegistrarTest() {} + ~PrefChangeRegistrarTest() override {} protected: - virtual void SetUp() override; + void SetUp() override; base::Closure observer() const { return base::Bind(&base::DoNothing); diff --git a/chromium/base/prefs/pref_member.cc b/chromium/base/prefs/pref_member.cc index 4fa616f4fb6..64c3d6a9934 100644 --- a/chromium/base/prefs/pref_member.cc +++ b/chromium/base/prefs/pref_member.cc @@ -7,11 +7,10 @@ #include "base/callback.h" #include "base/callback_helpers.h" #include "base/location.h" -#include "base/message_loop/message_loop_proxy.h" #include "base/prefs/pref_service.h" +#include "base/thread_task_runner_handle.h" #include "base/value_conversions.h" -using base::MessageLoopProxy; using base::SingleThreadTaskRunner; namespace subtle { @@ -25,23 +24,20 @@ PrefMemberBase::~PrefMemberBase() { Destroy(); } -void PrefMemberBase::Init(const char* pref_name, +void PrefMemberBase::Init(const std::string& pref_name, PrefService* prefs, const NamedChangeCallback& observer) { observer_ = observer; Init(pref_name, prefs); } -void PrefMemberBase::Init(const char* pref_name, - PrefService* prefs) { - DCHECK(pref_name); +void PrefMemberBase::Init(const std::string& pref_name, PrefService* prefs) { DCHECK(prefs); DCHECK(pref_name_.empty()); // Check that Init is only called once. prefs_ = prefs; pref_name_ = pref_name; // Check that the preference is registered. - DCHECK(prefs_->FindPreference(pref_name_.c_str())) - << pref_name << " not registered."; + DCHECK(prefs_->FindPreference(pref_name_)) << pref_name << " not registered."; // Add ourselves as a pref observer so we can keep our local value in sync. prefs_->AddPrefObserver(pref_name, this); @@ -49,18 +45,18 @@ void PrefMemberBase::Init(const char* pref_name, void PrefMemberBase::Destroy() { if (prefs_ && !pref_name_.empty()) { - prefs_->RemovePrefObserver(pref_name_.c_str(), this); + prefs_->RemovePrefObserver(pref_name_, this); prefs_ = NULL; } } void PrefMemberBase::MoveToThread( - const scoped_refptr<SingleThreadTaskRunner>& task_runner) { + scoped_refptr<SingleThreadTaskRunner> task_runner) { VerifyValuePrefName(); // Load the value from preferences if it hasn't been loaded so far. if (!internal()) UpdateValueFromPref(base::Closure()); - internal()->MoveToThread(task_runner); + internal()->MoveToThread(task_runner.Pass()); } void PrefMemberBase::OnPreferenceChanged(PrefService* service, @@ -72,8 +68,7 @@ void PrefMemberBase::OnPreferenceChanged(PrefService* service, void PrefMemberBase::UpdateValueFromPref(const base::Closure& callback) const { VerifyValuePrefName(); - const PrefService::Preference* pref = - prefs_->FindPreference(pref_name_.c_str()); + const PrefService::Preference* pref = prefs_->FindPreference(pref_name_); DCHECK(pref); if (!internal()) CreateInternal(); @@ -95,15 +90,14 @@ void PrefMemberBase::InvokeUnnamedCallback(const base::Closure& callback, } PrefMemberBase::Internal::Internal() - : thread_loop_(MessageLoopProxy::current()), + : thread_task_runner_(base::ThreadTaskRunnerHandle::Get()), is_managed_(false), is_user_modifiable_(false) { } PrefMemberBase::Internal::~Internal() { } bool PrefMemberBase::Internal::IsOnCorrectThread() const { - // In unit tests, there may not be a message loop. - return thread_loop_.get() == NULL || thread_loop_->BelongsToCurrentThread(); + return thread_task_runner_->BelongsToCurrentThread(); } void PrefMemberBase::Internal::UpdateValue( @@ -119,19 +113,18 @@ void PrefMemberBase::Internal::UpdateValue( is_managed_ = is_managed; is_user_modifiable_ = is_user_modifiable; } else { - bool may_run = thread_loop_->PostTask( - FROM_HERE, - base::Bind(&PrefMemberBase::Internal::UpdateValue, this, - value.release(), is_managed, is_user_modifiable, - closure_runner.Release())); + bool may_run = thread_task_runner_->PostTask( + FROM_HERE, base::Bind(&PrefMemberBase::Internal::UpdateValue, this, + value.release(), is_managed, is_user_modifiable, + closure_runner.Release())); DCHECK(may_run); } } void PrefMemberBase::Internal::MoveToThread( - const scoped_refptr<SingleThreadTaskRunner>& task_runner) { + scoped_refptr<SingleThreadTaskRunner> task_runner) { CheckOnCorrectThread(); - thread_loop_ = task_runner; + thread_task_runner_ = task_runner.Pass(); } bool PrefMemberVectorStringUpdate(const base::Value& value, @@ -158,7 +151,7 @@ bool PrefMemberVectorStringUpdate(const base::Value& value, template <> void PrefMember<bool>::UpdatePref(const bool& value) { - prefs()->SetBoolean(pref_name().c_str(), value); + prefs()->SetBoolean(pref_name(), value); } template <> @@ -169,7 +162,7 @@ bool PrefMember<bool>::Internal::UpdateValueInternal( template <> void PrefMember<int>::UpdatePref(const int& value) { - prefs()->SetInteger(pref_name().c_str(), value); + prefs()->SetInteger(pref_name(), value); } template <> @@ -180,7 +173,7 @@ bool PrefMember<int>::Internal::UpdateValueInternal( template <> void PrefMember<double>::UpdatePref(const double& value) { - prefs()->SetDouble(pref_name().c_str(), value); + prefs()->SetDouble(pref_name(), value); } template <> @@ -191,7 +184,7 @@ bool PrefMember<double>::Internal::UpdateValueInternal(const base::Value& value) template <> void PrefMember<std::string>::UpdatePref(const std::string& value) { - prefs()->SetString(pref_name().c_str(), value); + prefs()->SetString(pref_name(), value); } template <> @@ -203,7 +196,7 @@ bool PrefMember<std::string>::Internal::UpdateValueInternal( template <> void PrefMember<base::FilePath>::UpdatePref(const base::FilePath& value) { - prefs()->SetFilePath(pref_name().c_str(), value); + prefs()->SetFilePath(pref_name(), value); } template <> @@ -218,7 +211,7 @@ void PrefMember<std::vector<std::string> >::UpdatePref( const std::vector<std::string>& value) { base::ListValue list_value; list_value.AppendStrings(value); - prefs()->Set(pref_name().c_str(), list_value); + prefs()->Set(pref_name(), list_value); } template <> diff --git a/chromium/base/prefs/pref_member.h b/chromium/base/prefs/pref_member.h index a05d60ed36c..6dceb439ed7 100644 --- a/chromium/base/prefs/pref_member.h +++ b/chromium/base/prefs/pref_member.h @@ -65,8 +65,7 @@ class BASE_PREFS_EXPORT PrefMemberBase : public PrefObserver { bool is_user_modifiable, const base::Closure& callback) const; - void MoveToThread( - const scoped_refptr<base::SingleThreadTaskRunner>& task_runner); + void MoveToThread(scoped_refptr<base::SingleThreadTaskRunner> task_runner); // See PrefMember<> for description. bool IsManaged() const { @@ -92,7 +91,7 @@ class BASE_PREFS_EXPORT PrefMemberBase : public PrefObserver { bool IsOnCorrectThread() const; - scoped_refptr<base::SingleThreadTaskRunner> thread_loop_; + scoped_refptr<base::SingleThreadTaskRunner> thread_task_runner_; mutable bool is_managed_; mutable bool is_user_modifiable_; @@ -103,17 +102,17 @@ class BASE_PREFS_EXPORT PrefMemberBase : public PrefObserver { virtual ~PrefMemberBase(); // See PrefMember<> for description. - void Init(const char* pref_name, PrefService* prefs, + void Init(const std::string& pref_name, + PrefService* prefs, const NamedChangeCallback& observer); - void Init(const char* pref_name, PrefService* prefs); + void Init(const std::string& pref_name, PrefService* prefs); virtual void CreateInternal() const = 0; // See PrefMember<> for description. void Destroy(); - void MoveToThread( - const scoped_refptr<base::SingleThreadTaskRunner>& task_runner); + void MoveToThread(scoped_refptr<base::SingleThreadTaskRunner> task_runner); // PrefObserver void OnPreferenceChanged(PrefService* service, @@ -169,17 +168,19 @@ class PrefMember : public subtle::PrefMemberBase { // Do the actual initialization of the class. Use the two-parameter // version if you don't want any notifications of changes. This // method should only be called on the UI thread. - void Init(const char* pref_name, PrefService* prefs, + void Init(const std::string& pref_name, + PrefService* prefs, const NamedChangeCallback& observer) { subtle::PrefMemberBase::Init(pref_name, prefs, observer); } - void Init(const char* pref_name, PrefService* prefs, + void Init(const std::string& pref_name, + PrefService* prefs, const base::Closure& observer) { subtle::PrefMemberBase::Init( pref_name, prefs, base::Bind(&PrefMemberBase::InvokeUnnamedCallback, observer)); } - void Init(const char* pref_name, PrefService* prefs) { + void Init(const std::string& pref_name, PrefService* prefs) { subtle::PrefMemberBase::Init(pref_name, prefs); } @@ -198,8 +199,7 @@ class PrefMember : public subtle::PrefMemberBase { // via PostTask. // This method should only be used from the thread the PrefMember is currently // on, which is the UI thread by default. - void MoveToThread( - const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) { + void MoveToThread(scoped_refptr<base::SingleThreadTaskRunner> task_runner) { subtle::PrefMemberBase::MoveToThread(task_runner); } @@ -261,20 +261,21 @@ class PrefMember : public subtle::PrefMemberBase { } protected: - virtual ~Internal() {} + ~Internal() override {} - virtual BASE_PREFS_EXPORT bool UpdateValueInternal( + BASE_PREFS_EXPORT bool UpdateValueInternal( const base::Value& value) const override; // We cache the value of the pref so we don't have to keep walking the pref // tree. mutable ValueType value_; + private: DISALLOW_COPY_AND_ASSIGN(Internal); }; - virtual Internal* internal() const override { return internal_.get(); } - virtual void CreateInternal() const override { internal_ = new Internal(); } + Internal* internal() const override { return internal_.get(); } + void CreateInternal() const override { internal_ = new Internal(); } // This method is used to do the actual sync with pref of the specified type. void BASE_PREFS_EXPORT UpdatePref(const ValueType& value); diff --git a/chromium/base/prefs/pref_member_unittest.cc b/chromium/base/prefs/pref_member_unittest.cc index d4d7e02c978..a776e2c2553 100644 --- a/chromium/base/prefs/pref_member_unittest.cc +++ b/chromium/base/prefs/pref_member_unittest.cc @@ -5,9 +5,10 @@ #include "base/prefs/pref_member.h" #include "base/bind.h" -#include "base/message_loop/message_loop.h" +#include "base/location.h" #include "base/prefs/pref_registry_simple.h" #include "base/prefs/testing_pref_service.h" +#include "base/single_thread_task_runner.h" #include "base/synchronization/waitable_event.h" #include "base/threading/thread.h" #include "testing/gtest/include/gtest/gtest.h" @@ -35,9 +36,9 @@ class GetPrefValueHelper pref_thread_.Start(); } - void Init(const char* pref_name, PrefService* prefs) { + void Init(const std::string& pref_name, PrefService* prefs) { pref_.Init(pref_name, prefs); - pref_.MoveToThread(pref_thread_.message_loop_proxy()); + pref_.MoveToThread(pref_thread_.task_runner()); } void Destroy() { @@ -46,10 +47,9 @@ class GetPrefValueHelper void FetchValue() { base::WaitableEvent event(true, false); - ASSERT_TRUE( - pref_thread_.message_loop_proxy()->PostTask( - FROM_HERE, - base::Bind(&GetPrefValueHelper::GetPrefValue, this, &event))); + ASSERT_TRUE(pref_thread_.task_runner()->PostTask( + FROM_HERE, + base::Bind(&GetPrefValueHelper::GetPrefValue, this, &event))); event.Wait(); } @@ -100,7 +100,11 @@ class PrefMemberTestClass { } // anonymous namespace -TEST(PrefMemberTest, BasicGetAndSet) { +class PrefMemberTest : public testing::Test { + base::MessageLoop message_loop_; +}; + +TEST_F(PrefMemberTest, BasicGetAndSet) { TestingPrefServiceSimple prefs; RegisterTestPrefs(prefs.registry()); @@ -227,7 +231,7 @@ TEST(PrefMemberTest, BasicGetAndSet) { EXPECT_EQ(expected_vector, *string_list); } -TEST(PrefMemberTest, InvalidList) { +TEST_F(PrefMemberTest, InvalidList) { // Set the vector to an initial good value. std::vector<std::string> expected_vector; expected_vector.push_back("foo"); @@ -245,7 +249,7 @@ TEST(PrefMemberTest, InvalidList) { EXPECT_EQ(expected_vector, vector); } -TEST(PrefMemberTest, TwoPrefs) { +TEST_F(PrefMemberTest, TwoPrefs) { // Make sure two DoublePrefMembers stay in sync. TestingPrefServiceSimple prefs; RegisterTestPrefs(prefs.registry()); @@ -266,7 +270,7 @@ TEST(PrefMemberTest, TwoPrefs) { EXPECT_EQ(4.2, *pref2); } -TEST(PrefMemberTest, Observer) { +TEST_F(PrefMemberTest, Observer) { TestingPrefServiceSimple prefs; RegisterTestPrefs(prefs.registry()); @@ -293,12 +297,12 @@ TEST(PrefMemberTest, Observer) { EXPECT_EQ("hello", prefs.GetString(kStringPref)); } -TEST(PrefMemberTest, NoInit) { +TEST_F(PrefMemberTest, NoInit) { // Make sure not calling Init on a PrefMember doesn't cause problems. IntegerPrefMember pref; } -TEST(PrefMemberTest, MoveToThread) { +TEST_F(PrefMemberTest, MoveToThread) { TestingPrefServiceSimple prefs; scoped_refptr<GetPrefValueHelper> helper(new GetPrefValueHelper()); RegisterTestPrefs(prefs.registry()); diff --git a/chromium/base/prefs/pref_notifier_impl.cc b/chromium/base/prefs/pref_notifier_impl.cc index c02a7b3f5a5..7ae5fe679f8 100644 --- a/chromium/base/prefs/pref_notifier_impl.cc +++ b/chromium/base/prefs/pref_notifier_impl.cc @@ -22,7 +22,7 @@ PrefNotifierImpl::~PrefNotifierImpl() { // Verify that there are no pref observers when we shut down. for (PrefObserverMap::iterator it = pref_observers_.begin(); it != pref_observers_.end(); ++it) { - PrefObserverList::Iterator obs_iterator(*(it->second)); + PrefObserverList::Iterator obs_iterator(it->second); if (obs_iterator.GetNext()) { LOG(WARNING) << "pref observer found at shutdown " << it->first; } @@ -38,7 +38,7 @@ PrefNotifierImpl::~PrefNotifierImpl() { init_observers_.clear(); } -void PrefNotifierImpl::AddPrefObserver(const char* path, +void PrefNotifierImpl::AddPrefObserver(const std::string& path, PrefObserver* obs) { // Get the pref observer list associated with the path. PrefObserverList* observer_list = NULL; @@ -56,7 +56,7 @@ void PrefNotifierImpl::AddPrefObserver(const char* path, observer_list->AddObserver(obs); } -void PrefNotifierImpl::RemovePrefObserver(const char* path, +void PrefNotifierImpl::RemovePrefObserver(const std::string& path, PrefObserver* obs) { DCHECK(thread_checker_.CalledOnValidThread()); @@ -98,7 +98,7 @@ void PrefNotifierImpl::FireObservers(const std::string& path) { DCHECK(thread_checker_.CalledOnValidThread()); // Only send notifications for registered preferences. - if (!pref_service_->FindPreference(path.c_str())) + if (!pref_service_->FindPreference(path)) return; const PrefObserverMap::iterator observer_iterator = diff --git a/chromium/base/prefs/pref_notifier_impl.h b/chromium/base/prefs/pref_notifier_impl.h index 3f4c254c416..cfd46ff4211 100644 --- a/chromium/base/prefs/pref_notifier_impl.h +++ b/chromium/base/prefs/pref_notifier_impl.h @@ -29,8 +29,8 @@ class BASE_PREFS_EXPORT PrefNotifierImpl // If the pref at the given path changes, we call the observer's // OnPreferenceChanged method. - void AddPrefObserver(const char* path, PrefObserver* observer); - void RemovePrefObserver(const char* path, PrefObserver* observer); + void AddPrefObserver(const std::string& path, PrefObserver* observer); + void RemovePrefObserver(const std::string& path, PrefObserver* observer); // We run the callback once, when initialization completes. The bool // parameter will be set to true for successful initialization, diff --git a/chromium/base/prefs/pref_notifier_impl_unittest.cc b/chromium/base/prefs/pref_notifier_impl_unittest.cc index 8482c0289c7..c3cbf4f48c1 100644 --- a/chromium/base/prefs/pref_notifier_impl_unittest.cc +++ b/chromium/base/prefs/pref_notifier_impl_unittest.cc @@ -51,14 +51,14 @@ class MockPrefNotifier : public PrefNotifierImpl { MOCK_METHOD1(FireObservers, void(const std::string& path)); - size_t CountObserver(const char* path, PrefObserver* obs) { + size_t CountObserver(const std::string& path, PrefObserver* obs) { PrefObserverMap::const_iterator observer_iterator = pref_observers()->find(path); if (observer_iterator == pref_observers()->end()) return false; PrefObserverList* observer_list = observer_iterator->second; - PrefObserverList::Iterator it(*observer_list); + PrefObserverList::Iterator it(observer_list); PrefObserver* existing_obs; size_t count = 0; while ((existing_obs = it.GetNext()) != NULL) { @@ -85,7 +85,7 @@ class PrefObserverMock : public PrefObserver { // Test fixture class. class PrefNotifierTest : public testing::Test { protected: - virtual void SetUp() { + void SetUp() override { pref_service_.registry()->RegisterBooleanPref(kChangedPref, true); pref_service_.registry()->RegisterBooleanPref(kUnchangedPref, true); } diff --git a/chromium/base/prefs/pref_registry.cc b/chromium/base/prefs/pref_registry.cc index 134996185c4..74f4b522088 100644 --- a/chromium/base/prefs/pref_registry.cc +++ b/chromium/base/prefs/pref_registry.cc @@ -7,6 +7,7 @@ #include "base/logging.h" #include "base/prefs/default_pref_store.h" #include "base/prefs/pref_store.h" +#include "base/stl_util.h" #include "base/values.h" PrefRegistry::PrefRegistry() @@ -16,6 +17,13 @@ PrefRegistry::PrefRegistry() PrefRegistry::~PrefRegistry() { } +uint32 PrefRegistry::GetRegistrationFlags(const std::string& pref_name) const { + const auto& it = registration_flags_.find(pref_name); + if (it == registration_flags_.end()) + return NO_REGISTRATION_FLAGS; + return it->second; +} + scoped_refptr<PrefStore> PrefRegistry::defaults() { return defaults_.get(); } @@ -28,7 +36,7 @@ PrefRegistry::const_iterator PrefRegistry::end() const { return defaults_->end(); } -void PrefRegistry::SetDefaultPrefValue(const char* pref_name, +void PrefRegistry::SetDefaultPrefValue(const std::string& pref_name, base::Value* value) { DCHECK(value); const base::Value* current_value = NULL; @@ -40,14 +48,19 @@ void PrefRegistry::SetDefaultPrefValue(const char* pref_name, defaults_->ReplaceDefaultValue(pref_name, make_scoped_ptr(value)); } -void PrefRegistry::RegisterPreference(const char* path, - base::Value* default_value) { +void PrefRegistry::RegisterPreference(const std::string& path, + base::Value* default_value, + uint32 flags) { base::Value::Type orig_type = default_value->GetType(); DCHECK(orig_type != base::Value::TYPE_NULL && orig_type != base::Value::TYPE_BINARY) << "invalid preference type: " << orig_type; DCHECK(!defaults_->GetValue(path, NULL)) << "Trying to register a previously registered pref: " << path; + DCHECK(!ContainsKey(registration_flags_, path)) << + "Trying to register a previously registered pref: " << path; defaults_->SetDefaultValue(path, make_scoped_ptr(default_value)); + if (flags != NO_REGISTRATION_FLAGS) + registration_flags_[path] = flags; } diff --git a/chromium/base/prefs/pref_registry.h b/chromium/base/prefs/pref_registry.h index 896db3ffa62..caf2a1afaf1 100644 --- a/chromium/base/prefs/pref_registry.h +++ b/chromium/base/prefs/pref_registry.h @@ -5,6 +5,7 @@ #ifndef BASE_PREFS_PREF_REGISTRY_H_ #define BASE_PREFS_PREF_REGISTRY_H_ +#include "base/containers/hash_tables.h" #include "base/memory/ref_counted.h" #include "base/prefs/base_prefs_export.h" #include "base/prefs/pref_value_map.h" @@ -27,10 +28,30 @@ class PrefStore; // also work, but this is being deprecated. class BASE_PREFS_EXPORT PrefRegistry : public base::RefCounted<PrefRegistry> { public: + // Registration flags that can be specified which impact how the pref will + // behave or be stored. This will be passed in a bitmask when the pref is + // registered. Subclasses of PrefRegistry can specify their own flags. Care + // must be taken to ensure none of these overlap with the flags below. + enum PrefRegistrationFlags : uint32 { + // No flags are specified. + NO_REGISTRATION_FLAGS = 0, + + // The first 8 bits are reserved for subclasses of PrefRegistry to use. + + // This marks the pref as "lossy". There is no strict time guarantee on when + // a lossy pref will be persisted to permanent storage when it is modified. + LOSSY_PREF = 1 << 8, + }; + typedef PrefValueMap::const_iterator const_iterator; + typedef base::hash_map<std::string, uint32> PrefRegistrationFlagsMap; PrefRegistry(); + // Retrieve the set of registration flags for the given preference. The return + // value is a bitmask of PrefRegistrationFlags. + uint32 GetRegistrationFlags(const std::string& pref_name) const; + // Gets the registered defaults. scoped_refptr<PrefStore> defaults(); @@ -41,17 +62,23 @@ class BASE_PREFS_EXPORT PrefRegistry : public base::RefCounted<PrefRegistry> { // Changes the default value for a preference. Takes ownership of |value|. // // |pref_name| must be a previously registered preference. - void SetDefaultPrefValue(const char* pref_name, base::Value* value); + void SetDefaultPrefValue(const std::string& pref_name, base::Value* value); protected: friend class base::RefCounted<PrefRegistry>; virtual ~PrefRegistry(); - // Used by subclasses to register a default value for a preference. - void RegisterPreference(const char* path, base::Value* default_value); + // Used by subclasses to register a default value and registration flags for + // a preference. |flags| is a bitmask of |PrefRegistrationFlags|. + void RegisterPreference(const std::string& path, + base::Value* default_value, + uint32 flags); scoped_refptr<DefaultPrefStore> defaults_; + // A map of pref name to a bitmask of PrefRegistrationFlags. + PrefRegistrationFlagsMap registration_flags_; + private: DISALLOW_COPY_AND_ASSIGN(PrefRegistry); }; diff --git a/chromium/base/prefs/pref_registry_simple.cc b/chromium/base/prefs/pref_registry_simple.cc index 7453016a272..93c268686d5 100644 --- a/chromium/base/prefs/pref_registry_simple.cc +++ b/chromium/base/prefs/pref_registry_simple.cc @@ -14,53 +14,148 @@ PrefRegistrySimple::PrefRegistrySimple() { PrefRegistrySimple::~PrefRegistrySimple() { } -void PrefRegistrySimple::RegisterBooleanPref(const char* path, +void PrefRegistrySimple::RegisterBooleanPref(const std::string& path, bool default_value) { - RegisterPreference(path, new base::FundamentalValue(default_value)); + RegisterPrefAndNotify(path, new base::FundamentalValue(default_value), + NO_REGISTRATION_FLAGS); } -void PrefRegistrySimple::RegisterIntegerPref(const char* path, +void PrefRegistrySimple::RegisterIntegerPref(const std::string& path, int default_value) { - RegisterPreference(path, new base::FundamentalValue(default_value)); + RegisterPrefAndNotify(path, new base::FundamentalValue(default_value), + NO_REGISTRATION_FLAGS); } -void PrefRegistrySimple::RegisterDoublePref(const char* path, +void PrefRegistrySimple::RegisterDoublePref(const std::string& path, double default_value) { - RegisterPreference(path, new base::FundamentalValue(default_value)); + RegisterPrefAndNotify(path, new base::FundamentalValue(default_value), + NO_REGISTRATION_FLAGS); } -void PrefRegistrySimple::RegisterStringPref(const char* path, +void PrefRegistrySimple::RegisterStringPref(const std::string& path, const std::string& default_value) { - RegisterPreference(path, new base::StringValue(default_value)); + RegisterPrefAndNotify(path, new base::StringValue(default_value), + NO_REGISTRATION_FLAGS); } void PrefRegistrySimple::RegisterFilePathPref( - const char* path, + const std::string& path, const base::FilePath& default_value) { - RegisterPreference(path, new base::StringValue(default_value.value())); + RegisterPrefAndNotify(path, new base::StringValue(default_value.value()), + NO_REGISTRATION_FLAGS); } -void PrefRegistrySimple::RegisterListPref(const char* path) { - RegisterPreference(path, new base::ListValue()); +void PrefRegistrySimple::RegisterListPref(const std::string& path) { + RegisterPrefAndNotify(path, new base::ListValue(), NO_REGISTRATION_FLAGS); } -void PrefRegistrySimple::RegisterListPref(const char* path, +void PrefRegistrySimple::RegisterListPref(const std::string& path, base::ListValue* default_value) { - RegisterPreference(path, default_value); + RegisterPrefAndNotify(path, default_value, NO_REGISTRATION_FLAGS); } -void PrefRegistrySimple::RegisterDictionaryPref(const char* path) { - RegisterPreference(path, new base::DictionaryValue()); +void PrefRegistrySimple::RegisterDictionaryPref(const std::string& path) { + RegisterPrefAndNotify(path, new base::DictionaryValue(), + NO_REGISTRATION_FLAGS); } void PrefRegistrySimple::RegisterDictionaryPref( - const char* path, + const std::string& path, base::DictionaryValue* default_value) { - RegisterPreference(path, default_value); + RegisterPrefAndNotify(path, default_value, NO_REGISTRATION_FLAGS); } -void PrefRegistrySimple::RegisterInt64Pref(const char* path, +void PrefRegistrySimple::RegisterInt64Pref(const std::string& path, int64 default_value) { - RegisterPreference( - path, new base::StringValue(base::Int64ToString(default_value))); + RegisterPrefAndNotify( + path, new base::StringValue(base::Int64ToString(default_value)), + NO_REGISTRATION_FLAGS); +} + +void PrefRegistrySimple::RegisterUint64Pref(const std::string& path, + uint64 default_value) { + RegisterPrefAndNotify( + path, new base::StringValue(base::Uint64ToString(default_value)), + NO_REGISTRATION_FLAGS); +} + +void PrefRegistrySimple::RegisterBooleanPref(const std::string& path, + bool default_value, + uint32 flags) { + RegisterPrefAndNotify(path, new base::FundamentalValue(default_value), flags); +} + +void PrefRegistrySimple::RegisterIntegerPref(const std::string& path, + int default_value, + uint32 flags) { + RegisterPrefAndNotify(path, new base::FundamentalValue(default_value), flags); +} + +void PrefRegistrySimple::RegisterDoublePref(const std::string& path, + double default_value, + uint32 flags) { + RegisterPrefAndNotify(path, new base::FundamentalValue(default_value), flags); +} + +void PrefRegistrySimple::RegisterStringPref(const std::string& path, + const std::string& default_value, + uint32 flags) { + RegisterPrefAndNotify(path, new base::StringValue(default_value), flags); +} + +void PrefRegistrySimple::RegisterFilePathPref( + const std::string& path, + const base::FilePath& default_value, + uint32 flags) { + RegisterPrefAndNotify(path, new base::StringValue(default_value.value()), + flags); +} + +void PrefRegistrySimple::RegisterListPref(const std::string& path, + uint32 flags) { + RegisterPrefAndNotify(path, new base::ListValue(), flags); +} + +void PrefRegistrySimple::RegisterListPref(const std::string& path, + base::ListValue* default_value, + uint32 flags) { + RegisterPrefAndNotify(path, default_value, flags); +} + +void PrefRegistrySimple::RegisterDictionaryPref(const std::string& path, + uint32 flags) { + RegisterPrefAndNotify(path, new base::DictionaryValue(), flags); +} + +void PrefRegistrySimple::RegisterDictionaryPref( + const std::string& path, + base::DictionaryValue* default_value, + uint32 flags) { + RegisterPrefAndNotify(path, default_value, flags); +} + +void PrefRegistrySimple::RegisterInt64Pref(const std::string& path, + int64 default_value, + uint32 flags) { + RegisterPrefAndNotify( + path, new base::StringValue(base::Int64ToString(default_value)), flags); +} + +void PrefRegistrySimple::RegisterUint64Pref(const std::string& path, + uint64 default_value, + uint32 flags) { + RegisterPrefAndNotify( + path, new base::StringValue(base::Uint64ToString(default_value)), flags); +} + +void PrefRegistrySimple::OnPrefRegistered(const std::string& path, + base::Value* default_value, + uint32 flags) { +} + +void PrefRegistrySimple::RegisterPrefAndNotify(const std::string& path, + base::Value* default_value, + uint32 flags) { + RegisterPreference(path, default_value, flags); + OnPrefRegistered(path, default_value, flags); } diff --git a/chromium/base/prefs/pref_registry_simple.h b/chromium/base/prefs/pref_registry_simple.h index 41fe5900b19..6b69e30cc0d 100644 --- a/chromium/base/prefs/pref_registry_simple.h +++ b/chromium/base/prefs/pref_registry_simple.h @@ -21,23 +21,63 @@ class BASE_PREFS_EXPORT PrefRegistrySimple : public PrefRegistry { public: PrefRegistrySimple(); - void RegisterBooleanPref(const char* path, bool default_value); - void RegisterIntegerPref(const char* path, int default_value); - void RegisterDoublePref(const char* path, double default_value); - void RegisterStringPref(const char* path, const std::string& default_value); - void RegisterFilePathPref(const char* path, + void RegisterBooleanPref(const std::string& path, bool default_value); + void RegisterIntegerPref(const std::string& path, int default_value); + void RegisterDoublePref(const std::string& path, double default_value); + void RegisterStringPref(const std::string& path, + const std::string& default_value); + void RegisterFilePathPref(const std::string& path, const base::FilePath& default_value); - void RegisterListPref(const char* path); - void RegisterDictionaryPref(const char* path); - void RegisterListPref(const char* path, base::ListValue* default_value); - void RegisterDictionaryPref(const char* path, + void RegisterListPref(const std::string& path); + void RegisterDictionaryPref(const std::string& path); + void RegisterListPref(const std::string& path, + base::ListValue* default_value); + void RegisterDictionaryPref(const std::string& path, base::DictionaryValue* default_value); - void RegisterInt64Pref(const char* path, - int64 default_value); + void RegisterInt64Pref(const std::string& path, int64 default_value); + void RegisterUint64Pref(const std::string&, uint64 default_value); - private: + // Versions of registration functions that accept PrefRegistrationFlags. + // |flags| is a bitmask of PrefRegistrationFlags. + void RegisterBooleanPref(const std::string&, + bool default_value, + uint32 flags); + void RegisterIntegerPref(const std::string&, int default_value, uint32 flags); + void RegisterDoublePref(const std::string&, + double default_value, + uint32 flags); + void RegisterStringPref(const std::string&, + const std::string& default_value, + uint32 flags); + void RegisterFilePathPref(const std::string&, + const base::FilePath& default_value, + uint32 flags); + void RegisterListPref(const std::string&, uint32 flags); + void RegisterDictionaryPref(const std::string&, uint32 flags); + void RegisterListPref(const std::string&, + base::ListValue* default_value, + uint32 flags); + void RegisterDictionaryPref(const std::string&, + base::DictionaryValue* default_value, + uint32 flags); + void RegisterInt64Pref(const std::string&, int64 default_value, uint32 flags); + void RegisterUint64Pref(const std::string&, + uint64 default_value, + uint32 flags); + + protected: ~PrefRegistrySimple() override; + // Allows subclasses to hook into pref registration. + virtual void OnPrefRegistered(const std::string&, + base::Value* default_value, + uint32 flags); + + private: + void RegisterPrefAndNotify(const std::string&, + base::Value* default_value, + uint32 flags); + DISALLOW_COPY_AND_ASSIGN(PrefRegistrySimple); }; diff --git a/chromium/base/prefs/pref_service.cc b/chromium/base/prefs/pref_service.cc index 433f8145ec2..6e1d58ca0f1 100644 --- a/chromium/base/prefs/pref_service.cc +++ b/chromium/base/prefs/pref_service.cc @@ -8,16 +8,18 @@ #include "base/bind.h" #include "base/files/file_path.h" +#include "base/location.h" #include "base/logging.h" -#include "base/message_loop/message_loop.h" #include "base/metrics/histogram.h" #include "base/prefs/default_pref_store.h" #include "base/prefs/pref_notifier_impl.h" #include "base/prefs/pref_registry.h" #include "base/prefs/pref_value_store.h" +#include "base/single_thread_task_runner.h" #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" +#include "base/thread_task_runner_handle.h" #include "base/value_conversions.h" #include "build/build_config.h" @@ -36,6 +38,19 @@ class ReadErrorHandler : public PersistentPrefStore::ReadErrorDelegate { base::Callback<void(PersistentPrefStore::PrefReadError)> callback_; }; +// Returns the WriteablePrefStore::PrefWriteFlags for the pref with the given +// |path|. +uint32 GetWriteFlags(const PrefService::Preference* pref) { + uint32 write_flags = WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS; + + if (!pref) + return write_flags; + + if (pref->registration_flags() & PrefRegistry::LOSSY_PREF) + write_flags |= WriteablePrefStore::LOSSY_PREF_WRITE_FLAG; + return write_flags; +} + } // namespace PrefService::PrefService( @@ -53,6 +68,12 @@ PrefService::PrefService( read_error_callback_(read_error_callback) { pref_notifier_->SetPrefService(this); + // TODO(battre): This is a check for crbug.com/435208 to make sure that + // access violations are caused by a use-after-free bug and not by an + // initialization bug. + CHECK(pref_registry_); + CHECK(pref_value_store_); + InitFromStorage(async); } @@ -73,10 +94,9 @@ void PrefService::InitFromStorage(bool async) { read_error_callback_.Run(user_pref_store_->ReadPrefs()); } else { // Guarantee that initialization happens after this function returned. - base::MessageLoop::current()->PostTask( + base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, - base::Bind(&PersistentPrefStore::ReadPrefsAsync, - user_pref_store_.get(), + base::Bind(&PersistentPrefStore::ReadPrefsAsync, user_pref_store_.get(), new ReadErrorHandler(read_error_callback_))); } } @@ -86,7 +106,7 @@ void PrefService::CommitPendingWrite() { user_pref_store_->CommitPendingWrite(); } -bool PrefService::GetBoolean(const char* path) const { +bool PrefService::GetBoolean(const std::string& path) const { DCHECK(CalledOnValidThread()); bool result = false; @@ -101,7 +121,7 @@ bool PrefService::GetBoolean(const char* path) const { return result; } -int PrefService::GetInteger(const char* path) const { +int PrefService::GetInteger(const std::string& path) const { DCHECK(CalledOnValidThread()); int result = 0; @@ -116,7 +136,7 @@ int PrefService::GetInteger(const char* path) const { return result; } -double PrefService::GetDouble(const char* path) const { +double PrefService::GetDouble(const std::string& path) const { DCHECK(CalledOnValidThread()); double result = 0.0; @@ -131,7 +151,7 @@ double PrefService::GetDouble(const char* path) const { return result; } -std::string PrefService::GetString(const char* path) const { +std::string PrefService::GetString(const std::string& path) const { DCHECK(CalledOnValidThread()); std::string result; @@ -146,7 +166,7 @@ std::string PrefService::GetString(const char* path) const { return result; } -base::FilePath PrefService::GetFilePath(const char* path) const { +base::FilePath PrefService::GetFilePath(const std::string& path) const { DCHECK(CalledOnValidThread()); base::FilePath result; @@ -161,7 +181,7 @@ base::FilePath PrefService::GetFilePath(const char* path) const { return result; } -bool PrefService::HasPrefPath(const char* path) const { +bool PrefService::HasPrefPath(const std::string& path) const { const Preference* pref = FindPreference(path); return pref && !pref->IsDefaultValue(); } @@ -169,11 +189,21 @@ bool PrefService::HasPrefPath(const char* path) const { scoped_ptr<base::DictionaryValue> PrefService::GetPreferenceValues() const { DCHECK(CalledOnValidThread()); scoped_ptr<base::DictionaryValue> out(new base::DictionaryValue); - PrefRegistry::const_iterator i = pref_registry_->begin(); - for (; i != pref_registry_->end(); ++i) { - const base::Value* value = GetPreferenceValue(i->first); - DCHECK(value); - out->Set(i->first, value->DeepCopy()); + for (const auto& it : *pref_registry_) { + out->Set(it.first, GetPreferenceValue(it.first)->CreateDeepCopy()); + } + return out.Pass(); +} + +scoped_ptr<base::DictionaryValue> PrefService::GetPreferenceValuesOmitDefaults() + const { + DCHECK(CalledOnValidThread()); + scoped_ptr<base::DictionaryValue> out(new base::DictionaryValue); + for (const auto& it : *pref_registry_) { + const Preference* pref = FindPreference(it.first); + if (pref->IsDefaultValue()) + continue; + out->Set(it.first, pref->GetValue()->CreateDeepCopy()); } return out.Pass(); } @@ -182,17 +212,16 @@ scoped_ptr<base::DictionaryValue> PrefService::GetPreferenceValuesWithoutPathExpansion() const { DCHECK(CalledOnValidThread()); scoped_ptr<base::DictionaryValue> out(new base::DictionaryValue); - PrefRegistry::const_iterator i = pref_registry_->begin(); - for (; i != pref_registry_->end(); ++i) { - const base::Value* value = GetPreferenceValue(i->first); + for (const auto& it : *pref_registry_) { + const base::Value* value = GetPreferenceValue(it.first); DCHECK(value); - out->SetWithoutPathExpansion(i->first, value->DeepCopy()); + out->SetWithoutPathExpansion(it.first, value->CreateDeepCopy()); } return out.Pass(); } const PrefService::Preference* PrefService::FindPreference( - const char* pref_name) const { + const std::string& pref_name) const { DCHECK(CalledOnValidThread()); PreferenceMap::iterator it = prefs_map_.find(pref_name); if (it != prefs_map_.end()) @@ -225,18 +254,25 @@ PrefService::PrefInitializationStatus PrefService::GetInitializationStatus() } } -bool PrefService::IsManagedPreference(const char* pref_name) const { +bool PrefService::IsManagedPreference(const std::string& pref_name) const { const Preference* pref = FindPreference(pref_name); return pref && pref->IsManaged(); } -bool PrefService::IsUserModifiablePreference(const char* pref_name) const { +bool PrefService::IsPreferenceManagedByCustodian( + const std::string& pref_name) const { + const Preference* pref = FindPreference(pref_name); + return pref && pref->IsManagedByCustodian(); +} + +bool PrefService::IsUserModifiablePreference( + const std::string& pref_name) const { const Preference* pref = FindPreference(pref_name); return pref && pref->IsUserModifiable(); } const base::DictionaryValue* PrefService::GetDictionary( - const char* path) const { + const std::string& path) const { DCHECK(CalledOnValidThread()); const base::Value* value = GetPreferenceValue(path); @@ -251,7 +287,8 @@ const base::DictionaryValue* PrefService::GetDictionary( return static_cast<const base::DictionaryValue*>(value); } -const base::Value* PrefService::GetUserPrefValue(const char* path) const { +const base::Value* PrefService::GetUserPrefValue( + const std::string& path) const { DCHECK(CalledOnValidThread()); const Preference* pref = FindPreference(path); @@ -274,13 +311,14 @@ const base::Value* PrefService::GetUserPrefValue(const char* path) const { return value; } -void PrefService::SetDefaultPrefValue(const char* path, +void PrefService::SetDefaultPrefValue(const std::string& path, base::Value* value) { DCHECK(CalledOnValidThread()); pref_registry_->SetDefaultPrefValue(path, value); } -const base::Value* PrefService::GetDefaultPrefValue(const char* path) const { +const base::Value* PrefService::GetDefaultPrefValue( + const std::string& path) const { DCHECK(CalledOnValidThread()); // Lookup the preference in the default store. const base::Value* value = NULL; @@ -291,7 +329,7 @@ const base::Value* PrefService::GetDefaultPrefValue(const char* path) const { return value; } -const base::ListValue* PrefService::GetList(const char* path) const { +const base::ListValue* PrefService::GetList(const std::string& path) const { DCHECK(CalledOnValidThread()); const base::Value* value = GetPreferenceValue(path); @@ -306,11 +344,12 @@ const base::ListValue* PrefService::GetList(const char* path) const { return static_cast<const base::ListValue*>(value); } -void PrefService::AddPrefObserver(const char* path, PrefObserver* obs) { +void PrefService::AddPrefObserver(const std::string& path, PrefObserver* obs) { pref_notifier_->AddPrefObserver(path, obs); } -void PrefService::RemovePrefObserver(const char* path, PrefObserver* obs) { +void PrefService::RemovePrefObserver(const std::string& path, + PrefObserver* obs) { pref_notifier_->RemovePrefObserver(path, obs); } @@ -322,7 +361,7 @@ PrefRegistry* PrefService::DeprecatedGetPrefRegistry() { return pref_registry_.get(); } -void PrefService::ClearPref(const char* path) { +void PrefService::ClearPref(const std::string& path) { DCHECK(CalledOnValidThread()); const Preference* pref = FindPreference(path); @@ -330,38 +369,39 @@ void PrefService::ClearPref(const char* path) { NOTREACHED() << "Trying to clear an unregistered pref: " << path; return; } - user_pref_store_->RemoveValue(path); + user_pref_store_->RemoveValue(path, GetWriteFlags(pref)); } -void PrefService::Set(const char* path, const base::Value& value) { +void PrefService::Set(const std::string& path, const base::Value& value) { SetUserPrefValue(path, value.DeepCopy()); } -void PrefService::SetBoolean(const char* path, bool value) { +void PrefService::SetBoolean(const std::string& path, bool value) { SetUserPrefValue(path, new base::FundamentalValue(value)); } -void PrefService::SetInteger(const char* path, int value) { +void PrefService::SetInteger(const std::string& path, int value) { SetUserPrefValue(path, new base::FundamentalValue(value)); } -void PrefService::SetDouble(const char* path, double value) { +void PrefService::SetDouble(const std::string& path, double value) { SetUserPrefValue(path, new base::FundamentalValue(value)); } -void PrefService::SetString(const char* path, const std::string& value) { +void PrefService::SetString(const std::string& path, const std::string& value) { SetUserPrefValue(path, new base::StringValue(value)); } -void PrefService::SetFilePath(const char* path, const base::FilePath& value) { +void PrefService::SetFilePath(const std::string& path, + const base::FilePath& value) { SetUserPrefValue(path, base::CreateFilePathValue(value)); } -void PrefService::SetInt64(const char* path, int64 value) { +void PrefService::SetInt64(const std::string& path, int64 value) { SetUserPrefValue(path, new base::StringValue(base::Int64ToString(value))); } -int64 PrefService::GetInt64(const char* path) const { +int64 PrefService::GetInt64(const std::string& path) const { DCHECK(CalledOnValidThread()); const base::Value* value = GetPreferenceValue(path); @@ -378,11 +418,11 @@ int64 PrefService::GetInt64(const char* path) const { return val; } -void PrefService::SetUint64(const char* path, uint64 value) { +void PrefService::SetUint64(const std::string& path, uint64 value) { SetUserPrefValue(path, new base::StringValue(base::Uint64ToString(value))); } -uint64 PrefService::GetUint64(const char* path) const { +uint64 PrefService::GetUint64(const std::string& path) const { DCHECK(CalledOnValidThread()); const base::Value* value = GetPreferenceValue(path); @@ -399,7 +439,7 @@ uint64 PrefService::GetUint64(const char* path) const { return val; } -base::Value* PrefService::GetMutableUserPref(const char* path, +base::Value* PrefService::GetMutableUserPref(const std::string& path, base::Value::Type type) { CHECK(type == base::Value::TYPE_DICTIONARY || type == base::Value::TYPE_LIST); DCHECK(CalledOnValidThread()); @@ -426,17 +466,18 @@ base::Value* PrefService::GetMutableUserPref(const char* path, } else { NOTREACHED(); } - user_pref_store_->SetValueSilently(path, value); + user_pref_store_->SetValueSilently(path, value, GetWriteFlags(pref)); } return value; } void PrefService::ReportUserPrefChanged(const std::string& key) { DCHECK(CalledOnValidThread()); - user_pref_store_->ReportValueChanged(key); + user_pref_store_->ReportValueChanged(key, GetWriteFlags(FindPreference(key))); } -void PrefService::SetUserPrefValue(const char* path, base::Value* new_value) { +void PrefService::SetUserPrefValue(const std::string& path, + base::Value* new_value) { scoped_ptr<base::Value> owned_value(new_value); DCHECK(CalledOnValidThread()); @@ -452,7 +493,7 @@ void PrefService::SetUserPrefValue(const char* path, base::Value* new_value) { return; } - user_pref_store_->SetValue(path, owned_value.release()); + user_pref_store_->SetValue(path, owned_value.release(), GetWriteFlags(pref)); } void PrefService::UpdateCommandLinePrefStore(PrefStore* command_line_store) { @@ -463,13 +504,13 @@ void PrefService::UpdateCommandLinePrefStore(PrefStore* command_line_store) { // PrefService::Preference PrefService::Preference::Preference(const PrefService* service, - const char* name, + const std::string& name, base::Value::Type type) - : name_(name), - type_(type), - pref_service_(service) { - DCHECK(name); + : name_(name), type_(type), pref_service_(service) { DCHECK(service); + // Cache the registration flags at creation time to avoid multiple map lookups + // later. + registration_flags_ = service->pref_registry_->GetRegistrationFlags(name_); } const std::string PrefService::Preference::name() const { @@ -487,8 +528,8 @@ const base::Value* PrefService::Preference::GetValue() const { } const base::Value* PrefService::Preference::GetRecommendedValue() const { - DCHECK(pref_service_->FindPreference(name_.c_str())) << - "Must register pref before getting its value"; + DCHECK(pref_service_->FindPreference(name_)) + << "Must register pref before getting its value"; const base::Value* found_value = NULL; if (pref_value_store()->GetRecommendedValue(name_, type_, &found_value)) { @@ -501,44 +542,56 @@ const base::Value* PrefService::Preference::GetRecommendedValue() const { } bool PrefService::Preference::IsManaged() const { - return pref_value_store()->PrefValueInManagedStore(name_.c_str()); + return pref_value_store()->PrefValueInManagedStore(name_); +} + +bool PrefService::Preference::IsManagedByCustodian() const { + return pref_value_store()->PrefValueInSupervisedStore(name_.c_str()); } bool PrefService::Preference::IsRecommended() const { - return pref_value_store()->PrefValueFromRecommendedStore(name_.c_str()); + return pref_value_store()->PrefValueFromRecommendedStore(name_); } bool PrefService::Preference::HasExtensionSetting() const { - return pref_value_store()->PrefValueInExtensionStore(name_.c_str()); + return pref_value_store()->PrefValueInExtensionStore(name_); } bool PrefService::Preference::HasUserSetting() const { - return pref_value_store()->PrefValueInUserStore(name_.c_str()); + return pref_value_store()->PrefValueInUserStore(name_); } bool PrefService::Preference::IsExtensionControlled() const { - return pref_value_store()->PrefValueFromExtensionStore(name_.c_str()); + return pref_value_store()->PrefValueFromExtensionStore(name_); } bool PrefService::Preference::IsUserControlled() const { - return pref_value_store()->PrefValueFromUserStore(name_.c_str()); + return pref_value_store()->PrefValueFromUserStore(name_); } bool PrefService::Preference::IsDefaultValue() const { - return pref_value_store()->PrefValueFromDefaultStore(name_.c_str()); + return pref_value_store()->PrefValueFromDefaultStore(name_); } bool PrefService::Preference::IsUserModifiable() const { - return pref_value_store()->PrefValueUserModifiable(name_.c_str()); + return pref_value_store()->PrefValueUserModifiable(name_); } bool PrefService::Preference::IsExtensionModifiable() const { - return pref_value_store()->PrefValueExtensionModifiable(name_.c_str()); + return pref_value_store()->PrefValueExtensionModifiable(name_); } const base::Value* PrefService::GetPreferenceValue( const std::string& path) const { DCHECK(CalledOnValidThread()); + + // TODO(battre): This is a check for crbug.com/435208. After analyzing some + // crash dumps it looks like the PrefService is accessed even though it has + // been cleared already. + CHECK(pref_registry_); + CHECK(pref_registry_->defaults()); + CHECK(pref_value_store_); + const base::Value* default_value = NULL; if (pref_registry_->defaults()->GetValue(path, &default_value)) { const base::Value* found_value = NULL; diff --git a/chromium/base/prefs/pref_service.h b/chromium/base/prefs/pref_service.h index 186433c1b62..1fc6c127c05 100644 --- a/chromium/base/prefs/pref_service.h +++ b/chromium/base/prefs/pref_service.h @@ -65,7 +65,7 @@ class BASE_PREFS_EXPORT PrefService : public base::NonThreadSafe { // dictionary (a branch), or list. You shouldn't need to construct this on // your own; use the PrefService::Register*Pref methods instead. Preference(const PrefService* service, - const char* name, + const std::string& name, base::Value::Type type); ~Preference() {} @@ -88,6 +88,11 @@ class BASE_PREFS_EXPORT PrefService : public base::NonThreadSafe { // whether the pref is actually being controlled by the policy setting. bool IsManaged() const; + // Returns true if the Preference is controlled by the custodian of the + // supervised user. Since a supervised user is not expected to have an admin + // policy, this is the controlling pref if set. + bool IsManagedByCustodian() const; + // Returns true if the Preference is recommended, i.e. set by an admin // policy but the user is allowed to change it. bool IsRecommended() const; @@ -123,6 +128,10 @@ class BASE_PREFS_EXPORT PrefService : public base::NonThreadSafe { // the Preference. bool IsExtensionModifiable() const; + // Return the registration flags for this pref as a bitmask of + // PrefRegistry::PrefRegistrationFlags. + uint32 registration_flags() const { return registration_flags_; } + private: friend class PrefService; @@ -134,6 +143,8 @@ class BASE_PREFS_EXPORT PrefService : public base::NonThreadSafe { const base::Value::Type type_; + uint32 registration_flags_; + // Reference to the PrefService in which this pref was created. const PrefService* pref_service_; }; @@ -156,81 +167,87 @@ class BASE_PREFS_EXPORT PrefService : public base::NonThreadSafe { // Returns true if the preference for the given preference name is available // and is managed. - bool IsManagedPreference(const char* pref_name) const; + bool IsManagedPreference(const std::string& pref_name) const; + + // Returns true if the preference for the given preference name is available + // and is controlled by the parent/guardian of the child Account. + bool IsPreferenceManagedByCustodian(const std::string& pref_name) const; // Returns |true| if a preference with the given name is available and its // value can be changed by the user. - bool IsUserModifiablePreference(const char* pref_name) const; + bool IsUserModifiablePreference(const std::string& pref_name) const; // Look up a preference. Returns NULL if the preference is not // registered. - const PrefService::Preference* FindPreference(const char* path) const; + const PrefService::Preference* FindPreference(const std::string& path) const; // If the path is valid and the value at the end of the path matches the type // specified, it will return the specified value. Otherwise, the default // value (set when the pref was registered) will be returned. - bool GetBoolean(const char* path) const; - int GetInteger(const char* path) const; - double GetDouble(const char* path) const; - std::string GetString(const char* path) const; - base::FilePath GetFilePath(const char* path) const; + bool GetBoolean(const std::string& path) const; + int GetInteger(const std::string& path) const; + double GetDouble(const std::string& path) const; + std::string GetString(const std::string& path) const; + base::FilePath GetFilePath(const std::string& path) const; // Returns the branch if it exists, or the registered default value otherwise. // Note that |path| must point to a registered preference. In that case, these // functions will never return NULL. - const base::DictionaryValue* GetDictionary( - const char* path) const; - const base::ListValue* GetList(const char* path) const; + const base::DictionaryValue* GetDictionary(const std::string& path) const; + const base::ListValue* GetList(const std::string& path) const; // Removes a user pref and restores the pref to its default value. - void ClearPref(const char* path); + void ClearPref(const std::string& path); // If the path is valid (i.e., registered), update the pref value in the user // prefs. // To set the value of dictionary or list values in the pref tree use // Set(), but to modify the value of a dictionary or list use either // ListPrefUpdate or DictionaryPrefUpdate from scoped_user_pref_update.h. - void Set(const char* path, const base::Value& value); - void SetBoolean(const char* path, bool value); - void SetInteger(const char* path, int value); - void SetDouble(const char* path, double value); - void SetString(const char* path, const std::string& value); - void SetFilePath(const char* path, const base::FilePath& value); + void Set(const std::string& path, const base::Value& value); + void SetBoolean(const std::string& path, bool value); + void SetInteger(const std::string& path, int value); + void SetDouble(const std::string& path, double value); + void SetString(const std::string& path, const std::string& value); + void SetFilePath(const std::string& path, const base::FilePath& value); // Int64 helper methods that actually store the given value as a string. // Note that if obtaining the named value via GetDictionary or GetList, the // Value type will be TYPE_STRING. - void SetInt64(const char* path, int64 value); - int64 GetInt64(const char* path) const; + void SetInt64(const std::string& path, int64 value); + int64 GetInt64(const std::string& path) const; // As above, but for unsigned values. - void SetUint64(const char* path, uint64 value); - uint64 GetUint64(const char* path) const; + void SetUint64(const std::string& path, uint64 value); + uint64 GetUint64(const std::string& path) const; // Returns the value of the given preference, from the user pref store. If // the preference is not set in the user pref store, returns NULL. - const base::Value* GetUserPrefValue(const char* path) const; + const base::Value* GetUserPrefValue(const std::string& path) const; // Changes the default value for a preference. Takes ownership of |value|. // // Will cause a pref change notification to be fired if this causes // the effective value to change. - void SetDefaultPrefValue(const char* path, base::Value* value); + void SetDefaultPrefValue(const std::string& path, base::Value* value); // Returns the default value of the given preference. |path| must point to a // registered preference. In that case, will never return NULL. - const base::Value* GetDefaultPrefValue(const char* path) const; + const base::Value* GetDefaultPrefValue(const std::string& path) const; // Returns true if a value has been set for the specified path. // NOTE: this is NOT the same as FindPreference. In particular // FindPreference returns whether RegisterXXX has been invoked, where as // this checks if a value exists for the path. - bool HasPrefPath(const char* path) const; + bool HasPrefPath(const std::string& path) const; - // Returns a dictionary with effective preference values. The ownership - // is passed to the caller. + // Returns a dictionary with effective preference values. scoped_ptr<base::DictionaryValue> GetPreferenceValues() const; + // Returns a dictionary with effective preference values, omitting prefs that + // are at their default values. + scoped_ptr<base::DictionaryValue> GetPreferenceValuesOmitDefaults() const; + // Returns a dictionary with effective preference values. Contrary to // GetPreferenceValues(), the paths of registered preferences are not split on // '.' characters. If a registered preference stores a dictionary, however, @@ -238,7 +255,6 @@ class BASE_PREFS_EXPORT PrefService : public base::NonThreadSafe { // For example, if "foo.bar" is a registered preference, the result could look // like this: // {"foo.bar": {"a": {"b": true}}}. - // The ownership is passed to the caller. scoped_ptr<base::DictionaryValue> GetPreferenceValuesWithoutPathExpansion() const; @@ -297,6 +313,7 @@ class BASE_PREFS_EXPORT PrefService : public base::NonThreadSafe { // Give access to ReportUserPrefChanged() and GetMutableUserPref(). friend class subtle::ScopedUserPrefUpdateBase; + friend class PrefServiceTest_WriteablePrefStoreFlags_Test; // Registration of pref change observers must be done using the // PrefChangeRegistrar, which is declared as a friend here to grant it @@ -316,8 +333,8 @@ class BASE_PREFS_EXPORT PrefService : public base::NonThreadSafe { // make sure the observer gets cleaned up properly. // // Virtual for testing. - virtual void AddPrefObserver(const char* path, PrefObserver* obs); - virtual void RemovePrefObserver(const char* path, PrefObserver* obs); + virtual void AddPrefObserver(const std::string& path, PrefObserver* obs); + virtual void RemovePrefObserver(const std::string& path, PrefObserver* obs); // Sends notification of a changed preference. This needs to be called by // a ScopedUserPrefUpdate if a DictionaryValue or ListValue is changed. @@ -325,7 +342,7 @@ class BASE_PREFS_EXPORT PrefService : public base::NonThreadSafe { // Sets the value for this pref path in the user pref store and informs the // PrefNotifier of the change. - void SetUserPrefValue(const char* path, base::Value* new_value); + void SetUserPrefValue(const std::string& path, base::Value* new_value); // Load preferences from storage, attempting to diagnose and handle errors. // This should only be called from the constructor. @@ -338,7 +355,7 @@ class BASE_PREFS_EXPORT PrefService : public base::NonThreadSafe { // |type| may only be Values::TYPE_DICTIONARY or Values::TYPE_LIST and // |path| must point to a registered preference of type |type|. // Ownership of the returned value remains at the user pref store. - base::Value* GetMutableUserPref(const char* path, + base::Value* GetMutableUserPref(const std::string& path, base::Value::Type type); // GetPreferenceValue is the equivalent of FindPreference(path)->GetValue(), diff --git a/chromium/base/prefs/pref_service_factory.cc b/chromium/base/prefs/pref_service_factory.cc index d644cb1c2b0..8caf073e7bb 100644 --- a/chromium/base/prefs/pref_service_factory.cc +++ b/chromium/base/prefs/pref_service_factory.cc @@ -10,8 +10,8 @@ #include "base/prefs/pref_filter.h" #include "base/prefs/pref_notifier_impl.h" #include "base/prefs/pref_service.h" - #include "base/prefs/pref_value_store.h" +#include "base/sequenced_task_runner.h" namespace base { diff --git a/chromium/base/prefs/pref_service_unittest.cc b/chromium/base/prefs/pref_service_unittest.cc index 36ad887df2c..262d7e9de9d 100644 --- a/chromium/base/prefs/pref_service_unittest.cc +++ b/chromium/base/prefs/pref_service_unittest.cc @@ -8,6 +8,7 @@ #include "base/prefs/mock_pref_change_callback.h" #include "base/prefs/pref_change_registrar.h" #include "base/prefs/pref_registry_simple.h" +#include "base/prefs/pref_service_factory.h" #include "base/prefs/pref_value_store.h" #include "base/prefs/testing_pref_service.h" #include "base/prefs/testing_pref_store.h" @@ -227,6 +228,115 @@ TEST(PrefServiceTest, GetValueAndGetRecommendedValue) { EXPECT_EQ(kRecommendedValue, actual_int_value); } +// A PrefStore which just stores the last write flags that were used to write +// values to it. +class WriteFlagChecker : public TestingPrefStore { + public: + WriteFlagChecker() {} + + void ReportValueChanged(const std::string& key, uint32 flags) override { + SetLastWriteFlags(flags); + } + + void SetValue(const std::string& key, + base::Value* value, + uint32 flags) override { + SetLastWriteFlags(flags); + delete value; + } + + void SetValueSilently(const std::string& key, + base::Value* value, + uint32 flags) override { + SetLastWriteFlags(flags); + delete value; + } + + void RemoveValue(const std::string& key, uint32 flags) override { + SetLastWriteFlags(flags); + } + + uint32 GetLastFlagsAndClear() { + CHECK(last_write_flags_set_); + uint32 result = last_write_flags_; + last_write_flags_set_ = false; + last_write_flags_ = WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS; + return result; + } + + bool last_write_flags_set() { return last_write_flags_set_; } + + private: + ~WriteFlagChecker() override {} + + void SetLastWriteFlags(uint32 flags) { + CHECK(!last_write_flags_set_); + last_write_flags_set_ = true; + last_write_flags_ = flags; + } + + bool last_write_flags_set_ = false; + uint32 last_write_flags_ = WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS; +}; + +TEST(PrefServiceTest, WriteablePrefStoreFlags) { + scoped_refptr<WriteFlagChecker> flag_checker(new WriteFlagChecker); + scoped_refptr<PrefRegistrySimple> registry(new PrefRegistrySimple); + base::PrefServiceFactory factory; + factory.set_user_prefs(flag_checker); + scoped_ptr<PrefService> prefs(factory.Create(registry.get())); + + // The first 8 bits of write flags are reserved for subclasses. Create a + // custom flag in this range + uint32 kCustomRegistrationFlag = 1 << 2; + + // A map of the registration flags that will be tested and the write flags + // they are expected to convert to. + struct RegistrationToWriteFlags { + const char* pref_name; + uint32 registration_flags; + uint32 write_flags; + }; + const RegistrationToWriteFlags kRegistrationToWriteFlags[] = { + {"none", + PrefRegistry::NO_REGISTRATION_FLAGS, + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS}, + {"lossy", + PrefRegistry::LOSSY_PREF, + WriteablePrefStore::LOSSY_PREF_WRITE_FLAG}, + {"custom", + kCustomRegistrationFlag, + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS}, + {"lossyandcustom", + PrefRegistry::LOSSY_PREF | kCustomRegistrationFlag, + WriteablePrefStore::LOSSY_PREF_WRITE_FLAG}}; + + for (size_t i = 0; i < arraysize(kRegistrationToWriteFlags); ++i) { + RegistrationToWriteFlags entry = kRegistrationToWriteFlags[i]; + registry->RegisterDictionaryPref( + entry.pref_name, new base::DictionaryValue(), entry.registration_flags); + + SCOPED_TRACE("Currently testing pref with name: " + + std::string(entry.pref_name)); + + prefs->GetMutableUserPref(entry.pref_name, base::Value::TYPE_DICTIONARY); + EXPECT_TRUE(flag_checker->last_write_flags_set()); + EXPECT_EQ(entry.write_flags, flag_checker->GetLastFlagsAndClear()); + + prefs->ReportUserPrefChanged(entry.pref_name); + EXPECT_TRUE(flag_checker->last_write_flags_set()); + EXPECT_EQ(entry.write_flags, flag_checker->GetLastFlagsAndClear()); + + prefs->ClearPref(entry.pref_name); + EXPECT_TRUE(flag_checker->last_write_flags_set()); + EXPECT_EQ(entry.write_flags, flag_checker->GetLastFlagsAndClear()); + + prefs->SetUserPrefValue(entry.pref_name, new base::DictionaryValue()); + EXPECT_TRUE(flag_checker->last_write_flags_set()); + EXPECT_EQ(entry.write_flags, flag_checker->GetLastFlagsAndClear()); + } +} + class PrefServiceSetValueTest : public testing::Test { protected: static const char kName[]; diff --git a/chromium/base/prefs/pref_value_map.cc b/chromium/base/prefs/pref_value_map.cc index 7d3dbe7761a..5f2dc506b69 100644 --- a/chromium/base/prefs/pref_value_map.cc +++ b/chromium/base/prefs/pref_value_map.cc @@ -4,6 +4,8 @@ #include "base/prefs/pref_value_map.h" +#include <map> + #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/stl_util.h" @@ -18,56 +20,53 @@ 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()) { - if (value) - *value = entry->second; - return true; - } + if (entry == prefs_.end()) + return false; - return false; + if (value) + *value = entry->second; + return true; } bool PrefValueMap::GetValue(const std::string& key, base::Value** value) { const Map::const_iterator entry = prefs_.find(key); - if (entry != prefs_.end()) { - if (value) - *value = entry->second; - return true; - } + if (entry == prefs_.end()) + return false; - return false; + if (value) + *value = entry->second; + return true; } bool PrefValueMap::SetValue(const std::string& key, 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 = prefs_.find(key); - if (entry != prefs_.end()) { - if (base::Value::Equals(entry->second, value)) - return false; - delete entry->second; - entry->second = value_ptr.release(); - } else { - prefs_[key] = value_ptr.release(); - } + const Map::iterator& entry = result.first; + if (base::Value::Equals(entry->second, value)) + return false; + + delete entry->second; + entry->second = value_ptr.release(); return true; } bool PrefValueMap::RemoveValue(const std::string& key) { const Map::iterator entry = prefs_.find(key); - if (entry != prefs_.end()) { - delete entry->second; - prefs_.erase(entry); - return true; - } + if (entry == prefs_.end()) + return false; - return false; + delete entry->second; + prefs_.erase(entry); + return true; } void PrefValueMap::Clear() { STLDeleteValues(&prefs_); - prefs_.clear(); } void PrefValueMap::Swap(PrefValueMap* other) { @@ -92,7 +91,7 @@ PrefValueMap::const_iterator PrefValueMap::end() const { bool PrefValueMap::GetBoolean(const std::string& key, bool* value) const { - const base::Value* stored_value = NULL; + const base::Value* stored_value = nullptr; return GetValue(key, &stored_value) && stored_value->GetAsBoolean(value); } @@ -102,7 +101,7 @@ void PrefValueMap::SetBoolean(const std::string& key, bool value) { bool PrefValueMap::GetString(const std::string& key, std::string* value) const { - const base::Value* stored_value = NULL; + const base::Value* stored_value = nullptr; return GetValue(key, &stored_value) && stored_value->GetAsString(value); } @@ -112,7 +111,7 @@ void PrefValueMap::SetString(const std::string& key, } bool PrefValueMap::GetInteger(const std::string& key, int* value) const { - const base::Value* stored_value = NULL; + const base::Value* stored_value = nullptr; return GetValue(key, &stored_value) && stored_value->GetAsInteger(value); } @@ -129,10 +128,15 @@ void PrefValueMap::GetDifferingKeys( std::vector<std::string>* differing_keys) const { differing_keys->clear(); + // Put everything into ordered maps. + std::map<std::string, base::Value*> this_prefs(prefs_.begin(), prefs_.end()); + std::map<std::string, base::Value*> other_prefs(other->prefs_.begin(), + other->prefs_.end()); + // Walk over the maps in lockstep, adding everything that is different. - Map::const_iterator this_pref(prefs_.begin()); - Map::const_iterator other_pref(other->prefs_.begin()); - while (this_pref != prefs_.end() && other_pref != other->prefs_.end()) { + auto this_pref(this_prefs.begin()); + auto other_pref(other_prefs.begin()); + while (this_pref != this_prefs.end() && other_pref != other_prefs.end()) { const int diff = this_pref->first.compare(other_pref->first); if (diff == 0) { if (!this_pref->second->Equals(other_pref->second)) @@ -149,8 +153,8 @@ void PrefValueMap::GetDifferingKeys( } // Add the remaining entries. - for ( ; this_pref != prefs_.end(); ++this_pref) + for ( ; this_pref != this_prefs.end(); ++this_pref) differing_keys->push_back(this_pref->first); - for ( ; other_pref != other->prefs_.end(); ++other_pref) + for ( ; other_pref != other_prefs.end(); ++other_pref) differing_keys->push_back(other_pref->first); } diff --git a/chromium/base/prefs/pref_value_map.h b/chromium/base/prefs/pref_value_map.h index 2db18ab2a18..12b30c69739 100644 --- a/chromium/base/prefs/pref_value_map.h +++ b/chromium/base/prefs/pref_value_map.h @@ -5,11 +5,11 @@ #ifndef BASE_PREFS_PREF_VALUE_MAP_H_ #define BASE_PREFS_PREF_VALUE_MAP_H_ -#include <map> #include <string> #include <vector> #include "base/basictypes.h" +#include "base/containers/hash_tables.h" #include "base/prefs/base_prefs_export.h" namespace base { @@ -19,8 +19,9 @@ class Value; // A generic string to value map used by the PrefStore implementations. class BASE_PREFS_EXPORT PrefValueMap { public: - typedef std::map<std::string, base::Value*>::iterator iterator; - typedef std::map<std::string, base::Value*>::const_iterator const_iterator; + using Map = base::hash_map<std::string, base::Value*>; + using iterator = Map::iterator; + using const_iterator = Map::const_iterator; PrefValueMap(); virtual ~PrefValueMap(); @@ -81,8 +82,6 @@ class BASE_PREFS_EXPORT PrefValueMap { std::vector<std::string>* differing_keys) const; private: - typedef std::map<std::string, base::Value*> Map; - Map prefs_; DISALLOW_COPY_AND_ASSIGN(PrefValueMap); diff --git a/chromium/base/prefs/pref_value_store.cc b/chromium/base/prefs/pref_value_store.cc index 2c22f17febe..1a0ec08dccb 100644 --- a/chromium/base/prefs/pref_value_store.cc +++ b/chromium/base/prefs/pref_value_store.cc @@ -109,8 +109,8 @@ bool PrefValueStore::GetValue(const std::string& name, // Check the |PrefStore|s in order of their priority from highest to lowest, // looking for the first preference value with the given |name| and |type|. for (size_t i = 0; i <= PREF_STORE_TYPE_MAX; ++i) { - if (GetValueFromStoreWithType(name.c_str(), type, - static_cast<PrefStoreType>(i), out_value)) + if (GetValueFromStoreWithType(name, type, static_cast<PrefStoreType>(i), + out_value)) return true; } return false; @@ -119,12 +119,11 @@ bool PrefValueStore::GetValue(const std::string& name, bool PrefValueStore::GetRecommendedValue(const std::string& name, base::Value::Type type, const base::Value** out_value) const { - return GetValueFromStoreWithType(name.c_str(), type, RECOMMENDED_STORE, - out_value); + return GetValueFromStoreWithType(name, type, RECOMMENDED_STORE, out_value); } void PrefValueStore::NotifyPrefChanged( - const char* path, + const std::string& path, PrefValueStore::PrefStoreType new_store) { DCHECK(new_store != INVALID_STORE); // A notification is sent when the pref value in any store changes. If this @@ -135,41 +134,48 @@ void PrefValueStore::NotifyPrefChanged( pref_changed_callback_.Run(path); } -bool PrefValueStore::PrefValueInManagedStore(const char* name) const { +bool PrefValueStore::PrefValueInManagedStore(const std::string& name) const { return PrefValueInStore(name, MANAGED_STORE); } -bool PrefValueStore::PrefValueInExtensionStore(const char* name) const { +bool PrefValueStore::PrefValueInSupervisedStore(const std::string& name) const { + return PrefValueInStore(name, SUPERVISED_USER_STORE); +} + +bool PrefValueStore::PrefValueInExtensionStore(const std::string& name) const { return PrefValueInStore(name, EXTENSION_STORE); } -bool PrefValueStore::PrefValueInUserStore(const char* name) const { +bool PrefValueStore::PrefValueInUserStore(const std::string& name) const { return PrefValueInStore(name, USER_STORE); } -bool PrefValueStore::PrefValueFromExtensionStore(const char* name) const { +bool PrefValueStore::PrefValueFromExtensionStore( + const std::string& name) const { return ControllingPrefStoreForPref(name) == EXTENSION_STORE; } -bool PrefValueStore::PrefValueFromUserStore(const char* name) const { +bool PrefValueStore::PrefValueFromUserStore(const std::string& name) const { return ControllingPrefStoreForPref(name) == USER_STORE; } -bool PrefValueStore::PrefValueFromRecommendedStore(const char* name) const { +bool PrefValueStore::PrefValueFromRecommendedStore( + const std::string& name) const { return ControllingPrefStoreForPref(name) == RECOMMENDED_STORE; } -bool PrefValueStore::PrefValueFromDefaultStore(const char* name) const { +bool PrefValueStore::PrefValueFromDefaultStore(const std::string& name) const { return ControllingPrefStoreForPref(name) == DEFAULT_STORE; } -bool PrefValueStore::PrefValueUserModifiable(const char* name) const { +bool PrefValueStore::PrefValueUserModifiable(const std::string& name) const { PrefStoreType effective_store = ControllingPrefStoreForPref(name); return effective_store >= USER_STORE || effective_store == INVALID_STORE; } -bool PrefValueStore::PrefValueExtensionModifiable(const char* name) const { +bool PrefValueStore::PrefValueExtensionModifiable( + const std::string& name) const { PrefStoreType effective_store = ControllingPrefStoreForPref(name); return effective_store >= EXTENSION_STORE || effective_store == INVALID_STORE; @@ -180,7 +186,7 @@ void PrefValueStore::UpdateCommandLinePrefStore(PrefStore* command_line_prefs) { } bool PrefValueStore::PrefValueInStore( - const char* name, + const std::string& name, PrefValueStore::PrefStoreType store) const { // Declare a temp Value* and call GetValueFromStore, // ignoring the output value. @@ -189,7 +195,7 @@ bool PrefValueStore::PrefValueInStore( } bool PrefValueStore::PrefValueInStoreRange( - const char* name, + const std::string& name, PrefValueStore::PrefStoreType first_checked_store, PrefValueStore::PrefStoreType last_checked_store) const { if (first_checked_store > last_checked_store) { @@ -206,7 +212,7 @@ bool PrefValueStore::PrefValueInStoreRange( } PrefValueStore::PrefStoreType PrefValueStore::ControllingPrefStoreForPref( - const char* name) const { + const std::string& name) const { for (size_t i = 0; i <= PREF_STORE_TYPE_MAX; ++i) { if (PrefValueInStore(name, static_cast<PrefStoreType>(i))) return static_cast<PrefStoreType>(i); @@ -214,7 +220,7 @@ PrefValueStore::PrefStoreType PrefValueStore::ControllingPrefStoreForPref( return INVALID_STORE; } -bool PrefValueStore::GetValueFromStore(const char* name, +bool PrefValueStore::GetValueFromStore(const std::string& name, PrefValueStore::PrefStoreType store_type, const base::Value** out_value) const { // Only return true if we find a value and it is the correct type, so stale @@ -230,7 +236,7 @@ bool PrefValueStore::GetValueFromStore(const char* name, } bool PrefValueStore::GetValueFromStoreWithType( - const char* name, + const std::string& name, base::Value::Type type, PrefStoreType store, const base::Value** out_value) const { @@ -249,7 +255,7 @@ bool PrefValueStore::GetValueFromStoreWithType( void PrefValueStore::OnPrefValueChanged(PrefValueStore::PrefStoreType type, const std::string& key) { - NotifyPrefChanged(key.c_str(), type); + NotifyPrefChanged(key, type); } void PrefValueStore::OnInitializationCompleted( diff --git a/chromium/base/prefs/pref_value_store.h b/chromium/base/prefs/pref_value_store.h index db82a822e1d..51601156604 100644 --- a/chromium/base/prefs/pref_value_store.h +++ b/chromium/base/prefs/pref_value_store.h @@ -94,25 +94,26 @@ class BASE_PREFS_EXPORT PrefValueStore { // These methods return true if a preference with the given name is in the // indicated pref store, even if that value is currently being overridden by // a higher-priority source. - bool PrefValueInManagedStore(const char* name) const; - bool PrefValueInExtensionStore(const char* name) const; - bool PrefValueInUserStore(const char* name) const; + bool PrefValueInManagedStore(const std::string& name) const; + bool PrefValueInSupervisedStore(const std::string& name) const; + bool PrefValueInExtensionStore(const std::string& name) const; + bool PrefValueInUserStore(const std::string& name) const; // These methods return true if a preference with the given name is actually // being controlled by the indicated pref store and not being overridden by // a higher-priority source. - bool PrefValueFromExtensionStore(const char* name) const; - bool PrefValueFromUserStore(const char* name) const; - bool PrefValueFromRecommendedStore(const char* name) const; - bool PrefValueFromDefaultStore(const char* name) const; + bool PrefValueFromExtensionStore(const std::string& name) const; + bool PrefValueFromUserStore(const std::string& name) const; + bool PrefValueFromRecommendedStore(const std::string& name) const; + bool PrefValueFromDefaultStore(const std::string& name) const; // Check whether a Preference value is modifiable by the user, i.e. whether // there is no higher-priority source controlling it. - bool PrefValueUserModifiable(const char* name) const; + bool PrefValueUserModifiable(const std::string& name) const; // Check whether a Preference value is modifiable by an extension, i.e. // whether there is no higher-priority source controlling it. - bool PrefValueExtensionModifiable(const char* name) const; + bool PrefValueExtensionModifiable(const std::string& name) const; // Update the command line PrefStore with |command_line_prefs|. void UpdateCommandLinePrefStore(PrefStore* command_line_prefs); @@ -188,13 +189,13 @@ class BASE_PREFS_EXPORT PrefValueStore { // Returns true if the preference with the given name has a value in the // given PrefStoreType, of the same value type as the preference was // registered with. - bool PrefValueInStore(const char* name, PrefStoreType store) const; + bool PrefValueInStore(const std::string& name, PrefStoreType store) const; // Returns true if a preference has an explicit value in any of the // stores in the range specified by |first_checked_store| and // |last_checked_store|, even if that value is currently being // overridden by a higher-priority store. - bool PrefValueInStoreRange(const char* name, + bool PrefValueInStoreRange(const std::string& name, PrefStoreType first_checked_store, PrefStoreType last_checked_store) const; @@ -203,15 +204,15 @@ class BASE_PREFS_EXPORT PrefValueStore { // INVALID_STORE is returned. In practice, the default PrefStore // should always have a value for any registered preferencem, so INVALID_STORE // indicates an error. - PrefStoreType ControllingPrefStoreForPref(const char* name) const; + PrefStoreType ControllingPrefStoreForPref(const std::string& name) const; // Get a value from the specified |store|. - bool GetValueFromStore(const char* name, + bool GetValueFromStore(const std::string& name, PrefStoreType store, const base::Value** out_value) const; // Get a value from the specified |store| if its |type| matches. - bool GetValueFromStoreWithType(const char* name, + bool GetValueFromStoreWithType(const std::string& name, base::Value::Type type, PrefStoreType store, const base::Value** out_value) const; @@ -220,7 +221,7 @@ class BASE_PREFS_EXPORT PrefValueStore { // the user-visible pref value has changed. Triggers the change notification // if the effective value of the preference has changed, or if the store // controlling the pref has changed. - void NotifyPrefChanged(const char* path, PrefStoreType new_store); + void NotifyPrefChanged(const std::string& path, PrefStoreType new_store); // Called from the PrefStoreKeeper implementation when a pref value for |key| // changed in the pref store for |type|. diff --git a/chromium/base/prefs/pref_value_store_unittest.cc b/chromium/base/prefs/pref_value_store_unittest.cc index 3afe9dcefd1..e214adfa1c7 100644 --- a/chromium/base/prefs/pref_value_store_unittest.cc +++ b/chromium/base/prefs/pref_value_store_unittest.cc @@ -98,7 +98,7 @@ const char kDefaultValue[] = "default:default"; class PrefValueStoreTest : public testing::Test { protected: - virtual void SetUp() { + void SetUp() override { // Create TestingPrefStores. CreateManagedPrefs(); CreateSupervisedUserPrefs(); @@ -237,7 +237,7 @@ class PrefValueStoreTest : public testing::Test { default_pref::kDefaultValue); } - void ExpectValueChangeNotifications(const char* name) { + void ExpectValueChangeNotifications(const std::string& name) { EXPECT_CALL(pref_notifier_, OnPreferenceChanged(name)); EXPECT_CALL(*sync_associator_, ProcessPrefChange(name)); } diff --git a/chromium/base/prefs/scoped_user_pref_update.cc b/chromium/base/prefs/scoped_user_pref_update.cc index 7871c75ce37..1440a5711d8 100644 --- a/chromium/base/prefs/scoped_user_pref_update.cc +++ b/chromium/base/prefs/scoped_user_pref_update.cc @@ -11,10 +11,8 @@ namespace subtle { ScopedUserPrefUpdateBase::ScopedUserPrefUpdateBase(PrefService* service, - const char* path) - : service_(service), - path_(path), - value_(NULL) { + const std::string& path) + : service_(service), path_(path), value_(NULL) { DCHECK(service_->CalledOnValidThread()); } @@ -25,7 +23,7 @@ ScopedUserPrefUpdateBase::~ScopedUserPrefUpdateBase() { base::Value* ScopedUserPrefUpdateBase::GetValueOfType(base::Value::Type type) { DCHECK(CalledOnValidThread()); if (!value_) - value_ = service_->GetMutableUserPref(path_.c_str(), type); + value_ = service_->GetMutableUserPref(path_, type); return value_; } diff --git a/chromium/base/prefs/scoped_user_pref_update.h b/chromium/base/prefs/scoped_user_pref_update.h index 82d6739dd32..f8bebfe435c 100644 --- a/chromium/base/prefs/scoped_user_pref_update.h +++ b/chromium/base/prefs/scoped_user_pref_update.h @@ -33,7 +33,7 @@ namespace subtle { // PrefService::ReportUserPrefChanged. class BASE_PREFS_EXPORT ScopedUserPrefUpdateBase : public base::NonThreadSafe { protected: - ScopedUserPrefUpdateBase(PrefService* service, const char* path); + ScopedUserPrefUpdateBase(PrefService* service, const std::string& path); // Calls Notify(). ~ScopedUserPrefUpdateBase(); @@ -66,7 +66,7 @@ class BASE_PREFS_EXPORT ScopedUserPrefUpdateBase : public base::NonThreadSafe { template <typename T, base::Value::Type type_enum_value> class ScopedUserPrefUpdate : public subtle::ScopedUserPrefUpdateBase { public: - ScopedUserPrefUpdate(PrefService* service, const char* path) + ScopedUserPrefUpdate(PrefService* service, const std::string& path) : ScopedUserPrefUpdateBase(service, path) {} // Triggers an update notification if Get() was called. diff --git a/chromium/base/prefs/scoped_user_pref_update_unittest.cc b/chromium/base/prefs/scoped_user_pref_update_unittest.cc index dfe2074720d..48e3dc4f7d4 100644 --- a/chromium/base/prefs/scoped_user_pref_update_unittest.cc +++ b/chromium/base/prefs/scoped_user_pref_update_unittest.cc @@ -16,10 +16,10 @@ using testing::Mock; class ScopedUserPrefUpdateTest : public testing::Test { public: ScopedUserPrefUpdateTest() : observer_(&prefs_) {} - virtual ~ScopedUserPrefUpdateTest() {} + ~ScopedUserPrefUpdateTest() override {} protected: - virtual void SetUp() { + void SetUp() override { prefs_.registry()->RegisterDictionaryPref(kPref); registrar_.Init(&prefs_); registrar_.Add(kPref, observer_.GetCallback()); diff --git a/chromium/base/prefs/testing_pref_service.h b/chromium/base/prefs/testing_pref_service.h index 2f6d4940c81..75873836b41 100644 --- a/chromium/base/prefs/testing_pref_service.h +++ b/chromium/base/prefs/testing_pref_service.h @@ -27,25 +27,25 @@ class TestingPrefServiceBase : public SuperPrefService { // Read the value of a preference from the managed layer. Returns NULL if the // preference is not defined at the managed layer. - const base::Value* GetManagedPref(const char* path) const; + const base::Value* GetManagedPref(const std::string& path) const; // Set a preference on the managed layer and fire observers if the preference // changed. Assumes ownership of |value|. - void SetManagedPref(const char* path, base::Value* value); + void SetManagedPref(const std::string& path, base::Value* value); // Clear the preference on the managed layer and fire observers if the // preference has been defined previously. - void RemoveManagedPref(const char* path); + void RemoveManagedPref(const std::string& path); // Similar to the above, but for user preferences. - const base::Value* GetUserPref(const char* path) const; - void SetUserPref(const char* path, base::Value* value); - void RemoveUserPref(const char* path); + const base::Value* GetUserPref(const std::string& path) const; + void SetUserPref(const std::string& path, base::Value* value); + void RemoveUserPref(const std::string& path); // Similar to the above, but for recommended policy preferences. - const base::Value* GetRecommendedPref(const char* path) const; - void SetRecommendedPref(const char* path, base::Value* value); - void RemoveRecommendedPref(const char* path); + const base::Value* GetRecommendedPref(const std::string& path) const; + void SetRecommendedPref(const std::string& path, base::Value* value); + void RemoveRecommendedPref(const std::string& path); // Do-nothing implementation for TestingPrefService. static void HandleReadError(PersistentPrefStore::PrefReadError error) {} @@ -62,14 +62,15 @@ class TestingPrefServiceBase : public SuperPrefService { // Reads the value of the preference indicated by |path| from |pref_store|. // Returns NULL if the preference was not found. const base::Value* GetPref(TestingPrefStore* pref_store, - const char* path) const; + const std::string& path) const; // Sets the value for |path| in |pref_store|. - void SetPref(TestingPrefStore* pref_store, const char* path, + void SetPref(TestingPrefStore* pref_store, + const std::string& path, base::Value* value); // Removes the preference identified by |path| from |pref_store|. - void RemovePref(TestingPrefStore* pref_store, const char* path); + void RemovePref(TestingPrefStore* pref_store, const std::string& path); // Pointers to the pref stores our value store uses. scoped_refptr<TestingPrefStore> managed_prefs_; @@ -110,89 +111,85 @@ TestingPrefServiceBase< SuperPrefService, ConstructionPrefRegistry>::~TestingPrefServiceBase() { } -template<class SuperPrefService, class ConstructionPrefRegistry> +template <class SuperPrefService, class ConstructionPrefRegistry> const base::Value* TestingPrefServiceBase< - SuperPrefService, ConstructionPrefRegistry>::GetManagedPref( - const char* path) const { + SuperPrefService, + ConstructionPrefRegistry>::GetManagedPref(const std::string& path) const { return GetPref(managed_prefs_.get(), path); } -template<class SuperPrefService, class ConstructionPrefRegistry> -void TestingPrefServiceBase< - SuperPrefService, ConstructionPrefRegistry>::SetManagedPref( - const char* path, base::Value* value) { +template <class SuperPrefService, class ConstructionPrefRegistry> +void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>:: + SetManagedPref(const std::string& path, base::Value* value) { SetPref(managed_prefs_.get(), path, value); } -template<class SuperPrefService, class ConstructionPrefRegistry> -void TestingPrefServiceBase< - SuperPrefService, ConstructionPrefRegistry>::RemoveManagedPref( - const char* path) { +template <class SuperPrefService, class ConstructionPrefRegistry> +void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>:: + RemoveManagedPref(const std::string& path) { RemovePref(managed_prefs_.get(), path); } -template<class SuperPrefService, class ConstructionPrefRegistry> -const base::Value* TestingPrefServiceBase< - SuperPrefService, ConstructionPrefRegistry>::GetUserPref( - const char* path) const { +template <class SuperPrefService, class ConstructionPrefRegistry> +const base::Value* +TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>::GetUserPref( + const std::string& path) const { return GetPref(user_prefs_.get(), path); } -template<class SuperPrefService, class ConstructionPrefRegistry> -void TestingPrefServiceBase< - SuperPrefService, ConstructionPrefRegistry>::SetUserPref( - const char* path, base::Value* value) { +template <class SuperPrefService, class ConstructionPrefRegistry> +void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>:: + SetUserPref(const std::string& path, base::Value* value) { SetPref(user_prefs_.get(), path, value); } -template<class SuperPrefService, class ConstructionPrefRegistry> -void TestingPrefServiceBase< - SuperPrefService, ConstructionPrefRegistry>::RemoveUserPref( - const char* path) { +template <class SuperPrefService, class ConstructionPrefRegistry> +void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>:: + RemoveUserPref(const std::string& path) { RemovePref(user_prefs_.get(), path); } -template<class SuperPrefService, class ConstructionPrefRegistry> -const base::Value* TestingPrefServiceBase< - SuperPrefService, ConstructionPrefRegistry>::GetRecommendedPref( - const char* path) const { +template <class SuperPrefService, class ConstructionPrefRegistry> +const base::Value* +TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>:: + GetRecommendedPref(const std::string& path) const { return GetPref(recommended_prefs_, path); } -template<class SuperPrefService, class ConstructionPrefRegistry> -void TestingPrefServiceBase< - SuperPrefService, ConstructionPrefRegistry>::SetRecommendedPref( - const char* path, base::Value* value) { +template <class SuperPrefService, class ConstructionPrefRegistry> +void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>:: + SetRecommendedPref(const std::string& path, base::Value* value) { SetPref(recommended_prefs_.get(), path, value); } -template<class SuperPrefService, class ConstructionPrefRegistry> -void TestingPrefServiceBase< - SuperPrefService, ConstructionPrefRegistry>::RemoveRecommendedPref( - const char* path) { +template <class SuperPrefService, class ConstructionPrefRegistry> +void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>:: + RemoveRecommendedPref(const std::string& path) { RemovePref(recommended_prefs_.get(), path); } -template<class SuperPrefService, class ConstructionPrefRegistry> -const base::Value* TestingPrefServiceBase< - SuperPrefService, ConstructionPrefRegistry>::GetPref( - TestingPrefStore* pref_store, const char* path) const { +template <class SuperPrefService, class ConstructionPrefRegistry> +const base::Value* +TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>::GetPref( + TestingPrefStore* pref_store, + const std::string& path) const { const base::Value* res; return pref_store->GetValue(path, &res) ? res : NULL; } -template<class SuperPrefService, class ConstructionPrefRegistry> -void TestingPrefServiceBase< - SuperPrefService, ConstructionPrefRegistry>::SetPref( - TestingPrefStore* pref_store, const char* path, base::Value* value) { - pref_store->SetValue(path, value); +template <class SuperPrefService, class ConstructionPrefRegistry> +void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>:: + SetPref(TestingPrefStore* pref_store, + const std::string& path, + base::Value* value) { + pref_store->SetValue(path, value, + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); } -template<class SuperPrefService, class ConstructionPrefRegistry> -void TestingPrefServiceBase< - SuperPrefService, ConstructionPrefRegistry>::RemovePref( - TestingPrefStore* pref_store, const char* path) { - pref_store->RemoveValue(path); +template <class SuperPrefService, class ConstructionPrefRegistry> +void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>:: + RemovePref(TestingPrefStore* pref_store, const std::string& path) { + pref_store->RemoveValue(path, WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); } #endif // BASE_PREFS_TESTING_PREF_SERVICE_H_ diff --git a/chromium/base/prefs/testing_pref_store.cc b/chromium/base/prefs/testing_pref_store.cc index f20824e1733..35c9763eec5 100644 --- a/chromium/base/prefs/testing_pref_store.cc +++ b/chromium/base/prefs/testing_pref_store.cc @@ -42,7 +42,9 @@ bool TestingPrefStore::IsInitializationComplete() const { return init_complete_; } -void TestingPrefStore::SetValue(const std::string& key, base::Value* value) { +void TestingPrefStore::SetValue(const std::string& key, + base::Value* value, + uint32 flags) { if (prefs_.SetValue(key, value)) { committed_ = false; NotifyPrefValueChanged(key); @@ -50,12 +52,13 @@ void TestingPrefStore::SetValue(const std::string& key, base::Value* value) { } void TestingPrefStore::SetValueSilently(const std::string& key, - base::Value* value) { + base::Value* value, + uint32 flags) { if (prefs_.SetValue(key, value)) committed_ = false; } -void TestingPrefStore::RemoveValue(const std::string& key) { +void TestingPrefStore::RemoveValue(const std::string& key, uint32 flags) { if (prefs_.RemoveValue(key)) { committed_ = false; NotifyPrefValueChanged(key); @@ -103,21 +106,22 @@ void TestingPrefStore::NotifyInitializationCompleted() { Observer, observers_, OnInitializationCompleted(read_success_)); } -void TestingPrefStore::ReportValueChanged(const std::string& key) { +void TestingPrefStore::ReportValueChanged(const std::string& key, + uint32 flags) { FOR_EACH_OBSERVER(Observer, observers_, OnPrefValueChanged(key)); } void TestingPrefStore::SetString(const std::string& key, const std::string& value) { - SetValue(key, new base::StringValue(value)); + SetValue(key, new base::StringValue(value), DEFAULT_PREF_WRITE_FLAGS); } void TestingPrefStore::SetInteger(const std::string& key, int value) { - SetValue(key, new base::FundamentalValue(value)); + SetValue(key, new base::FundamentalValue(value), DEFAULT_PREF_WRITE_FLAGS); } void TestingPrefStore::SetBoolean(const std::string& key, bool value) { - SetValue(key, new base::FundamentalValue(value)); + SetValue(key, new base::FundamentalValue(value), DEFAULT_PREF_WRITE_FLAGS); } bool TestingPrefStore::GetString(const std::string& key, diff --git a/chromium/base/prefs/testing_pref_store.h b/chromium/base/prefs/testing_pref_store.h index 866b4aeb029..3de5cacb1bf 100644 --- a/chromium/base/prefs/testing_pref_store.h +++ b/chromium/base/prefs/testing_pref_store.h @@ -30,10 +30,14 @@ class TestingPrefStore : public PersistentPrefStore { // PersistentPrefStore overrides: bool GetMutableValue(const std::string& key, base::Value** result) override; - void ReportValueChanged(const std::string& key) override; - void SetValue(const std::string& key, base::Value* value) override; - void SetValueSilently(const std::string& key, base::Value* value) override; - void RemoveValue(const std::string& key) override; + void ReportValueChanged(const std::string& key, uint32 flags) override; + void SetValue(const std::string& key, + base::Value* value, + uint32 flags) override; + void SetValueSilently(const std::string& key, + base::Value* value, + uint32 flags) override; + void RemoveValue(const std::string& key, uint32 flags) override; bool ReadOnly() const override; PrefReadError GetReadError() const override; PersistentPrefStore::PrefReadError ReadPrefs() override; diff --git a/chromium/base/prefs/value_map_pref_store.cc b/chromium/base/prefs/value_map_pref_store.cc index 8af12826df3..d8501501f04 100644 --- a/chromium/base/prefs/value_map_pref_store.cc +++ b/chromium/base/prefs/value_map_pref_store.cc @@ -28,12 +28,14 @@ bool ValueMapPrefStore::HasObservers() const { return observers_.might_have_observers(); } -void ValueMapPrefStore::SetValue(const std::string& key, base::Value* value) { +void ValueMapPrefStore::SetValue(const std::string& key, + base::Value* value, + uint32 flags) { if (prefs_.SetValue(key, value)) FOR_EACH_OBSERVER(Observer, observers_, OnPrefValueChanged(key)); } -void ValueMapPrefStore::RemoveValue(const std::string& key) { +void ValueMapPrefStore::RemoveValue(const std::string& key, uint32 flags) { if (prefs_.RemoveValue(key)) FOR_EACH_OBSERVER(Observer, observers_, OnPrefValueChanged(key)); } @@ -43,12 +45,14 @@ bool ValueMapPrefStore::GetMutableValue(const std::string& key, return prefs_.GetValue(key, value); } -void ValueMapPrefStore::ReportValueChanged(const std::string& key) { +void ValueMapPrefStore::ReportValueChanged(const std::string& key, + uint32 flags) { FOR_EACH_OBSERVER(Observer, observers_, OnPrefValueChanged(key)); } void ValueMapPrefStore::SetValueSilently(const std::string& key, - base::Value* value) { + base::Value* value, + uint32 flags) { prefs_.SetValue(key, value); } diff --git a/chromium/base/prefs/value_map_pref_store.h b/chromium/base/prefs/value_map_pref_store.h index 8c515ed345d..86c94bb50ca 100644 --- a/chromium/base/prefs/value_map_pref_store.h +++ b/chromium/base/prefs/value_map_pref_store.h @@ -28,11 +28,15 @@ class BASE_PREFS_EXPORT ValueMapPrefStore : public WriteablePrefStore { bool HasObservers() const override; // WriteablePrefStore overrides: - void SetValue(const std::string& key, base::Value* value) override; - void RemoveValue(const std::string& key) override; + void SetValue(const std::string& key, + 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) override; - void SetValueSilently(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, + uint32 flags) override; protected: ~ValueMapPrefStore() override; diff --git a/chromium/base/prefs/writeable_pref_store.h b/chromium/base/prefs/writeable_pref_store.h index 5ebab642823..d85b4c8d427 100644 --- a/chromium/base/prefs/writeable_pref_store.h +++ b/chromium/base/prefs/writeable_pref_store.h @@ -17,14 +17,27 @@ class Value; // A pref store that can be written to as well as read from. class BASE_PREFS_EXPORT WriteablePrefStore : public PrefStore { public: + // PrefWriteFlags can be used to change the way a pref will be written to + // storage. + enum PrefWriteFlags : uint32 { + // No flags are specified. + DEFAULT_PREF_WRITE_FLAGS = 0, + + // This marks the pref as "lossy". There is no strict time guarantee on when + // a lossy pref will be persisted to permanent storage when it is modified. + LOSSY_PREF_WRITE_FLAG = 1 << 1 + }; + WriteablePrefStore() {} // Sets a |value| for |key| in the store. Assumes ownership of |value|, which - // must be non-NULL. - virtual void SetValue(const std::string& key, base::Value* value) = 0; + // must be non-NULL. |flags| is a bitmask of PrefWriteFlags. + virtual void SetValue(const std::string& key, + base::Value* value, + uint32 flags) = 0; // Removes the value for |key|. - virtual void RemoveValue(const std::string& key) = 0; + virtual void RemoveValue(const std::string& key, uint32 flags) = 0; // Equivalent to PrefStore::GetValue but returns a mutable value. virtual bool GetMutableValue(const std::string& key, @@ -34,13 +47,17 @@ class BASE_PREFS_EXPORT WriteablePrefStore : public PrefStore { // if one retrieves a list or dictionary with GetMutableValue and change its // value. SetValue takes care of notifications itself. Note that // ReportValueChanged will trigger notifications even if nothing has changed. - virtual void ReportValueChanged(const std::string& key) = 0; + // |flags| is a bitmask of PrefWriteFlags. + virtual void ReportValueChanged(const std::string& key, uint32 flags) = 0; // Same as SetValue, but doesn't generate notifications. This is used by // PrefService::GetMutableUserPref() in order to put empty entries // into the user pref store. Using SetValue is not an option since existing - // tests rely on the number of notifications generated. - virtual void SetValueSilently(const std::string& key, base::Value* value) = 0; + // tests rely on the number of notifications generated. |flags| is a bitmask + // of PrefWriteFlags. + virtual void SetValueSilently(const std::string& key, + base::Value* value, + uint32 flags) = 0; protected: ~WriteablePrefStore() override {} diff --git a/chromium/base/process/BUILD.gn b/chromium/base/process/BUILD.gn new file mode 100644 index 00000000000..814459b13e0 --- /dev/null +++ b/chromium/base/process/BUILD.gn @@ -0,0 +1,108 @@ +# Copyright (c) 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. + +source_set("process") { + sources = [ + "internal_linux.cc", + "internal_linux.h", + "kill.cc", + "kill.h", + "kill_mac.cc", + "kill_posix.cc", + "kill_win.cc", + "launch.cc", + "launch.h", + "launch_ios.cc", + "launch_mac.cc", + "launch_posix.cc", + "launch_win.cc", + "memory.cc", + "memory.h", + "memory_linux.cc", + "memory_mac.mm", + "memory_win.cc", + "process.h", + "process_handle_freebsd.cc", + "process_handle_linux.cc", + "process_handle_mac.cc", + "process_handle_openbsd.cc", + "process_handle_posix.cc", + "process_handle_win.cc", + "process_info.h", + "process_info_linux.cc", + "process_info_mac.cc", + "process_info_win.cc", + "process_iterator.cc", + "process_iterator.h", + "process_iterator_freebsd.cc", + "process_iterator_linux.cc", + "process_iterator_mac.cc", + "process_iterator_openbsd.cc", + "process_iterator_win.cc", + "process_linux.cc", + "process_mac.cc", + "process_metrics.cc", + "process_metrics.h", + "process_metrics_freebsd.cc", + "process_metrics_ios.cc", + "process_metrics_linux.cc", + "process_metrics_mac.cc", + "process_metrics_openbsd.cc", + "process_metrics_posix.cc", + "process_metrics_win.cc", + "process_posix.cc", + "process_win.cc", + ] + + sources -= [ + "process_handle_freebsd.cc", + "process_handle_openbsd.cc", + "process_iterator_freebsd.cc", + "process_iterator_openbsd.cc", + "process_metrics_freebsd.cc", + "process_metrics_openbsd.cc", + ] + + if (is_android) { + # Android uses some Linux sources, put those back. + set_sources_assignment_filter([]) + sources += [ + "internal_linux.cc", + "memory_linux.cc", + "process_handle_linux.cc", + "process_iterator_linux.cc", + "process_metrics_linux.cc", + ] + set_sources_assignment_filter(sources_assignment_filter) + } + + if (is_nacl) { + sources -= [ + "kill.cc", + "kill.h", + "kill_posix.cc", + "launch.cc", + "launch.h", + "launch_posix.cc", + "memory.cc", + "memory.h", + "process_iterator.cc", + "process_iterator.h", + "process_metrics.cc", + "process_metrics_posix.cc", + "process_posix.cc", + ] + } + + configs += [ "//base:base_implementation" ] + + deps = [ + "//base/memory", + "//base/third_party/dynamic_annotations", + ] + + allow_circular_includes_from = [ "//base/memory" ] + + visibility = [ "//base/*" ] +} diff --git a/chromium/base/process/internal_linux.cc b/chromium/base/process/internal_linux.cc index ed7d7f4cf5e..d2e9ec52e53 100644 --- a/chromium/base/process/internal_linux.cc +++ b/chromium/base/process/internal_linux.cc @@ -106,12 +106,10 @@ bool ParseProcStats(const std::string& stats_data, typedef std::map<std::string, std::string> ProcStatMap; void ParseProcStat(const std::string& contents, ProcStatMap* output) { - typedef std::pair<std::string, std::string> StringPair; - std::vector<StringPair> key_value_pairs; + base::StringPairs key_value_pairs; SplitStringIntoKeyValuePairs(contents, ' ', '\n', &key_value_pairs); for (size_t i = 0; i < key_value_pairs.size(); ++i) { - const StringPair& key_value_pair = key_value_pairs[i]; - output->insert(key_value_pair); + output->insert(key_value_pairs[i]); } } diff --git a/chromium/base/process/internal_linux.h b/chromium/base/process/internal_linux.h index 5fc33566607..1837f94ce52 100644 --- a/chromium/base/process/internal_linux.h +++ b/chromium/base/process/internal_linux.h @@ -5,8 +5,8 @@ // This file contains internal routines that are called by other files in // base/process/. -#ifndef BASE_PROCESS_LINUX_INTERNAL_H_ -#define BASE_PROCESS_LINUX_INTERNAL_H_ +#ifndef BASE_PROCESS_INTERNAL_LINUX_H_ +#define BASE_PROCESS_INTERNAL_LINUX_H_ #include <unistd.h> @@ -87,4 +87,4 @@ TimeDelta ClockTicksToTimeDelta(int clock_ticks); } // namespace internal } // namespace base -#endif // BASE_PROCESS_LINUX_INTERNAL_H_ +#endif // BASE_PROCESS_INTERNAL_LINUX_H_ diff --git a/chromium/base/process/kill.cc b/chromium/base/process/kill.cc index caca3484a11..5d8ba6a2d75 100644 --- a/chromium/base/process/kill.cc +++ b/chromium/base/process/kill.cc @@ -14,11 +14,8 @@ bool KillProcesses(const FilePath::StringType& executable_name, bool result = true; NamedProcessIterator iter(executable_name, filter); while (const ProcessEntry* entry = iter.NextProcessEntry()) { -#if defined(OS_WIN) - result &= KillProcessById(entry->pid(), exit_code, true); -#else - result &= KillProcess(entry->pid(), exit_code, true); -#endif + Process process = Process::Open(entry->pid()); + result &= process.Terminate(exit_code, true); } return result; } diff --git a/chromium/base/process/kill.h b/chromium/base/process/kill.h index de72d7a8dca..af00b03d984 100644 --- a/chromium/base/process/kill.h +++ b/chromium/base/process/kill.h @@ -9,6 +9,7 @@ #define BASE_PROCESS_KILL_H_ #include "base/files/file_path.h" +#include "base/process/process.h" #include "base/process/process_handle.h" #include "base/time/time.h" @@ -44,24 +45,12 @@ BASE_EXPORT bool KillProcesses(const FilePath::StringType& executable_name, int exit_code, const ProcessFilter* filter); -// Attempts to kill the process identified by the given process -// entry structure, giving it the specified exit code. If |wait| is true, wait -// for the process to be actually terminated before returning. -// Returns true if this is successful, false otherwise. -BASE_EXPORT bool KillProcess(ProcessHandle process, int exit_code, bool wait); - #if defined(OS_POSIX) // Attempts to kill the process group identified by |process_group_id|. Returns // true on success. BASE_EXPORT bool KillProcessGroup(ProcessHandle process_group_id); #endif // defined(OS_POSIX) -#if defined(OS_WIN) -BASE_EXPORT bool KillProcessById(ProcessId process_id, - int exit_code, - bool wait); -#endif // defined(OS_WIN) - // Get the termination status of the process by interpreting the // circumstances of the child process' death. |exit_code| is set to // the status returned by waitpid() on POSIX, and from @@ -93,22 +82,6 @@ BASE_EXPORT TerminationStatus GetKnownDeadTerminationStatus( ProcessHandle handle, int* exit_code); #endif // defined(OS_POSIX) -// Waits for process to exit. On POSIX systems, if the process hasn't been -// signaled then puts the exit code in |exit_code|; otherwise it's considered -// a failure. On Windows |exit_code| is always filled. Returns true on success, -// and closes |handle| in any case. -BASE_EXPORT bool WaitForExitCode(ProcessHandle handle, int* exit_code); - -// Waits for process to exit. If it did exit within |timeout_milliseconds|, -// then puts the exit code in |exit_code|, and returns true. -// In POSIX systems, if the process has been signaled then |exit_code| is set -// to -1. Returns false on failure (the caller is then responsible for closing -// |handle|). -// The caller is always responsible for closing the |handle|. -BASE_EXPORT bool WaitForExitCodeWithTimeout(ProcessHandle handle, - int* exit_code, - base::TimeDelta timeout); - // Wait for all the processes based on the named executable to exit. If filter // is non-null, then only processes selected by the filter are waited on. // Returns after all processes have exited or wait_milliseconds have expired. @@ -118,12 +91,6 @@ BASE_EXPORT bool WaitForProcessesToExit( base::TimeDelta wait, const ProcessFilter* filter); -// Wait for a single process to exit. Return true if it exited cleanly within -// the given time limit. On Linux |handle| must be a child process, however -// on Mac and Windows it can be any process. -BASE_EXPORT bool WaitForSingleProcess(ProcessHandle handle, - base::TimeDelta wait); - // Waits a certain amount of time (can be 0) for all the processes with a given // executable name to exit, then kills off any of them that are still around. // If filter is non-null, then only processes selected by the filter are waited @@ -146,15 +113,15 @@ BASE_EXPORT bool CleanupProcesses(const FilePath::StringType& executable_name, // On Linux this method does not block the calling thread. // On OS X this method may block for up to 2 seconds. // -// NOTE: The process handle must have been opened with the PROCESS_TERMINATE -// and SYNCHRONIZE permissions. +// NOTE: The process must have been opened with the PROCESS_TERMINATE and +// SYNCHRONIZE permissions. // -BASE_EXPORT void EnsureProcessTerminated(ProcessHandle process_handle); +BASE_EXPORT void EnsureProcessTerminated(Process process); #if defined(OS_POSIX) && !defined(OS_MACOSX) // The nicer version of EnsureProcessTerminated() that is patient and will -// wait for |process_handle| to finish and then reap it. -BASE_EXPORT void EnsureProcessGetsReaped(ProcessHandle process_handle); +// wait for |pid| to finish and then reap it. +BASE_EXPORT void EnsureProcessGetsReaped(ProcessId pid); #endif } // namespace base diff --git a/chromium/base/process/kill_mac.cc b/chromium/base/process/kill_mac.cc index 9257ee6929a..a4e0a14cf8f 100644 --- a/chromium/base/process/kill_mac.cc +++ b/chromium/base/process/kill_mac.cc @@ -66,8 +66,8 @@ void BlockingReap(pid_t child) { // work in that case, but waitpid won't, and killing a non-child might not be // the best approach. void WaitForChildToDie(pid_t child, int timeout) { - DCHECK(child > 0); - DCHECK(timeout > 0); + DCHECK_GT(child, 0); + DCHECK_GT(timeout, 0); // DON'T ADD ANY EARLY RETURNS TO THIS FUNCTION without ensuring that // |child| has been reaped. Specifically, even if a kqueue, kevent, or other @@ -165,8 +165,8 @@ void WaitForChildToDie(pid_t child, int timeout) { } // namespace -void EnsureProcessTerminated(ProcessHandle process) { - WaitForChildToDie(process, kWaitBeforeKillSeconds); +void EnsureProcessTerminated(Process process) { + WaitForChildToDie(process.Pid(), kWaitBeforeKillSeconds); } } // namespace base diff --git a/chromium/base/process/kill_posix.cc b/chromium/base/process/kill_posix.cc index d17e9907626..0e303c67d3c 100644 --- a/chromium/base/process/kill_posix.cc +++ b/chromium/base/process/kill_posix.cc @@ -15,77 +15,12 @@ #include "base/posix/eintr_wrapper.h" #include "base/process/process_iterator.h" #include "base/synchronization/waitable_event.h" -#include "base/third_party/dynamic_annotations/dynamic_annotations.h" #include "base/threading/platform_thread.h" namespace base { namespace { -#if !defined(OS_NACL_NONSFI) -bool WaitpidWithTimeout(ProcessHandle handle, - int* status, - base::TimeDelta wait) { - // This POSIX version of this function only guarantees that we wait no less - // than |wait| for the process to exit. The child process may - // exit sometime before the timeout has ended but we may still block for up - // to 256 milliseconds after the fact. - // - // waitpid() has no direct support on POSIX for specifying a timeout, you can - // either ask it to block indefinitely or return immediately (WNOHANG). - // When a child process terminates a SIGCHLD signal is sent to the parent. - // Catching this signal would involve installing a signal handler which may - // affect other parts of the application and would be difficult to debug. - // - // Our strategy is to call waitpid() once up front to check if the process - // has already exited, otherwise to loop for |wait|, sleeping for - // at most 256 milliseconds each time using usleep() and then calling - // waitpid(). The amount of time we sleep starts out at 1 milliseconds, and - // we double it every 4 sleep cycles. - // - // usleep() is speced to exit if a signal is received for which a handler - // has been installed. This means that when a SIGCHLD is sent, it will exit - // depending on behavior external to this function. - // - // This function is used primarily for unit tests, if we want to use it in - // the application itself it would probably be best to examine other routes. - - if (wait.InMilliseconds() == base::kNoTimeout) { - return HANDLE_EINTR(waitpid(handle, status, 0)) > 0; - } - - pid_t ret_pid = HANDLE_EINTR(waitpid(handle, status, WNOHANG)); - static const int64 kMaxSleepInMicroseconds = 1 << 18; // ~256 milliseconds. - int64 max_sleep_time_usecs = 1 << 10; // ~1 milliseconds. - int64 double_sleep_time = 0; - - // If the process hasn't exited yet, then sleep and try again. - TimeTicks wakeup_time = TimeTicks::Now() + wait; - while (ret_pid == 0) { - TimeTicks now = TimeTicks::Now(); - if (now > wakeup_time) - break; - // Guaranteed to be non-negative! - int64 sleep_time_usecs = (wakeup_time - now).InMicroseconds(); - // Sleep for a bit while we wait for the process to finish. - if (sleep_time_usecs > max_sleep_time_usecs) - sleep_time_usecs = max_sleep_time_usecs; - - // usleep() will return 0 and set errno to EINTR on receipt of a signal - // such as SIGCHLD. - usleep(sleep_time_usecs); - ret_pid = HANDLE_EINTR(waitpid(handle, status, WNOHANG)); - - if ((max_sleep_time_usecs < kMaxSleepInMicroseconds) && - (double_sleep_time++ % 4 == 0)) { - max_sleep_time_usecs *= 2; - } - } - - return ret_pid > 0; -} -#endif // !defined(OS_NACL_NONSFI) - TerminationStatus GetTerminationStatusImpl(ProcessHandle handle, bool can_block, int* exit_code) { @@ -133,61 +68,6 @@ TerminationStatus GetTerminationStatusImpl(ProcessHandle handle, } // namespace #if !defined(OS_NACL_NONSFI) -// Attempts to kill the process identified by the given process -// entry structure. Ignores specified exit_code; posix can't force that. -// Returns true if this is successful, false otherwise. -bool KillProcess(ProcessHandle process_id, int exit_code, bool wait) { - DCHECK_GT(process_id, 1) << " tried to kill invalid process_id"; - if (process_id <= 1) - return false; - bool result = kill(process_id, SIGTERM) == 0; - if (result && wait) { - int tries = 60; - - if (RunningOnValgrind()) { - // Wait for some extra time when running under Valgrind since the child - // processes may take some time doing leak checking. - tries *= 2; - } - - unsigned sleep_ms = 4; - - // The process may not end immediately due to pending I/O - bool exited = false; - while (tries-- > 0) { - pid_t pid = HANDLE_EINTR(waitpid(process_id, NULL, WNOHANG)); - if (pid == process_id) { - exited = true; - break; - } - if (pid == -1) { - if (errno == ECHILD) { - // The wait may fail with ECHILD if another process also waited for - // the same pid, causing the process state to get cleaned up. - exited = true; - break; - } - DPLOG(ERROR) << "Error waiting for process " << process_id; - } - - usleep(sleep_ms * 1000); - const unsigned kMaxSleepMs = 1000; - if (sleep_ms < kMaxSleepMs) - sleep_ms *= 2; - } - - // If we're waiting and the child hasn't died by now, force it - // with a SIGKILL. - if (!exited) - result = kill(process_id, SIGKILL) == 0; - } - - if (!result) - DPLOG(ERROR) << "Unable to terminate process " << process_id; - - return result; -} - bool KillProcessGroup(ProcessHandle process_group_id) { bool result = kill(-1 * process_group_id, SIGKILL) == 0; if (!result) @@ -211,173 +91,29 @@ TerminationStatus GetKnownDeadTerminationStatus(ProcessHandle handle, } #if !defined(OS_NACL_NONSFI) -bool WaitForExitCode(ProcessHandle handle, int* exit_code) { - int status; - if (HANDLE_EINTR(waitpid(handle, &status, 0)) == -1) { - NOTREACHED(); - return false; - } - - if (WIFEXITED(status)) { - *exit_code = WEXITSTATUS(status); - return true; - } - - // If it didn't exit cleanly, it must have been signaled. - DCHECK(WIFSIGNALED(status)); - return false; -} - -bool WaitForExitCodeWithTimeout(ProcessHandle handle, - int* exit_code, - base::TimeDelta timeout) { - int status; - if (!WaitpidWithTimeout(handle, &status, timeout)) - return false; - if (WIFSIGNALED(status)) { - *exit_code = -1; - return true; - } - if (WIFEXITED(status)) { - *exit_code = WEXITSTATUS(status); - return true; - } - return false; -} - bool WaitForProcessesToExit(const FilePath::StringType& executable_name, - base::TimeDelta wait, + TimeDelta wait, const ProcessFilter* filter) { bool result = false; // TODO(port): This is inefficient, but works if there are multiple procs. // TODO(port): use waitpid to avoid leaving zombies around - base::TimeTicks end_time = base::TimeTicks::Now() + wait; + TimeTicks end_time = TimeTicks::Now() + wait; do { NamedProcessIterator iter(executable_name, filter); if (!iter.NextProcessEntry()) { result = true; break; } - base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100)); - } while ((end_time - base::TimeTicks::Now()) > base::TimeDelta()); + PlatformThread::Sleep(TimeDelta::FromMilliseconds(100)); + } while ((end_time - TimeTicks::Now()) > TimeDelta()); return result; } -#if defined(OS_MACOSX) -// Using kqueue on Mac so that we can wait on non-child processes. -// We can't use kqueues on child processes because we need to reap -// our own children using wait. -static bool WaitForSingleNonChildProcess(ProcessHandle handle, - base::TimeDelta wait) { - DCHECK_GT(handle, 0); - DCHECK(wait.InMilliseconds() == base::kNoTimeout || wait > base::TimeDelta()); - - ScopedFD kq(kqueue()); - if (!kq.is_valid()) { - DPLOG(ERROR) << "kqueue"; - return false; - } - - struct kevent change = {0}; - EV_SET(&change, handle, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL); - int result = HANDLE_EINTR(kevent(kq.get(), &change, 1, NULL, 0, NULL)); - if (result == -1) { - if (errno == ESRCH) { - // If the process wasn't found, it must be dead. - return true; - } - - DPLOG(ERROR) << "kevent (setup " << handle << ")"; - return false; - } - - // Keep track of the elapsed time to be able to restart kevent if it's - // interrupted. - bool wait_forever = wait.InMilliseconds() == base::kNoTimeout; - base::TimeDelta remaining_delta; - base::TimeTicks deadline; - if (!wait_forever) { - remaining_delta = wait; - deadline = base::TimeTicks::Now() + remaining_delta; - } - - result = -1; - struct kevent event = {0}; - - while (wait_forever || remaining_delta > base::TimeDelta()) { - struct timespec remaining_timespec; - struct timespec* remaining_timespec_ptr; - if (wait_forever) { - remaining_timespec_ptr = NULL; - } else { - remaining_timespec = remaining_delta.ToTimeSpec(); - remaining_timespec_ptr = &remaining_timespec; - } - - result = kevent(kq.get(), NULL, 0, &event, 1, remaining_timespec_ptr); - - if (result == -1 && errno == EINTR) { - if (!wait_forever) { - remaining_delta = deadline - base::TimeTicks::Now(); - } - result = 0; - } else { - break; - } - } - - if (result < 0) { - DPLOG(ERROR) << "kevent (wait " << handle << ")"; - return false; - } else if (result > 1) { - DLOG(ERROR) << "kevent (wait " << handle << "): unexpected result " - << result; - return false; - } else if (result == 0) { - // Timed out. - return false; - } - - DCHECK_EQ(result, 1); - - if (event.filter != EVFILT_PROC || - (event.fflags & NOTE_EXIT) == 0 || - event.ident != static_cast<uintptr_t>(handle)) { - DLOG(ERROR) << "kevent (wait " << handle - << "): unexpected event: filter=" << event.filter - << ", fflags=" << event.fflags - << ", ident=" << event.ident; - return false; - } - - return true; -} -#endif // OS_MACOSX - -bool WaitForSingleProcess(ProcessHandle handle, base::TimeDelta wait) { - ProcessHandle parent_pid = GetParentProcessId(handle); - ProcessHandle our_pid = GetCurrentProcessHandle(); - if (parent_pid != our_pid) { -#if defined(OS_MACOSX) - // On Mac we can wait on non child processes. - return WaitForSingleNonChildProcess(handle, wait); -#else - // Currently on Linux we can't handle non child processes. - NOTIMPLEMENTED(); -#endif // OS_MACOSX - } - - int status; - if (!WaitpidWithTimeout(handle, &status, wait)) - return false; - return WIFEXITED(status); -} - bool CleanupProcesses(const FilePath::StringType& executable_name, - base::TimeDelta wait, + TimeDelta wait, int exit_code, const ProcessFilter* filter) { bool exited_cleanly = WaitForProcessesToExit(executable_name, wait, filter); @@ -463,22 +199,22 @@ class BackgroundReaper : public PlatformThread::Delegate { } // namespace -void EnsureProcessTerminated(ProcessHandle process) { +void EnsureProcessTerminated(Process process) { // If the child is already dead, then there's nothing to do. - if (IsChildDead(process)) + if (IsChildDead(process.Pid())) return; const unsigned timeout = 2; // seconds - BackgroundReaper* reaper = new BackgroundReaper(process, timeout); + BackgroundReaper* reaper = new BackgroundReaper(process.Pid(), timeout); PlatformThread::CreateNonJoinable(0, reaper); } -void EnsureProcessGetsReaped(ProcessHandle process) { +void EnsureProcessGetsReaped(ProcessId pid) { // If the child is already dead, then there's nothing to do. - if (IsChildDead(process)) + if (IsChildDead(pid)) return; - BackgroundReaper* reaper = new BackgroundReaper(process, 0); + BackgroundReaper* reaper = new BackgroundReaper(pid, 0); PlatformThread::CreateNonJoinable(0, reaper); } diff --git a/chromium/base/process/kill_win.cc b/chromium/base/process/kill_win.cc index b102a8781d0..0da3a26ae4f 100644 --- a/chromium/base/process/kill_win.cc +++ b/chromium/base/process/kill_win.cc @@ -12,7 +12,6 @@ #include "base/logging.h" #include "base/message_loop/message_loop.h" #include "base/process/process_iterator.h" -#include "base/profiler/scoped_tracker.h" #include "base/win/object_watcher.h" namespace base { @@ -38,46 +37,40 @@ static const int kWaitInterval = 2000; class TimerExpiredTask : public win::ObjectWatcher::Delegate { public: - explicit TimerExpiredTask(ProcessHandle process); - ~TimerExpiredTask(); + explicit TimerExpiredTask(Process process); + ~TimerExpiredTask() override; void TimedOut(); // MessageLoop::Watcher ----------------------------------------------------- - virtual void OnObjectSignaled(HANDLE object); + void OnObjectSignaled(HANDLE object) override; private: void KillProcess(); // The process that we are watching. - ProcessHandle process_; + Process process_; win::ObjectWatcher watcher_; DISALLOW_COPY_AND_ASSIGN(TimerExpiredTask); }; -TimerExpiredTask::TimerExpiredTask(ProcessHandle process) : process_(process) { - watcher_.StartWatching(process_, this); +TimerExpiredTask::TimerExpiredTask(Process process) : process_(process.Pass()) { + watcher_.StartWatching(process_.Handle(), this); } TimerExpiredTask::~TimerExpiredTask() { TimedOut(); - DCHECK(!process_) << "Make sure to close the handle."; } void TimerExpiredTask::TimedOut() { - if (process_) + if (process_.IsValid()) KillProcess(); } void TimerExpiredTask::OnObjectSignaled(HANDLE object) { - // TODO(vadimt): Remove ScopedTracker below once crbug.com/418183 is fixed. - tracked_objects::ScopedTracker tracking_profile( - FROM_HERE_WITH_EXPLICIT_FUNCTION("TimerExpiredTask_OnObjectSignaled")); - - CloseHandle(process_); - process_ = NULL; + process_.Close(); } void TimerExpiredTask::KillProcess() { @@ -88,42 +81,14 @@ void TimerExpiredTask::KillProcess() { // terminates. We just care that it eventually terminates, and that's what // TerminateProcess should do for us. Don't check for the result code since // it fails quite often. This should be investigated eventually. - base::KillProcess(process_, kProcessKilledExitCode, false); + process_.Terminate(kProcessKilledExitCode, false); // Now, just cleanup as if the process exited normally. - OnObjectSignaled(process_); + OnObjectSignaled(process_.Handle()); } } // namespace -bool KillProcess(ProcessHandle process, int exit_code, bool wait) { - bool result = (TerminateProcess(process, exit_code) != FALSE); - if (result && wait) { - // The process may not end immediately due to pending I/O - if (WAIT_OBJECT_0 != WaitForSingleObject(process, 60 * 1000)) - DPLOG(ERROR) << "Error waiting for process exit"; - } else if (!result) { - DPLOG(ERROR) << "Unable to terminate process"; - } - return result; -} - -// Attempts to kill the process identified by the given process -// entry structure, giving it the specified exit code. -// Returns true if this is successful, false otherwise. -bool KillProcessById(ProcessId process_id, int exit_code, bool wait) { - HANDLE process = OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE, - FALSE, // Don't inherit handle - process_id); - if (!process) { - DPLOG(ERROR) << "Unable to open process " << process_id; - return false; - } - bool ret = KillProcess(process, exit_code, wait); - CloseHandle(process); - return ret; -} - TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) { DWORD tmp_exit_code = 0; @@ -182,29 +147,8 @@ TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) { } } -bool WaitForExitCode(ProcessHandle handle, int* exit_code) { - bool success = WaitForExitCodeWithTimeout( - handle, exit_code, base::TimeDelta::FromMilliseconds(INFINITE)); - CloseProcessHandle(handle); - return success; -} - -bool WaitForExitCodeWithTimeout(ProcessHandle handle, - int* exit_code, - base::TimeDelta timeout) { - if (::WaitForSingleObject( - handle, static_cast<DWORD>(timeout.InMilliseconds())) != WAIT_OBJECT_0) - return false; - DWORD temp_code; // Don't clobber out-parameters in case of failure. - if (!::GetExitCodeProcess(handle, &temp_code)) - return false; - - *exit_code = temp_code; - return true; -} - bool WaitForProcessesToExit(const FilePath::StringType& executable_name, - base::TimeDelta wait, + TimeDelta wait, const ProcessFilter* filter) { bool result = true; DWORD start_time = GetTickCount(); @@ -226,13 +170,8 @@ bool WaitForProcessesToExit(const FilePath::StringType& executable_name, return result; } -bool WaitForSingleProcess(ProcessHandle handle, base::TimeDelta wait) { - int exit_code; - return WaitForExitCodeWithTimeout(handle, &exit_code, wait) && exit_code == 0; -} - bool CleanupProcesses(const FilePath::StringType& executable_name, - base::TimeDelta wait, + TimeDelta wait, int exit_code, const ProcessFilter* filter) { if (WaitForProcessesToExit(executable_name, wait, filter)) @@ -241,20 +180,19 @@ bool CleanupProcesses(const FilePath::StringType& executable_name, return false; } -void EnsureProcessTerminated(ProcessHandle process) { - DCHECK(process != GetCurrentProcess()); +void EnsureProcessTerminated(Process process) { + DCHECK(!process.is_current()); // If already signaled, then we are done! - if (WaitForSingleObject(process, 0) == WAIT_OBJECT_0) { - CloseHandle(process); + if (WaitForSingleObject(process.Handle(), 0) == WAIT_OBJECT_0) { return; } MessageLoop::current()->PostDelayedTask( FROM_HERE, - base::Bind(&TimerExpiredTask::TimedOut, - base::Owned(new TimerExpiredTask(process))), - base::TimeDelta::FromMilliseconds(kWaitInterval)); + Bind(&TimerExpiredTask::TimedOut, + Owned(new TimerExpiredTask(process.Pass()))), + TimeDelta::FromMilliseconds(kWaitInterval)); } } // namespace base diff --git a/chromium/base/process/launch.cc b/chromium/base/process/launch.cc index a1c4d2159cb..c179b2f5f37 100644 --- a/chromium/base/process/launch.cc +++ b/chromium/base/process/launch.cc @@ -27,7 +27,11 @@ LaunchOptions::LaunchOptions() #if defined(OS_LINUX) , clone_flags(0) , allow_new_privs(false) + , kill_on_parent_death(false) #endif // OS_LINUX +#if defined(OS_POSIX) + , pre_exec_delegate(NULL) +#endif // OS_POSIX #if defined(OS_CHROMEOS) , ctrl_terminal_fd(-1) #endif // OS_CHROMEOS diff --git a/chromium/base/process/launch.h b/chromium/base/process/launch.h index 261019b138f..56f27a82109 100644 --- a/chromium/base/process/launch.h +++ b/chromium/base/process/launch.h @@ -14,6 +14,7 @@ #include "base/base_export.h" #include "base/basictypes.h" #include "base/environment.h" +#include "base/process/process.h" #include "base/process/process_handle.h" #include "base/strings/string_piece.h" @@ -21,7 +22,6 @@ #include "base/posix/file_descriptor_shuffle.h" #elif defined(OS_WIN) #include <windows.h> -#include "base/win/scoped_handle.h" #endif namespace base { @@ -37,6 +37,24 @@ typedef std::vector<std::pair<int, int> > FileHandleMappingVector; // Options for launching a subprocess that are passed to LaunchProcess(). // The default constructor constructs the object with default options. struct BASE_EXPORT LaunchOptions { +#if defined(OS_POSIX) + // Delegate to be run in between fork and exec in the subprocess (see + // pre_exec_delegate below) + class BASE_EXPORT PreExecDelegate { + public: + PreExecDelegate() {} + virtual ~PreExecDelegate() {} + + // Since this is to be run between fork and exec, and fork may have happened + // while multiple threads were running, this function needs to be async + // safe. + virtual void RunAsyncSafe() = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(PreExecDelegate); + }; +#endif // defined(OS_POSIX) + LaunchOptions(); ~LaunchOptions(); @@ -115,13 +133,32 @@ struct BASE_EXPORT LaunchOptions { #if defined(OS_LINUX) // If non-zero, start the process using clone(), using flags as provided. + // Unlike in clone, clone_flags may not contain a custom termination signal + // that is sent to the parent when the child dies. The termination signal will + // always be set to SIGCHLD. int clone_flags; // By default, child processes will have the PR_SET_NO_NEW_PRIVS bit set. If // true, then this bit will not be set in the new child process. bool allow_new_privs; + + // Sets parent process death signal to SIGKILL. + bool kill_on_parent_death; #endif // defined(OS_LINUX) +#if defined(OS_POSIX) + // If not empty, change to this directory before execing the new process. + base::FilePath current_directory; + + // If non-null, a delegate to be run immediately prior to executing the new + // program in the child process. + // + // WARNING: If LaunchProcess is called in the presence of multiple threads, + // code running in this delegate essentially needs to be async-signal safe + // (see man 7 signal for a list of allowed functions). + PreExecDelegate* pre_exec_delegate; +#endif // defined(OS_POSIX) + #if defined(OS_CHROMEOS) // If non-negative, the specified file descriptor will be set as the launched // process' controlling terminal. @@ -143,12 +180,7 @@ struct BASE_EXPORT LaunchOptions { // Launch a process via the command line |cmdline|. // See the documentation of LaunchOptions for details on |options|. // -// Returns true upon success. -// -// Upon success, if |process_handle| is non-null, it will be filled in with the -// handle of the launched process. NOTE: In this case, the caller is -// responsible for closing the handle so that it doesn't leak! -// Otherwise, the process handle will be implicitly closed. +// Returns a valid Process upon success. // // Unix-specific notes: // - All file descriptors open in the parent process will be closed in the @@ -158,9 +190,8 @@ struct BASE_EXPORT LaunchOptions { // parent's stdout and stderr. // - If the first argument on the command line does not contain a slash, // PATH will be searched. (See man execvp.) -BASE_EXPORT bool LaunchProcess(const CommandLine& cmdline, - const LaunchOptions& options, - ProcessHandle* process_handle); +BASE_EXPORT Process LaunchProcess(const CommandLine& cmdline, + const LaunchOptions& options); #if defined(OS_WIN) // Windows-specific LaunchProcess that takes the command line as a @@ -173,28 +204,24 @@ BASE_EXPORT bool LaunchProcess(const CommandLine& cmdline, // // Example (including literal quotes) // cmdline = "c:\windows\explorer.exe" -foo "c:\bar\" -BASE_EXPORT bool LaunchProcess(const string16& cmdline, - const LaunchOptions& options, - win::ScopedHandle* process_handle); +BASE_EXPORT Process LaunchProcess(const string16& cmdline, + const LaunchOptions& options); // Launches a process with elevated privileges. This does not behave exactly // like LaunchProcess as it uses ShellExecuteEx instead of CreateProcess to // create the process. This means the process will have elevated privileges -// and thus some common operations like OpenProcess will fail. The process will -// be available through the |process_handle| argument. Currently the only -// supported LaunchOptions are |start_hidden| and |wait|. -BASE_EXPORT bool LaunchElevatedProcess(const CommandLine& cmdline, - const LaunchOptions& options, - ProcessHandle* process_handle); +// and thus some common operations like OpenProcess will fail. Currently the +// only supported LaunchOptions are |start_hidden| and |wait|. +BASE_EXPORT Process LaunchElevatedProcess(const CommandLine& cmdline, + const LaunchOptions& options); #elif defined(OS_POSIX) // A POSIX-specific version of LaunchProcess that takes an argv array // instead of a CommandLine. Useful for situations where you need to // control the command line arguments directly, but prefer the // CommandLine version if launching Chrome itself. -BASE_EXPORT bool LaunchProcess(const std::vector<std::string>& argv, - const LaunchOptions& options, - ProcessHandle* process_handle); +BASE_EXPORT Process LaunchProcess(const std::vector<std::string>& argv, + const LaunchOptions& options); // Close all file descriptors, except those which are a destination in the // given multimap. Only call this function in a child process where you know @@ -270,6 +297,25 @@ void ReplaceBootstrapPort(const std::string& replacement_bootstrap_name); // binary. This should not be called in production/released code. BASE_EXPORT LaunchOptions LaunchOptionsForTest(); +#if defined(OS_LINUX) +// A wrapper for clone with fork-like behavior, meaning that it returns the +// child's pid in the parent and 0 in the child. |flags|, |ptid|, and |ctid| are +// as in the clone system call (the CLONE_VM flag is not supported). +// +// This function uses the libc clone wrapper (which updates libc's pid cache) +// internally, so callers may expect things like getpid() to work correctly +// after in both the child and parent. An exception is when this code is run +// under Valgrind. Valgrind does not support the libc clone wrapper, so the libc +// pid cache may be incorrect after this function is called under Valgrind. +// +// As with fork(), callers should be extremely careful when calling this while +// multiple threads are running, since at the time the fork happened, the +// threads could have been in any state (potentially holding locks, etc.). +// Callers should most likely call execve() in the child soon after calling +// this. +BASE_EXPORT pid_t ForkWithFlags(unsigned long flags, pid_t* ptid, pid_t* ctid); +#endif + } // namespace base #endif // BASE_PROCESS_LAUNCH_H_ diff --git a/chromium/base/process/launch_posix.cc b/chromium/base/process/launch_posix.cc index bc6294b2dad..77edc128319 100644 --- a/chromium/base/process/launch_posix.cc +++ b/chromium/base/process/launch_posix.cc @@ -7,9 +7,12 @@ #include <dirent.h> #include <errno.h> #include <fcntl.h> +#include <sched.h> +#include <setjmp.h> #include <signal.h> #include <stdlib.h> #include <sys/resource.h> +#include <sys/syscall.h> #include <sys/time.h> #include <sys/types.h> #include <sys/wait.h> @@ -30,13 +33,15 @@ #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/posix/eintr_wrapper.h" -#include "base/process/kill.h" +#include "base/process/process.h" #include "base/process/process_metrics.h" #include "base/strings/stringprintf.h" #include "base/synchronization/waitable_event.h" #include "base/third_party/dynamic_annotations/dynamic_annotations.h" +#include "base/third_party/valgrind/valgrind.h" #include "base/threading/platform_thread.h" #include "base/threading/thread_restrictions.h" +#include "build/build_config.h" #if defined(OS_LINUX) #include <sys/prctl.h> @@ -184,6 +189,54 @@ void ResetChildSignalHandlersToDefaults(void) { #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). @@ -277,9 +330,13 @@ void CloseSuperfluousFds(const base::InjectiveMultimap& saved_mapping) { } } -bool LaunchProcess(const std::vector<std::string>& argv, - const LaunchOptions& options, - ProcessHandle* process_handle) { +Process LaunchProcess(const CommandLine& cmdline, + const LaunchOptions& options) { + return LaunchProcess(cmdline.argv(), options); +} + +Process LaunchProcess(const std::vector<std::string>& argv, + const LaunchOptions& options) { size_t fd_shuffle_size = 0; if (options.fds_to_remap) { fd_shuffle_size = options.fds_to_remap->size(); @@ -290,7 +347,12 @@ bool LaunchProcess(const std::vector<std::string>& argv, fd_shuffle1.reserve(fd_shuffle_size); fd_shuffle2.reserve(fd_shuffle_size); - scoped_ptr<char*[]> argv_cstr(new char*[argv.size() + 1]); + scoped_ptr<char* []> argv_cstr(new char* [argv.size() + 1]); + for (size_t i = 0; i < argv.size(); i++) { + argv_cstr[i] = const_cast<char*>(argv[i].c_str()); + } + argv_cstr[argv.size()] = NULL; + scoped_ptr<char*[]> new_environ; char* const empty_environ = NULL; char* const* old_environ = GetEnvironment(); @@ -303,6 +365,11 @@ bool LaunchProcess(const std::vector<std::string>& argv, sigfillset(&full_sigset); const sigset_t orig_sigmask = SetSignalMask(full_sigset); + const char* current_directory = nullptr; + if (!options.current_directory.empty()) { + current_directory = options.current_directory.value().c_str(); + } + pid_t pid; #if defined(OS_LINUX) if (options.clone_flags) { @@ -311,7 +378,17 @@ bool LaunchProcess(const std::vector<std::string>& argv, // and that signal handling follows the process-creation rules. RAW_CHECK( !(options.clone_flags & (CLONE_SIGHAND | CLONE_THREAD | CLONE_VM))); - pid = syscall(__NR_clone, options.clone_flags, 0, 0, 0); + + // We specify a null ptid and ctid. + RAW_CHECK( + !(options.clone_flags & + (CLONE_CHILD_CLEARTID | CLONE_CHILD_SETTID | CLONE_PARENT_SETTID))); + + // Since we use waitpid, we do not support custom termination signals in the + // clone flags. + RAW_CHECK((options.clone_flags & 0xff) == 0); + + pid = ForkWithFlags(options.clone_flags | SIGCHLD, nullptr, nullptr); } else #endif { @@ -325,7 +402,7 @@ bool LaunchProcess(const std::vector<std::string>& argv, if (pid < 0) { DPLOG(ERROR) << "fork"; - return false; + return Process(); } else if (pid == 0) { // Child process @@ -446,11 +523,23 @@ bool LaunchProcess(const std::vector<std::string>& argv, RAW_LOG(FATAL, "prctl(PR_SET_NO_NEW_PRIVS) failed"); } } + + if (options.kill_on_parent_death) { + if (prctl(PR_SET_PDEATHSIG, SIGKILL) != 0) { + RAW_LOG(ERROR, "prctl(PR_SET_PDEATHSIG) failed"); + _exit(127); + } + } #endif - for (size_t i = 0; i < argv.size(); i++) - argv_cstr[i] = const_cast<char*>(argv[i].c_str()); - argv_cstr[argv.size()] = NULL; + if (current_directory != nullptr) { + RAW_CHECK(chdir(current_directory) == 0); + } + + if (options.pre_exec_delegate != nullptr) { + options.pre_exec_delegate->RunAsyncSafe(); + } + execvp(argv_cstr[0], argv_cstr.get()); RAW_LOG(ERROR, "LaunchProcess: failed to execvp:"); @@ -465,19 +554,9 @@ bool LaunchProcess(const std::vector<std::string>& argv, pid_t ret = HANDLE_EINTR(waitpid(pid, 0, 0)); DPCHECK(ret > 0); } - - if (process_handle) - *process_handle = pid; } - return true; -} - - -bool LaunchProcess(const CommandLine& cmdline, - const LaunchOptions& options, - ProcessHandle* process_handle) { - return LaunchProcess(cmdline.argv(), options, process_handle); + return Process(pid); } void RaiseProcessToHighPriority() { @@ -612,7 +691,8 @@ static GetAppOutputInternalResult GetAppOutputInternal( // Always wait for exit code (even if we know we'll declare // GOT_MAX_OUTPUT). - bool success = WaitForExitCode(pid, exit_code); + Process process(pid); + bool success = process.WaitForExit(exit_code); // If we stopped because we read as much as we wanted, we return // GOT_MAX_OUTPUT (because the child may exit due to |SIGPIPE|). @@ -661,4 +741,45 @@ bool GetAppOutputWithExitCode(const CommandLine& cl, return result == EXECUTE_SUCCESS; } +#if defined(OS_LINUX) +pid_t ForkWithFlags(unsigned long flags, pid_t* ptid, pid_t* ctid) { + const bool clone_tls_used = flags & CLONE_SETTLS; + const bool invalid_ctid = + (flags & (CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID)) && !ctid; + const bool invalid_ptid = (flags & CLONE_PARENT_SETTID) && !ptid; + + // We do not support CLONE_VM. + const bool clone_vm_used = flags & CLONE_VM; + + if (clone_tls_used || invalid_ctid || invalid_ptid || clone_vm_used) { + RAW_LOG(FATAL, "Invalid usage of ForkWithFlags"); + } + + // Valgrind's clone implementation does not support specifiying a child_stack + // without CLONE_VM, so we cannot use libc's clone wrapper when running under + // Valgrind. As a result, the libc pid cache may be incorrect under Valgrind. + // See crbug.com/442817 for more details. + if (IsRunningOnValgrind()) { + // See kernel/fork.c in Linux. There is different ordering of sys_clone + // parameters depending on CONFIG_CLONE_BACKWARDS* configuration options. +#if defined(ARCH_CPU_X86_64) + return syscall(__NR_clone, flags, nullptr, ptid, ctid, nullptr); +#elif defined(ARCH_CPU_X86) || defined(ARCH_CPU_ARM_FAMILY) || \ + defined(ARCH_CPU_MIPS_FAMILY) || defined(ARCH_CPU_MIPS64_FAMILY) + // CONFIG_CLONE_BACKWARDS defined. + return syscall(__NR_clone, flags, nullptr, ptid, nullptr, ctid); +#else +#error "Unsupported architecture" +#endif + } + + jmp_buf env; + if (setjmp(env) == 0) { + return CloneAndLongjmpInChild(flags, ptid, ctid, &env); + } + + return 0; +} +#endif // defined(OS_LINUX) + } // namespace base diff --git a/chromium/base/process/launch_win.cc b/chromium/base/process/launch_win.cc index a3303a5e085..ebc19b83130 100644 --- a/chromium/base/process/launch_win.cc +++ b/chromium/base/process/launch_win.cc @@ -105,9 +105,13 @@ void RouteStdioToConsole() { std::ios::sync_with_stdio(); } -bool LaunchProcess(const string16& cmdline, - const LaunchOptions& options, - win::ScopedHandle* process_handle) { +Process LaunchProcess(const CommandLine& cmdline, + const LaunchOptions& options) { + return LaunchProcess(cmdline.GetCommandLineString(), options); +} + +Process LaunchProcess(const string16& cmdline, + const LaunchOptions& options) { win::StartupInformation startup_info_wrapper; STARTUPINFO* startup_info = startup_info_wrapper.startup_info(); @@ -119,18 +123,18 @@ bool LaunchProcess(const string16& cmdline, } else { if (base::win::GetVersion() < base::win::VERSION_VISTA) { DLOG(ERROR) << "Specifying handles to inherit requires Vista or later."; - return false; + return Process(); } if (options.handles_to_inherit->size() > std::numeric_limits<DWORD>::max() / sizeof(HANDLE)) { DLOG(ERROR) << "Too many handles to inherit."; - return false; + return Process(); } if (!startup_info_wrapper.InitializeProcThreadAttributeList(1)) { DPLOG(ERROR); - return false; + return Process(); } if (!startup_info_wrapper.UpdateProcThreadAttribute( @@ -139,7 +143,7 @@ bool LaunchProcess(const string16& cmdline, static_cast<DWORD>(options.handles_to_inherit->size() * sizeof(HANDLE)))) { DPLOG(ERROR); - return false; + return Process(); } inherit_handles = true; @@ -184,7 +188,7 @@ bool LaunchProcess(const string16& cmdline, if (!CreateEnvironmentBlock(&enviroment_block, options.as_user, FALSE)) { DPLOG(ERROR); - return false; + return Process(); } BOOL launched = @@ -197,7 +201,7 @@ bool LaunchProcess(const string16& cmdline, if (!launched) { DPLOG(ERROR) << "Command line:" << std::endl << UTF16ToUTF8(cmdline) << std::endl;; - return false; + return Process(); } } else { if (!CreateProcess(NULL, @@ -206,7 +210,7 @@ bool LaunchProcess(const string16& cmdline, startup_info, &temp_process_info)) { DPLOG(ERROR) << "Command line:" << std::endl << UTF16ToUTF8(cmdline) << std::endl;; - return false; + return Process(); } } base::win::ScopedProcessInformation process_info(temp_process_info); @@ -215,8 +219,9 @@ bool LaunchProcess(const string16& cmdline, if (0 == AssignProcessToJobObject(options.job_handle, process_info.process_handle())) { DLOG(ERROR) << "Could not AssignProcessToObject."; - KillProcess(process_info.process_handle(), kProcessKilledExitCode, true); - return false; + Process scoped_process(process_info.TakeProcessHandle()); + scoped_process.Terminate(kProcessKilledExitCode, true); + return Process(); } ResumeThread(process_info.thread_handle()); @@ -225,28 +230,11 @@ bool LaunchProcess(const string16& cmdline, if (options.wait) WaitForSingleObject(process_info.process_handle(), INFINITE); - // If the caller wants the process handle, we won't close it. - if (process_handle) - process_handle->Set(process_info.TakeProcessHandle()); - - return true; -} - -bool LaunchProcess(const CommandLine& cmdline, - const LaunchOptions& options, - ProcessHandle* process_handle) { - if (!process_handle) - return LaunchProcess(cmdline.GetCommandLineString(), options, NULL); - - win::ScopedHandle process; - bool rv = LaunchProcess(cmdline.GetCommandLineString(), options, &process); - *process_handle = process.Take(); - return rv; + return Process(process_info.TakeProcessHandle()); } -bool LaunchElevatedProcess(const CommandLine& cmdline, - const LaunchOptions& options, - ProcessHandle* process_handle) { +Process LaunchElevatedProcess(const CommandLine& cmdline, + const LaunchOptions& options) { const string16 file = cmdline.GetProgram().value(); const string16 arguments = cmdline.GetArgumentsString(); @@ -263,20 +251,13 @@ bool LaunchElevatedProcess(const CommandLine& cmdline, if (!ShellExecuteEx(&shex_info)) { DPLOG(ERROR); - return false; + return Process(); } if (options.wait) WaitForSingleObject(shex_info.hProcess, INFINITE); - // If the caller wants the process handle give it to them, otherwise just - // close it. Closing it does not terminate the process. - if (process_handle) - *process_handle = shex_info.hProcess; - else - CloseHandle(shex_info.hProcess); - - return true; + return Process(shex_info.hProcess); } bool SetJobObjectLimitFlags(HANDLE job_object, DWORD limit_flags) { diff --git a/chromium/base/process/memory.cc b/chromium/base/process/memory.cc index 1dbc3630703..133a72a0f75 100644 --- a/chromium/base/process/memory.cc +++ b/chromium/base/process/memory.cc @@ -2,10 +2,28 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/debug/alias.h" +#include "base/logging.h" #include "base/process/memory.h" namespace base { +namespace { + +// Breakpad server classifies base::`anonymous namespace'::OnNoMemory as +// out-of-memory crash. +NOINLINE void OnNoMemory(size_t size) { + size_t tmp_size = size; + base::debug::Alias(&tmp_size); + LOG(FATAL) << "Out of memory. size=" << tmp_size; +} + +} // namespace + +void TerminateBecauseOutOfMemory(size_t size) { + OnNoMemory(size); +} + // Defined in memory_mac.mm for Mac. #if !defined(OS_MACOSX) @@ -27,4 +45,4 @@ bool UncheckedCalloc(size_t num_items, size_t size, void** result) { #endif -} +} // namespace base diff --git a/chromium/base/process/memory.h b/chromium/base/process/memory.h index 100d9c72f43..da27151d1b1 100644 --- a/chromium/base/process/memory.h +++ b/chromium/base/process/memory.h @@ -39,6 +39,10 @@ BASE_EXPORT void EnableTerminationOnHeapCorruption(); // Turns on process termination if memory runs out. BASE_EXPORT void EnableTerminationOnOutOfMemory(); +// Terminates process. Should be called only for out of memory errors. +// Crash reporting classifies such crashes as OOM. +BASE_EXPORT void TerminateBecauseOutOfMemory(size_t size); + #if defined(OS_WIN) // Returns the module handle to which an address belongs. The reference count // of the module is not incremented. diff --git a/chromium/base/process/memory_mac.mm b/chromium/base/process/memory_mac.mm index e59e63fa87f..4d719f8054e 100644 --- a/chromium/base/process/memory_mac.mm +++ b/chromium/base/process/memory_mac.mm @@ -4,12 +4,6 @@ #include "base/process/memory.h" -// AddressSanitizer handles heap corruption, and on 64 bit Macs, the malloc -// system automatically abort()s on heap corruption. -#if !defined(ADDRESS_SANITIZER) && ARCH_CPU_32_BITS -#define HANDLE_MEMORY_CORRUPTION_MANUALLY -#endif - #include <CoreFoundation/CoreFoundation.h> #include <errno.h> #include <mach/mach.h> @@ -27,170 +21,12 @@ #include "third_party/apple_apsl/CFBase.h" #include "third_party/apple_apsl/malloc.h" -#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY) -#include <dlfcn.h> -#include <mach-o/nlist.h> - -#include "base/threading/thread_local.h" -#include "third_party/mach_override/mach_override.h" -#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY) - namespace base { -// These are helpers for EnableTerminationOnHeapCorruption, which is a no-op -// on 64 bit Macs. -#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY) -namespace { - -// Finds the library path for malloc() and thus the libC part of libSystem, -// which in Lion is in a separate image. -const char* LookUpLibCPath() { - const void* addr = reinterpret_cast<void*>(&malloc); - - Dl_info info; - if (dladdr(addr, &info)) - return info.dli_fname; - - DLOG(WARNING) << "Could not find image path for malloc()"; - return NULL; -} - -typedef void(*malloc_error_break_t)(void); -malloc_error_break_t g_original_malloc_error_break = NULL; - -// Returns the function pointer for malloc_error_break. This symbol is declared -// as __private_extern__ and cannot be dlsym()ed. Instead, use nlist() to -// get it. -malloc_error_break_t LookUpMallocErrorBreak() { - const char* lib_c_path = LookUpLibCPath(); - if (!lib_c_path) - return NULL; - - // Only need to look up two symbols, but nlist() requires a NULL-terminated - // array and takes no count. - struct nlist nl[3]; - bzero(&nl, sizeof(nl)); - - // The symbol to find. - nl[0].n_un.n_name = const_cast<char*>("_malloc_error_break"); - - // A reference symbol by which the address of the desired symbol will be - // calculated. - nl[1].n_un.n_name = const_cast<char*>("_malloc"); - - int rv = nlist(lib_c_path, nl); - if (rv != 0 || nl[0].n_type == N_UNDF || nl[1].n_type == N_UNDF) { - return NULL; - } - - // nlist() returns addresses as offsets in the image, not the instruction - // pointer in memory. Use the known in-memory address of malloc() - // to compute the offset for malloc_error_break(). - uintptr_t reference_addr = reinterpret_cast<uintptr_t>(&malloc); - reference_addr -= nl[1].n_value; - reference_addr += nl[0].n_value; - - return reinterpret_cast<malloc_error_break_t>(reference_addr); -} - -// Combines ThreadLocalBoolean with AutoReset. It would be convenient -// to compose ThreadLocalPointer<bool> with base::AutoReset<bool>, but that -// would require allocating some storage for the bool. -class ThreadLocalBooleanAutoReset { - public: - ThreadLocalBooleanAutoReset(ThreadLocalBoolean* tlb, bool new_value) - : scoped_tlb_(tlb), - original_value_(tlb->Get()) { - scoped_tlb_->Set(new_value); - } - ~ThreadLocalBooleanAutoReset() { - scoped_tlb_->Set(original_value_); - } - - private: - ThreadLocalBoolean* scoped_tlb_; - bool original_value_; - - DISALLOW_COPY_AND_ASSIGN(ThreadLocalBooleanAutoReset); -}; - -base::LazyInstance<ThreadLocalBoolean>::Leaky - g_unchecked_alloc = LAZY_INSTANCE_INITIALIZER; - -// NOTE(shess): This is called when the malloc library noticed that the heap -// is fubar. Avoid calls which will re-enter the malloc library. -void CrMallocErrorBreak() { - g_original_malloc_error_break(); - - // Out of memory is certainly not heap corruption, and not necessarily - // something for which the process should be terminated. Leave that decision - // to the OOM killer. - if (errno == ENOMEM) - return; - - // The malloc library attempts to log to ASL (syslog) before calling this - // code, which fails accessing a Unix-domain socket when sandboxed. The - // failed socket results in writing to a -1 fd, leaving EBADF in errno. If - // UncheckedMalloc() is on the stack, for large allocations (15k and up) only - // an OOM failure leads here. Smaller allocations could also arrive here due - // to freelist corruption, but there is no way to distinguish that from OOM at - // this point. - // - // NOTE(shess): I hypothesize that EPERM case in 10.9 is the same root cause - // as EBADF. Unfortunately, 10.9's opensource releases don't include malloc - // source code at this time. - // <http://crbug.com/312234> - if ((errno == EBADF || errno == EPERM) && g_unchecked_alloc.Get().Get()) - return; - - // A unit test checks this error message, so it needs to be in release builds. - char buf[1024] = - "Terminating process due to a potential for future heap corruption: " - "errno="; - char errnobuf[] = { - '0' + ((errno / 100) % 10), - '0' + ((errno / 10) % 10), - '0' + (errno % 10), - '\000' - }; - COMPILE_ASSERT(ELAST <= 999, errno_too_large_to_encode); - strlcat(buf, errnobuf, sizeof(buf)); - RAW_LOG(ERROR, buf); - - // Crash by writing to NULL+errno to allow analyzing errno from - // crash dump info (setting a breakpad key would re-enter the malloc - // library). Max documented errno in intro(2) is actually 102, but - // it really just needs to be "small" to stay on the right vm page. - const int kMaxErrno = 256; - char* volatile death_ptr = NULL; - death_ptr += std::min(errno, kMaxErrno); - *death_ptr = '!'; -} - -} // namespace -#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY) - void EnableTerminationOnHeapCorruption() { -#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY) - // Only override once, otherwise CrMallocErrorBreak() will recurse - // to itself. - if (g_original_malloc_error_break) - return; - - malloc_error_break_t malloc_error_break = LookUpMallocErrorBreak(); - if (!malloc_error_break) { - DLOG(WARNING) << "Could not find malloc_error_break"; - return; - } - - mach_error_t err = mach_override_ptr( - (void*)malloc_error_break, - (void*)&CrMallocErrorBreak, - (void**)&g_original_malloc_error_break); - - if (err != err_none) - DLOG(WARNING) << "Could not override malloc_error_break; error = " << err; -#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY) +#if !ARCH_CPU_64_BITS + DLOG(WARNING) << "EnableTerminationOnHeapCorruption only works on 64-bit"; +#endif } // ------------------------------------------------------------------------ @@ -293,142 +129,106 @@ memalign_type g_old_memalign_purgeable; void* oom_killer_malloc(struct _malloc_zone_t* zone, size_t size) { -#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY) - ScopedClearErrno clear_errno; -#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY) void* result = g_old_malloc(zone, size); if (!result && size) - debug::BreakDebugger(); + TerminateBecauseOutOfMemory(size); return result; } void* oom_killer_calloc(struct _malloc_zone_t* zone, size_t num_items, size_t size) { -#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY) - ScopedClearErrno clear_errno; -#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY) void* result = g_old_calloc(zone, num_items, size); if (!result && num_items && size) - debug::BreakDebugger(); + TerminateBecauseOutOfMemory(num_items * size); return result; } void* oom_killer_valloc(struct _malloc_zone_t* zone, size_t size) { -#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY) - ScopedClearErrno clear_errno; -#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY) void* result = g_old_valloc(zone, size); if (!result && size) - debug::BreakDebugger(); + TerminateBecauseOutOfMemory(size); return result; } void oom_killer_free(struct _malloc_zone_t* zone, void* ptr) { -#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY) - ScopedClearErrno clear_errno; -#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY) g_old_free(zone, ptr); } void* oom_killer_realloc(struct _malloc_zone_t* zone, void* ptr, size_t size) { -#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY) - ScopedClearErrno clear_errno; -#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY) void* result = g_old_realloc(zone, ptr, size); if (!result && size) - debug::BreakDebugger(); + TerminateBecauseOutOfMemory(size); return result; } void* oom_killer_memalign(struct _malloc_zone_t* zone, size_t alignment, size_t size) { -#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY) - ScopedClearErrno clear_errno; -#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY) void* result = g_old_memalign(zone, alignment, size); // Only die if posix_memalign would have returned ENOMEM, since there are // other reasons why NULL might be returned (see // http://opensource.apple.com/source/Libc/Libc-583/gen/malloc.c ). - if (!result && size && alignment >= sizeof(void*) - && (alignment & (alignment - 1)) == 0) { - debug::BreakDebugger(); + if (!result && size && alignment >= sizeof(void*) && + (alignment & (alignment - 1)) == 0) { + TerminateBecauseOutOfMemory(size); } return result; } void* oom_killer_malloc_purgeable(struct _malloc_zone_t* zone, size_t size) { -#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY) - ScopedClearErrno clear_errno; -#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY) void* result = g_old_malloc_purgeable(zone, size); if (!result && size) - debug::BreakDebugger(); + TerminateBecauseOutOfMemory(size); return result; } void* oom_killer_calloc_purgeable(struct _malloc_zone_t* zone, size_t num_items, size_t size) { -#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY) - ScopedClearErrno clear_errno; -#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY) void* result = g_old_calloc_purgeable(zone, num_items, size); if (!result && num_items && size) - debug::BreakDebugger(); + TerminateBecauseOutOfMemory(num_items * size); return result; } void* oom_killer_valloc_purgeable(struct _malloc_zone_t* zone, size_t size) { -#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY) - ScopedClearErrno clear_errno; -#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY) void* result = g_old_valloc_purgeable(zone, size); if (!result && size) - debug::BreakDebugger(); + TerminateBecauseOutOfMemory(size); return result; } void oom_killer_free_purgeable(struct _malloc_zone_t* zone, void* ptr) { -#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY) - ScopedClearErrno clear_errno; -#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY) g_old_free_purgeable(zone, ptr); } void* oom_killer_realloc_purgeable(struct _malloc_zone_t* zone, void* ptr, size_t size) { -#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY) - ScopedClearErrno clear_errno; -#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY) void* result = g_old_realloc_purgeable(zone, ptr, size); if (!result && size) - debug::BreakDebugger(); + TerminateBecauseOutOfMemory(size); return result; } void* oom_killer_memalign_purgeable(struct _malloc_zone_t* zone, size_t alignment, size_t size) { -#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY) - ScopedClearErrno clear_errno; -#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY) void* result = g_old_memalign_purgeable(zone, alignment, size); // Only die if posix_memalign would have returned ENOMEM, since there are // other reasons why NULL might be returned (see // http://opensource.apple.com/source/Libc/Libc-583/gen/malloc.c ). if (!result && size && alignment >= sizeof(void*) && (alignment & (alignment - 1)) == 0) { - debug::BreakDebugger(); + TerminateBecauseOutOfMemory(size); } return result; } @@ -438,7 +238,7 @@ void* oom_killer_memalign_purgeable(struct _malloc_zone_t* zone, // === C++ operator new === void oom_killer_new() { - debug::BreakDebugger(); + TerminateBecauseOutOfMemory(0); } #if !defined(ADDRESS_SANITIZER) @@ -477,7 +277,7 @@ void* oom_killer_cfallocator_system_default(CFIndex alloc_size, void* info) { void* result = g_old_cfallocator_system_default(alloc_size, hint, info); if (!result) - debug::BreakDebugger(); + TerminateBecauseOutOfMemory(alloc_size); return result; } @@ -486,7 +286,7 @@ void* oom_killer_cfallocator_malloc(CFIndex alloc_size, void* info) { void* result = g_old_cfallocator_malloc(alloc_size, hint, info); if (!result) - debug::BreakDebugger(); + TerminateBecauseOutOfMemory(alloc_size); return result; } @@ -495,7 +295,7 @@ void* oom_killer_cfallocator_malloc_zone(CFIndex alloc_size, void* info) { void* result = g_old_cfallocator_malloc_zone(alloc_size, hint, info); if (!result) - debug::BreakDebugger(); + TerminateBecauseOutOfMemory(alloc_size); return result; } @@ -510,7 +310,7 @@ id oom_killer_allocWithZone(id self, SEL _cmd, NSZone* zone) { id result = g_old_allocWithZone(self, _cmd, zone); if (!result) - debug::BreakDebugger(); + TerminateBecauseOutOfMemory(0); return result; } @@ -521,10 +321,6 @@ bool UncheckedMalloc(size_t size, void** result) { *result = malloc(size); #else if (g_old_malloc) { -#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY) - ScopedClearErrno clear_errno; - ThreadLocalBooleanAutoReset flag(g_unchecked_alloc.Pointer(), true); -#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY) *result = g_old_malloc(malloc_default_zone(), size); } else { *result = malloc(size); @@ -539,10 +335,6 @@ bool UncheckedCalloc(size_t num_items, size_t size, void** result) { *result = calloc(num_items, size); #else if (g_old_calloc) { -#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY) - ScopedClearErrno clear_errno; - ThreadLocalBooleanAutoReset flag(g_unchecked_alloc.Pointer(), true); -#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY) *result = g_old_calloc(malloc_default_zone(), num_items, size); } else { *result = calloc(num_items, size); diff --git a/chromium/base/process/memory_unittest.cc b/chromium/base/process/memory_unittest.cc index afbf5c629c2..0276b495915 100644 --- a/chromium/base/process/memory_unittest.cc +++ b/chromium/base/process/memory_unittest.cc @@ -26,6 +26,7 @@ #endif #if defined(OS_LINUX) #include <malloc.h> +#include "base/test/malloc_wrapper.h" #endif #if defined(OS_WIN) @@ -105,37 +106,6 @@ TEST(ProcessMemoryTest, EnableLFH) { // test suite setup and does not need to be done again, else mach_override // will fail. -#if !defined(ADDRESS_SANITIZER) -// The following code tests the system implementation of malloc() thus no need -// to test it under AddressSanitizer. -TEST(ProcessMemoryTest, MacMallocFailureDoesNotTerminate) { -#if ARCH_CPU_32_BITS - // The Mavericks malloc library changed in a way which breaks the tricks used - // to implement EnableTerminationOnOutOfMemory() with UncheckedMalloc() under - // 32-bit. Under 64-bit the oom_killer code handles this. - if (base::mac::IsOSMavericksOrLater()) - return; -#endif - - // Test that ENOMEM doesn't crash via CrMallocErrorBreak two ways: the exit - // code and lack of the error string. The number of bytes is one less than - // MALLOC_ABSOLUTE_MAX_SIZE, more than which the system early-returns NULL and - // does not call through malloc_error_break(). See the comment at - // EnableTerminationOnOutOfMemory() for more information. - void* buf = NULL; - ASSERT_EXIT( - { - base::EnableTerminationOnOutOfMemory(); - - buf = malloc(std::numeric_limits<size_t>::max() - (2 * PAGE_SIZE) - 1); - }, - testing::KilledBySignal(SIGTRAP), - "\\*\\*\\* error: can't allocate region.*\\n?.*"); - - base::debug::Alias(buf); -} -#endif // !defined(ADDRESS_SANITIZER) - TEST(ProcessMemoryTest, MacTerminateOnHeapCorruption) { // Assert that freeing an unallocated pointer will crash the process. char buf[9]; @@ -150,20 +120,19 @@ TEST(ProcessMemoryTest, MacTerminateOnHeapCorruption) { ASSERT_DEATH(free(buf), "attempting free on address which " "was not malloc\\(\\)-ed"); #else - ASSERT_DEATH(free(buf), "being freed.*\\n?\\.*" - "\\*\\*\\* set a breakpoint in malloc_error_break to debug.*\\n?.*" - "Terminating process due to a potential for future heap corruption"); -#endif // ARCH_CPU_64_BITS || defined(ADDRESS_SANITIZER) + ADD_FAILURE() << "This test is not supported in this build configuration."; +#endif } #endif // defined(OS_MACOSX) // Android doesn't implement set_new_handler, so we can't use the -// OutOfMemoryTest cases. -// OpenBSD does not support these tests either. +// OutOfMemoryTest cases. OpenBSD does not support these tests either. +// Don't test these on ASan/TSan/MSan configurations: only test the real +// allocator. // TODO(vandebo) make this work on Windows too. -#if !defined(OS_ANDROID) && !defined(OS_OPENBSD) && \ - !defined(OS_WIN) +#if !defined(OS_ANDROID) && !defined(OS_OPENBSD) && !defined(OS_WIN) && \ + !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) #if defined(USE_TCMALLOC) extern "C" { @@ -171,6 +140,10 @@ int tc_set_new_mode(int mode); } #endif // defined(USE_TCMALLOC) +namespace { +const char *kOomRegex = "Out of memory"; +} // namespace + class OutOfMemoryTest : public testing::Test { public: OutOfMemoryTest() @@ -182,13 +155,9 @@ class OutOfMemoryTest : public testing::Test { } #if defined(USE_TCMALLOC) - virtual void SetUp() override { - tc_set_new_mode(1); - } + void SetUp() override { tc_set_new_mode(1); } - virtual void TearDown() override { - tc_set_new_mode(0); - } + void TearDown() override { tc_set_new_mode(0); } #endif // defined(USE_TCMALLOC) protected: @@ -213,42 +182,42 @@ TEST_F(OutOfMemoryDeathTest, New) { ASSERT_DEATH({ SetUpInDeathAssert(); value_ = operator new(test_size_); - }, ""); + }, kOomRegex); } TEST_F(OutOfMemoryDeathTest, NewArray) { ASSERT_DEATH({ SetUpInDeathAssert(); value_ = new char[test_size_]; - }, ""); + }, kOomRegex); } TEST_F(OutOfMemoryDeathTest, Malloc) { ASSERT_DEATH({ SetUpInDeathAssert(); value_ = malloc(test_size_); - }, ""); + }, kOomRegex); } TEST_F(OutOfMemoryDeathTest, Realloc) { ASSERT_DEATH({ SetUpInDeathAssert(); value_ = realloc(NULL, test_size_); - }, ""); + }, kOomRegex); } TEST_F(OutOfMemoryDeathTest, Calloc) { ASSERT_DEATH({ SetUpInDeathAssert(); value_ = calloc(1024, test_size_ / 1024L); - }, ""); + }, kOomRegex); } TEST_F(OutOfMemoryDeathTest, Valloc) { ASSERT_DEATH({ SetUpInDeathAssert(); value_ = valloc(test_size_); - }, ""); + }, kOomRegex); } #if defined(OS_LINUX) @@ -258,7 +227,7 @@ TEST_F(OutOfMemoryDeathTest, Pvalloc) { ASSERT_DEATH({ SetUpInDeathAssert(); value_ = pvalloc(test_size_); - }, ""); + }, kOomRegex); } #endif // PVALLOC_AVAILABLE == 1 @@ -266,18 +235,16 @@ TEST_F(OutOfMemoryDeathTest, Memalign) { ASSERT_DEATH({ SetUpInDeathAssert(); value_ = memalign(4, test_size_); - }, ""); + }, kOomRegex); } TEST_F(OutOfMemoryDeathTest, ViaSharedLibraries) { // This tests that the run-time symbol resolution is overriding malloc for - // shared libraries (including libc itself) as well as for our code. - std::string format = base::StringPrintf("%%%zud", test_size_); - char *value = NULL; + // shared libraries as well as for our code. ASSERT_DEATH({ - SetUpInDeathAssert(); - EXPECT_EQ(-1, asprintf(&value, format.c_str(), 0)); - }, ""); + SetUpInDeathAssert(); + value_ = MallocWrapper(test_size_); + }, kOomRegex); } #endif // OS_LINUX @@ -290,7 +257,7 @@ TEST_F(OutOfMemoryDeathTest, Posix_memalign) { ASSERT_DEATH({ SetUpInDeathAssert(); EXPECT_EQ(ENOMEM, posix_memalign(&value_, 8, test_size_)); - }, ""); + }, kOomRegex); } #endif // defined(OS_POSIX) && !defined(OS_ANDROID) @@ -303,7 +270,7 @@ TEST_F(OutOfMemoryDeathTest, MallocPurgeable) { ASSERT_DEATH({ SetUpInDeathAssert(); value_ = malloc_zone_malloc(zone, test_size_); - }, ""); + }, kOomRegex); } TEST_F(OutOfMemoryDeathTest, ReallocPurgeable) { @@ -311,7 +278,7 @@ TEST_F(OutOfMemoryDeathTest, ReallocPurgeable) { ASSERT_DEATH({ SetUpInDeathAssert(); value_ = malloc_zone_realloc(zone, NULL, test_size_); - }, ""); + }, kOomRegex); } TEST_F(OutOfMemoryDeathTest, CallocPurgeable) { @@ -319,7 +286,7 @@ TEST_F(OutOfMemoryDeathTest, CallocPurgeable) { ASSERT_DEATH({ SetUpInDeathAssert(); value_ = malloc_zone_calloc(zone, 1024, test_size_ / 1024L); - }, ""); + }, kOomRegex); } TEST_F(OutOfMemoryDeathTest, VallocPurgeable) { @@ -327,7 +294,7 @@ TEST_F(OutOfMemoryDeathTest, VallocPurgeable) { ASSERT_DEATH({ SetUpInDeathAssert(); value_ = malloc_zone_valloc(zone, test_size_); - }, ""); + }, kOomRegex); } TEST_F(OutOfMemoryDeathTest, PosixMemalignPurgeable) { @@ -335,7 +302,7 @@ TEST_F(OutOfMemoryDeathTest, PosixMemalignPurgeable) { ASSERT_DEATH({ SetUpInDeathAssert(); value_ = malloc_zone_memalign(zone, 8, test_size_); - }, ""); + }, kOomRegex); } // Since these allocation functions take a signed size, it's possible that @@ -350,7 +317,7 @@ TEST_F(OutOfMemoryDeathTest, CFAllocatorSystemDefault) { SetUpInDeathAssert(); while ((value_ = base::AllocateViaCFAllocatorSystemDefault(signed_test_size_))) {} - }, ""); + }, kOomRegex); } TEST_F(OutOfMemoryDeathTest, CFAllocatorMalloc) { @@ -358,7 +325,7 @@ TEST_F(OutOfMemoryDeathTest, CFAllocatorMalloc) { SetUpInDeathAssert(); while ((value_ = base::AllocateViaCFAllocatorMalloc(signed_test_size_))) {} - }, ""); + }, kOomRegex); } TEST_F(OutOfMemoryDeathTest, CFAllocatorMallocZone) { @@ -366,7 +333,7 @@ TEST_F(OutOfMemoryDeathTest, CFAllocatorMallocZone) { SetUpInDeathAssert(); while ((value_ = base::AllocateViaCFAllocatorMallocZone(signed_test_size_))) {} - }, ""); + }, kOomRegex); } #if !defined(ARCH_CPU_64_BITS) @@ -378,7 +345,7 @@ TEST_F(OutOfMemoryDeathTest, PsychoticallyBigObjCObject) { ASSERT_DEATH({ SetUpInDeathAssert(); while ((value_ = base::AllocatePsychoticallyBigObjCObject())) {} - }, ""); + }, kOomRegex); } #endif // !ARCH_CPU_64_BITS @@ -390,7 +357,7 @@ class OutOfMemoryHandledTest : public OutOfMemoryTest { static const size_t kSafeCallocSize = 128; static const size_t kSafeCallocItems = 4; - virtual void SetUp() { + void SetUp() override { OutOfMemoryTest::SetUp(); // We enable termination on OOM - just as Chrome does at early @@ -448,4 +415,5 @@ TEST_F(OutOfMemoryHandledTest, UncheckedCalloc) { EXPECT_TRUE(value_ == NULL); } #endif // !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) -#endif // !defined(OS_ANDROID) && !defined(OS_OPENBSD) && !defined(OS_WIN) +#endif // !defined(OS_ANDROID) && !defined(OS_OPENBSD) && !defined(OS_WIN) && + // !defined(ADDRESS_SANITIZER) diff --git a/chromium/base/process/memory_win.cc b/chromium/base/process/memory_win.cc index 668214ceaf0..fc57b48f1f7 100644 --- a/chromium/base/process/memory_win.cc +++ b/chromium/base/process/memory_win.cc @@ -4,6 +4,7 @@ #include "base/process/memory.h" +#include <new.h> #include <psapi.h> #include "base/logging.h" @@ -13,15 +14,19 @@ namespace base { namespace { -void OnNoMemory() { - // Kill the process. This is important for security, since WebKit doesn't - // NULL-check many memory allocations. If a malloc fails, returns NULL, and - // the buffer is then used, it provides a handy mapping of memory starting at - // address 0 for an attacker to utilize. +#pragma warning(push) +#pragma warning(disable: 4702) + +int OnNoMemory(size_t) { + // Kill the process. This is important for security since most of code + // does not check the result of memory allocation. __debugbreak(); _exit(1); + return 0; } +#pragma warning(pop) + // HeapSetInformation function pointer. typedef BOOL (WINAPI* HeapSetFn)(HANDLE, HEAP_INFORMATION_CLASS, PVOID, SIZE_T); @@ -68,7 +73,8 @@ void EnableTerminationOnHeapCorruption() { } void EnableTerminationOnOutOfMemory() { - std::set_new_handler(&OnNoMemory); + _set_new_handler(&OnNoMemory); + _set_new_mode(1); } HMODULE GetModuleFromAddress(void* address) { diff --git a/chromium/base/process/process.h b/chromium/base/process/process.h index 701947491d7..7fb1b69954c 100644 --- a/chromium/base/process/process.h +++ b/chromium/base/process/process.h @@ -1,14 +1,15 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_PROCESS_PROCESS_PROCESS_H_ -#define BASE_PROCESS_PROCESS_PROCESS_H_ +#ifndef BASE_PROCESS_PROCESS_H_ +#define BASE_PROCESS_PROCESS_H_ #include "base/base_export.h" #include "base/basictypes.h" #include "base/move.h" #include "base/process/process_handle.h" +#include "base/time/time.h" #include "build/build_config.h" #if defined(OS_WIN) @@ -40,7 +41,7 @@ class BASE_EXPORT Process { Process(RValue other); // The destructor does not terminate the process. - ~Process() {} + ~Process(); // Move operator= for C++03 move emulation of this type. Process& operator=(RValue other); @@ -48,6 +49,26 @@ class BASE_EXPORT Process { // Returns an object for the current process. static Process Current(); + // Returns a Process for the given |pid|. + static Process Open(ProcessId pid); + + // Returns a Process for the given |pid|. On Windows the handle is opened + // with more access rights and must only be used by trusted code (can read the + // address space and duplicate handles). + static Process OpenWithExtraPrivileges(ProcessId pid); + +#if defined(OS_WIN) + // Returns a Process for the given |pid|, using some |desired_access|. + // See ::OpenProcess documentation for valid |desired_access|. + static Process OpenWithAccess(ProcessId pid, DWORD desired_access); +#endif + + // Creates an object from a |handle| owned by someone else. + // Don't use this for new code. It is only intended to ease the migration to + // a strict ownership model. + // TODO(rvargas) crbug.com/417532: Remove this code. + static Process DeprecatedGetProcessFromHandle(ProcessHandle handle); + // Returns true if processes can be backgrounded. static bool CanBackgroundProcesses(); @@ -62,7 +83,7 @@ class BASE_EXPORT Process { Process Duplicate() const; // Get the PID for this process. - ProcessId pid() const; + ProcessId Pid() const; // Returns true if this process is the current process. bool is_current() const; @@ -70,11 +91,40 @@ class BASE_EXPORT Process { // Close the process handle. This will not terminate the process. void Close(); - // Terminates the process with extreme prejudice. The given |result_code| will - // be the exit code of the process. - // NOTE: On POSIX |result_code| is ignored. - void Terminate(int result_code); - + // Terminates the process with extreme prejudice. The given |exit_code| will + // be the exit code of the process. If |wait| is true, this method will wait + // for up to one minute for the process to actually terminate. + // Returns true if the process terminates within the allowed time. + // NOTE: On POSIX |exit_code| is ignored. + bool Terminate(int exit_code, bool wait) const; + + // Waits for the process to exit. Returns true on success. + // On POSIX, if the process has been signaled then |exit_code| is set to -1. + // On Linux this must be a child process, however on Mac and Windows it can be + // any process. + bool WaitForExit(int* exit_code); + + // Same as WaitForExit() but only waits for up to |timeout|. + bool WaitForExitWithTimeout(TimeDelta timeout, int* exit_code); + +#if defined(OS_MACOSX) + // The Mac needs a Mach port in order to manipulate a process's priority, + // and there's no good way to get that from base given the pid. These Mac + // variants of the IsProcessBackgrounded and SetProcessBackgrounded API take + // the Mach port for this reason. See crbug.com/460102 + // + // A process is backgrounded when its priority is lower than normal. + // Return true if the process with mach port |task_port| is backgrounded, + // false otherwise. + bool IsProcessBackgrounded(mach_port_t task_port) const; + + // Set the process with the specified mach port as backgrounded. If value is + // true, the priority of the process will be lowered. If value is false, the + // priority of the process will be made "normal" - equivalent to default + // process priority. Returns true if the priority was changed, false + // otherwise. + bool SetProcessBackgrounded(mach_port_t task_port, bool value); +#else // A process is backgrounded when it's priority is lower than normal. // Return true if this process is backgrounded, false otherwise. bool IsProcessBackgrounded() const; @@ -84,7 +134,7 @@ class BASE_EXPORT Process { // will be made "normal" - equivalent to default process priority. // Returns true if the priority was changed, false otherwise. bool SetProcessBackgrounded(bool value); - +#endif // defined(OS_MACOSX) // Returns an integer representing the priority of a process. The meaning // of this value is OS dependent. int GetPriority() const; @@ -100,4 +150,4 @@ class BASE_EXPORT Process { } // namespace base -#endif // BASE_PROCESS_PROCESS_PROCESS_H_ +#endif // BASE_PROCESS_PROCESS_H_ diff --git a/chromium/base/process/process_handle.h b/chromium/base/process/process_handle.h index 6f8094ee80b..77f2c585cfc 100644 --- a/chromium/base/process/process_handle.h +++ b/chromium/base/process/process_handle.h @@ -40,47 +40,12 @@ BASE_EXPORT ProcessId GetCurrentProcId(); // Returns the ProcessHandle of the current process. BASE_EXPORT ProcessHandle GetCurrentProcessHandle(); -// Converts a PID to a process handle. This handle must be closed by -// CloseProcessHandle when you are done with it. Returns true on success. -BASE_EXPORT bool OpenProcessHandle(ProcessId pid, ProcessHandle* handle); - -// Converts a PID to a process handle. On Windows the handle is opened -// with more access rights and must only be used by trusted code. -// You have to close returned handle using CloseProcessHandle. Returns true -// on success. -// TODO(sanjeevr): Replace all calls to OpenPrivilegedProcessHandle with the -// more specific OpenProcessHandleWithAccess method and delete this. -BASE_EXPORT bool OpenPrivilegedProcessHandle(ProcessId pid, - ProcessHandle* handle); - -// Converts a PID to a process handle using the desired access flags. Use a -// combination of the kProcessAccess* flags defined above for |access_flags|. -BASE_EXPORT bool OpenProcessHandleWithAccess(ProcessId pid, - uint32 access_flags, - ProcessHandle* handle); - -// Closes the process handle opened by OpenProcessHandle. -BASE_EXPORT void CloseProcessHandle(ProcessHandle process); - // Returns the unique ID for the specified process. This is functionally the // same as Windows' GetProcessId(), but works on versions of Windows before // Win XP SP1 as well. +// DEPRECATED. New code should be using Process::Pid() instead. BASE_EXPORT ProcessId GetProcId(ProcessHandle process); -#if defined(OS_WIN) -enum IntegrityLevel { - INTEGRITY_UNKNOWN, - LOW_INTEGRITY, - MEDIUM_INTEGRITY, - HIGH_INTEGRITY, -}; -// Determine the integrity level of the specified process. Returns false -// if the system does not support integrity levels (pre-Vista) or in the case -// of an underlying system failure. -BASE_EXPORT bool GetProcessIntegrityLevel(ProcessHandle process, - IntegrityLevel* level); -#endif - #if defined(OS_POSIX) // Returns the path to the executable of the given process. BASE_EXPORT FilePath GetProcessExecutablePath(ProcessHandle process); diff --git a/chromium/base/process/process_handle_posix.cc b/chromium/base/process/process_handle_posix.cc index 4013254a5c2..4e332df1a0f 100644 --- a/chromium/base/process/process_handle_posix.cc +++ b/chromium/base/process/process_handle_posix.cc @@ -16,32 +16,6 @@ ProcessHandle GetCurrentProcessHandle() { return GetCurrentProcId(); } -bool OpenProcessHandle(ProcessId pid, ProcessHandle* handle) { - // On Posix platforms, process handles are the same as PIDs, so we - // don't need to do anything. - *handle = pid; - return true; -} - -bool OpenPrivilegedProcessHandle(ProcessId pid, ProcessHandle* handle) { - // On POSIX permissions are checked for each operation on process, - // not when opening a "handle". - return OpenProcessHandle(pid, handle); -} - -bool OpenProcessHandleWithAccess(ProcessId pid, - uint32 access_flags, - ProcessHandle* handle) { - // On POSIX permissions are checked for each operation on process, - // not when opening a "handle". - return OpenProcessHandle(pid, handle); -} - -void CloseProcessHandle(ProcessHandle process) { - // See OpenProcessHandle, nothing to do. - return; -} - ProcessId GetProcId(ProcessHandle process) { return process; } diff --git a/chromium/base/process/process_handle_win.cc b/chromium/base/process/process_handle_win.cc index 3bc3a125e0d..f2ffff8e882 100644 --- a/chromium/base/process/process_handle_win.cc +++ b/chromium/base/process/process_handle_win.cc @@ -20,107 +20,9 @@ ProcessHandle GetCurrentProcessHandle() { return ::GetCurrentProcess(); } -bool OpenProcessHandle(ProcessId pid, ProcessHandle* handle) { - // We try to limit privileges granted to the handle. If you need this - // for test code, consider using OpenPrivilegedProcessHandle instead of - // adding more privileges here. - ProcessHandle result = OpenProcess(PROCESS_TERMINATE | - PROCESS_QUERY_INFORMATION | - SYNCHRONIZE, - FALSE, pid); - - if (result == NULL) - return false; - - *handle = result; - return true; -} - -bool OpenPrivilegedProcessHandle(ProcessId pid, ProcessHandle* handle) { - ProcessHandle result = OpenProcess(PROCESS_DUP_HANDLE | - PROCESS_TERMINATE | - PROCESS_QUERY_INFORMATION | - PROCESS_VM_READ | - SYNCHRONIZE, - FALSE, pid); - - if (result == NULL) - return false; - - *handle = result; - return true; -} - -bool OpenProcessHandleWithAccess(ProcessId pid, - uint32 access_flags, - ProcessHandle* handle) { - ProcessHandle result = OpenProcess(access_flags, FALSE, pid); - - if (result == NULL) - return false; - - *handle = result; - return true; -} - -void CloseProcessHandle(ProcessHandle process) { - CloseHandle(process); -} - ProcessId GetProcId(ProcessHandle process) { // This returns 0 if we have insufficient rights to query the process handle. return GetProcessId(process); } -bool GetProcessIntegrityLevel(ProcessHandle process, IntegrityLevel *level) { - if (!level) - return false; - - if (win::GetVersion() < base::win::VERSION_VISTA) - return false; - - HANDLE process_token; - if (!OpenProcessToken(process, TOKEN_QUERY | TOKEN_QUERY_SOURCE, - &process_token)) - return false; - - win::ScopedHandle scoped_process_token(process_token); - - DWORD token_info_length = 0; - if (GetTokenInformation(process_token, TokenIntegrityLevel, NULL, 0, - &token_info_length) || - GetLastError() != ERROR_INSUFFICIENT_BUFFER) - return false; - - scoped_ptr<char[]> token_label_bytes(new char[token_info_length]); - if (!token_label_bytes.get()) - return false; - - TOKEN_MANDATORY_LABEL* token_label = - reinterpret_cast<TOKEN_MANDATORY_LABEL*>(token_label_bytes.get()); - if (!token_label) - return false; - - if (!GetTokenInformation(process_token, TokenIntegrityLevel, token_label, - token_info_length, &token_info_length)) - return false; - - DWORD integrity_level = *GetSidSubAuthority(token_label->Label.Sid, - (DWORD)(UCHAR)(*GetSidSubAuthorityCount(token_label->Label.Sid)-1)); - - if (integrity_level < SECURITY_MANDATORY_MEDIUM_RID) { - *level = LOW_INTEGRITY; - } else if (integrity_level >= SECURITY_MANDATORY_MEDIUM_RID && - integrity_level < SECURITY_MANDATORY_HIGH_RID) { - *level = MEDIUM_INTEGRITY; - } else if (integrity_level >= SECURITY_MANDATORY_HIGH_RID) { - *level = HIGH_INTEGRITY; - } else { - NOTREACHED(); - return false; - } - - return true; -} - } // namespace base diff --git a/chromium/base/process/process_info.h b/chromium/base/process/process_info.h index e9e7b4e8151..85f204d04cf 100644 --- a/chromium/base/process/process_info.h +++ b/chromium/base/process/process_info.h @@ -2,11 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_PROCESS_PROCESS_PROCESS_INFO_H_ -#define BASE_PROCESS_PROCESS_PROCESS_INFO_H_ +#ifndef BASE_PROCESS_PROCESS_INFO_H_ +#define BASE_PROCESS_PROCESS_INFO_H_ #include "base/base_export.h" #include "base/basictypes.h" +#include "build/build_config.h" namespace base { @@ -20,6 +21,24 @@ class BASE_EXPORT CurrentProcessInfo { static const Time CreationTime(); }; +#if defined(OS_WIN) + +enum IntegrityLevel { + INTEGRITY_UNKNOWN, + LOW_INTEGRITY, + MEDIUM_INTEGRITY, + HIGH_INTEGRITY, +}; + +// Returns the integrity level of the process. Returns INTEGRITY_UNKNOWN if the +// system does not support integrity levels (pre-Vista) or in the case of an +// underlying system failure. +BASE_EXPORT IntegrityLevel GetCurrentProcessIntegrityLevel(); + +#endif // defined(OS_WIN) + + + } // namespace base -#endif // BASE_PROCESS_PROCESS_PROCESS_INFO_H_ +#endif // BASE_PROCESS_PROCESS_INFO_H_ diff --git a/chromium/base/process/process_info_win.cc b/chromium/base/process/process_info_win.cc index b930ae6dd88..2b9c40653fd 100644 --- a/chromium/base/process/process_info_win.cc +++ b/chromium/base/process/process_info_win.cc @@ -7,11 +7,14 @@ #include <windows.h> #include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" #include "base/time/time.h" +#include "base/win/scoped_handle.h" +#include "base/win/windows_version.h" namespace base { -//static +// static const Time CurrentProcessInfo::CreationTime() { FILETIME creation_time = {}; FILETIME ignore = {}; @@ -22,4 +25,55 @@ const Time CurrentProcessInfo::CreationTime() { return Time::FromFileTime(creation_time); } +IntegrityLevel GetCurrentProcessIntegrityLevel() { + if (win::GetVersion() < base::win::VERSION_VISTA) + return INTEGRITY_UNKNOWN; + + HANDLE process_token; + if (!::OpenProcessToken(::GetCurrentProcess(), + TOKEN_QUERY | TOKEN_QUERY_SOURCE, &process_token)) { + return INTEGRITY_UNKNOWN; + } + win::ScopedHandle scoped_process_token(process_token); + + DWORD token_info_length = 0; + if (::GetTokenInformation(process_token, TokenIntegrityLevel, NULL, 0, + &token_info_length) || + ::GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + return INTEGRITY_UNKNOWN; + } + + scoped_ptr<char[]> token_label_bytes(new char[token_info_length]); + if (!token_label_bytes.get()) + return INTEGRITY_UNKNOWN; + + TOKEN_MANDATORY_LABEL* token_label = + reinterpret_cast<TOKEN_MANDATORY_LABEL*>(token_label_bytes.get()); + if (!token_label) + return INTEGRITY_UNKNOWN; + + if (!::GetTokenInformation(process_token, TokenIntegrityLevel, token_label, + token_info_length, &token_info_length)) { + return INTEGRITY_UNKNOWN; + } + + DWORD integrity_level = *::GetSidSubAuthority( + token_label->Label.Sid, + static_cast<DWORD>(*::GetSidSubAuthorityCount(token_label->Label.Sid)-1)); + + if (integrity_level < SECURITY_MANDATORY_MEDIUM_RID) + return LOW_INTEGRITY; + + if (integrity_level >= SECURITY_MANDATORY_MEDIUM_RID && + integrity_level < SECURITY_MANDATORY_HIGH_RID) { + return MEDIUM_INTEGRITY; + } + + if (integrity_level >= SECURITY_MANDATORY_HIGH_RID) + return HIGH_INTEGRITY; + + NOTREACHED(); + return INTEGRITY_UNKNOWN; +} + } // namespace base diff --git a/chromium/base/process/process_iterator.h b/chromium/base/process/process_iterator.h index bdd07c698de..ec6500e653c 100644 --- a/chromium/base/process/process_iterator.h +++ b/chromium/base/process/process_iterator.h @@ -36,26 +36,6 @@ struct ProcessEntry : public PROCESSENTRY32 { ProcessId parent_pid() const { return th32ParentProcessID; } const wchar_t* exe_file() const { return szExeFile; } }; - -// Process access masks. These constants provide platform-independent -// definitions for the standard Windows access masks. -// See http://msdn.microsoft.com/en-us/library/ms684880(VS.85).aspx for -// the specific semantics of each mask value. -const uint32 kProcessAccessTerminate = PROCESS_TERMINATE; -const uint32 kProcessAccessCreateThread = PROCESS_CREATE_THREAD; -const uint32 kProcessAccessSetSessionId = PROCESS_SET_SESSIONID; -const uint32 kProcessAccessVMOperation = PROCESS_VM_OPERATION; -const uint32 kProcessAccessVMRead = PROCESS_VM_READ; -const uint32 kProcessAccessVMWrite = PROCESS_VM_WRITE; -const uint32 kProcessAccessDuplicateHandle = PROCESS_DUP_HANDLE; -const uint32 kProcessAccessCreateProcess = PROCESS_CREATE_PROCESS; -const uint32 kProcessAccessSetQuota = PROCESS_SET_QUOTA; -const uint32 kProcessAccessSetInformation = PROCESS_SET_INFORMATION; -const uint32 kProcessAccessQueryInformation = PROCESS_QUERY_INFORMATION; -const uint32 kProcessAccessSuspendResume = PROCESS_SUSPEND_RESUME; -const uint32 kProcessAccessQueryLimitedInfomation = - PROCESS_QUERY_LIMITED_INFORMATION; -const uint32 kProcessAccessWaitForTermination = SYNCHRONIZE; #elif defined(OS_POSIX) struct BASE_EXPORT ProcessEntry { ProcessEntry(); @@ -75,23 +55,6 @@ struct BASE_EXPORT ProcessEntry { std::string exe_file_; std::vector<std::string> cmd_line_args_; }; - -// Process access masks. They are not used on Posix because access checking -// does not happen during handle creation. -const uint32 kProcessAccessTerminate = 0; -const uint32 kProcessAccessCreateThread = 0; -const uint32 kProcessAccessSetSessionId = 0; -const uint32 kProcessAccessVMOperation = 0; -const uint32 kProcessAccessVMRead = 0; -const uint32 kProcessAccessVMWrite = 0; -const uint32 kProcessAccessDuplicateHandle = 0; -const uint32 kProcessAccessCreateProcess = 0; -const uint32 kProcessAccessSetQuota = 0; -const uint32 kProcessAccessSetInformation = 0; -const uint32 kProcessAccessQueryInformation = 0; -const uint32 kProcessAccessSuspendResume = 0; -const uint32 kProcessAccessQueryLimitedInfomation = 0; -const uint32 kProcessAccessWaitForTermination = 0; #endif // defined(OS_POSIX) // Used to filter processes by process ID. diff --git a/chromium/base/process/process_linux.cc b/chromium/base/process/process_linux.cc index 59ee288f880..88a310eccdb 100644 --- a/chromium/base/process/process_linux.cc +++ b/chromium/base/process/process_linux.cc @@ -103,7 +103,7 @@ bool Process::IsProcessBackgrounded() const { &proc)) { std::vector<std::string> proc_parts; base::SplitString(proc, ':', &proc_parts); - DCHECK(proc_parts.size() == 3); + DCHECK_EQ(proc_parts.size(), 3u); bool ret = proc_parts[2] == std::string(kBackground); return ret; } else { diff --git a/chromium/base/process/process_mac.cc b/chromium/base/process/process_mac.cc new file mode 100644 index 00000000000..1913cc378e8 --- /dev/null +++ b/chromium/base/process/process_mac.cc @@ -0,0 +1,128 @@ +// 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/process/process.h" + +#include "base/mac/mac_util.h" +#include "base/mac/mach_logging.h" + +#include <mach/mach.h> + +// The following was added to <mach/task_policy.h> after 10.8. +// TODO(shrike): Remove the TASK_OVERRIDE_QOS_POLICY ifndef once builders +// reach 10.9 or higher. +#ifndef TASK_OVERRIDE_QOS_POLICY + +#define TASK_OVERRIDE_QOS_POLICY 9 + +typedef struct task_category_policy task_category_policy_data_t; +typedef struct task_category_policy* task_category_policy_t; + +enum task_latency_qos { + LATENCY_QOS_TIER_UNSPECIFIED = 0x0, + LATENCY_QOS_TIER_0 = ((0xFF << 16) | 1), + LATENCY_QOS_TIER_1 = ((0xFF << 16) | 2), + LATENCY_QOS_TIER_2 = ((0xFF << 16) | 3), + LATENCY_QOS_TIER_3 = ((0xFF << 16) | 4), + LATENCY_QOS_TIER_4 = ((0xFF << 16) | 5), + LATENCY_QOS_TIER_5 = ((0xFF << 16) | 6) +}; +typedef integer_t task_latency_qos_t; +enum task_throughput_qos { + THROUGHPUT_QOS_TIER_UNSPECIFIED = 0x0, + THROUGHPUT_QOS_TIER_0 = ((0xFE << 16) | 1), + THROUGHPUT_QOS_TIER_1 = ((0xFE << 16) | 2), + THROUGHPUT_QOS_TIER_2 = ((0xFE << 16) | 3), + THROUGHPUT_QOS_TIER_3 = ((0xFE << 16) | 4), + THROUGHPUT_QOS_TIER_4 = ((0xFE << 16) | 5), + THROUGHPUT_QOS_TIER_5 = ((0xFE << 16) | 6), +}; + +#define LATENCY_QOS_LAUNCH_DEFAULT_TIER LATENCY_QOS_TIER_3 +#define THROUGHPUT_QOS_LAUNCH_DEFAULT_TIER THROUGHPUT_QOS_TIER_3 + +typedef integer_t task_throughput_qos_t; + +struct task_qos_policy { + task_latency_qos_t task_latency_qos_tier; + task_throughput_qos_t task_throughput_qos_tier; +}; + +typedef struct task_qos_policy* task_qos_policy_t; +#define TASK_QOS_POLICY_COUNT \ + ((mach_msg_type_number_t)(sizeof(struct task_qos_policy) / sizeof(integer_t))) + +#endif // TASK_OVERRIDE_QOS_POLICY + +namespace base { + +bool Process::CanBackgroundProcesses() { + return true; +} + +bool Process::IsProcessBackgrounded(mach_port_t task_port) const { + // See SetProcessBackgrounded(). + DCHECK(IsValid()); + DCHECK_NE(task_port, TASK_NULL); + + task_category_policy_data_t category_policy; + mach_msg_type_number_t task_info_count = TASK_CATEGORY_POLICY_COUNT; + boolean_t get_default = FALSE; + + kern_return_t result = + task_policy_get(task_port, TASK_CATEGORY_POLICY, + reinterpret_cast<task_policy_t>(&category_policy), + &task_info_count, &get_default); + MACH_LOG_IF(ERROR, result != KERN_SUCCESS, result) << + "task_policy_get TASK_CATEGORY_POLICY"; + + if (result == KERN_SUCCESS && get_default == FALSE) { + return category_policy.role == TASK_BACKGROUND_APPLICATION; + } + return false; +} + +bool Process::SetProcessBackgrounded(mach_port_t task_port, bool background) { + DCHECK(IsValid()); + DCHECK_NE(task_port, TASK_NULL); + + if (!CanBackgroundProcesses()) { + return false; + } else if (IsProcessBackgrounded(task_port) == background) { + return true; + } + + task_category_policy category_policy; + category_policy.role = + background ? TASK_BACKGROUND_APPLICATION : TASK_FOREGROUND_APPLICATION; + kern_return_t result = + task_policy_set(task_port, TASK_CATEGORY_POLICY, + reinterpret_cast<task_policy_t>(&category_policy), + TASK_CATEGORY_POLICY_COUNT); + + if (result != KERN_SUCCESS) { + MACH_LOG(ERROR, result) << "task_policy_set TASK_CATEGORY_POLICY"; + return false; + } else if (!mac::IsOSMavericksOrLater()) { + return true; + } + + // Latency QoS regulates timer throttling/accuracy. Select default tier + // on foreground because precise timer firing isn't needed. + struct task_qos_policy qos_policy = { + background ? LATENCY_QOS_TIER_5 : LATENCY_QOS_TIER_UNSPECIFIED, + background ? THROUGHPUT_QOS_TIER_5 : THROUGHPUT_QOS_TIER_UNSPECIFIED + }; + result = task_policy_set(task_port, TASK_OVERRIDE_QOS_POLICY, + reinterpret_cast<task_policy_t>(&qos_policy), + TASK_QOS_POLICY_COUNT); + if (result != KERN_SUCCESS) { + MACH_LOG(ERROR, result) << "task_policy_set TASK_OVERRIDE_QOS_POLICY"; + return false; + } + + return true; +} + +} // namespace base diff --git a/chromium/base/process/process_metrics.cc b/chromium/base/process/process_metrics.cc index 2edd9c755de..e4863391076 100644 --- a/chromium/base/process/process_metrics.cc +++ b/chromium/base/process/process_metrics.cc @@ -33,11 +33,11 @@ scoped_ptr<Value> SystemMetrics::ToValue() const { res->SetInteger("committed_memory", static_cast<int>(committed_memory_)); #if defined(OS_LINUX) || defined(OS_ANDROID) - res->Set("meminfo", memory_info_.ToValue().release()); - res->Set("diskinfo", disk_info_.ToValue().release()); + res->Set("meminfo", memory_info_.ToValue()); + res->Set("diskinfo", disk_info_.ToValue()); #endif #if defined(OS_CHROMEOS) - res->Set("swapinfo", swap_info_.ToValue().release()); + res->Set("swapinfo", swap_info_.ToValue()); #endif return res.Pass(); diff --git a/chromium/base/process/process_metrics.h b/chromium/base/process/process_metrics.h index 3eb3604b7a3..5916b94148c 100644 --- a/chromium/base/process/process_metrics.h +++ b/chromium/base/process/process_metrics.h @@ -85,17 +85,6 @@ struct CommittedKBytes { size_t image; }; -// Free memory (Megabytes marked as free) in the 2G process address space. -// total : total amount in megabytes marked as free. Maximum value is 2048. -// largest : size of the largest contiguous amount of memory found. It is -// always smaller or equal to FreeMBytes::total. -// largest_ptr: starting address of the largest memory block. -struct FreeMBytes { - size_t total; - size_t largest; - void* largest_ptr; -}; - // Convert a POSIX timeval to microseconds. BASE_EXPORT int64 TimeValToMicroseconds(const struct timeval& tv); @@ -154,6 +143,14 @@ class BASE_EXPORT ProcessMetrics { // usage in bytes, as per definition of WorkingSetBytes. bool GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const; +#if defined(OS_MACOSX) + // Fills both CommitedKBytes and WorkingSetKBytes in a single operation. This + // is more efficient on Mac OS X, as the two can be retrieved with a single + // system call. + bool GetCommittedAndWorkingSetKBytes(CommittedKBytes* usage, + WorkingSetKBytes* ws_usage) const; +#endif + // Returns the CPU usage in percent since the last time this method or // GetPlatformIndependentCPUUsage() was called. The first time this method // is called it returns 0 and will return the actual CPU info on subsequent @@ -231,10 +228,13 @@ class BASE_EXPORT ProcessMetrics { // Returns 0 if it can't compute the commit charge. BASE_EXPORT size_t GetSystemCommitCharge(); +// Returns the number of bytes in a memory page. +BASE_EXPORT size_t GetPageSize(); + #if defined(OS_POSIX) // Returns the maximum number of file descriptors that can be open by a process // at once. If the number is unavailable, a conservative best guess is returned. -size_t GetMaxFds(); +BASE_EXPORT size_t GetMaxFds(); // Sets the file descriptor soft limit to |max_descriptors| or the OS hard // limit, whichever is lower. diff --git a/chromium/base/process/process_metrics_ios.cc b/chromium/base/process/process_metrics_ios.cc index 405c373c9fd..07f2c8de1d4 100644 --- a/chromium/base/process/process_metrics_ios.cc +++ b/chromium/base/process/process_metrics_ios.cc @@ -6,6 +6,8 @@ #include <mach/task.h> +#include "base/logging.h" + namespace base { namespace { @@ -30,6 +32,11 @@ ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) { return new ProcessMetrics(process); } +double ProcessMetrics::GetCPUUsage() { + NOTIMPLEMENTED(); + return 0; +} + size_t ProcessMetrics::GetPagefileUsage() const { task_basic_info_64 task_info_data; if (!GetTaskInfo(&task_info_data)) @@ -65,4 +72,14 @@ void SetFdLimit(unsigned int max_descriptors) { // Unimplemented. } +size_t GetPageSize() { + return getpagesize(); +} + +// Bytes committed by the system. +size_t GetSystemCommitCharge() { + NOTIMPLEMENTED(); + return 0; +} + } // namespace base diff --git a/chromium/base/process/process_metrics_linux.cc b/chromium/base/process/process_metrics_linux.cc index e8db571a317..2d54c4c7265 100644 --- a/chromium/base/process/process_metrics_linux.cc +++ b/chromium/base/process/process_metrics_linux.cc @@ -399,17 +399,36 @@ size_t GetSystemCommitCharge() { return meminfo.total - meminfo.free - meminfo.buffers - meminfo.cached; } -// Exposed for testing. int ParseProcStatCPU(const std::string& input) { - std::vector<std::string> proc_stats; - if (!internal::ParseProcStats(input, &proc_stats)) + // |input| may be empty if the process disappeared somehow. + // e.g. http://crbug.com/145811. + if (input.empty()) return -1; - if (proc_stats.size() <= internal::VM_STIME) + size_t start = input.find_last_of(')'); + if (start == input.npos) return -1; - int utime = GetProcStatsFieldAsInt64(proc_stats, internal::VM_UTIME); - int stime = GetProcStatsFieldAsInt64(proc_stats, internal::VM_STIME); - return utime + stime; + + // Number of spaces remaining until reaching utime's index starting after the + // last ')'. + int num_spaces_remaining = internal::VM_UTIME - 1; + + size_t i = start; + while ((i = input.find(' ', i + 1)) != input.npos) { + // Validate the assumption that there aren't any contiguous spaces + // in |input| before utime. + DCHECK_NE(input[i - 1], ' '); + if (--num_spaces_remaining == 0) { + int utime = 0; + int stime = 0; + if (sscanf(&input.data()[i], "%d %d", &utime, &stime) != 2) + return -1; + + return utime + stime; + } + } + + return -1; } const char kProcSelfExe[] = "/proc/self/exe"; @@ -650,13 +669,13 @@ bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo) { } #if defined(OS_CHROMEOS) - // Report on Chrome OS GEM object graphics memory. /var/run/debugfs_gpu is a + // Report on Chrome OS GEM object graphics memory. /run/debugfs_gpu is a // bind mount into /sys/kernel/debug and synchronously reading the in-memory // files in /sys is fast. #if defined(ARCH_CPU_ARM_FAMILY) - FilePath geminfo_file("/var/run/debugfs_gpu/exynos_gem_objects"); + FilePath geminfo_file("/run/debugfs_gpu/exynos_gem_objects"); #else - FilePath geminfo_file("/var/run/debugfs_gpu/i915_gem_objects"); + FilePath geminfo_file("/run/debugfs_gpu/i915_gem_objects"); #endif std::string geminfo_data; meminfo->gem_objects = -1; diff --git a/chromium/base/process/process_metrics_mac.cc b/chromium/base/process/process_metrics_mac.cc index a07d3cd1049..f84b435a109 100644 --- a/chromium/base/process/process_metrics_mac.cc +++ b/chromium/base/process/process_metrics_mac.cc @@ -209,15 +209,32 @@ bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes, } void ProcessMetrics::GetCommittedKBytes(CommittedKBytes* usage) const { + WorkingSetKBytes unused; + if (!GetCommittedAndWorkingSetKBytes(usage, &unused)) { + *usage = CommittedKBytes(); + } } bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const { - size_t priv = GetWorkingSetSize(); - if (!priv) + CommittedKBytes unused; + return GetCommittedAndWorkingSetKBytes(&unused, ws_usage); +} + +bool ProcessMetrics::GetCommittedAndWorkingSetKBytes( + CommittedKBytes* usage, + WorkingSetKBytes* ws_usage) const { + task_basic_info_64 task_info_data; + if (!GetTaskInfo(TaskForPid(process_), &task_info_data)) return false; - ws_usage->priv = priv / 1024; + + usage->priv = task_info_data.virtual_size / 1024; + usage->mapped = 0; + usage->image = 0; + + ws_usage->priv = task_info_data.resident_size / 1024; ws_usage->shareable = 0; ws_usage->shared = 0; + return true; } diff --git a/chromium/base/process/process_metrics_posix.cc b/chromium/base/process/process_metrics_posix.cc index ea79d7348ff..42b3f2d6655 100644 --- a/chromium/base/process/process_metrics_posix.cc +++ b/chromium/base/process/process_metrics_posix.cc @@ -12,9 +12,8 @@ namespace base { int64 TimeValToMicroseconds(const struct timeval& tv) { - static const int kMicrosecondsPerSecond = 1000000; int64 ret = tv.tv_sec; // Avoid (int * int) integer overflow. - ret *= kMicrosecondsPerSecond; + ret *= Time::kMicrosecondsPerSecond; ret += tv.tv_usec; return ret; } @@ -69,4 +68,8 @@ void SetFdLimit(unsigned int max_descriptors) { } } +size_t GetPageSize() { + return getpagesize(); +} + } // namespace base diff --git a/chromium/base/process/process_metrics_unittest.cc b/chromium/base/process/process_metrics_unittest.cc index 69f5e837cb9..76767b09a95 100644 --- a/chromium/base/process/process_metrics_unittest.cc +++ b/chromium/base/process/process_metrics_unittest.cc @@ -143,29 +143,29 @@ TEST_F(SystemMetricsTest, ParseMeminfo) { "Hugepagesize: 4096 kB\n"; EXPECT_TRUE(ParseProcMeminfo(valid_input1, &meminfo)); - EXPECT_TRUE(meminfo.total == 3981504); - EXPECT_TRUE(meminfo.free == 140764); - EXPECT_TRUE(meminfo.buffers == 116480); - EXPECT_TRUE(meminfo.cached == 406160); - EXPECT_TRUE(meminfo.active_anon == 2972352); - EXPECT_TRUE(meminfo.active_file == 179688); - EXPECT_TRUE(meminfo.inactive_anon == 270108); - EXPECT_TRUE(meminfo.inactive_file == 202748); - EXPECT_TRUE(meminfo.swap_total == 5832280); - EXPECT_TRUE(meminfo.swap_free == 3672368); - EXPECT_TRUE(meminfo.dirty == 184); + EXPECT_EQ(meminfo.total, 3981504); + EXPECT_EQ(meminfo.free, 140764); + EXPECT_EQ(meminfo.buffers, 116480); + EXPECT_EQ(meminfo.cached, 406160); + EXPECT_EQ(meminfo.active_anon, 2972352); + EXPECT_EQ(meminfo.active_file, 179688); + EXPECT_EQ(meminfo.inactive_anon, 270108); + EXPECT_EQ(meminfo.inactive_file, 202748); + EXPECT_EQ(meminfo.swap_total, 5832280); + EXPECT_EQ(meminfo.swap_free, 3672368); + EXPECT_EQ(meminfo.dirty, 184); #if defined(OS_CHROMEOS) - EXPECT_TRUE(meminfo.shmem == 140204); - EXPECT_TRUE(meminfo.slab == 54212); + EXPECT_EQ(meminfo.shmem, 140204); + EXPECT_EQ(meminfo.slab, 54212); #endif EXPECT_TRUE(ParseProcMeminfo(valid_input2, &meminfo)); - EXPECT_TRUE(meminfo.total == 255908); - EXPECT_TRUE(meminfo.free == 69936); - EXPECT_TRUE(meminfo.buffers == 15812); - EXPECT_TRUE(meminfo.cached == 115124); - EXPECT_TRUE(meminfo.swap_total == 524280); - EXPECT_TRUE(meminfo.swap_free == 524200); - EXPECT_TRUE(meminfo.dirty == 4); + EXPECT_EQ(meminfo.total, 255908); + EXPECT_EQ(meminfo.free, 69936); + EXPECT_EQ(meminfo.buffers, 15812); + EXPECT_EQ(meminfo.cached, 115124); + EXPECT_EQ(meminfo.swap_total, 524280); + EXPECT_EQ(meminfo.swap_free, 524200); + EXPECT_EQ(meminfo.dirty, 4); } TEST_F(SystemMetricsTest, ParseVmstat) { @@ -260,13 +260,13 @@ TEST_F(SystemMetricsTest, ParseVmstat) { "pgrefill_high 0\n" "pgrefill_movable 0\n"; EXPECT_TRUE(ParseProcVmstat(valid_input1, &meminfo)); - EXPECT_TRUE(meminfo.pswpin == 179); - EXPECT_TRUE(meminfo.pswpout == 406); - EXPECT_TRUE(meminfo.pgmajfault == 487192); + EXPECT_EQ(meminfo.pswpin, 179); + EXPECT_EQ(meminfo.pswpout, 406); + EXPECT_EQ(meminfo.pgmajfault, 487192); EXPECT_TRUE(ParseProcVmstat(valid_input2, &meminfo)); - EXPECT_TRUE(meminfo.pswpin == 12); - EXPECT_TRUE(meminfo.pswpout == 901); - EXPECT_TRUE(meminfo.pgmajfault == 2023); + EXPECT_EQ(meminfo.pswpin, 12); + EXPECT_EQ(meminfo.pswpout, 901); + EXPECT_EQ(meminfo.pgmajfault, 2023); } #endif // defined(OS_LINUX) || defined(OS_ANDROID) @@ -323,6 +323,17 @@ TEST(ProcessMetricsTest, ParseProcStatCPU) { "3221224832 3221224344 3086339742 0 0 0 0 0 0 0 17 0 0 0"; EXPECT_EQ(0, base::ParseProcStatCPU(kSelfStat)); + + // Some weird long-running process with a weird name that I created for the + // purposes of this test. + const char kWeirdNameStat[] = "26115 (Hello) You ())) ) R 24614 26115 24614" + " 34839 26115 4218880 227 0 0 0 " + "5186 11 0 0 " + "20 0 1 0 36933953 4296704 90 18446744073709551615 4194304 4196116 " + "140735857761568 140735857761160 4195644 0 0 0 0 0 0 0 17 14 0 0 0 0 0 " + "6295056 6295616 16519168 140735857770710 140735857770737 " + "140735857770737 140735857774557 0"; + EXPECT_EQ(5186 + 11, base::ParseProcStatCPU(kWeirdNameStat)); } #endif // defined(OS_LINUX) || defined(OS_ANDROID) diff --git a/chromium/base/process/process_metrics_win.cc b/chromium/base/process/process_metrics_win.cc index 16db44f63f6..1dd97e629cf 100644 --- a/chromium/base/process/process_metrics_win.cc +++ b/chromium/base/process/process_metrics_win.cc @@ -284,4 +284,8 @@ size_t GetSystemCommitCharge() { return (info.CommitTotal * system_info.dwPageSize) / 1024; } +size_t GetPageSize() { + return PAGESIZE_KB * 1024; +} + } // namespace base diff --git a/chromium/base/process/process_posix.cc b/chromium/base/process/process_posix.cc index ea8fd8cea1e..8037da3ac6e 100644 --- a/chromium/base/process/process_posix.cc +++ b/chromium/base/process/process_posix.cc @@ -1,20 +1,217 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/process/process.h" #include <sys/resource.h> -#include <sys/time.h> -#include <sys/types.h> +#include <sys/wait.h> +#include "base/files/scoped_file.h" #include "base/logging.h" +#include "base/posix/eintr_wrapper.h" #include "base/process/kill.h" +#include "base/third_party/dynamic_annotations/dynamic_annotations.h" + +#if defined(OS_MACOSX) +#include <sys/event.h> +#endif + +namespace { + +#if !defined(OS_NACL_NONSFI) + +bool WaitpidWithTimeout(base::ProcessHandle handle, + int* status, + base::TimeDelta wait) { + // This POSIX version of this function only guarantees that we wait no less + // than |wait| for the process to exit. The child process may + // exit sometime before the timeout has ended but we may still block for up + // to 256 milliseconds after the fact. + // + // waitpid() has no direct support on POSIX for specifying a timeout, you can + // either ask it to block indefinitely or return immediately (WNOHANG). + // When a child process terminates a SIGCHLD signal is sent to the parent. + // Catching this signal would involve installing a signal handler which may + // affect other parts of the application and would be difficult to debug. + // + // Our strategy is to call waitpid() once up front to check if the process + // has already exited, otherwise to loop for |wait|, sleeping for + // at most 256 milliseconds each time using usleep() and then calling + // waitpid(). The amount of time we sleep starts out at 1 milliseconds, and + // we double it every 4 sleep cycles. + // + // usleep() is speced to exit if a signal is received for which a handler + // has been installed. This means that when a SIGCHLD is sent, it will exit + // depending on behavior external to this function. + // + // This function is used primarily for unit tests, if we want to use it in + // the application itself it would probably be best to examine other routes. + + if (wait == base::TimeDelta::Max()) { + return HANDLE_EINTR(waitpid(handle, status, 0)) > 0; + } + + pid_t ret_pid = HANDLE_EINTR(waitpid(handle, status, WNOHANG)); + static const int64 kMaxSleepInMicroseconds = 1 << 18; // ~256 milliseconds. + int64 max_sleep_time_usecs = 1 << 10; // ~1 milliseconds. + int64 double_sleep_time = 0; + + // If the process hasn't exited yet, then sleep and try again. + base::TimeTicks wakeup_time = base::TimeTicks::Now() + wait; + while (ret_pid == 0) { + base::TimeTicks now = base::TimeTicks::Now(); + if (now > wakeup_time) + break; + // Guaranteed to be non-negative! + int64 sleep_time_usecs = (wakeup_time - now).InMicroseconds(); + // Sleep for a bit while we wait for the process to finish. + if (sleep_time_usecs > max_sleep_time_usecs) + sleep_time_usecs = max_sleep_time_usecs; + + // usleep() will return 0 and set errno to EINTR on receipt of a signal + // such as SIGCHLD. + usleep(sleep_time_usecs); + ret_pid = HANDLE_EINTR(waitpid(handle, status, WNOHANG)); + + if ((max_sleep_time_usecs < kMaxSleepInMicroseconds) && + (double_sleep_time++ % 4 == 0)) { + max_sleep_time_usecs *= 2; + } + } + + return ret_pid > 0; +} + +#if defined(OS_MACOSX) +// Using kqueue on Mac so that we can wait on non-child processes. +// We can't use kqueues on child processes because we need to reap +// our own children using wait. +static bool WaitForSingleNonChildProcess(base::ProcessHandle handle, + base::TimeDelta wait) { + DCHECK_GT(handle, 0); + DCHECK_GT(wait, base::TimeDelta()); + + base::ScopedFD kq(kqueue()); + if (!kq.is_valid()) { + DPLOG(ERROR) << "kqueue"; + return false; + } + + struct kevent change = {0}; + EV_SET(&change, handle, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL); + int result = HANDLE_EINTR(kevent(kq.get(), &change, 1, NULL, 0, NULL)); + if (result == -1) { + if (errno == ESRCH) { + // If the process wasn't found, it must be dead. + return true; + } + + DPLOG(ERROR) << "kevent (setup " << handle << ")"; + return false; + } + + // Keep track of the elapsed time to be able to restart kevent if it's + // interrupted. + bool wait_forever = (wait == base::TimeDelta::Max()); + base::TimeDelta remaining_delta; + base::TimeTicks deadline; + if (!wait_forever) { + remaining_delta = wait; + deadline = base::TimeTicks::Now() + remaining_delta; + } + + result = -1; + struct kevent event = {0}; + + while (wait_forever || remaining_delta > base::TimeDelta()) { + struct timespec remaining_timespec; + struct timespec* remaining_timespec_ptr; + if (wait_forever) { + remaining_timespec_ptr = NULL; + } else { + remaining_timespec = remaining_delta.ToTimeSpec(); + remaining_timespec_ptr = &remaining_timespec; + } + + result = kevent(kq.get(), NULL, 0, &event, 1, remaining_timespec_ptr); + + if (result == -1 && errno == EINTR) { + if (!wait_forever) { + remaining_delta = deadline - base::TimeTicks::Now(); + } + result = 0; + } else { + break; + } + } + + if (result < 0) { + DPLOG(ERROR) << "kevent (wait " << handle << ")"; + return false; + } else if (result > 1) { + DLOG(ERROR) << "kevent (wait " << handle << "): unexpected result " + << result; + return false; + } else if (result == 0) { + // Timed out. + return false; + } + + DCHECK_EQ(result, 1); + + if (event.filter != EVFILT_PROC || + (event.fflags & NOTE_EXIT) == 0 || + event.ident != static_cast<uintptr_t>(handle)) { + DLOG(ERROR) << "kevent (wait " << handle + << "): unexpected event: filter=" << event.filter + << ", fflags=" << event.fflags + << ", ident=" << event.ident; + return false; + } + + return true; +} +#endif // OS_MACOSX + +bool WaitForExitWithTimeoutImpl(base::ProcessHandle handle, + int* exit_code, + base::TimeDelta timeout) { + base::ProcessHandle parent_pid = base::GetParentProcessId(handle); + base::ProcessHandle our_pid = base::GetCurrentProcessHandle(); + if (parent_pid != our_pid) { +#if defined(OS_MACOSX) + // On Mac we can wait on non child processes. + return WaitForSingleNonChildProcess(handle, timeout); +#else + // Currently on Linux we can't handle non child processes. + NOTIMPLEMENTED(); +#endif // OS_MACOSX + } + + int status; + if (!WaitpidWithTimeout(handle, &status, timeout)) + return false; + if (WIFSIGNALED(status)) { + *exit_code = -1; + return true; + } + if (WIFEXITED(status)) { + *exit_code = WEXITSTATUS(status); + return true; + } + return false; +} +#endif // !defined(OS_NACL_NONSFI) + +} // namespace namespace base { Process::Process(ProcessHandle handle) : process_(handle) { - CHECK_NE(handle, GetCurrentProcessHandle()); +} + +Process::~Process() { } Process::Process(RValue other) @@ -32,17 +229,36 @@ Process& Process::operator=(RValue other) { // static Process Process::Current() { - Process process; - process.process_ = GetCurrentProcessHandle(); - return process.Pass(); + return Process(GetCurrentProcessHandle()); +} + +// static +Process Process::Open(ProcessId pid) { + if (pid == GetCurrentProcId()) + return Current(); + + // On POSIX process handles are the same as PIDs. + return Process(pid); +} + +// static +Process Process::OpenWithExtraPrivileges(ProcessId pid) { + // On POSIX there are no privileges to set. + return Open(pid); } -#if !defined(OS_LINUX) +// static +Process Process::DeprecatedGetProcessFromHandle(ProcessHandle handle) { + DCHECK_NE(handle, GetCurrentProcessHandle()); + return Process(handle); +} + +#if !defined(OS_LINUX) && !defined(OS_MACOSX) // static bool Process::CanBackgroundProcesses() { return false; } -#endif // !defined(OS_LINUX) +#endif // !defined(OS_LINUX) && !defined(OS_MACOSX) bool Process::IsValid() const { return process_ != kNullProcessHandle; @@ -59,7 +275,7 @@ Process Process::Duplicate() const { return Process(process_); } -ProcessId Process::pid() const { +ProcessId Process::Pid() const { DCHECK(IsValid()); return GetProcId(process_); } @@ -75,15 +291,69 @@ void Process::Close() { // end up w/ a zombie when it does finally exit. } -void Process::Terminate(int result_code) { +#if !defined(OS_NACL_NONSFI) +bool Process::Terminate(int exit_code, bool wait) const { // result_code isn't supportable. DCHECK(IsValid()); - // We don't wait here. It's the responsibility of other code to reap the - // child. - KillProcess(process_, result_code, false); + DCHECK_GT(process_, 1); + bool result = kill(process_, SIGTERM) == 0; + if (result && wait) { + int tries = 60; + + if (RunningOnValgrind()) { + // Wait for some extra time when running under Valgrind since the child + // processes may take some time doing leak checking. + tries *= 2; + } + + unsigned sleep_ms = 4; + + // The process may not end immediately due to pending I/O + bool exited = false; + while (tries-- > 0) { + pid_t pid = HANDLE_EINTR(waitpid(process_, NULL, WNOHANG)); + if (pid == process_) { + exited = true; + break; + } + if (pid == -1) { + if (errno == ECHILD) { + // The wait may fail with ECHILD if another process also waited for + // the same pid, causing the process state to get cleaned up. + exited = true; + break; + } + DPLOG(ERROR) << "Error waiting for process " << process_; + } + + usleep(sleep_ms * 1000); + const unsigned kMaxSleepMs = 1000; + if (sleep_ms < kMaxSleepMs) + sleep_ms *= 2; + } + + // If we're waiting and the child hasn't died by now, force it + // with a SIGKILL. + if (!exited) + result = kill(process_, SIGKILL) == 0; + } + + if (!result) + DPLOG(ERROR) << "Unable to terminate process " << process_; + + return result; +} +#endif // !defined(OS_NACL_NONSFI) + +bool Process::WaitForExit(int* exit_code) { + return WaitForExitWithTimeout(TimeDelta::Max(), exit_code); +} + +bool Process::WaitForExitWithTimeout(TimeDelta timeout, int* exit_code) { + return WaitForExitWithTimeoutImpl(Handle(), exit_code, timeout); } -#if !defined(OS_LINUX) +#if !defined(OS_LINUX) && !defined(OS_MACOSX) bool Process::IsProcessBackgrounded() const { // See SetProcessBackgrounded(). DCHECK(IsValid()); @@ -91,17 +361,17 @@ bool Process::IsProcessBackgrounded() const { } bool Process::SetProcessBackgrounded(bool value) { - // POSIX only allows lowering the priority of a process, so if we - // were to lower it we wouldn't be able to raise it back to its initial - // priority. - DCHECK(IsValid()); + // Not implemented for POSIX systems other than Mac and Linux. With POSIX, if + // we were to lower the process priority we wouldn't be able to raise it back + // to its initial priority. + NOTIMPLEMENTED(); return false; } -#endif // !defined(OS_LINUX) +#endif // !defined(OS_LINUX) && !defined(OS_MACOSX) int Process::GetPriority() const { DCHECK(IsValid()); return getpriority(PRIO_PROCESS, process_); } -} // namspace base +} // namespace base diff --git a/chromium/base/process/process_unittest.cc b/chromium/base/process/process_unittest.cc index 66d6e634661..e094c032f3b 100644 --- a/chromium/base/process/process_unittest.cc +++ b/chromium/base/process/process_unittest.cc @@ -11,6 +11,9 @@ #include "testing/gtest/include/gtest/gtest.h" #include "testing/multiprocess_func_list.h" +#if defined(OS_MACOSX) +#include <mach/mach.h> +#endif // OS_MACOSX namespace { @@ -69,7 +72,7 @@ TEST_F(ProcessTest, Duplicate) { Process process2 = process1.Duplicate(); ASSERT_TRUE(process1.IsValid()); ASSERT_TRUE(process2.IsValid()); - EXPECT_EQ(process1.pid(), process2.pid()); + EXPECT_EQ(process1.Pid(), process2.Pid()); EXPECT_FALSE(process1.is_current()); EXPECT_FALSE(process2.is_current()); @@ -84,7 +87,7 @@ TEST_F(ProcessTest, DuplicateCurrent) { Process process2 = process1.Duplicate(); ASSERT_TRUE(process1.IsValid()); ASSERT_TRUE(process2.IsValid()); - EXPECT_EQ(process1.pid(), process2.pid()); + EXPECT_EQ(process1.Pid(), process2.Pid()); EXPECT_TRUE(process1.is_current()); EXPECT_TRUE(process2.is_current()); @@ -92,6 +95,21 @@ TEST_F(ProcessTest, DuplicateCurrent) { ASSERT_TRUE(process2.IsValid()); } +TEST_F(ProcessTest, DeprecatedGetProcessFromHandle) { + Process process1(SpawnChild("SimpleChildProcess")); + ASSERT_TRUE(process1.IsValid()); + + Process process2 = Process::DeprecatedGetProcessFromHandle(process1.Handle()); + ASSERT_TRUE(process1.IsValid()); + ASSERT_TRUE(process2.IsValid()); + EXPECT_EQ(process1.Pid(), process2.Pid()); + EXPECT_FALSE(process1.is_current()); + EXPECT_FALSE(process2.is_current()); + + process1.Close(); + ASSERT_TRUE(process2.IsValid()); +} + MULTIPROCESS_TEST_MAIN(SleepyChildProcess) { PlatformThread::Sleep(TestTimeouts::action_max_timeout()); return 0; @@ -109,8 +127,9 @@ TEST_F(ProcessTest, Terminate) { exit_code = kDummyExitCode; int kExpectedExitCode = 250; - process.Terminate(kExpectedExitCode); - WaitForSingleProcess(process.Handle(), TestTimeouts::action_max_timeout()); + process.Terminate(kExpectedExitCode, false); + process.WaitForExitWithTimeout(TestTimeouts::action_max_timeout(), + &exit_code); EXPECT_NE(TERMINATION_STATUS_STILL_RUNNING, GetTerminationStatus(process.Handle(), &exit_code)); @@ -120,6 +139,34 @@ TEST_F(ProcessTest, Terminate) { #endif } +MULTIPROCESS_TEST_MAIN(FastSleepyChildProcess) { + PlatformThread::Sleep(TestTimeouts::tiny_timeout() * 10); + return 0; +} + +TEST_F(ProcessTest, WaitForExit) { + Process process(SpawnChild("FastSleepyChildProcess")); + ASSERT_TRUE(process.IsValid()); + + const int kDummyExitCode = 42; + int exit_code = kDummyExitCode; + EXPECT_TRUE(process.WaitForExit(&exit_code)); + EXPECT_EQ(0, exit_code); +} + +TEST_F(ProcessTest, WaitForExitWithTimeout) { + Process process(SpawnChild("SleepyChildProcess")); + ASSERT_TRUE(process.IsValid()); + + const int kDummyExitCode = 42; + int exit_code = kDummyExitCode; + TimeDelta timeout = TestTimeouts::tiny_timeout(); + EXPECT_FALSE(process.WaitForExitWithTimeout(timeout, &exit_code)); + EXPECT_EQ(kDummyExitCode, exit_code); + + process.Terminate(kDummyExitCode, false); +} + // Ensure that the priority of a process is restored correctly after // backgrounding and restoring. // Note: a platform may not be willing or able to lower the priority of @@ -127,7 +174,19 @@ TEST_F(ProcessTest, Terminate) { TEST_F(ProcessTest, SetProcessBackgrounded) { Process process(SpawnChild("SimpleChildProcess")); int old_priority = process.GetPriority(); -#if defined(OS_WIN) +#if defined(OS_MACOSX) + // On the Mac, backgrounding a process requires a port to that process. + // In the browser it's available through the MachBroker class, which is not + // part of base. Additionally, there is an indefinite amount of time between + // spawning a process and receiving its port. Because this test just checks + // the ability to background/foreground a process, we can use the current + // process's port instead. + mach_port_t process_port = mach_task_self(); + EXPECT_TRUE(process.SetProcessBackgrounded(process_port, true)); + EXPECT_TRUE(process.IsProcessBackgrounded(process_port)); + EXPECT_TRUE(process.SetProcessBackgrounded(process_port, false)); + EXPECT_FALSE(process.IsProcessBackgrounded(process_port)); +#elif defined(OS_WIN) EXPECT_TRUE(process.SetProcessBackgrounded(true)); EXPECT_TRUE(process.IsProcessBackgrounded()); EXPECT_TRUE(process.SetProcessBackgrounded(false)); @@ -145,7 +204,13 @@ TEST_F(ProcessTest, SetProcessBackgrounded) { TEST_F(ProcessTest, SetProcessBackgroundedSelf) { Process process = Process::Current(); int old_priority = process.GetPriority(); -#if defined(OS_WIN) +#if defined(OS_MACOSX) + mach_port_t process_port = mach_task_self(); + EXPECT_TRUE(process.SetProcessBackgrounded(process_port, true)); + EXPECT_TRUE(process.IsProcessBackgrounded(process_port)); + EXPECT_TRUE(process.SetProcessBackgrounded(process_port, false)); + EXPECT_FALSE(process.IsProcessBackgrounded(process_port)); +#elif defined(OS_WIN) EXPECT_TRUE(process.SetProcessBackgrounded(true)); EXPECT_TRUE(process.IsProcessBackgrounded()); EXPECT_TRUE(process.SetProcessBackgrounded(false)); diff --git a/chromium/base/process/process_util_unittest.cc b/chromium/base/process/process_util_unittest.cc index d846d1acfae..11d8874a52a 100644 --- a/chromium/base/process/process_util_unittest.cc +++ b/chromium/base/process/process_util_unittest.cc @@ -10,6 +10,8 @@ #include "base/debug/alias.h" #include "base/debug/stack_trace.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/files/scoped_file.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/path_service.h" @@ -27,21 +29,26 @@ #include "base/third_party/dynamic_annotations/dynamic_annotations.h" #include "base/threading/platform_thread.h" #include "base/threading/thread.h" +#include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/multiprocess_func_list.h" #if defined(OS_LINUX) #include <malloc.h> #include <sched.h> +#include <sys/syscall.h> #endif #if defined(OS_POSIX) #include <dlfcn.h> #include <errno.h> #include <fcntl.h> +#include <sched.h> #include <signal.h> #include <sys/resource.h> #include <sys/socket.h> +#include <sys/types.h> #include <sys/wait.h> +#include <unistd.h> #endif #if defined(OS_WIN) #include <windows.h> @@ -142,11 +149,11 @@ MULTIPROCESS_TEST_MAIN(SimpleChildProcess) { // TODO(viettrungluu): This should be in a "MultiProcessTestTest". TEST_F(ProcessUtilTest, SpawnChild) { - base::ProcessHandle handle = SpawnChild("SimpleChildProcess"); - ASSERT_NE(base::kNullProcessHandle, handle); - EXPECT_TRUE(base::WaitForSingleProcess( - handle, TestTimeouts::action_max_timeout())); - base::CloseProcessHandle(handle); + base::Process process = SpawnChild("SimpleChildProcess"); + ASSERT_TRUE(process.IsValid()); + int exit_code; + EXPECT_TRUE(process.WaitForExitWithTimeout( + TestTimeouts::action_max_timeout(), &exit_code)); } MULTIPROCESS_TEST_MAIN(SlowChildProcess) { @@ -158,12 +165,12 @@ TEST_F(ProcessUtilTest, KillSlowChild) { const std::string signal_file = ProcessUtilTest::GetSignalFilePath(kSignalFileSlow); remove(signal_file.c_str()); - base::ProcessHandle handle = SpawnChild("SlowChildProcess"); - ASSERT_NE(base::kNullProcessHandle, handle); + base::Process process = SpawnChild("SlowChildProcess"); + ASSERT_TRUE(process.IsValid()); SignalChildren(signal_file.c_str()); - EXPECT_TRUE(base::WaitForSingleProcess( - handle, TestTimeouts::action_max_timeout())); - base::CloseProcessHandle(handle); + int exit_code; + EXPECT_TRUE(process.WaitForExitWithTimeout( + TestTimeouts::action_max_timeout(), &exit_code)); remove(signal_file.c_str()); } @@ -172,21 +179,20 @@ TEST_F(ProcessUtilTest, DISABLED_GetTerminationStatusExit) { const std::string signal_file = ProcessUtilTest::GetSignalFilePath(kSignalFileSlow); remove(signal_file.c_str()); - base::ProcessHandle handle = SpawnChild("SlowChildProcess"); - ASSERT_NE(base::kNullProcessHandle, handle); + base::Process process = SpawnChild("SlowChildProcess"); + ASSERT_TRUE(process.IsValid()); int exit_code = 42; EXPECT_EQ(base::TERMINATION_STATUS_STILL_RUNNING, - base::GetTerminationStatus(handle, &exit_code)); + base::GetTerminationStatus(process.Handle(), &exit_code)); EXPECT_EQ(kExpectedStillRunningExitCode, exit_code); SignalChildren(signal_file.c_str()); exit_code = 42; base::TerminationStatus status = - WaitForChildTermination(handle, &exit_code); + WaitForChildTermination(process.Handle(), &exit_code); EXPECT_EQ(base::TERMINATION_STATUS_NORMAL_TERMINATION, status); EXPECT_EQ(0, exit_code); - base::CloseProcessHandle(handle); remove(signal_file.c_str()); } @@ -195,12 +201,11 @@ TEST_F(ProcessUtilTest, DISABLED_GetTerminationStatusExit) { TEST_F(ProcessUtilTest, GetProcId) { base::ProcessId id1 = base::GetProcId(GetCurrentProcess()); EXPECT_NE(0ul, id1); - base::ProcessHandle handle = SpawnChild("SimpleChildProcess"); - ASSERT_NE(base::kNullProcessHandle, handle); - base::ProcessId id2 = base::GetProcId(handle); + base::Process process = SpawnChild("SimpleChildProcess"); + ASSERT_TRUE(process.IsValid()); + base::ProcessId id2 = process.Pid(); EXPECT_NE(0ul, id2); EXPECT_NE(id1, id2); - base::CloseProcessHandle(handle); } #endif @@ -239,18 +244,18 @@ TEST_F(ProcessUtilTest, MAYBE_GetTerminationStatusCrash) { const std::string signal_file = ProcessUtilTest::GetSignalFilePath(kSignalFileCrash); remove(signal_file.c_str()); - base::ProcessHandle handle = SpawnChild("CrashingChildProcess"); - ASSERT_NE(base::kNullProcessHandle, handle); + base::Process process = SpawnChild("CrashingChildProcess"); + ASSERT_TRUE(process.IsValid()); int exit_code = 42; EXPECT_EQ(base::TERMINATION_STATUS_STILL_RUNNING, - base::GetTerminationStatus(handle, &exit_code)); + base::GetTerminationStatus(process.Handle(), &exit_code)); EXPECT_EQ(kExpectedStillRunningExitCode, exit_code); SignalChildren(signal_file.c_str()); exit_code = 42; base::TerminationStatus status = - WaitForChildTermination(handle, &exit_code); + WaitForChildTermination(process.Handle(), &exit_code); EXPECT_EQ(base::TERMINATION_STATUS_PROCESS_CRASHED, status); #if defined(OS_WIN) @@ -261,7 +266,6 @@ TEST_F(ProcessUtilTest, MAYBE_GetTerminationStatusCrash) { int signal = WTERMSIG(exit_code); EXPECT_EQ(SIGSEGV, signal); #endif - base::CloseProcessHandle(handle); // Reset signal handlers back to "normal". base::debug::EnableInProcessStackDumping(); @@ -286,18 +290,18 @@ TEST_F(ProcessUtilTest, GetTerminationStatusKill) { const std::string signal_file = ProcessUtilTest::GetSignalFilePath(kSignalFileKill); remove(signal_file.c_str()); - base::ProcessHandle handle = SpawnChild("KilledChildProcess"); - ASSERT_NE(base::kNullProcessHandle, handle); + base::Process process = SpawnChild("KilledChildProcess"); + ASSERT_TRUE(process.IsValid()); int exit_code = 42; EXPECT_EQ(base::TERMINATION_STATUS_STILL_RUNNING, - base::GetTerminationStatus(handle, &exit_code)); + base::GetTerminationStatus(process.Handle(), &exit_code)); EXPECT_EQ(kExpectedStillRunningExitCode, exit_code); SignalChildren(signal_file.c_str()); exit_code = 42; base::TerminationStatus status = - WaitForChildTermination(handle, &exit_code); + WaitForChildTermination(process.Handle(), &exit_code); EXPECT_EQ(base::TERMINATION_STATUS_PROCESS_WAS_KILLED, status); #if defined(OS_WIN) EXPECT_EQ(kExpectedKilledExitCode, exit_code); @@ -307,7 +311,6 @@ TEST_F(ProcessUtilTest, GetTerminationStatusKill) { int signal = WTERMSIG(exit_code); EXPECT_EQ(SIGKILL, signal); #endif - base::CloseProcessHandle(handle); remove(signal_file.c_str()); } @@ -325,7 +328,7 @@ TEST_F(ProcessUtilTest, GetAppOutput) { expected += "\r\n"; FilePath cmd(L"cmd.exe"); - CommandLine cmd_line(cmd); + base::CommandLine cmd_line(cmd); cmd_line.AppendArg("/c"); cmd_line.AppendArg("echo " + message + ""); std::string output; @@ -333,7 +336,7 @@ TEST_F(ProcessUtilTest, GetAppOutput) { EXPECT_EQ(expected, output); // Let's make sure stderr is ignored. - CommandLine other_cmd_line(cmd); + base::CommandLine other_cmd_line(cmd); other_cmd_line.AppendArg("/c"); // http://msdn.microsoft.com/library/cc772622.aspx cmd_line.AppendArg("echo " + message + " >&2"); @@ -348,22 +351,23 @@ TEST_F(ProcessUtilTest, LaunchAsUser) { ASSERT_TRUE(OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token)); base::LaunchOptions options; options.as_user = token; - EXPECT_TRUE(base::LaunchProcess(MakeCmdLine("SimpleChildProcess"), options, - NULL)); + EXPECT_TRUE(base::LaunchProcess(MakeCmdLine("SimpleChildProcess"), + options).IsValid()); } static const char kEventToTriggerHandleSwitch[] = "event-to-trigger-handle"; MULTIPROCESS_TEST_MAIN(TriggerEventChildProcess) { std::string handle_value_string = - CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( kEventToTriggerHandleSwitch); CHECK(!handle_value_string.empty()); uint64 handle_value_uint64; CHECK(base::StringToUint64(handle_value_string, &handle_value_uint64)); // Give ownership of the handle to |event|. - base::WaitableEvent event(reinterpret_cast<HANDLE>(handle_value_uint64)); + base::WaitableEvent event(base::win::ScopedHandle( + reinterpret_cast<HANDLE>(handle_value_uint64))); event.Signal(); @@ -378,26 +382,26 @@ TEST_F(ProcessUtilTest, InheritSpecifiedHandles) { security_attributes.bInheritHandle = true; // Takes ownership of the event handle. - base::WaitableEvent event( - CreateEvent(&security_attributes, true, false, NULL)); + base::WaitableEvent event(base::win::ScopedHandle( + CreateEvent(&security_attributes, true, false, NULL))); base::HandlesToInheritVector handles_to_inherit; handles_to_inherit.push_back(event.handle()); base::LaunchOptions options; options.handles_to_inherit = &handles_to_inherit; - CommandLine cmd_line = MakeCmdLine("TriggerEventChildProcess"); + base::CommandLine cmd_line = MakeCmdLine("TriggerEventChildProcess"); cmd_line.AppendSwitchASCII(kEventToTriggerHandleSwitch, base::Uint64ToString(reinterpret_cast<uint64>(event.handle()))); // This functionality actually requires Vista or later. Make sure that it // fails properly on XP. if (base::win::GetVersion() < base::win::VERSION_VISTA) { - EXPECT_FALSE(base::LaunchProcess(cmd_line, options, NULL)); + EXPECT_FALSE(base::LaunchProcess(cmd_line, options).IsValid()); return; } // Launch the process and wait for it to trigger the event. - ASSERT_TRUE(base::LaunchProcess(cmd_line, options, NULL)); + ASSERT_TRUE(base::LaunchProcess(cmd_line, options).IsValid()); EXPECT_TRUE(event.TimedWait(TestTimeouts::action_max_timeout())); } #endif // defined(OS_WIN) @@ -534,9 +538,9 @@ int ProcessUtilTest::CountOpenFDsInChild() { fd_mapping_vec.push_back(std::pair<int, int>(fds[1], kChildPipe)); base::LaunchOptions options; options.fds_to_remap = &fd_mapping_vec; - base::ProcessHandle handle = + base::Process process = SpawnChildWithOptions("ProcessUtilsLeakFDChildProcess", options); - CHECK(handle); + CHECK(process.IsValid()); int ret = IGNORE_EINTR(close(fds[1])); DPCHECK(ret == 0); @@ -548,11 +552,12 @@ int ProcessUtilTest::CountOpenFDsInChild() { #if defined(THREAD_SANITIZER) // Compiler-based ThreadSanitizer makes this test slow. - CHECK(base::WaitForSingleProcess(handle, base::TimeDelta::FromSeconds(3))); + base::TimeDelta timeout = base::TimeDelta::FromSeconds(3); #else - CHECK(base::WaitForSingleProcess(handle, base::TimeDelta::FromSeconds(1))); + base::TimeDelta timeout = base::TimeDelta::FromSeconds(1); #endif - base::CloseProcessHandle(handle); + int exit_code; + CHECK(process.WaitForExitWithTimeout(timeout, &exit_code)); ret = IGNORE_EINTR(close(fds[0])); DPCHECK(ret == 0); @@ -611,7 +616,7 @@ std::string TestLaunchProcess(const std::vector<std::string>& args, #else CHECK_EQ(0, clone_flags); #endif // OS_LINUX - EXPECT_TRUE(base::LaunchProcess(args, options, NULL)); + EXPECT_TRUE(base::LaunchProcess(args, options).IsValid()); PCHECK(IGNORE_EINTR(close(fds[1])) == 0); char buf[512]; @@ -683,10 +688,8 @@ TEST_F(ProcessUtilTest, LaunchProcess) { // Test a non-trival value for clone_flags. // Don't test on Valgrind as it has limited support for clone(). if (!RunningOnValgrind()) { - EXPECT_EQ( - "wibble\n", - TestLaunchProcess( - echo_base_test, env_changes, no_clear_environ, CLONE_FS | SIGCHLD)); + EXPECT_EQ("wibble\n", TestLaunchProcess(echo_base_test, env_changes, + no_clear_environ, CLONE_FS)); } EXPECT_EQ( @@ -710,27 +713,29 @@ TEST_F(ProcessUtilTest, GetAppOutput) { argv.push_back("-c"); argv.push_back("exit 0"); - EXPECT_TRUE(base::GetAppOutput(CommandLine(argv), &output)); + EXPECT_TRUE(base::GetAppOutput(base::CommandLine(argv), &output)); EXPECT_STREQ("", output.c_str()); argv[2] = "exit 1"; - EXPECT_FALSE(base::GetAppOutput(CommandLine(argv), &output)); + EXPECT_FALSE(base::GetAppOutput(base::CommandLine(argv), &output)); EXPECT_STREQ("", output.c_str()); argv[2] = "echo foobar42"; - EXPECT_TRUE(base::GetAppOutput(CommandLine(argv), &output)); + EXPECT_TRUE(base::GetAppOutput(base::CommandLine(argv), &output)); EXPECT_STREQ("foobar42\n", output.c_str()); #else - EXPECT_TRUE(base::GetAppOutput(CommandLine(FilePath("true")), &output)); + EXPECT_TRUE(base::GetAppOutput(base::CommandLine(FilePath("true")), + &output)); EXPECT_STREQ("", output.c_str()); - EXPECT_FALSE(base::GetAppOutput(CommandLine(FilePath("false")), &output)); + EXPECT_FALSE(base::GetAppOutput(base::CommandLine(FilePath("false")), + &output)); std::vector<std::string> argv; argv.push_back("/bin/echo"); argv.push_back("-n"); argv.push_back("foobar42"); - EXPECT_TRUE(base::GetAppOutput(CommandLine(argv), &output)); + EXPECT_TRUE(base::GetAppOutput(base::CommandLine(argv), &output)); EXPECT_STREQ("foobar42", output.c_str()); #endif // defined(OS_ANDROID) } @@ -754,34 +759,39 @@ TEST_F(ProcessUtilTest, MAYBE_GetAppOutputRestricted) { // need absolute paths). argv.push_back("exit 0"); // argv[2]; equivalent to "true" std::string output = "abc"; - EXPECT_TRUE(base::GetAppOutputRestricted(CommandLine(argv), &output, 100)); + EXPECT_TRUE(base::GetAppOutputRestricted(base::CommandLine(argv), &output, + 100)); EXPECT_STREQ("", output.c_str()); argv[2] = "exit 1"; // equivalent to "false" output = "before"; - EXPECT_FALSE(base::GetAppOutputRestricted(CommandLine(argv), - &output, 100)); + EXPECT_FALSE(base::GetAppOutputRestricted(base::CommandLine(argv), &output, + 100)); EXPECT_STREQ("", output.c_str()); // Amount of output exactly equal to space allowed. argv[2] = "echo 123456789"; // (the sh built-in doesn't take "-n") output.clear(); - EXPECT_TRUE(base::GetAppOutputRestricted(CommandLine(argv), &output, 10)); + EXPECT_TRUE(base::GetAppOutputRestricted(base::CommandLine(argv), &output, + 10)); EXPECT_STREQ("123456789\n", output.c_str()); // Amount of output greater than space allowed. output.clear(); - EXPECT_TRUE(base::GetAppOutputRestricted(CommandLine(argv), &output, 5)); + EXPECT_TRUE(base::GetAppOutputRestricted(base::CommandLine(argv), &output, + 5)); EXPECT_STREQ("12345", output.c_str()); // Amount of output less than space allowed. output.clear(); - EXPECT_TRUE(base::GetAppOutputRestricted(CommandLine(argv), &output, 15)); + EXPECT_TRUE(base::GetAppOutputRestricted(base::CommandLine(argv), &output, + 15)); EXPECT_STREQ("123456789\n", output.c_str()); // Zero space allowed. output = "abc"; - EXPECT_TRUE(base::GetAppOutputRestricted(CommandLine(argv), &output, 0)); + EXPECT_TRUE(base::GetAppOutputRestricted(base::CommandLine(argv), &output, + 0)); EXPECT_STREQ("", output.c_str()); } @@ -796,11 +806,13 @@ TEST_F(ProcessUtilTest, GetAppOutputRestrictedSIGPIPE) { argv.push_back("-c"); #if defined(OS_ANDROID) argv.push_back("while echo 12345678901234567890; do :; done"); - EXPECT_TRUE(base::GetAppOutputRestricted(CommandLine(argv), &output, 10)); + EXPECT_TRUE(base::GetAppOutputRestricted(base::CommandLine(argv), &output, + 10)); EXPECT_STREQ("1234567890", output.c_str()); #else argv.push_back("yes"); - EXPECT_TRUE(base::GetAppOutputRestricted(CommandLine(argv), &output, 10)); + EXPECT_TRUE(base::GetAppOutputRestricted(base::CommandLine(argv), &output, + 10)); EXPECT_STREQ("y\ny\ny\ny\ny\n", output.c_str()); #endif } @@ -826,14 +838,16 @@ TEST_F(ProcessUtilTest, MAYBE_GetAppOutputRestrictedNoZombies) { // 10.5) times with an output buffer big enough to capture all output. for (int i = 0; i < 300; i++) { std::string output; - EXPECT_TRUE(base::GetAppOutputRestricted(CommandLine(argv), &output, 100)); + EXPECT_TRUE(base::GetAppOutputRestricted(base::CommandLine(argv), &output, + 100)); EXPECT_STREQ("123456789012345678901234567890\n", output.c_str()); } // Ditto, but with an output buffer too small to capture all output. for (int i = 0; i < 300; i++) { std::string output; - EXPECT_TRUE(base::GetAppOutputRestricted(CommandLine(argv), &output, 10)); + EXPECT_TRUE(base::GetAppOutputRestricted(base::CommandLine(argv), &output, + 10)); EXPECT_STREQ("1234567890", output.c_str()); } } @@ -846,7 +860,7 @@ TEST_F(ProcessUtilTest, GetAppOutputWithExitCode) { argv.push_back(std::string(kShellPath)); // argv[0] argv.push_back("-c"); // argv[1] argv.push_back("echo foo"); // argv[2]; - EXPECT_TRUE(base::GetAppOutputWithExitCode(CommandLine(argv), &output, + EXPECT_TRUE(base::GetAppOutputWithExitCode(base::CommandLine(argv), &output, &exit_code)); EXPECT_STREQ("foo\n", output.c_str()); EXPECT_EQ(exit_code, 0); @@ -855,7 +869,7 @@ TEST_F(ProcessUtilTest, GetAppOutputWithExitCode) { // code. output.clear(); argv[2] = "echo foo; exit 2"; - EXPECT_TRUE(base::GetAppOutputWithExitCode(CommandLine(argv), &output, + EXPECT_TRUE(base::GetAppOutputWithExitCode(base::CommandLine(argv), &output, &exit_code)); EXPECT_STREQ("foo\n", output.c_str()); EXPECT_EQ(exit_code, 2); @@ -876,14 +890,15 @@ bool IsProcessDead(base::ProcessHandle child) { } TEST_F(ProcessUtilTest, DelayedTermination) { - base::ProcessHandle child_process = SpawnChild("process_util_test_never_die"); - ASSERT_TRUE(child_process); - base::EnsureProcessTerminated(child_process); - base::WaitForSingleProcess(child_process, base::TimeDelta::FromSeconds(5)); + base::Process child_process = SpawnChild("process_util_test_never_die"); + ASSERT_TRUE(child_process.IsValid()); + base::EnsureProcessTerminated(child_process.Duplicate()); + int exit_code; + child_process.WaitForExitWithTimeout(base::TimeDelta::FromSeconds(5), + &exit_code); // Check that process was really killed. - EXPECT_TRUE(IsProcessDead(child_process)); - base::CloseProcessHandle(child_process); + EXPECT_TRUE(IsProcessDead(child_process.Handle())); } MULTIPROCESS_TEST_MAIN(process_util_test_never_die) { @@ -894,20 +909,159 @@ MULTIPROCESS_TEST_MAIN(process_util_test_never_die) { } TEST_F(ProcessUtilTest, ImmediateTermination) { - base::ProcessHandle child_process = - SpawnChild("process_util_test_die_immediately"); - ASSERT_TRUE(child_process); + base::Process child_process = SpawnChild("process_util_test_die_immediately"); + ASSERT_TRUE(child_process.IsValid()); // Give it time to die. sleep(2); - base::EnsureProcessTerminated(child_process); + base::EnsureProcessTerminated(child_process.Duplicate()); // Check that process was really killed. - EXPECT_TRUE(IsProcessDead(child_process)); - base::CloseProcessHandle(child_process); + EXPECT_TRUE(IsProcessDead(child_process.Handle())); } MULTIPROCESS_TEST_MAIN(process_util_test_die_immediately) { return 0; } +#if !defined(OS_ANDROID) +const char kPipeValue = '\xcc'; + +class ReadFromPipeDelegate : public base::LaunchOptions::PreExecDelegate { + public: + explicit ReadFromPipeDelegate(int fd) : fd_(fd) {} + ~ReadFromPipeDelegate() override {} + void RunAsyncSafe() override { + char c; + RAW_CHECK(HANDLE_EINTR(read(fd_, &c, 1)) == 1); + RAW_CHECK(IGNORE_EINTR(close(fd_)) == 0); + RAW_CHECK(c == kPipeValue); + } + + private: + int fd_; + DISALLOW_COPY_AND_ASSIGN(ReadFromPipeDelegate); +}; + +TEST_F(ProcessUtilTest, PreExecHook) { + int pipe_fds[2]; + ASSERT_EQ(0, pipe(pipe_fds)); + + base::ScopedFD read_fd(pipe_fds[0]); + base::ScopedFD write_fd(pipe_fds[1]); + base::FileHandleMappingVector fds_to_remap; + fds_to_remap.push_back(std::make_pair(read_fd.get(), read_fd.get())); + + ReadFromPipeDelegate read_from_pipe_delegate(read_fd.get()); + base::LaunchOptions options; + options.fds_to_remap = &fds_to_remap; + options.pre_exec_delegate = &read_from_pipe_delegate; + base::Process process(SpawnChildWithOptions("SimpleChildProcess", options)); + ASSERT_TRUE(process.IsValid()); + + read_fd.reset(); + ASSERT_EQ(1, HANDLE_EINTR(write(write_fd.get(), &kPipeValue, 1))); + + int exit_code = 42; + EXPECT_TRUE(process.WaitForExit(&exit_code)); + EXPECT_EQ(0, exit_code); +} +#endif // !defined(OS_ANDROID) + #endif // defined(OS_POSIX) + +#if defined(OS_LINUX) +const int kSuccess = 0; + +MULTIPROCESS_TEST_MAIN(CheckPidProcess) { + const pid_t kInitPid = 1; + const pid_t pid = syscall(__NR_getpid); + CHECK(pid == kInitPid); + CHECK(getpid() == pid); + return kSuccess; +} + +TEST_F(ProcessUtilTest, CloneFlags) { + if (RunningOnValgrind() || + !base::PathExists(FilePath("/proc/self/ns/user")) || + !base::PathExists(FilePath("/proc/self/ns/pid"))) { + // User or PID namespaces are not supported. + return; + } + + base::LaunchOptions options; + options.clone_flags = CLONE_NEWUSER | CLONE_NEWPID; + + base::Process process(SpawnChildWithOptions("CheckPidProcess", options)); + ASSERT_TRUE(process.IsValid()); + + int exit_code = 42; + EXPECT_TRUE(process.WaitForExit(&exit_code)); + EXPECT_EQ(kSuccess, exit_code); +} + +TEST(ForkWithFlagsTest, UpdatesPidCache) { + // The libc clone function, which allows ForkWithFlags to keep the pid cache + // up to date, does not work on Valgrind. + if (RunningOnValgrind()) { + return; + } + + // Warm up the libc pid cache, if there is one. + ASSERT_EQ(syscall(__NR_getpid), getpid()); + + pid_t ctid = 0; + const pid_t pid = + base::ForkWithFlags(SIGCHLD | CLONE_CHILD_SETTID, nullptr, &ctid); + if (pid == 0) { + // In child. Check both the raw getpid syscall and the libc getpid wrapper + // (which may rely on a pid cache). + RAW_CHECK(syscall(__NR_getpid) == ctid); + RAW_CHECK(getpid() == ctid); + _exit(kSuccess); + } + + ASSERT_NE(-1, pid); + int status = 42; + ASSERT_EQ(pid, HANDLE_EINTR(waitpid(pid, &status, 0))); + ASSERT_TRUE(WIFEXITED(status)); + EXPECT_EQ(kSuccess, WEXITSTATUS(status)); +} + +MULTIPROCESS_TEST_MAIN(CheckCwdProcess) { + base::FilePath expected; + CHECK(base::GetTempDir(&expected)); + base::FilePath actual; + CHECK(base::GetCurrentDirectory(&actual)); + CHECK(actual == expected); + return kSuccess; +} + +TEST_F(ProcessUtilTest, CurrentDirectory) { + // TODO(rickyz): Add support for passing arguments to multiprocess children, + // then create a special directory for this test. + base::FilePath tmp_dir; + ASSERT_TRUE(base::GetTempDir(&tmp_dir)); + + base::LaunchOptions options; + options.current_directory = tmp_dir; + + base::Process process(SpawnChildWithOptions("CheckCwdProcess", options)); + ASSERT_TRUE(process.IsValid()); + + int exit_code = 42; + EXPECT_TRUE(process.WaitForExit(&exit_code)); + EXPECT_EQ(kSuccess, exit_code); +} + +TEST_F(ProcessUtilTest, InvalidCurrentDirectory) { + base::LaunchOptions options; + options.current_directory = base::FilePath("/dev/null"); + + base::Process process(SpawnChildWithOptions("SimpleChildProcess", options)); + ASSERT_TRUE(process.IsValid()); + + int exit_code = kSuccess; + EXPECT_TRUE(process.WaitForExit(&exit_code)); + EXPECT_NE(kSuccess, exit_code); +} +#endif diff --git a/chromium/base/process/process_win.cc b/chromium/base/process/process_win.cc index 05041b20dda..0d312a35990 100644 --- a/chromium/base/process/process_win.cc +++ b/chromium/base/process/process_win.cc @@ -6,8 +6,18 @@ #include "base/logging.h" #include "base/memory/scoped_ptr.h" +#include "base/metrics/field_trial.h" +#include "base/numerics/safe_conversions.h" +#include "base/process/kill.h" #include "base/win/windows_version.h" +namespace { + +DWORD kBasicProcessAccess = + PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | SYNCHRONIZE; + +} // namespace + namespace base { Process::Process(ProcessHandle handle) @@ -22,6 +32,9 @@ Process::Process(RValue other) other.object->Close(); } +Process::~Process() { +} + Process& Process::operator=(RValue other) { if (this != other.object) { process_.Set(other.object->process_.Take()); @@ -39,6 +52,34 @@ Process Process::Current() { } // static +Process Process::Open(ProcessId pid) { + return Process(::OpenProcess(kBasicProcessAccess, FALSE, pid)); +} + +// static +Process Process::OpenWithExtraPrivileges(ProcessId pid) { + DWORD access = kBasicProcessAccess | PROCESS_DUP_HANDLE | PROCESS_VM_READ; + return Process(::OpenProcess(access, FALSE, pid)); +} + +// static +Process Process::OpenWithAccess(ProcessId pid, DWORD desired_access) { + return Process(::OpenProcess(desired_access, FALSE, pid)); +} + +// static +Process Process::DeprecatedGetProcessFromHandle(ProcessHandle handle) { + DCHECK_NE(handle, ::GetCurrentProcess()); + ProcessHandle out_handle; + if (!::DuplicateHandle(GetCurrentProcess(), handle, + GetCurrentProcess(), &out_handle, + 0, FALSE, DUPLICATE_SAME_ACCESS)) { + return Process(); + } + return Process(out_handle); +} + +// static bool Process::CanBackgroundProcesses() { return true; } @@ -68,7 +109,7 @@ Process Process::Duplicate() const { return Process(out_handle); } -ProcessId Process::pid() const { +ProcessId Process::Pid() const { DCHECK(IsValid()); return GetProcId(Handle()); } @@ -85,17 +126,36 @@ void Process::Close() { process_.Close(); } -void Process::Terminate(int result_code) { +bool Process::Terminate(int exit_code, bool wait) const { DCHECK(IsValid()); + bool result = (::TerminateProcess(Handle(), exit_code) != FALSE); + if (result && wait) { + // The process may not end immediately due to pending I/O + if (::WaitForSingleObject(Handle(), 60 * 1000) != WAIT_OBJECT_0) + DPLOG(ERROR) << "Error waiting for process exit"; + } else if (!result) { + DPLOG(ERROR) << "Unable to terminate process"; + } + return result; +} + +bool Process::WaitForExit(int* exit_code) { + return WaitForExitWithTimeout(TimeDelta::FromMilliseconds(INFINITE), + exit_code); +} - // Call NtTerminateProcess directly, without going through the import table, - // which might have been hooked with a buggy replacement by third party - // software. http://crbug.com/81449. - HMODULE module = GetModuleHandle(L"ntdll.dll"); - typedef UINT (WINAPI *TerminateProcessPtr)(HANDLE handle, UINT code); - TerminateProcessPtr terminate_process = reinterpret_cast<TerminateProcessPtr>( - GetProcAddress(module, "NtTerminateProcess")); - terminate_process(Handle(), result_code); +bool Process::WaitForExitWithTimeout(TimeDelta timeout, int* exit_code) { + // Limit timeout to INFINITE. + DWORD timeout_ms = saturated_cast<DWORD>(timeout.InMilliseconds()); + if (::WaitForSingleObject(Handle(), timeout_ms) != WAIT_OBJECT_0) + return false; + + DWORD temp_code; // Don't clobber out-parameters in case of failure. + if (!::GetExitCodeProcess(Handle(), &temp_code)) + return false; + + *exit_code = temp_code; + return true; } bool Process::IsProcessBackgrounded() const { @@ -117,7 +177,21 @@ bool Process::SetProcessBackgrounded(bool value) { priority = value ? PROCESS_MODE_BACKGROUND_BEGIN : PROCESS_MODE_BACKGROUND_END; } else { - priority = value ? BELOW_NORMAL_PRIORITY_CLASS : NORMAL_PRIORITY_CLASS; + // Experiment (http://crbug.com/458594) with using IDLE_PRIORITY_CLASS as a + // background priority for background renderers (this code path is + // technically for more than just the renderers but they're the only use + // case in practice and experimenting here direclty is thus easier -- plus + // it doesn't really hurt as above we already state our intent of using + // PROCESS_MODE_BACKGROUND_BEGIN if available which is essentially + // IDLE_PRIORITY_CLASS plus lowered IO priority). Enabled by default in the + // asbence of field trials to get coverage on the perf waterfall. + DWORD background_priority = IDLE_PRIORITY_CLASS; + base::FieldTrial* trial = + base::FieldTrialList::Find("BackgroundRendererProcesses"); + if (trial && trial->group_name() == "AllowBelowNormalFromBrowser") + background_priority = BELOW_NORMAL_PRIORITY_CLASS; + + priority = value ? background_priority : NORMAL_PRIORITY_CLASS; } return (::SetPriorityClass(Handle(), priority) != 0); diff --git a/chromium/base/profiler/alternate_timer.cc b/chromium/base/profiler/alternate_timer.cc index 4eba89c255e..02763cd9ef9 100644 --- a/chromium/base/profiler/alternate_timer.cc +++ b/chromium/base/profiler/alternate_timer.cc @@ -4,7 +4,7 @@ #include "base/profiler/alternate_timer.h" -#include "base/logging.h" +#include "base/basictypes.h" namespace { @@ -21,7 +21,6 @@ const char kAlternateProfilerTime[] = "CHROME_PROFILER_TIME"; // Set an alternate timer function to replace the OS time function when // profiling. void SetAlternateTimeSource(NowFunction* now_function, TimeSourceType type) { - DCHECK_EQ(reinterpret_cast<NowFunction*>(NULL), g_time_function); g_time_function = now_function; g_time_source_type = type; } diff --git a/chromium/base/profiler/native_stack_sampler.cc b/chromium/base/profiler/native_stack_sampler.cc new file mode 100644 index 00000000000..8b4731b6cce --- /dev/null +++ b/chromium/base/profiler/native_stack_sampler.cc @@ -0,0 +1,13 @@ +// 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/profiler/native_stack_sampler.h" + +namespace base { + +NativeStackSampler::NativeStackSampler() {} + +NativeStackSampler::~NativeStackSampler() {} + +} // namespace base diff --git a/chromium/base/profiler/native_stack_sampler.h b/chromium/base/profiler/native_stack_sampler.h new file mode 100644 index 00000000000..bc170dcf933 --- /dev/null +++ b/chromium/base/profiler/native_stack_sampler.h @@ -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. + +#ifndef BASE_PROFILER_NATIVE_STACK_SAMPLER_H_ +#define BASE_PROFILER_NATIVE_STACK_SAMPLER_H_ + +#include "base/memory/scoped_ptr.h" +#include "base/profiler/stack_sampling_profiler.h" +#include "base/threading/platform_thread.h" + +namespace base { + +// NativeStackSampler is an implementation detail of StackSamplingProfiler. It +// abstracts the native implementation required to record a stack sample for a +// given thread. +class NativeStackSampler { + public: + virtual ~NativeStackSampler(); + + // Creates a stack sampler that records samples for |thread_handle|. Returns + // null if this platform does not support stack sampling. + static scoped_ptr<NativeStackSampler> Create(PlatformThreadId thread_id); + + // The following functions are all called on the SamplingThread (not the + // thread being sampled). + + // Notifies the sampler that we're starting to record a new profile. Modules + // shared across samples in the profile should be recorded in |modules|. + virtual void ProfileRecordingStarting( + std::vector<StackSamplingProfiler::Module>* modules) = 0; + + // Records a stack sample to |sample|. + virtual void RecordStackSample(StackSamplingProfiler::Sample* sample) = 0; + + // Notifies the sampler that we've stopped recording the current + // profile. + virtual void ProfileRecordingStopped() = 0; + + protected: + NativeStackSampler(); + + private: + DISALLOW_COPY_AND_ASSIGN(NativeStackSampler); +}; + +} // namespace base + +#endif // BASE_PROFILER_NATIVE_STACK_SAMPLER_H_ + diff --git a/chromium/base/profiler/scoped_profile.cc b/chromium/base/profiler/scoped_profile.cc index 4f8bc2d1e0e..f06a8c6f5dd 100644 --- a/chromium/base/profiler/scoped_profile.cc +++ b/chromium/base/profiler/scoped_profile.cc @@ -20,7 +20,6 @@ ScopedProfile::ScopedProfile(const Location& location, Mode mode) if (!birth_) return; - ThreadData::PrepareForStartOfRun(birth_); stopwatch_.Start(); } diff --git a/chromium/base/profiler/scoped_profile.h b/chromium/base/profiler/scoped_profile.h index c1e283026dc..2c4105d24aa 100644 --- a/chromium/base/profiler/scoped_profile.h +++ b/chromium/base/profiler/scoped_profile.h @@ -30,6 +30,14 @@ FROM_HERE_WITH_EXPLICIT_FUNCTION(#dispatch_function_name), \ ::tracked_objects::ScopedProfile::ENABLED) +// Same as TRACK_RUN_IN_THIS_SCOPED_REGION except that there's an extra param +// which is concatenated with the function name for better filtering. +#define TRACK_SCOPED_REGION(category_name, dispatch_function_name) \ + ::tracked_objects::ScopedProfile LINE_BASED_VARIABLE_NAME_FOR_PROFILING( \ + FROM_HERE_WITH_EXPLICIT_FUNCTION( \ + "[" category_name "]" dispatch_function_name), \ + ::tracked_objects::ScopedProfile::ENABLED) + namespace tracked_objects { class Births; @@ -54,4 +62,4 @@ class BASE_EXPORT ScopedProfile { } // namespace tracked_objects -#endif // BASE_PROFILER_SCOPED_PROFILE_H_ +#endif // BASE_PROFILER_SCOPED_PROFILE_H_ diff --git a/chromium/base/profiler/scoped_tracker.cc b/chromium/base/profiler/scoped_tracker.cc index 26b17c01553..d15b7de6dcd 100644 --- a/chromium/base/profiler/scoped_tracker.cc +++ b/chromium/base/profiler/scoped_tracker.cc @@ -12,13 +12,6 @@ namespace { ScopedProfile::Mode g_scoped_profile_mode = ScopedProfile::DISABLED; -// Executes |callback|, augmenting it with provided |location|. -void ExecuteAndTrackCallback(const Location& location, - const base::Closure& callback) { - ScopedProfile tracking_profile(location, ScopedProfile::ENABLED); - callback.Run(); -} - } // namespace // static @@ -26,15 +19,6 @@ void ScopedTracker::Enable() { g_scoped_profile_mode = ScopedProfile::ENABLED; } -// static -base::Closure ScopedTracker::TrackCallback(const Location& location, - const base::Closure& callback) { - if (g_scoped_profile_mode != ScopedProfile::ENABLED) - return callback; - - return base::Bind(ExecuteAndTrackCallback, location, callback); -} - ScopedTracker::ScopedTracker(const Location& location) : scoped_profile_(location, g_scoped_profile_mode) { } diff --git a/chromium/base/profiler/scoped_tracker.h b/chromium/base/profiler/scoped_tracker.h index f83654e1bd4..23e2f07b61f 100644 --- a/chromium/base/profiler/scoped_tracker.h +++ b/chromium/base/profiler/scoped_tracker.h @@ -10,6 +10,7 @@ // found using profiler data. #include "base/base_export.h" +#include "base/bind.h" #include "base/callback_forward.h" #include "base/location.h" #include "base/profiler/scoped_profile.h" @@ -47,10 +48,24 @@ class BASE_EXPORT ScopedTracker { // many possible callbacks, but they come from a relatively small number of // places. We can instrument these few places and at least know which one // passes the janky callback. - static base::Closure TrackCallback(const Location& location, - const base::Closure& callback); + template <typename P1> + static base::Callback<void(P1)> TrackCallback( + const Location& location, + const base::Callback<void(P1)>& callback) { + return base::Bind(&ScopedTracker::ExecuteAndTrackCallback<P1>, location, + callback); + } private: + // Executes |callback|, augmenting it with provided |location|. + template <typename P1> + static void ExecuteAndTrackCallback(const Location& location, + const base::Callback<void(P1)>& callback, + P1 p1) { + ScopedTracker tracking_profile(location); + callback.Run(p1); + } + const ScopedProfile scoped_profile_; DISALLOW_COPY_AND_ASSIGN(ScopedTracker); diff --git a/chromium/base/profiler/stack_sampling_profiler.cc b/chromium/base/profiler/stack_sampling_profiler.cc new file mode 100644 index 00000000000..9da662859fd --- /dev/null +++ b/chromium/base/profiler/stack_sampling_profiler.cc @@ -0,0 +1,311 @@ +// 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/profiler/stack_sampling_profiler.h" + +#include <algorithm> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/memory/singleton.h" +#include "base/profiler/native_stack_sampler.h" +#include "base/synchronization/lock.h" +#include "base/timer/elapsed_timer.h" + +namespace base { + +// DefaultProfileProcessor ---------------------------------------------------- + +namespace { + +// Singleton class responsible for providing the default processing for profiles +// (i.e. for profiles generated by profilers without their own completed +// callback). +class DefaultProfileProcessor { + public: + using CompletedCallback = StackSamplingProfiler::CompletedCallback; + + ~DefaultProfileProcessor(); + + static DefaultProfileProcessor* GetInstance(); + + // Sets the callback to use for processing profiles captured without a + // per-profiler completed callback. Pending completed profiles are stored in + // this object until a non-null callback is provided here. This function is + // thread-safe. + void SetCompletedCallback(CompletedCallback callback); + + // Processes |profiles|. This function is thread safe. + void ProcessProfiles( + const StackSamplingProfiler::CallStackProfiles& profiles); + + private: + friend struct DefaultSingletonTraits<DefaultProfileProcessor>; + + DefaultProfileProcessor(); + + // Copies the pending profiles from |profiles_| into |profiles|, and clears + // |profiles_|. This function may be called on any thread. + void GetAndClearPendingProfiles( + StackSamplingProfiler::CallStackProfiles* profiles); + + // Gets the current completed callback, with proper locking. + CompletedCallback GetCompletedCallback() const; + + mutable Lock callback_lock_; + CompletedCallback default_completed_callback_; + + Lock profiles_lock_; + StackSamplingProfiler::CallStackProfiles profiles_; + + DISALLOW_COPY_AND_ASSIGN(DefaultProfileProcessor); +}; + +DefaultProfileProcessor::~DefaultProfileProcessor() {} + +// static +DefaultProfileProcessor* DefaultProfileProcessor::GetInstance() { + return Singleton<DefaultProfileProcessor>::get(); +} + +void DefaultProfileProcessor::SetCompletedCallback(CompletedCallback callback) { + { + AutoLock scoped_lock(callback_lock_); + default_completed_callback_ = callback; + } + + if (!callback.is_null()) { + // Provide any pending profiles to the callback immediately. + StackSamplingProfiler::CallStackProfiles profiles; + GetAndClearPendingProfiles(&profiles); + if (!profiles.empty()) + callback.Run(profiles); + } +} + +void DefaultProfileProcessor::ProcessProfiles( + const StackSamplingProfiler::CallStackProfiles& profiles) { + CompletedCallback callback = GetCompletedCallback(); + + // Store pending profiles if we don't have a valid callback. + if (!callback.is_null()) { + callback.Run(profiles); + } else { + AutoLock scoped_lock(profiles_lock_); + profiles_.insert(profiles_.end(), profiles.begin(), profiles.end()); + } +} + +DefaultProfileProcessor::DefaultProfileProcessor() {} + +void DefaultProfileProcessor::GetAndClearPendingProfiles( + StackSamplingProfiler::CallStackProfiles* profiles) { + profiles->clear(); + + AutoLock scoped_lock(profiles_lock_); + profiles_.swap(*profiles); +} + +DefaultProfileProcessor::CompletedCallback +DefaultProfileProcessor::GetCompletedCallback() const { + AutoLock scoped_lock(callback_lock_); + return default_completed_callback_; +} + +} // namespace + +// StackSamplingProfiler::Module ---------------------------------------------- + +StackSamplingProfiler::Module::Module() : base_address(nullptr) {} +StackSamplingProfiler::Module::Module(const void* base_address, + const std::string& id, + const FilePath& filename) + : base_address(base_address), id(id), filename(filename) {} + +StackSamplingProfiler::Module::~Module() {} + +// StackSamplingProfiler::Frame ----------------------------------------------- + +StackSamplingProfiler::Frame::Frame(const void* instruction_pointer, + size_t module_index) + : instruction_pointer(instruction_pointer), + module_index(module_index) {} + +StackSamplingProfiler::Frame::~Frame() {} + +// StackSamplingProfiler::CallStackProfile ------------------------------------ + +StackSamplingProfiler::CallStackProfile::CallStackProfile() + : preserve_sample_ordering(false), user_data(0) {} + +StackSamplingProfiler::CallStackProfile::~CallStackProfile() {} + +// StackSamplingProfiler::SamplingThread -------------------------------------- + +StackSamplingProfiler::SamplingThread::SamplingThread( + scoped_ptr<NativeStackSampler> native_sampler, + const SamplingParams& params, + CompletedCallback completed_callback) + : native_sampler_(native_sampler.Pass()), + params_(params), + stop_event_(false, false), + completed_callback_(completed_callback) { +} + +StackSamplingProfiler::SamplingThread::~SamplingThread() {} + +void StackSamplingProfiler::SamplingThread::ThreadMain() { + PlatformThread::SetName("Chrome_SamplingProfilerThread"); + + CallStackProfiles profiles; + CollectProfiles(&profiles); + completed_callback_.Run(profiles); +} + +// Depending on how long the sampling takes and the length of the sampling +// interval, a burst of samples could take arbitrarily longer than +// samples_per_burst * sampling_interval. In this case, we (somewhat +// arbitrarily) honor the number of samples requested rather than strictly +// adhering to the sampling intervals. Once we have established users for the +// StackSamplingProfiler and the collected data to judge, we may go the other +// way or make this behavior configurable. +bool StackSamplingProfiler::SamplingThread::CollectProfile( + CallStackProfile* profile, + TimeDelta* elapsed_time) { + ElapsedTimer profile_timer; + CallStackProfile current_profile; + native_sampler_->ProfileRecordingStarting(¤t_profile.modules); + current_profile.sampling_period = params_.sampling_interval; + bool burst_completed = true; + TimeDelta previous_elapsed_sample_time; + for (int i = 0; i < params_.samples_per_burst; ++i) { + if (i != 0) { + // Always wait, even if for 0 seconds, so we can observe a signal on + // stop_event_. + if (stop_event_.TimedWait( + std::max(params_.sampling_interval - previous_elapsed_sample_time, + TimeDelta()))) { + burst_completed = false; + break; + } + } + ElapsedTimer sample_timer; + current_profile.samples.push_back(Sample()); + native_sampler_->RecordStackSample(¤t_profile.samples.back()); + previous_elapsed_sample_time = sample_timer.Elapsed(); + } + + *elapsed_time = profile_timer.Elapsed(); + current_profile.profile_duration = *elapsed_time; + current_profile.preserve_sample_ordering = params_.preserve_sample_ordering; + current_profile.user_data = params_.user_data; + native_sampler_->ProfileRecordingStopped(); + + if (burst_completed) + *profile = current_profile; + + return burst_completed; +} + +// In an analogous manner to CollectProfile() and samples exceeding the expected +// total sampling time, bursts may also exceed the burst_interval. We adopt the +// same wait-and-see approach here. +void StackSamplingProfiler::SamplingThread::CollectProfiles( + CallStackProfiles* profiles) { + if (stop_event_.TimedWait(params_.initial_delay)) + return; + + TimeDelta previous_elapsed_profile_time; + for (int i = 0; i < params_.bursts; ++i) { + if (i != 0) { + // Always wait, even if for 0 seconds, so we can observe a signal on + // stop_event_. + if (stop_event_.TimedWait( + std::max(params_.burst_interval - previous_elapsed_profile_time, + TimeDelta()))) + return; + } + + CallStackProfile profile; + if (!CollectProfile(&profile, &previous_elapsed_profile_time)) + return; + profiles->push_back(profile); + } +} + +void StackSamplingProfiler::SamplingThread::Stop() { + stop_event_.Signal(); +} + +// StackSamplingProfiler ------------------------------------------------------ + +StackSamplingProfiler::SamplingParams::SamplingParams() + : initial_delay(TimeDelta::FromMilliseconds(0)), + bursts(1), + burst_interval(TimeDelta::FromMilliseconds(10000)), + samples_per_burst(300), + sampling_interval(TimeDelta::FromMilliseconds(100)), + preserve_sample_ordering(false), + user_data(0) { +} + +StackSamplingProfiler::StackSamplingProfiler(PlatformThreadId thread_id, + const SamplingParams& params) + : thread_id_(thread_id), params_(params) {} + +StackSamplingProfiler::StackSamplingProfiler(PlatformThreadId thread_id, + const SamplingParams& params, + CompletedCallback callback) + : thread_id_(thread_id), params_(params), completed_callback_(callback) {} + +StackSamplingProfiler::~StackSamplingProfiler() { + Stop(); + if (!sampling_thread_handle_.is_null()) + PlatformThread::Join(sampling_thread_handle_); +} + +void StackSamplingProfiler::Start() { + scoped_ptr<NativeStackSampler> native_sampler = + NativeStackSampler::Create(thread_id_); + if (!native_sampler) + return; + + CompletedCallback callback = + !completed_callback_.is_null() ? completed_callback_ : + Bind(&DefaultProfileProcessor::ProcessProfiles, + Unretained(DefaultProfileProcessor::GetInstance())); + sampling_thread_.reset( + new SamplingThread(native_sampler.Pass(), params_, callback)); + if (!PlatformThread::Create(0, sampling_thread_.get(), + &sampling_thread_handle_)) + sampling_thread_.reset(); +} + +void StackSamplingProfiler::Stop() { + if (sampling_thread_) + sampling_thread_->Stop(); +} + +// static +void StackSamplingProfiler::SetDefaultCompletedCallback( + CompletedCallback callback) { + DefaultProfileProcessor::GetInstance()->SetCompletedCallback(callback); +} + +// StackSamplingProfiler::Frame global functions ------------------------------ + +bool operator==(const StackSamplingProfiler::Frame &a, + const StackSamplingProfiler::Frame &b) { + return a.instruction_pointer == b.instruction_pointer && + a.module_index == b.module_index; +} + +bool operator<(const StackSamplingProfiler::Frame &a, + const StackSamplingProfiler::Frame &b) { + return (a.module_index < b.module_index) || + (a.module_index == b.module_index && + a.instruction_pointer < b.instruction_pointer); +} + +} // namespace base diff --git a/chromium/base/profiler/stack_sampling_profiler.h b/chromium/base/profiler/stack_sampling_profiler.h new file mode 100644 index 00000000000..9d52f27a7ef --- /dev/null +++ b/chromium/base/profiler/stack_sampling_profiler.h @@ -0,0 +1,264 @@ +// 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_PROFILER_STACK_SAMPLING_PROFILER_H_ +#define BASE_PROFILER_STACK_SAMPLING_PROFILER_H_ + +#include <string> +#include <vector> + +#include "base/base_export.h" +#include "base/callback.h" +#include "base/files/file_path.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string16.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/platform_thread.h" +#include "base/time/time.h" + +namespace base { + +class NativeStackSampler; + +// StackSamplingProfiler periodically stops a thread to sample its stack, for +// the purpose of collecting information about which code paths are +// executing. This information is used in aggregate by UMA to identify hot +// and/or janky code paths. +// +// Sample StackSamplingProfiler usage: +// +// // Create and customize params as desired. +// base::StackStackSamplingProfiler::SamplingParams params; +// // Any thread's ID may be passed as the target. +// base::StackSamplingProfiler profiler(base::PlatformThread::CurrentId()), +// params); +// +// // Or, to process the profiles within Chrome rather than via UMA, use a +// // custom completed callback: +// base::StackStackSamplingProfiler::CompletedCallback +// thread_safe_callback = ...; +// base::StackSamplingProfiler profiler(base::PlatformThread::CurrentId()), +// params, thread_safe_callback); +// +// profiler.Start(); +// // ... work being done on the target thread here ... +// profiler.Stop(); // optional, stops collection before complete per params +// +// The default SamplingParams causes stacks to be recorded in a single burst at +// a 10Hz interval for a total of 30 seconds. All of these parameters may be +// altered as desired. +// +// When all call stack profiles are complete or the profiler is stopped, if the +// custom completed callback was set it is called from a thread created by the +// profiler with the completed profiles. A profile is considered complete if all +// requested samples were recorded for the profile (i.e. it was not stopped +// prematurely). If no callback was set, the default completed callback will be +// called with the profiles. It is expected that the the default completed +// callback is set by the metrics system to allow profiles to be provided via +// UMA. +// +// The results of the profiling are passed to the completed callback and consist +// of a vector of CallStackProfiles. Each CallStackProfile corresponds to a +// burst as specified in SamplingParams and contains a set of Samples and +// Modules. One Sample corresponds to a single recorded stack, and the Modules +// record those modules associated with the recorded stack frames. +class BASE_EXPORT StackSamplingProfiler { + public: + // Module represents the module (DLL or exe) corresponding to a stack frame. + struct BASE_EXPORT Module { + Module(); + Module(const void* base_address, const std::string& id, + const FilePath& filename); + ~Module(); + + // Points to the base address of the module. + const void* base_address; + + // An opaque binary string that uniquely identifies a particular program + // version with high probability. This is parsed from headers of the loaded + // module. + // For binaries generated by GNU tools: + // Contents of the .note.gnu.build-id field. + // On Windows: + // GUID + AGE in the debug image headers of a module. + std::string id; + + // The filename of the module. + FilePath filename; + }; + + // Frame represents an individual sampled stack frame with module information. + struct BASE_EXPORT Frame { + // Identifies an unknown module. + static const size_t kUnknownModuleIndex = static_cast<size_t>(-1); + + Frame(const void* instruction_pointer, size_t module_index); + ~Frame(); + + // The sampled instruction pointer within the function. + const void* instruction_pointer; + + // Index of the module in CallStackProfile::modules. We don't represent + // module state directly here to save space. + size_t module_index; + }; + + // Sample represents a set of stack frames. + using Sample = std::vector<Frame>; + + // CallStackProfile represents a set of samples. + struct BASE_EXPORT CallStackProfile { + CallStackProfile(); + ~CallStackProfile(); + + std::vector<Module> modules; + std::vector<Sample> samples; + + // Duration of this profile. + TimeDelta profile_duration; + + // Time between samples. + TimeDelta sampling_period; + + // True if sample ordering is important and should be preserved if and when + // this profile is compressed and processed. + bool preserve_sample_ordering; + + // User data associated with this profile. + uintptr_t user_data; + }; + + using CallStackProfiles = std::vector<CallStackProfile>; + + // Represents parameters that configure the sampling. + struct BASE_EXPORT SamplingParams { + SamplingParams(); + + // Time to delay before first samples are taken. Defaults to 0. + TimeDelta initial_delay; + + // Number of sampling bursts to perform. Defaults to 1. + int bursts; + + // Interval between sampling bursts. This is the desired duration from the + // start of one burst to the start of the next burst. Defaults to 10s. + TimeDelta burst_interval; + + // Number of samples to record per burst. Defaults to 300. + int samples_per_burst; + + // Interval between samples during a sampling burst. This is the desired + // duration from the start of one sample to the start of the next + // sample. Defaults to 100ms. + TimeDelta sampling_interval; + + // True if sample ordering is important and should be preserved if and when + // this profile is compressed and processed. Defaults to false. + bool preserve_sample_ordering; + + // User data associated with this profile. + uintptr_t user_data; + }; + + // The callback type used to collect completed profiles. + // + // IMPORTANT NOTE: the callback is invoked on a thread the profiler + // constructs, rather than on the thread used to construct the profiler and + // set the callback, and thus the callback must be callable on any thread. For + // threads with message loops that create StackSamplingProfilers, posting a + // task to the message loop with a copy of the profiles is the recommended + // thread-safe callback implementation. + using CompletedCallback = Callback<void(const CallStackProfiles&)>; + + // Creates a profiler that sends completed profiles to the default completed + // callback. + StackSamplingProfiler(PlatformThreadId thread_id, + const SamplingParams& params); + // Creates a profiler that sends completed profiles to |completed_callback|. + StackSamplingProfiler(PlatformThreadId thread_id, + const SamplingParams& params, + CompletedCallback callback); + ~StackSamplingProfiler(); + + // Initializes the profiler and starts sampling. + void Start(); + + // Stops the profiler and any ongoing sampling. Calling this function is + // optional; if not invoked profiling terminates when all the profiling bursts + // specified in the SamplingParams are completed. + void Stop(); + + // Sets a callback to process profiles collected by profiler instances without + // a completed callback. Profiles are queued internally until a non-null + // callback is provided to this function, + // + // The callback is typically called on a thread created by the profiler. If + // completed profiles are queued when set, however, it will also be called + // immediately on the calling thread. + static void SetDefaultCompletedCallback(CompletedCallback callback); + + private: + // SamplingThread is a separate thread used to suspend and sample stacks from + // the target thread. + class SamplingThread : public PlatformThread::Delegate { + public: + // Samples stacks using |native_sampler|. When complete, invokes + // |completed_callback| with the collected call stack profiles. + // |completed_callback| must be callable on any thread. + SamplingThread(scoped_ptr<NativeStackSampler> native_sampler, + const SamplingParams& params, + CompletedCallback completed_callback); + ~SamplingThread() override; + + // PlatformThread::Delegate: + void ThreadMain() override; + + void Stop(); + + private: + // Collects a call stack profile from a single burst. Returns true if the + // profile was collected, or false if collection was stopped before it + // completed. + bool CollectProfile(CallStackProfile* profile, TimeDelta* elapsed_time); + + // Collects call stack profiles from all bursts, or until the sampling is + // stopped. If stopped before complete, |call_stack_profiles| will contain + // only full bursts. + void CollectProfiles(CallStackProfiles* profiles); + + scoped_ptr<NativeStackSampler> native_sampler_; + const SamplingParams params_; + + // If Stop() is called, it signals this event to force the sampling to + // terminate before all the samples specified in |params_| are collected. + WaitableEvent stop_event_; + + const CompletedCallback completed_callback_; + + DISALLOW_COPY_AND_ASSIGN(SamplingThread); + }; + + // The thread whose stack will be sampled. + PlatformThreadId thread_id_; + + const SamplingParams params_; + + scoped_ptr<SamplingThread> sampling_thread_; + PlatformThreadHandle sampling_thread_handle_; + + const CompletedCallback completed_callback_; + + DISALLOW_COPY_AND_ASSIGN(StackSamplingProfiler); +}; + +// The metrics provider code wants to put Samples in a map and compare them, +// which requires us to define a few operators. +BASE_EXPORT bool operator==(const StackSamplingProfiler::Frame& a, + const StackSamplingProfiler::Frame& b); +BASE_EXPORT bool operator<(const StackSamplingProfiler::Frame& a, + const StackSamplingProfiler::Frame& b); + +} // namespace base + +#endif // BASE_PROFILER_STACK_SAMPLING_PROFILER_H_ diff --git a/chromium/base/profiler/stack_sampling_profiler_posix.cc b/chromium/base/profiler/stack_sampling_profiler_posix.cc new file mode 100644 index 00000000000..bce37e10c3a --- /dev/null +++ b/chromium/base/profiler/stack_sampling_profiler_posix.cc @@ -0,0 +1,14 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/profiler/native_stack_sampler.h" + +namespace base { + +scoped_ptr<NativeStackSampler> NativeStackSampler::Create( + PlatformThreadId thread_id) { + return scoped_ptr<NativeStackSampler>(); +} + +} // namespace base diff --git a/chromium/base/profiler/stack_sampling_profiler_unittest.cc b/chromium/base/profiler/stack_sampling_profiler_unittest.cc new file mode 100644 index 00000000000..5ade15a6edc --- /dev/null +++ b/chromium/base/profiler/stack_sampling_profiler_unittest.cc @@ -0,0 +1,445 @@ +// 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/bind.h" +#include "base/compiler_specific.h" +#include "base/path_service.h" +#include "base/profiler/stack_sampling_profiler.h" +#include "base/strings/stringprintf.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/platform_thread.h" +#include "base/time/time.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { + +using SamplingParams = StackSamplingProfiler::SamplingParams; +using Frame = StackSamplingProfiler::Frame; +using Module = StackSamplingProfiler::Module; +using Sample = StackSamplingProfiler::Sample; +using CallStackProfile = StackSamplingProfiler::CallStackProfile; +using CallStackProfiles = StackSamplingProfiler::CallStackProfiles; + +namespace { + +// A thread to target for profiling, whose stack is guaranteed to contain +// SignalAndWaitUntilSignaled() when coordinated with the main thread. +class TargetThread : public PlatformThread::Delegate { + public: + TargetThread(); + + // PlatformThread::Delegate: + void ThreadMain() override; + + // Waits for the thread to have started and be executing in + // SignalAndWaitUntilSignaled(). + void WaitForThreadStart(); + + // Allows the thread to return from SignalAndWaitUntilSignaled() and finish + // execution. + void SignalThreadToFinish(); + + // This function is guaranteed to be executing between calls to + // WaitForThreadStart() and SignalThreadToFinish(). This function is static so + // that we can get a straightforward address for it in one of the tests below, + // rather than dealing with the complexity of a member function pointer + // representation. + static void SignalAndWaitUntilSignaled(WaitableEvent* thread_started_event, + WaitableEvent* finish_event); + + PlatformThreadId id() const { return id_; } + + private: + WaitableEvent thread_started_event_; + WaitableEvent finish_event_; + PlatformThreadId id_; + + DISALLOW_COPY_AND_ASSIGN(TargetThread); +}; + +TargetThread::TargetThread() + : thread_started_event_(false, false), finish_event_(false, false), + id_(0) {} + +void TargetThread::ThreadMain() { + id_ = PlatformThread::CurrentId(); + SignalAndWaitUntilSignaled(&thread_started_event_, &finish_event_); +} + +void TargetThread::WaitForThreadStart() { + thread_started_event_.Wait(); +} + +void TargetThread::SignalThreadToFinish() { + finish_event_.Signal(); +} + +// static +// Disable inlining for this function so that it gets its own stack frame. +NOINLINE void TargetThread::SignalAndWaitUntilSignaled( + WaitableEvent* thread_started_event, + WaitableEvent* finish_event) { + thread_started_event->Signal(); + volatile int x = 1; + finish_event->Wait(); + x = 0; // Prevent tail call to WaitableEvent::Wait(). + ALLOW_UNUSED_LOCAL(x); +} + +// Called on the profiler thread when complete, to collect profiles. +void SaveProfiles(CallStackProfiles* profiles, + const CallStackProfiles& pending_profiles) { + *profiles = pending_profiles; +} + +// Called on the profiler thread when complete. Collects profiles produced by +// the profiler, and signals an event to allow the main thread to know that that +// the profiler is done. +void SaveProfilesAndSignalEvent(CallStackProfiles* profiles, + WaitableEvent* event, + const CallStackProfiles& pending_profiles) { + *profiles = pending_profiles; + event->Signal(); +} + +// Executes the function with the target thread running and executing within +// SignalAndWaitUntilSignaled(). Performs all necessary target thread startup +// and shutdown work before and afterward. +template <class Function> +void WithTargetThread(Function function) { + TargetThread target_thread; + PlatformThreadHandle target_thread_handle; + EXPECT_TRUE(PlatformThread::Create(0, &target_thread, &target_thread_handle)); + + target_thread.WaitForThreadStart(); + + function(target_thread.id()); + + target_thread.SignalThreadToFinish(); + + PlatformThread::Join(target_thread_handle); +} + +// Captures profiles as specified by |params| on the TargetThread, and returns +// them in |profiles|. Waits up to |profiler_wait_time| for the profiler to +// complete. +void CaptureProfilesWithObjectCallback(const SamplingParams& params, + CallStackProfiles* profiles, + TimeDelta profiler_wait_time) { + profiles->clear(); + + WithTargetThread([¶ms, profiles, profiler_wait_time]( + PlatformThreadId target_thread_id) { + WaitableEvent sampling_thread_completed(true, false); + const StackSamplingProfiler::CompletedCallback callback = + Bind(&SaveProfilesAndSignalEvent, Unretained(profiles), + Unretained(&sampling_thread_completed)); + StackSamplingProfiler profiler(target_thread_id, params, callback); + profiler.Start(); + sampling_thread_completed.TimedWait(profiler_wait_time); + profiler.Stop(); + sampling_thread_completed.Wait(); + }); +} + +// Captures profiles as specified by |params| on the TargetThread, and returns +// them in |profiles|. Uses the default callback rather than a per-object +// callback. +void CaptureProfilesWithDefaultCallback(const SamplingParams& params, + CallStackProfiles* profiles) { + profiles->clear(); + + WithTargetThread([¶ms, profiles](PlatformThreadId target_thread_id) { + WaitableEvent sampling_thread_completed(false, false); + StackSamplingProfiler::SetDefaultCompletedCallback( + Bind(&SaveProfilesAndSignalEvent, Unretained(profiles), + Unretained(&sampling_thread_completed))); + + StackSamplingProfiler profiler(target_thread_id, params); + profiler.Start(); + sampling_thread_completed.Wait(); + + StackSamplingProfiler::SetDefaultCompletedCallback( + StackSamplingProfiler::CompletedCallback()); + }); +} + +// Runs the profiler with |params| on the TargetThread, with no default or +// per-object callback. +void RunProfilerWithNoCallback(const SamplingParams& params, + TimeDelta profiler_wait_time) { + WithTargetThread([¶ms, profiler_wait_time]( + PlatformThreadId target_thread_id) { + StackSamplingProfiler profiler(target_thread_id, params); + profiler.Start(); + // Since we don't specify a callback, we don't have a synchronization + // mechanism with the sampling thread. Just sleep instead. + PlatformThread::Sleep(profiler_wait_time); + profiler.Stop(); + }); +} + +// If this executable was linked with /INCREMENTAL (the default for non-official +// debug and release builds on Windows), function addresses do not correspond to +// function code itself, but instead to instructions in the Incremental Link +// Table that jump to the functions. Checks for a jump instruction and if +// present does a little decompilation to find the function's actual starting +// address. +const void* MaybeFixupFunctionAddressForILT(const void* function_address) { +#if defined(_WIN64) + const unsigned char* opcode = + reinterpret_cast<const unsigned char*>(function_address); + if (*opcode == 0xe9) { + // This is a relative jump instruction. Assume we're in the ILT and compute + // the function start address from the instruction offset. + const int32* offset = reinterpret_cast<const int32*>(opcode + 1); + const unsigned char* next_instruction = + reinterpret_cast<const unsigned char*>(offset + 1); + return next_instruction + *offset; + } +#endif + return function_address; +} + +// Searches through the frames in |sample|, returning an iterator to the first +// frame that has an instruction pointer between |function_address| and +// |function_address| + |size|. Returns sample.end() if no such frames are +// found. +Sample::const_iterator FindFirstFrameWithinFunction( + const Sample& sample, + const void* function_address, + int function_size) { + function_address = MaybeFixupFunctionAddressForILT(function_address); + for (auto it = sample.begin(); it != sample.end(); ++it) { + if ((it->instruction_pointer >= function_address) && + (it->instruction_pointer < + (static_cast<const unsigned char*>(function_address) + function_size))) + return it; + } + return sample.end(); +} + +// Formats a sample into a string that can be output for test diagnostics. +std::string FormatSampleForDiagnosticOutput( + const Sample& sample, + const std::vector<Module>& modules) { + std::string output; + for (const Frame& frame: sample) { + output += StringPrintf( + "0x%p %s\n", frame.instruction_pointer, + modules[frame.module_index].filename.AsUTF8Unsafe().c_str()); + } + return output; +} + +// Returns a duration that is longer than the test timeout. We would use +// TimeDelta::Max() but https://crbug.com/465948. +TimeDelta AVeryLongTimeDelta() { return TimeDelta::FromDays(1); } + +} // namespace + + +// The tests below are enabled for Win x64 only, pending implementation of the +// tested functionality on other platforms/architectures. + +// Checks that the basic expected information is present in a sampled call stack +// profile. +#if defined(_WIN64) +#define MAYBE_Basic Basic +#else +#define MAYBE_Basic DISABLED_Basic +#endif +TEST(StackSamplingProfilerTest, MAYBE_Basic) { + SamplingParams params; + params.sampling_interval = TimeDelta::FromMilliseconds(0); + params.samples_per_burst = 1; + params.user_data = 100; + params.preserve_sample_ordering = true; + + std::vector<CallStackProfile> profiles; + CaptureProfilesWithObjectCallback(params, &profiles, AVeryLongTimeDelta()); + + // Check that the profile and samples sizes are correct, and the module + // indices are in range. + ASSERT_EQ(1u, profiles.size()); + const CallStackProfile& profile = profiles[0]; + ASSERT_EQ(1u, profile.samples.size()); + EXPECT_EQ(params.sampling_interval, profile.sampling_period); + const Sample& sample = profile.samples[0]; + for (const auto& frame : sample) { + ASSERT_GE(frame.module_index, 0u); + ASSERT_LT(frame.module_index, profile.modules.size()); + } + EXPECT_EQ(100u, profile.user_data); + EXPECT_EQ(true, profile.preserve_sample_ordering); + + // Check that the stack contains a frame for + // TargetThread::SignalAndWaitUntilSignaled() and that the frame has this + // executable's module. + // + // Since we don't have a good way to know the function size, use 100 bytes as + // a reasonable window to locate the instruction pointer. + Sample::const_iterator loc = FindFirstFrameWithinFunction( + sample, + reinterpret_cast<const void*>(&TargetThread::SignalAndWaitUntilSignaled), + 100); + ASSERT_TRUE(loc != sample.end()) + << "Function at " + << MaybeFixupFunctionAddressForILT( + reinterpret_cast<const void*>( + &TargetThread::SignalAndWaitUntilSignaled)) + << " was not found in stack:\n" + << FormatSampleForDiagnosticOutput(sample, profile.modules); + FilePath executable_path; + EXPECT_TRUE(PathService::Get(FILE_EXE, &executable_path)); + EXPECT_EQ(executable_path, profile.modules[loc->module_index].filename); +} + +// Checks that the expected number of profiles and samples are present in the +// call stack profiles produced. +#if defined(_WIN64) +#define MAYBE_MultipleProfilesAndSamples MultipleProfilesAndSamples +#else +#define MAYBE_MultipleProfilesAndSamples DISABLED_MultipleProfilesAndSamples +#endif +TEST(StackSamplingProfilerTest, MAYBE_MultipleProfilesAndSamples) { + SamplingParams params; + params.burst_interval = params.sampling_interval = + TimeDelta::FromMilliseconds(0); + params.bursts = 2; + params.samples_per_burst = 3; + + std::vector<CallStackProfile> profiles; + CaptureProfilesWithObjectCallback(params, &profiles, AVeryLongTimeDelta()); + + ASSERT_EQ(2u, profiles.size()); + EXPECT_EQ(3u, profiles[0].samples.size()); + EXPECT_EQ(3u, profiles[1].samples.size()); +} + +// Checks that no call stack profiles are captured if the profiling is stopped +// during the initial delay. +#if defined(_WIN64) +#define MAYBE_StopDuringInitialDelay StopDuringInitialDelay +#else +#define MAYBE_StopDuringInitialDelay DISABLED_StopDuringInitialDelay +#endif +TEST(StackSamplingProfilerTest, MAYBE_StopDuringInitialDelay) { + SamplingParams params; + params.initial_delay = TimeDelta::FromSeconds(60); + + std::vector<CallStackProfile> profiles; + CaptureProfilesWithObjectCallback(params, &profiles, + TimeDelta::FromMilliseconds(0)); + + EXPECT_TRUE(profiles.empty()); +} + +// Checks that the single completed call stack profile is captured if the +// profiling is stopped between bursts. +#if defined(_WIN64) +#define MAYBE_StopDuringInterBurstInterval StopDuringInterBurstInterval +#else +#define MAYBE_StopDuringInterBurstInterval DISABLED_StopDuringInterBurstInterval +#endif +TEST(StackSamplingProfilerTest, MAYBE_StopDuringInterBurstInterval) { + SamplingParams params; + params.sampling_interval = TimeDelta::FromMilliseconds(0); + params.burst_interval = TimeDelta::FromSeconds(60); + params.bursts = 2; + params.samples_per_burst = 1; + + std::vector<CallStackProfile> profiles; + CaptureProfilesWithObjectCallback(params, &profiles, + TimeDelta::FromMilliseconds(50)); + + ASSERT_EQ(1u, profiles.size()); + EXPECT_EQ(1u, profiles[0].samples.size()); +} + +// Checks that only completed call stack profiles are captured. +#if defined(_WIN64) +#define MAYBE_StopDuringInterSampleInterval StopDuringInterSampleInterval +#else +#define MAYBE_StopDuringInterSampleInterval \ + DISABLED_StopDuringInterSampleInterval +#endif +TEST(StackSamplingProfilerTest, MAYBE_StopDuringInterSampleInterval) { + SamplingParams params; + params.sampling_interval = TimeDelta::FromSeconds(60); + params.samples_per_burst = 2; + + std::vector<CallStackProfile> profiles; + CaptureProfilesWithObjectCallback(params, &profiles, + TimeDelta::FromMilliseconds(50)); + + EXPECT_TRUE(profiles.empty()); +} + +// Checks that profiles are captured via the default completed callback. +#if defined(_WIN64) +#define MAYBE_DefaultCallback DefaultCallback +#else +#define MAYBE_DefaultCallback DISABLED_DefaultCallback +#endif +TEST(StackSamplingProfilerTest, MAYBE_DefaultCallback) { + SamplingParams params; + params.samples_per_burst = 1; + + CallStackProfiles profiles; + CaptureProfilesWithDefaultCallback(params, &profiles); + + EXPECT_EQ(1u, profiles.size()); + EXPECT_EQ(1u, profiles[0].samples.size()); +} + +// Checks that profiles are queued until a default callback is set, then +// delivered. +#if defined(_WIN64) +#define MAYBE_ProfilesQueuedWithNoCallback ProfilesQueuedWithNoCallback +#else +#define MAYBE_ProfilesQueuedWithNoCallback DISABLED_ProfilesQueuedWithNoCallback +#endif +TEST(StackSamplingProfilerTest, MAYBE_ProfilesQueuedWithNoCallback) { + SamplingParams params; + params.samples_per_burst = 1; + + RunProfilerWithNoCallback(params, TimeDelta::FromMilliseconds(50)); + + CallStackProfiles profiles; + // This should immediately call SaveProfiles on this thread. + StackSamplingProfiler::SetDefaultCompletedCallback( + Bind(&SaveProfiles, Unretained(&profiles))); + EXPECT_EQ(1u, profiles.size()); + EXPECT_EQ(1u, profiles[0].samples.size()); + StackSamplingProfiler::SetDefaultCompletedCallback( + StackSamplingProfiler::CompletedCallback()); +} + +// Checks that we can destroy the profiler while profiling. +#if defined(_WIN64) +#define MAYBE_DestroyProfilerWhileProfiling DestroyProfilerWhileProfiling +#else +#define MAYBE_DestroyProfilerWhileProfiling \ + DISABLED_DestroyProfilerWhileProfiling +#endif +TEST(StackSamplingProfilerTest, MAYBE_DestroyProfilerWhileProfiling) { + SamplingParams params; + params.sampling_interval = TimeDelta::FromMilliseconds(10); + + CallStackProfiles profiles; + WithTargetThread([¶ms, &profiles](PlatformThreadId target_thread_id) { + scoped_ptr<StackSamplingProfiler> profiler; + profiler.reset(new StackSamplingProfiler( + target_thread_id, params, Bind(&SaveProfiles, Unretained(&profiles)))); + profiler->Start(); + profiler.reset(); + + // Wait longer than a sample interval to catch any use-after-free actions by + // the profiler thread. + PlatformThread::Sleep(TimeDelta::FromMilliseconds(50)); + }); +} + +} // namespace base diff --git a/chromium/base/profiler/stack_sampling_profiler_win.cc b/chromium/base/profiler/stack_sampling_profiler_win.cc new file mode 100644 index 00000000000..1ccd13412a6 --- /dev/null +++ b/chromium/base/profiler/stack_sampling_profiler_win.cc @@ -0,0 +1,357 @@ +// 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 <objbase.h> +#include <windows.h> + +#include <map> +#include <utility> + +#include "base/logging.h" +#include "base/profiler/native_stack_sampler.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "base/time/time.h" +#include "base/win/pe_image.h" +#include "base/win/scoped_handle.h" + +namespace base { + +namespace { + +// Walks the stack represented by |context| from the current frame downwards, +// recording the instruction pointers for each frame in |instruction_pointers|. +int RecordStack(CONTEXT* context, + int max_stack_size, + const void* instruction_pointers[], + bool* last_frame_is_unknown_function) { +#ifdef _WIN64 + *last_frame_is_unknown_function = false; + + int i = 0; + for (; (i < max_stack_size) && context->Rip; ++i) { + // Try to look up unwind metadata for the current function. + ULONG64 image_base; + PRUNTIME_FUNCTION runtime_function = + RtlLookupFunctionEntry(context->Rip, &image_base, nullptr); + + instruction_pointers[i] = reinterpret_cast<const void*>(context->Rip); + + if (runtime_function) { + KNONVOLATILE_CONTEXT_POINTERS nvcontext = {0}; + void* handler_data; + ULONG64 establisher_frame; + RtlVirtualUnwind(0, image_base, context->Rip, runtime_function, context, + &handler_data, &establisher_frame, &nvcontext); + } else { + // If we don't have a RUNTIME_FUNCTION, then in theory this should be a + // leaf function whose frame contains only a return address, at + // RSP. However, crash data also indicates that some third party libraries + // do not provide RUNTIME_FUNCTION information for non-leaf functions. We + // could manually unwind the stack in the former case, but attempting to + // do so in the latter case would produce wrong results and likely crash, + // so just bail out. + // + // Ad hoc runs with instrumentation show that ~5% of stack traces end with + // a valid leaf function. To avoid selectively omitting these traces it + // makes sense to ultimately try to distinguish these two cases and + // selectively unwind the stack for legitimate leaf functions. For the + // purposes of avoiding crashes though, just ignore them all for now. + return i; + } + } + return i; +#else + return 0; +#endif +} + +// Fills in |module_handles| corresponding to the pointers to code in +// |addresses|. The module handles are returned with reference counts +// incremented and should be freed with FreeModuleHandles. See note in +// SuspendThreadAndRecordStack for why |addresses| and |module_handles| are +// arrays. +void FindModuleHandlesForAddresses(const void* const addresses[], + HMODULE module_handles[], int stack_depth, + bool last_frame_is_unknown_function) { + const int module_frames = + last_frame_is_unknown_function ? stack_depth - 1 : stack_depth; + for (int i = 0; i < module_frames; ++i) { + HMODULE module_handle = NULL; + if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, + reinterpret_cast<LPCTSTR>(addresses[i]), + &module_handle)) { + // HMODULE actually represents the base address of the module, so we can + // use it directly as an address. + DCHECK_LE(reinterpret_cast<const void*>(module_handle), addresses[i]); + module_handles[i] = module_handle; + } + } +} + +// Frees the modules handles returned by FindModuleHandlesForAddresses. See note +// in SuspendThreadAndRecordStack for why |module_handles| is an array. +void FreeModuleHandles(int stack_depth, HMODULE module_handles[]) { + for (int i = 0; i < stack_depth; ++i) { + if (module_handles[i]) + ::FreeLibrary(module_handles[i]); + } +} + +// Gets the unique build ID for a module. Windows build IDs are created by a +// concatenation of a GUID and AGE fields found in the headers of a module. The +// GUID is stored in the first 16 bytes and the AGE is stored in the last 4 +// bytes. Returns the empty string if the function fails to get the build ID. +// +// Example: +// dumpbin chrome.exe /headers | find "Format:" +// ... Format: RSDS, {16B2A428-1DED-442E-9A36-FCE8CBD29726}, 10, ... +// +// The resulting buildID string of this instance of chrome.exe is +// "16B2A4281DED442E9A36FCE8CBD2972610". +// +// Note that the AGE field is encoded in decimal, not hex. +std::string GetBuildIDForModule(HMODULE module_handle) { + GUID guid; + DWORD age; + win::PEImage(module_handle).GetDebugId(&guid, &age); + const int kGUIDSize = 39; + std::wstring build_id; + int result = + ::StringFromGUID2(guid, WriteInto(&build_id, kGUIDSize), kGUIDSize); + if (result != kGUIDSize) + return std::string(); + RemoveChars(build_id, L"{}-", &build_id); + build_id += StringPrintf(L"%d", age); + return WideToUTF8(build_id); +} + +// Disables priority boost on a thread for the lifetime of the object. +class ScopedDisablePriorityBoost { + public: + ScopedDisablePriorityBoost(HANDLE thread_handle); + ~ScopedDisablePriorityBoost(); + + private: + HANDLE thread_handle_; + BOOL got_previous_boost_state_; + BOOL boost_state_was_disabled_; + + DISALLOW_COPY_AND_ASSIGN(ScopedDisablePriorityBoost); +}; + +ScopedDisablePriorityBoost::ScopedDisablePriorityBoost(HANDLE thread_handle) + : thread_handle_(thread_handle), + got_previous_boost_state_(false), + boost_state_was_disabled_(false) { + got_previous_boost_state_ = + ::GetThreadPriorityBoost(thread_handle_, &boost_state_was_disabled_); + if (got_previous_boost_state_) { + // Confusingly, TRUE disables priority boost. + ::SetThreadPriorityBoost(thread_handle_, TRUE); + } +} + +ScopedDisablePriorityBoost::~ScopedDisablePriorityBoost() { + if (got_previous_boost_state_) + ::SetThreadPriorityBoost(thread_handle_, boost_state_was_disabled_); +} + +// Suspends the thread with |thread_handle|, records the stack into +// |instruction_pointers|, then resumes the thread. Returns the size of the +// stack. +// +// IMPORTANT NOTE: No heap allocations may occur between SuspendThread and +// ResumeThread. Otherwise this code can deadlock on heap locks acquired by the +// target thread before it was suspended. This is why we pass instruction +// pointers and module handles as preallocated arrays rather than vectors, since +// vectors make it too easy to subtly allocate memory. +int SuspendThreadAndRecordStack(HANDLE thread_handle, int max_stack_size, + const void* instruction_pointers[], + bool* last_frame_is_unknown_function) { + if (::SuspendThread(thread_handle) == -1) + return 0; + + int stack_depth = 0; + CONTEXT thread_context = {0}; + thread_context.ContextFlags = CONTEXT_FULL; + if (::GetThreadContext(thread_handle, &thread_context)) { + stack_depth = RecordStack(&thread_context, max_stack_size, + instruction_pointers, + last_frame_is_unknown_function); + } + + // Disable the priority boost that the thread would otherwise receive on + // resume. We do this to avoid artificially altering the dynamics of the + // executing application any more than we already are by suspending and + // resuming the thread. + // + // Note that this can racily disable a priority boost that otherwise would + // have been given to the thread, if the thread is waiting on other wait + // conditions at the time of SuspendThread and those conditions are satisfied + // before priority boost is reenabled. The measured length of this window is + // ~100us, so this should occur fairly rarely. + ScopedDisablePriorityBoost disable_priority_boost(thread_handle); + bool resume_thread_succeeded = ::ResumeThread(thread_handle) != -1; + CHECK(resume_thread_succeeded) << "ResumeThread failed: " << GetLastError(); + + return stack_depth; +} + +class NativeStackSamplerWin : public NativeStackSampler { + public: + explicit NativeStackSamplerWin(win::ScopedHandle thread_handle); + ~NativeStackSamplerWin() override; + + // StackSamplingProfiler::NativeStackSampler: + void ProfileRecordingStarting( + std::vector<StackSamplingProfiler::Module>* modules) override; + void RecordStackSample(StackSamplingProfiler::Sample* sample) override; + void ProfileRecordingStopped() override; + + private: + // Attempts to query the module filename, base address, and id for + // |module_handle|, and store them in |module|. Returns true if it succeeded. + static bool GetModuleForHandle(HMODULE module_handle, + StackSamplingProfiler::Module* module); + + // Gets the index for the Module corresponding to |module_handle| in + // |modules|, adding it if it's not already present. Returns + // StackSamplingProfiler::Frame::kUnknownModuleIndex if no Module can be + // determined for |module|. + size_t GetModuleIndex(HMODULE module_handle, + std::vector<StackSamplingProfiler::Module>* modules); + + // Copies the stack information represented by |instruction_pointers| into + // |sample| and |modules|. + void CopyToSample(const void* const instruction_pointers[], + const HMODULE module_handles[], + int stack_depth, + StackSamplingProfiler::Sample* sample, + std::vector<StackSamplingProfiler::Module>* modules); + + win::ScopedHandle thread_handle_; + // Weak. Points to the modules associated with the profile being recorded + // between ProfileRecordingStarting() and ProfileRecordingStopped(). + std::vector<StackSamplingProfiler::Module>* current_modules_; + // Maps a module handle to the corresponding Module's index within + // current_modules_. + std::map<HMODULE, size_t> profile_module_index_; + + DISALLOW_COPY_AND_ASSIGN(NativeStackSamplerWin); +}; + +NativeStackSamplerWin::NativeStackSamplerWin(win::ScopedHandle thread_handle) + : thread_handle_(thread_handle.Take()) { +} + +NativeStackSamplerWin::~NativeStackSamplerWin() { +} + +void NativeStackSamplerWin::ProfileRecordingStarting( + std::vector<StackSamplingProfiler::Module>* modules) { + current_modules_ = modules; + profile_module_index_.clear(); +} + +void NativeStackSamplerWin::RecordStackSample( + StackSamplingProfiler::Sample* sample) { + DCHECK(current_modules_); + + const int max_stack_size = 64; + const void* instruction_pointers[max_stack_size] = {0}; + HMODULE module_handles[max_stack_size] = {0}; + + bool last_frame_is_unknown_function = false; + int stack_depth = SuspendThreadAndRecordStack( + thread_handle_.Get(), max_stack_size, instruction_pointers, + &last_frame_is_unknown_function); + FindModuleHandlesForAddresses(instruction_pointers, module_handles, + stack_depth, last_frame_is_unknown_function); + CopyToSample(instruction_pointers, module_handles, stack_depth, sample, + current_modules_); + FreeModuleHandles(stack_depth, module_handles); +} + +void NativeStackSamplerWin::ProfileRecordingStopped() { + current_modules_ = nullptr; +} + +// static +bool NativeStackSamplerWin::GetModuleForHandle( + HMODULE module_handle, + StackSamplingProfiler::Module* module) { + wchar_t module_name[MAX_PATH]; + DWORD result_length = + GetModuleFileName(module_handle, module_name, arraysize(module_name)); + if (result_length == 0) + return false; + + module->filename = base::FilePath(module_name); + + module->base_address = reinterpret_cast<const void*>(module_handle); + + module->id = GetBuildIDForModule(module_handle); + if (module->id.empty()) + return false; + + return true; +} + +size_t NativeStackSamplerWin::GetModuleIndex( + HMODULE module_handle, + std::vector<StackSamplingProfiler::Module>* modules) { + if (!module_handle) + return StackSamplingProfiler::Frame::kUnknownModuleIndex; + + auto loc = profile_module_index_.find(module_handle); + if (loc == profile_module_index_.end()) { + StackSamplingProfiler::Module module; + if (!GetModuleForHandle(module_handle, &module)) + return StackSamplingProfiler::Frame::kUnknownModuleIndex; + modules->push_back(module); + loc = profile_module_index_.insert(std::make_pair( + module_handle, modules->size() - 1)).first; + } + + return loc->second; +} + +void NativeStackSamplerWin::CopyToSample( + const void* const instruction_pointers[], + const HMODULE module_handles[], + int stack_depth, + StackSamplingProfiler::Sample* sample, + std::vector<StackSamplingProfiler::Module>* module) { + sample->clear(); + sample->reserve(stack_depth); + + for (int i = 0; i < stack_depth; ++i) { + sample->push_back(StackSamplingProfiler::Frame( + instruction_pointers[i], + GetModuleIndex(module_handles[i], module))); + } +} + +} // namespace + +scoped_ptr<NativeStackSampler> NativeStackSampler::Create( + PlatformThreadId thread_id) { +#if _WIN64 + // Get the thread's handle. + HANDLE thread_handle = ::OpenThread( + THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION, + FALSE, + thread_id); + + if (thread_handle) { + return scoped_ptr<NativeStackSampler>(new NativeStackSamplerWin( + win::ScopedHandle(thread_handle))); + } +#endif + return scoped_ptr<NativeStackSampler>(); +} + +} // namespace base diff --git a/chromium/base/profiler/tracked_time.cc b/chromium/base/profiler/tracked_time.cc index 7791c3adc6b..e5da68f5221 100644 --- a/chromium/base/profiler/tracked_time.cc +++ b/chromium/base/profiler/tracked_time.cc @@ -46,20 +46,12 @@ int32 Duration::InMilliseconds() const { return ms_; } TrackedTime::TrackedTime() : ms_(0) {} TrackedTime::TrackedTime(int32 ms) : ms_(ms) {} TrackedTime::TrackedTime(const base::TimeTicks& time) - : ms_((time - base::TimeTicks()).InMilliseconds()) { + : ms_(static_cast<int32>((time - base::TimeTicks()).InMilliseconds())) { } // static TrackedTime TrackedTime::Now() { -#if defined(OS_WIN) - // Use lock-free accessor to 32 bit time. - // Note that TimeTicks::Now() is built on this, so we have "compatible" - // times when we down-convert a TimeTicks sample. - return TrackedTime(base::TimeTicks::UnprotectedNow()); -#else - // Posix has nice cheap 64 bit times, so we just down-convert it. return TrackedTime(base::TimeTicks::Now()); -#endif // OS_WIN } Duration TrackedTime::operator-(const TrackedTime& other) const { diff --git a/chromium/base/profiler/tracked_time_unittest.cc b/chromium/base/profiler/tracked_time_unittest.cc index 3fa93830af0..c105688b31d 100644 --- a/chromium/base/profiler/tracked_time_unittest.cc +++ b/chromium/base/profiler/tracked_time_unittest.cc @@ -70,17 +70,14 @@ TEST(TrackedTimeTest, TrackedTimerVsTimeTicks) { TEST(TrackedTimeTest, TrackedTimerDisabled) { // Check to be sure disabling the collection of data induces a null time // (which we know will return much faster). - if (!ThreadData::InitializeAndSetTrackingStatus(ThreadData::DEACTIVATED)) - return; + ThreadData::InitializeAndSetTrackingStatus(ThreadData::DEACTIVATED); // Since we disabled tracking, we should get a null response. TrackedTime track_now = ThreadData::Now(); EXPECT_TRUE(track_now.is_null()); } TEST(TrackedTimeTest, TrackedTimerEnabled) { - if (!ThreadData::InitializeAndSetTrackingStatus( - ThreadData::PROFILING_CHILDREN_ACTIVE)) - return; + ThreadData::InitializeAndSetTrackingStatus(ThreadData::PROFILING_ACTIVE); // Make sure that when we enable tracking, we get a real timer result. // First get a 64 bit timer (which should not be null). diff --git a/chromium/base/rand_util_unittest.cc b/chromium/base/rand_util_unittest.cc index d26227505d0..90690ec2dbb 100644 --- a/chromium/base/rand_util_unittest.cc +++ b/chromium/base/rand_util_unittest.cc @@ -134,10 +134,10 @@ TEST(RandUtilTest, DISABLED_RandBytesPerf) { const size_t kTestBufferSize = 1 * 1024 * 1024; scoped_ptr<uint8[]> buffer(new uint8[kTestBufferSize]); - const base::TimeTicks now = base::TimeTicks::HighResNow(); + const base::TimeTicks now = base::TimeTicks::Now(); for (int i = 0; i < kTestIterations; ++i) base::RandBytes(buffer.get(), kTestBufferSize); - const base::TimeTicks end = base::TimeTicks::HighResNow(); + const base::TimeTicks end = base::TimeTicks::Now(); LOG(INFO) << "RandBytes(" << kTestBufferSize << ") took: " << (end - now).InMicroseconds() << "µs"; diff --git a/chromium/base/scoped_generic.h b/chromium/base/scoped_generic.h index da42609e5c4..f6807e2b58f 100644 --- a/chromium/base/scoped_generic.h +++ b/chromium/base/scoped_generic.h @@ -53,7 +53,7 @@ namespace base { // typedef ScopedGeneric<int, FooScopedTraits> ScopedFoo; template<typename T, typename Traits> class ScopedGeneric { - MOVE_ONLY_TYPE_FOR_CPP_03(ScopedGeneric, RValue) + MOVE_ONLY_TYPE_WITH_MOVE_CONSTRUCTOR_FOR_CPP_03(ScopedGeneric) private: // This must be first since it's used inline below. @@ -83,15 +83,21 @@ class ScopedGeneric { : data_(value, traits) { } - // Move constructor for C++03 move emulation. - ScopedGeneric(RValue rvalue) - : data_(rvalue.object->release(), rvalue.object->get_traits()) { + // Move constructor. Allows initialization from a ScopedGeneric rvalue. + ScopedGeneric(ScopedGeneric<T, Traits>&& rvalue) + : data_(rvalue.release(), rvalue.get_traits()) { } ~ScopedGeneric() { FreeIfNecessary(); } + // operator=. Allows assignment from a ScopedGeneric rvalue. + ScopedGeneric& operator=(ScopedGeneric<T, Traits>&& rvalue) { + reset(rvalue.release()); + return *this; + } + // Frees the currently owned object, if any. Then takes ownership of a new // object, if given. Self-resets are not allowd as on scoped_ptr. See // http://crbug.com/162971 diff --git a/chromium/base/scoped_generic_unittest.cc b/chromium/base/scoped_generic_unittest.cc index f0dca22e693..b28e154372e 100644 --- a/chromium/base/scoped_generic_unittest.cc +++ b/chromium/base/scoped_generic_unittest.cc @@ -85,7 +85,7 @@ TEST(ScopedGenericTest, ScopedGeneric) { EXPECT_EQ(kSecond, values_freed[1]); values_freed.clear(); - // Pass. + // Pass constructor. { ScopedInt a(kFirst, traits); ScopedInt b(a.Pass()); @@ -93,8 +93,25 @@ TEST(ScopedGenericTest, ScopedGeneric) { ASSERT_EQ(IntTraits::InvalidValue(), a.get()); ASSERT_EQ(kFirst, b.get()); } + ASSERT_EQ(1u, values_freed.size()); ASSERT_EQ(kFirst, values_freed[0]); + values_freed.clear(); + + // Pass assign. + { + ScopedInt a(kFirst, traits); + ScopedInt b(kSecond, traits); + b = a.Pass(); + ASSERT_EQ(1u, values_freed.size()); + EXPECT_EQ(kSecond, values_freed[0]); + ASSERT_EQ(IntTraits::InvalidValue(), a.get()); + ASSERT_EQ(kFirst, b.get()); + } + + ASSERT_EQ(2u, values_freed.size()); + EXPECT_EQ(kFirst, values_freed[1]); + values_freed.clear(); } TEST(ScopedGenericTest, Operators) { diff --git a/chromium/base/scoped_native_library.h b/chromium/base/scoped_native_library.h index e9923f43424..c0e93f37212 100644 --- a/chromium/base/scoped_native_library.h +++ b/chromium/base/scoped_native_library.h @@ -49,4 +49,4 @@ class BASE_EXPORT ScopedNativeLibrary { } // namespace base -#endif // BASE_MEMORY_NATIVE_LIBRARY_H_ +#endif // BASE_SCOPED_NATIVE_LIBRARY_H_ diff --git a/chromium/base/scoped_observer.h b/chromium/base/scoped_observer.h index 5b0d53353ba..422701bc219 100644 --- a/chromium/base/scoped_observer.h +++ b/chromium/base/scoped_observer.h @@ -48,6 +48,8 @@ class ScopedObserver { sources_.end(); } + bool IsObservingSources() const { return !sources_.empty(); } + private: Observer* observer_; diff --git a/chromium/base/security_unittest.cc b/chromium/base/security_unittest.cc index a6d3480ef55..07ba6f5a0f2 100644 --- a/chromium/base/security_unittest.cc +++ b/chromium/base/security_unittest.cc @@ -23,17 +23,53 @@ #include <unistd.h> #endif +#if defined(OS_WIN) +#include <new.h> +#endif + using std::nothrow; using std::numeric_limits; namespace { +#if defined(OS_WIN) +// This is a permitted size but exhausts memory pretty quickly. +const size_t kLargePermittedAllocation = 0x7FFFE000; + +int OnNoMemory(size_t) { + _exit(1); +} + +void ExhaustMemoryWithMalloc() { + for (;;) { + // Without the |volatile|, clang optimizes away the allocation. + void* volatile buf = malloc(kLargePermittedAllocation); + if (!buf) + break; + } +} + +void ExhaustMemoryWithRealloc() { + size_t size = kLargePermittedAllocation; + void* buf = malloc(size); + if (!buf) + return; + for (;;) { + size += kLargePermittedAllocation; + void* new_buf = realloc(buf, size); + if (!buf) + break; + buf = new_buf; + } +} +#endif + // This function acts as a compiler optimization barrier. We use it to // prevent the compiler from making an expression a compile-time constant. // We also use it so that the compiler doesn't discard certain return values // as something we don't need (see the comment with calloc below). template <typename Type> -Type HideValueFromCompiler(volatile Type value) { +NOINLINE Type HideValueFromCompiler(volatile Type value) { #if defined(__GNUC__) // In a GCC compatible compiler (GCC or Clang), make this compiler barrier // more robust than merely using "volatile". @@ -42,15 +78,18 @@ Type HideValueFromCompiler(volatile Type value) { return value; } +// Tcmalloc and Windows allocator shim support setting malloc limits. // - NO_TCMALLOC (should be defined if compiled with use_allocator!="tcmalloc") // - ADDRESS_SANITIZER and SYZYASAN because they have their own memory allocator // - IOS does not use tcmalloc // - OS_MACOSX does not use tcmalloc -#if !defined(NO_TCMALLOC) && !defined(ADDRESS_SANITIZER) && \ - !defined(OS_IOS) && !defined(OS_MACOSX) && !defined(SYZYASAN) - #define TCMALLOC_TEST(function) function +// - Windows allocator shim defines ALLOCATOR_SHIM +#if (!defined(NO_TCMALLOC) || defined(ALLOCATOR_SHIM)) && \ + !defined(ADDRESS_SANITIZER) && !defined(OS_IOS) && !defined(OS_MACOSX) && \ + !defined(SYZYASAN) +#define MALLOC_OVERFLOW_TEST(function) function #else - #define TCMALLOC_TEST(function) DISABLED_##function +#define MALLOC_OVERFLOW_TEST(function) DISABLED_##function #endif // TODO(jln): switch to std::numeric_limits<int>::max() when we switch to @@ -64,12 +103,6 @@ bool IsTcMallocBypassed() { char* g_slice = getenv("G_SLICE"); if (g_slice && !strcmp(g_slice, "always-malloc")) return true; -#elif defined(OS_WIN) - // This should detect a TCMalloc bypass from setting - // the CHROME_ALLOCATOR environment variable. - char* allocator = getenv("CHROME_ALLOCATOR"); - if (allocator && strcmp(allocator, "tcmalloc")) - return true; #endif return false; } @@ -89,7 +122,7 @@ bool CallocDiesOnOOM() { } // Fake test that allow to know the state of TCMalloc by looking at bots. -TEST(SecurityTest, TCMALLOC_TEST(IsTCMallocDynamicallyBypassed)) { +TEST(SecurityTest, MALLOC_OVERFLOW_TEST(IsTCMallocDynamicallyBypassed)) { printf("Malloc is dynamically bypassed: %s\n", IsTcMallocBypassed() ? "yes." : "no."); } @@ -99,7 +132,7 @@ TEST(SecurityTest, TCMALLOC_TEST(IsTCMallocDynamicallyBypassed)) { // vulnerabilities in libraries that use int instead of size_t. See // crbug.com/169327. -TEST(SecurityTest, TCMALLOC_TEST(MemoryAllocationRestrictionsMalloc)) { +TEST(SecurityTest, MALLOC_OVERFLOW_TEST(MemoryAllocationRestrictionsMalloc)) { if (!IsTcMallocBypassed()) { scoped_ptr<char, base::FreeDeleter> ptr(static_cast<char*>( HideValueFromCompiler(malloc(kTooBigAllocSize)))); @@ -107,7 +140,43 @@ TEST(SecurityTest, TCMALLOC_TEST(MemoryAllocationRestrictionsMalloc)) { } } -TEST(SecurityTest, TCMALLOC_TEST(MemoryAllocationRestrictionsCalloc)) { +#if defined(GTEST_HAS_DEATH_TEST) && defined(OS_WIN) +TEST(SecurityTest, MALLOC_OVERFLOW_TEST(MemoryAllocationMallocDeathTest)) { + _set_new_handler(&OnNoMemory); + _set_new_mode(1); + { + scoped_ptr<char, base::FreeDeleter> ptr; + EXPECT_DEATH(ptr.reset(static_cast<char*>( + HideValueFromCompiler(malloc(kTooBigAllocSize)))), + ""); + ASSERT_TRUE(!ptr); + } + _set_new_handler(NULL); + _set_new_mode(0); +} + +TEST(SecurityTest, MALLOC_OVERFLOW_TEST(MemoryAllocationExhaustDeathTest)) { + _set_new_handler(&OnNoMemory); + _set_new_mode(1); + { + ASSERT_DEATH(ExhaustMemoryWithMalloc(), ""); + } + _set_new_handler(NULL); + _set_new_mode(0); +} + +TEST(SecurityTest, MALLOC_OVERFLOW_TEST(MemoryReallocationExhaustDeathTest)) { + _set_new_handler(&OnNoMemory); + _set_new_mode(1); + { + ASSERT_DEATH(ExhaustMemoryWithRealloc(), ""); + } + _set_new_handler(NULL); + _set_new_mode(0); +} +#endif + +TEST(SecurityTest, MALLOC_OVERFLOW_TEST(MemoryAllocationRestrictionsCalloc)) { if (!IsTcMallocBypassed()) { scoped_ptr<char, base::FreeDeleter> ptr(static_cast<char*>( HideValueFromCompiler(calloc(kTooBigAllocSize, 1)))); @@ -115,7 +184,7 @@ TEST(SecurityTest, TCMALLOC_TEST(MemoryAllocationRestrictionsCalloc)) { } } -TEST(SecurityTest, TCMALLOC_TEST(MemoryAllocationRestrictionsRealloc)) { +TEST(SecurityTest, MALLOC_OVERFLOW_TEST(MemoryAllocationRestrictionsRealloc)) { if (!IsTcMallocBypassed()) { char* orig_ptr = static_cast<char*>(malloc(1)); ASSERT_TRUE(orig_ptr); @@ -131,7 +200,7 @@ typedef struct { char large_array[kTooBigAllocSize]; } VeryLargeStruct; -TEST(SecurityTest, TCMALLOC_TEST(MemoryAllocationRestrictionsNew)) { +TEST(SecurityTest, MALLOC_OVERFLOW_TEST(MemoryAllocationRestrictionsNew)) { if (!IsTcMallocBypassed()) { scoped_ptr<VeryLargeStruct> ptr( HideValueFromCompiler(new (nothrow) VeryLargeStruct)); @@ -139,7 +208,20 @@ TEST(SecurityTest, TCMALLOC_TEST(MemoryAllocationRestrictionsNew)) { } } -TEST(SecurityTest, TCMALLOC_TEST(MemoryAllocationRestrictionsNewArray)) { +#if defined(GTEST_HAS_DEATH_TEST) && defined(OS_WIN) +TEST(SecurityTest, MALLOC_OVERFLOW_TEST(MemoryAllocationNewDeathTest)) { + _set_new_handler(&OnNoMemory); + { + scoped_ptr<VeryLargeStruct> ptr; + EXPECT_DEATH( + ptr.reset(HideValueFromCompiler(new (nothrow) VeryLargeStruct)), ""); + ASSERT_TRUE(!ptr); + } + _set_new_handler(NULL); +} +#endif + +TEST(SecurityTest, MALLOC_OVERFLOW_TEST(MemoryAllocationRestrictionsNewArray)) { if (!IsTcMallocBypassed()) { scoped_ptr<char[]> ptr( HideValueFromCompiler(new (nothrow) char[kTooBigAllocSize])); @@ -194,7 +276,9 @@ TEST(SecurityTest, MAYBE_NewOverflow) { } // On windows, the compiler prevents static array sizes of more than // 0x7fffffff (error C2148). -#if !defined(OS_WIN) || !defined(ARCH_CPU_64_BITS) +#if defined(OS_WIN) && defined(ARCH_CPU_64_BITS) + ALLOW_UNUSED_LOCAL(kDynamicArraySize); +#else { scoped_ptr<char[][kArraySize2]> array_pointer(new (nothrow) char[kDynamicArraySize][kArraySize2]); @@ -209,8 +293,8 @@ bool CallocReturnsNull(size_t nmemb, size_t size) { scoped_ptr<char, base::FreeDeleter> array_pointer( static_cast<char*>(calloc(nmemb, size))); // We need the call to HideValueFromCompiler(): we have seen LLVM - // optimize away the call to calloc() entirely and assume - // the pointer to not be NULL. + // optimize away the call to calloc() entirely and assume the pointer to not + // be NULL. return HideValueFromCompiler(array_pointer.get()) == NULL; } @@ -240,7 +324,7 @@ bool ArePointersToSameArea(void* ptr1, void* ptr2, size_t size) { } // Check if TCMalloc uses an underlying random memory allocator. -TEST(SecurityTest, TCMALLOC_TEST(RandomMemoryAllocations)) { +TEST(SecurityTest, MALLOC_OVERFLOW_TEST(RandomMemoryAllocations)) { if (IsTcMallocBypassed()) return; size_t kPageSize = 4096; // We support x86_64 only. diff --git a/chromium/base/sequence_checker_unittest.cc b/chromium/base/sequence_checker_unittest.cc index ad77db0999d..0aa0f9cdbb7 100644 --- a/chromium/base/sequence_checker_unittest.cc +++ b/chromium/base/sequence_checker_unittest.cc @@ -9,8 +9,8 @@ #include "base/logging.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" -#include "base/message_loop/message_loop.h" #include "base/sequence_checker.h" +#include "base/single_thread_task_runner.h" #include "base/test/sequenced_worker_pool_owner.h" #include "base/threading/thread.h" #include "testing/gtest/include/gtest/gtest.h" @@ -56,14 +56,12 @@ class SequenceCheckerTest : public testing::Test { public: SequenceCheckerTest() : other_thread_("sequence_checker_test_other_thread") {} - virtual ~SequenceCheckerTest() {} - - virtual void SetUp() override { + void SetUp() override { other_thread_.Start(); ResetPool(); } - virtual void TearDown() override { + void TearDown() override { other_thread_.Stop(); pool()->Shutdown(); } @@ -86,10 +84,9 @@ class SequenceCheckerTest : public testing::Test { void PostDoStuffToOtherThread( SequenceCheckedObject* sequence_checked_object) { - other_thread()->message_loop()->PostTask( - FROM_HERE, - base::Bind(&SequenceCheckedObject::DoStuff, - base::Unretained(sequence_checked_object))); + other_thread()->task_runner()->PostTask( + FROM_HERE, base::Bind(&SequenceCheckedObject::DoStuff, + base::Unretained(sequence_checked_object))); } void PostDeleteToOtherThread( diff --git a/chromium/base/sequenced_task_runner.h b/chromium/base/sequenced_task_runner.h index 71dcf05c750..6bb3f2b8717 100644 --- a/chromium/base/sequenced_task_runner.h +++ b/chromium/base/sequenced_task_runner.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_SEQUENCED_TASKRUNNER_H_ -#define BASE_SEQUENCED_TASKRUNNER_H_ +#ifndef BASE_SEQUENCED_TASK_RUNNER_H_ +#define BASE_SEQUENCED_TASK_RUNNER_H_ #include "base/base_export.h" #include "base/sequenced_task_runner_helpers.h" @@ -156,4 +156,4 @@ class BASE_EXPORT SequencedTaskRunner : public TaskRunner { } // namespace base -#endif // BASE_SEQUENCED_TASKRUNNER_H_ +#endif // BASE_SEQUENCED_TASK_RUNNER_H_ diff --git a/chromium/base/single_thread_task_runner.h b/chromium/base/single_thread_task_runner.h index b5311db1694..6e931931489 100644 --- a/chromium/base/single_thread_task_runner.h +++ b/chromium/base/single_thread_task_runner.h @@ -16,7 +16,8 @@ namespace base { // there is a specific need to run tasks on only a single thread. // // SingleThreadTaskRunner implementations might: -// - Post tasks to an existing thread's MessageLoop (see MessageLoopProxy). +// - Post tasks to an existing thread's MessageLoop (see +// MessageLoop::task_runner()). // - Create their own worker thread and MessageLoop to post tasks to. // - Add tasks to a FIFO and signal to a non-MessageLoop thread for them to // be processed. This allows TaskRunner-oriented code run on threads diff --git a/chromium/base/stl_util.h b/chromium/base/stl_util.h index 3602df0377a..e937d2f3ed9 100644 --- a/chromium/base/stl_util.h +++ b/chromium/base/stl_util.h @@ -90,6 +90,14 @@ void STLDeleteContainerPairSecondPointers(ForwardIterator begin, } } +// Counts the number of instances of val in a container. +template <typename Container, typename T> +typename std::iterator_traits< + typename Container::const_iterator>::difference_type +STLCount(const Container& container, const T& val) { + return std::count(container.begin(), container.end(), val); +} + // To treat a possibly-empty vector as an array, use these functions. // If you know the array will never be empty, you can use &*v.begin() // directly, but that is undefined behaviour if |v| is empty. @@ -148,8 +156,7 @@ template <class T> void STLDeleteValues(T* container) { if (!container) return; - for (typename T::iterator i(container->begin()); i != container->end(); ++i) - delete i->second; + STLDeleteContainerPairSecondPointers(container->begin(), container->end()); container->clear(); } @@ -196,6 +203,14 @@ bool ContainsKey(const Collection& collection, const Key& key) { return collection.find(key) != collection.end(); } +// Test to see if a collection like a vector contains a particular value. +// Returns true if the value is in the collection. +template <typename Collection, typename Value> +bool ContainsValue(const Collection& collection, const Value& value) { + return std::find(collection.begin(), collection.end(), value) != + collection.end(); +} + namespace base { // Returns true if the container is sorted. diff --git a/chromium/base/stl_util_unittest.cc b/chromium/base/stl_util_unittest.cc index a3f8e16f2ce..42004eb869b 100644 --- a/chromium/base/stl_util_unittest.cc +++ b/chromium/base/stl_util_unittest.cc @@ -28,7 +28,7 @@ class ComparableValue { int value_; }; -} +} // namespace namespace base { namespace { @@ -238,5 +238,30 @@ TEST(STLUtilTest, STLIncludes) { EXPECT_TRUE(STLIncludes<std::set<int> >(a3, a2)); } +TEST(StringAsArrayTest, Empty) { + std::string empty; + EXPECT_EQ(nullptr, string_as_array(&empty)); +} + +TEST(StringAsArrayTest, NullTerminated) { + // If any std::string implementation is not null-terminated, this should + // fail. All compilers we use return a null-terminated buffer, but please do + // not rely on this fact in your code. + std::string str("abcde"); + str.resize(3); + EXPECT_STREQ("abc", string_as_array(&str)); +} + +TEST(StringAsArrayTest, WriteCopy) { + // With a COW implementation, this test will fail if + // string_as_array(&str) is implemented as + // const_cast<char*>(str->data()). + std::string s1("abc"); + const std::string s2(s1); + string_as_array(&s1)[1] = 'x'; + EXPECT_EQ("axc", s1); + EXPECT_EQ("abc", s2); +} + } // namespace } // namespace base diff --git a/chromium/base/strings/nullable_string16.h b/chromium/base/strings/nullable_string16.h index 5997d174419..016c25c2501 100644 --- a/chromium/base/strings/nullable_string16.h +++ b/chromium/base/strings/nullable_string16.h @@ -41,6 +41,6 @@ inline bool operator!=(const NullableString16& a, const NullableString16& b) { BASE_EXPORT std::ostream& operator<<(std::ostream& out, const NullableString16& value); -} // namespace +} // namespace base #endif // BASE_STRINGS_NULLABLE_STRING16_H_ diff --git a/chromium/base/strings/safe_sprintf.cc b/chromium/base/strings/safe_sprintf.cc index 5b575635519..b1fcf45b24f 100644 --- a/chromium/base/strings/safe_sprintf.cc +++ b/chromium/base/strings/safe_sprintf.cc @@ -510,11 +510,11 @@ ssize_t SafeSNPrintf(char* buf, size_t sz, const char* fmt, const Arg* args, buffer.Pad(' ', padding, 1); // Convert the argument to an ASCII character and output it. - char ch = static_cast<char>(arg.integer.i); - if (!ch) { + char as_char = static_cast<char>(arg.integer.i); + if (!as_char) { goto end_of_output_buffer; } - buffer.Out(ch); + buffer.Out(as_char); break; } case 'd': // Output a possibly signed decimal value. case 'o': // Output an unsigned octal value. diff --git a/chromium/base/strings/safe_sprintf_unittest.cc b/chromium/base/strings/safe_sprintf_unittest.cc index 02c75f990d1..ff05c6e2990 100644 --- a/chromium/base/strings/safe_sprintf_unittest.cc +++ b/chromium/base/strings/safe_sprintf_unittest.cc @@ -61,7 +61,7 @@ TEST(SafeSPrintfTest, NoArguments) { // always add a trailing NUL; it always deduplicates '%' characters). static const char text[] = "hello world"; char ref[20], buf[20]; - memset(ref, 'X', sizeof(char) * arraysize(buf)); + memset(ref, 'X', sizeof(ref)); memcpy(buf, ref, sizeof(buf)); // A negative buffer size should always result in an error. diff --git a/chromium/base/strings/string16.h b/chromium/base/strings/string16.h index 804dca42e45..1a01a9613e7 100644 --- a/chromium/base/strings/string16.h +++ b/chromium/base/strings/string16.h @@ -94,7 +94,7 @@ struct string16_char_traits { return c16memchr(s, a, n); } - static char_type* move(char_type* s1, const char_type* s2, int_type n) { + static char_type* move(char_type* s1, const char_type* s2, size_t n) { return c16memmove(s1, s2, n); } diff --git a/chromium/base/strings/string_piece_unittest.cc b/chromium/base/strings/string_piece_unittest.cc index 7f50cfbf679..53366036d6f 100644 --- a/chromium/base/strings/string_piece_unittest.cc +++ b/chromium/base/strings/string_piece_unittest.cc @@ -602,13 +602,13 @@ TYPED_TEST(CommonStringPieceTest, CheckComparisons2) { // check comparison operations on strings longer than 4 bytes. ASSERT_TRUE(abc == BasicStringPiece<TypeParam>(alphabet)); - ASSERT_TRUE(abc.compare(BasicStringPiece<TypeParam>(alphabet)) == 0); + ASSERT_EQ(abc.compare(BasicStringPiece<TypeParam>(alphabet)), 0); ASSERT_TRUE(abc < BasicStringPiece<TypeParam>(alphabet_z)); - ASSERT_TRUE(abc.compare(BasicStringPiece<TypeParam>(alphabet_z)) < 0); + ASSERT_LT(abc.compare(BasicStringPiece<TypeParam>(alphabet_z)), 0); ASSERT_TRUE(abc > BasicStringPiece<TypeParam>(alphabet_y)); - ASSERT_TRUE(abc.compare(BasicStringPiece<TypeParam>(alphabet_y)) > 0); + ASSERT_GT(abc.compare(BasicStringPiece<TypeParam>(alphabet_y)), 0); } // Test operations only supported by std::string version. diff --git a/chromium/base/strings/string_split.cc b/chromium/base/strings/string_split.cc index 7ef4760c58f..88a623664fc 100644 --- a/chromium/base/strings/string_split.cc +++ b/chromium/base/strings/string_split.cc @@ -6,7 +6,6 @@ #include "base/logging.h" #include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" #include "base/third_party/icu/icu_utf.h" namespace base { @@ -138,9 +137,9 @@ void SplitString(const std::string& str, char c, std::vector<std::string>* r) { #if CHAR_MIN < 0 - DCHECK(c >= 0); + DCHECK_GE(c, 0); #endif - DCHECK(c < 0x7F); + DCHECK_LT(c, 0x7F); SplitStringT(str, c, true, r); } @@ -193,11 +192,10 @@ void SplitStringDontTrim(const string16& str, void SplitStringDontTrim(const std::string& str, char c, std::vector<std::string>* r) { - DCHECK(IsStringUTF8(str)); #if CHAR_MIN < 0 - DCHECK(c >= 0); + DCHECK_GE(c, 0); #endif - DCHECK(c < 0x7F); + DCHECK_LT(c, 0x7F); SplitStringT(str, c, false, r); } diff --git a/chromium/base/strings/string_util.cc b/chromium/base/strings/string_util.cc index 43f15a360d9..e084cb5597d 100644 --- a/chromium/base/strings/string_util.cc +++ b/chromium/base/strings/string_util.cc @@ -406,7 +406,7 @@ bool IsStringASCII(const std::wstring& str) { } #endif -bool IsStringUTF8(const std::string& str) { +bool IsStringUTF8(const StringPiece& str) { const char *src = str.data(); int32 src_len = static_cast<int32>(str.length()); int32 char_index = 0; @@ -556,23 +556,103 @@ string16 FormatBytesUnlocalized(int64 bytes) { return base::ASCIIToUTF16(buf); } +// Runs in O(n) time in the length of |str|. template<class StringType> void DoReplaceSubstringsAfterOffset(StringType* str, - size_t start_offset, + size_t offset, const StringType& find_this, const StringType& replace_with, bool replace_all) { - if ((start_offset == StringType::npos) || (start_offset >= str->length())) + DCHECK(!find_this.empty()); + + // If the find string doesn't appear, there's nothing to do. + offset = str->find(find_this, offset); + if (offset == StringType::npos) return; - DCHECK(!find_this.empty()); - for (size_t offs(str->find(find_this, start_offset)); - offs != StringType::npos; offs = str->find(find_this, offs)) { - str->replace(offs, find_this.length(), replace_with); - offs += replace_with.length(); + // If we're only replacing one instance, there's no need to do anything + // complicated. + size_t find_length = find_this.length(); + if (!replace_all) { + str->replace(offset, find_length, replace_with); + return; + } - if (!replace_all) - break; + // If the find and replace strings are the same length, we can simply use + // replace() on each instance, and finish the entire operation in O(n) time. + 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); + } while (offset != StringType::npos); + return; + } + + // Since the find and replace strings aren't the same length, a loop like the + // one above would be O(n^2) in the worst case, as replace() will shift the + // entire remaining string each time. We need to be more clever to keep + // things O(n). + // + // If we're shortening the string, we can alternate replacements with shifting + // forward the intervening characters using memmove(). + size_t str_length = str->length(); + if (find_length > replace_length) { + size_t write_offset = offset; + do { + if (replace_length) { + str->replace(write_offset, replace_length, replace_with); + write_offset += replace_length; + } + size_t read_offset = offset + find_length; + offset = std::min(str->find(find_this, read_offset), str_length); + size_t length = offset - read_offset; + if (length) { + memmove(&(*str)[write_offset], &(*str)[read_offset], + length * sizeof(typename StringType::value_type)); + write_offset += length; + } + } while (offset < str_length); + str->resize(write_offset); + return; + } + + // We're lengthening the string. We can use alternating replacements and + // memmove() calls like above, but we need to precalculate the final string + // length and then expand from back-to-front to avoid overwriting the string + // as we're reading it, needing to shift, or having to copy to a second string + // temporarily. + size_t first_match = offset; + + // First, calculate the final length and resize the string. + size_t final_length = str_length; + size_t expansion = replace_length - find_length; + size_t current_match; + do { + final_length += expansion; + // Minor optimization: save this offset into |current_match|, so that on + // 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); + } 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)) { + size_t read_offset = current_match + find_length; + size_t length = prev_match - read_offset; + if (length) { + write_offset -= length; + memmove(&(*str)[write_offset], &(*str)[read_offset], + length * sizeof(typename StringType::value_type)); + } + write_offset -= replace_length; + str->replace(write_offset, replace_length, replace_with); + if (current_match == first_match) + return; + prev_match = current_match; } } diff --git a/chromium/base/strings/string_util.h b/chromium/base/strings/string_util.h index 1e2ac700e10..5ab2ad5d547 100644 --- a/chromium/base/strings/string_util.h +++ b/chromium/base/strings/string_util.h @@ -248,7 +248,7 @@ BASE_EXPORT bool ContainsOnlyChars(const StringPiece16& input, // // IsStringASCII assumes the input is likely all ASCII, and does not leave early // if it is not the case. -BASE_EXPORT bool IsStringUTF8(const std::string& str); +BASE_EXPORT bool IsStringUTF8(const StringPiece& str); BASE_EXPORT bool IsStringASCII(const StringPiece& str); BASE_EXPORT bool IsStringASCII(const StringPiece16& str); // A convenience adaptor for WebStrings, as they don't convert into diff --git a/chromium/base/strings/string_util_unittest.cc b/chromium/base/strings/string_util_unittest.cc index f29baac17b4..d887c0b35db 100644 --- a/chromium/base/strings/string_util_unittest.cc +++ b/chromium/base/strings/string_util_unittest.cc @@ -494,10 +494,10 @@ TEST(StringUtilTest, ConvertASCII) { const char chars_with_nul[] = "test\0string"; const int length_with_nul = arraysize(chars_with_nul) - 1; std::string string_with_nul(chars_with_nul, length_with_nul); - std::wstring wide_with_nul = ASCIIToWide(string_with_nul); - EXPECT_EQ(static_cast<std::wstring::size_type>(length_with_nul), - wide_with_nul.length()); - std::string narrow_with_nul = UTF16ToASCII(WideToUTF16(wide_with_nul)); + base::string16 string16_with_nul = ASCIIToUTF16(string_with_nul); + EXPECT_EQ(static_cast<base::string16::size_type>(length_with_nul), + string16_with_nul.length()); + std::string narrow_with_nul = UTF16ToASCII(string16_with_nul); EXPECT_EQ(static_cast<std::string::size_type>(length_with_nul), narrow_with_nul.length()); EXPECT_EQ(0, string_with_nul.compare(narrow_with_nul)); @@ -669,39 +669,6 @@ TEST(StringUtilTest, HexDigitToInt) { EXPECT_EQ(15, HexDigitToInt('f')); } -// This checks where we can use the assignment operator for a va_list. We need -// a way to do this since Visual C doesn't support va_copy, but assignment on -// va_list is not guaranteed to be a copy. See StringAppendVT which uses this -// capability. -static void VariableArgsFunc(const char* format, ...) { - va_list org; - va_start(org, format); - - va_list dup; - GG_VA_COPY(dup, org); - int i1 = va_arg(org, int); - int j1 = va_arg(org, int); - char* s1 = va_arg(org, char*); - double d1 = va_arg(org, double); - va_end(org); - - int i2 = va_arg(dup, int); - int j2 = va_arg(dup, int); - char* s2 = va_arg(dup, char*); - double d2 = va_arg(dup, double); - - EXPECT_EQ(i1, i2); - EXPECT_EQ(j1, j2); - EXPECT_STREQ(s1, s2); - EXPECT_EQ(d1, d2); - - va_end(dup); -} - -TEST(StringUtilTest, VAList) { - VariableArgsFunc("%d %d %s %lf", 45, 92, "This is interesting", 9.21); -} - // Test for Tokenize template <typename STR> void TokenizeTest() { diff --git a/chromium/base/strings/string_util_win.h b/chromium/base/strings/string_util_win.h index 602ba273784..61eda2009d0 100644 --- a/chromium/base/strings/string_util_win.h +++ b/chromium/base/strings/string_util_win.h @@ -34,12 +34,9 @@ inline int strncmp16(const char16* s1, const char16* s2, size_t count) { inline int vsnprintf(char* buffer, size_t size, const char* format, va_list arguments) { - int length = _vsprintf_p(buffer, size, format, arguments); - if (length < 0) { - if (size > 0) - buffer[0] = 0; - return _vscprintf_p(format, arguments); - } + int length = vsnprintf_s(buffer, size, size - 1, format, arguments); + if (length < 0) + return _vscprintf(format, arguments); return length; } @@ -47,12 +44,9 @@ inline int vswprintf(wchar_t* buffer, size_t size, const wchar_t* format, va_list arguments) { DCHECK(IsWprintfFormatPortable(format)); - int length = _vswprintf_p(buffer, size, format, arguments); - if (length < 0) { - if (size > 0) - buffer[0] = 0; - return _vscwprintf_p(format, arguments); - } + int length = _vsnwprintf_s(buffer, size, size - 1, format, arguments); + if (length < 0) + return _vscwprintf(format, arguments); return length; } diff --git a/chromium/base/strings/stringprintf.cc b/chromium/base/strings/stringprintf.cc index 3d024fa65aa..537873d71c8 100644 --- a/chromium/base/strings/stringprintf.cc +++ b/chromium/base/strings/stringprintf.cc @@ -6,6 +6,8 @@ #include <errno.h> +#include <vector> + #include "base/scoped_clear_errno.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" @@ -27,7 +29,7 @@ inline int vsnprintfT(char* buffer, return base::vsnprintf(buffer, buf_size, format, argptr); } -#if !defined(OS_ANDROID) +#if defined(OS_WIN) inline int vsnprintfT(wchar_t* buffer, size_t buf_size, const wchar_t* format, @@ -48,7 +50,7 @@ static void StringAppendVT(StringType* dst, typename StringType::value_type stack_buf[1024]; va_list ap_copy; - GG_VA_COPY(ap_copy, ap); + va_copy(ap_copy, ap); #if !defined(OS_WIN) ScopedClearErrno clear_errno; @@ -94,7 +96,7 @@ static void StringAppendVT(StringType* dst, // NOTE: You can only use a va_list once. Since we're in a while loop, we // need to make a new copy each time so we don't use up the original. - GG_VA_COPY(ap_copy, ap); + va_copy(ap_copy, ap); result = vsnprintfT(&mem_buf[0], mem_length, format, ap_copy); va_end(ap_copy); @@ -117,7 +119,7 @@ std::string StringPrintf(const char* format, ...) { return result; } -#if !defined(OS_ANDROID) +#if defined(OS_WIN) std::wstring StringPrintf(const wchar_t* format, ...) { va_list ap; va_start(ap, format); @@ -143,7 +145,7 @@ const std::string& SStringPrintf(std::string* dst, const char* format, ...) { return *dst; } -#if !defined(OS_ANDROID) +#if defined(OS_WIN) const std::wstring& SStringPrintf(std::wstring* dst, const wchar_t* format, ...) { va_list ap; @@ -162,7 +164,7 @@ void StringAppendF(std::string* dst, const char* format, ...) { va_end(ap); } -#if !defined(OS_ANDROID) +#if defined(OS_WIN) void StringAppendF(std::wstring* dst, const wchar_t* format, ...) { va_list ap; va_start(ap, format); @@ -175,7 +177,7 @@ void StringAppendV(std::string* dst, const char* format, va_list ap) { StringAppendVT(dst, format, ap); } -#if !defined(OS_ANDROID) +#if defined(OS_WIN) void StringAppendV(std::wstring* dst, const wchar_t* format, va_list ap) { StringAppendVT(dst, format, ap); } diff --git a/chromium/base/strings/stringprintf.h b/chromium/base/strings/stringprintf.h index d924a0e59f2..523f7ee55bf 100644 --- a/chromium/base/strings/stringprintf.h +++ b/chromium/base/strings/stringprintf.h @@ -17,8 +17,7 @@ namespace base { // Return a C++ string given printf-like input. BASE_EXPORT std::string StringPrintf(const char* format, ...) PRINTF_FORMAT(1, 2) WARN_UNUSED_RESULT; -// OS_ANDROID's libc does not support wchar_t, so several overloads are omitted. -#if !defined(OS_ANDROID) +#if defined(OS_WIN) BASE_EXPORT std::wstring StringPrintf(const wchar_t* format, ...) WPRINTF_FORMAT(1, 2) WARN_UNUSED_RESULT; #endif @@ -31,7 +30,7 @@ BASE_EXPORT std::string StringPrintV(const char* format, va_list ap) BASE_EXPORT const std::string& SStringPrintf(std::string* dst, const char* format, ...) PRINTF_FORMAT(2, 3); -#if !defined(OS_ANDROID) +#if defined(OS_WIN) BASE_EXPORT const std::wstring& SStringPrintf(std::wstring* dst, const wchar_t* format, ...) WPRINTF_FORMAT(2, 3); @@ -40,9 +39,7 @@ BASE_EXPORT const std::wstring& SStringPrintf(std::wstring* dst, // Append result to a supplied string. BASE_EXPORT void StringAppendF(std::string* dst, const char* format, ...) PRINTF_FORMAT(2, 3); -#if !defined(OS_ANDROID) -// TODO(evanm): this is only used in a few places in the code; -// replace with string16 version. +#if defined(OS_WIN) BASE_EXPORT void StringAppendF(std::wstring* dst, const wchar_t* format, ...) WPRINTF_FORMAT(2, 3); #endif @@ -51,7 +48,7 @@ BASE_EXPORT void StringAppendF(std::wstring* dst, const wchar_t* format, ...) // string. All other routines are just convenience wrappers around it. BASE_EXPORT void StringAppendV(std::string* dst, const char* format, va_list ap) PRINTF_FORMAT(2, 0); -#if !defined(OS_ANDROID) +#if defined(OS_WIN) BASE_EXPORT void StringAppendV(std::wstring* dst, const wchar_t* format, va_list ap) WPRINTF_FORMAT(2, 0); diff --git a/chromium/base/strings/stringprintf_unittest.cc b/chromium/base/strings/stringprintf_unittest.cc index 4935b553b0e..c49637c23f2 100644 --- a/chromium/base/strings/stringprintf_unittest.cc +++ b/chromium/base/strings/stringprintf_unittest.cc @@ -31,7 +31,7 @@ TEST(StringPrintfTest, StringPrintfEmpty) { TEST(StringPrintfTest, StringPrintfMisc) { EXPECT_EQ("123hello w", StringPrintf("%3d%2s %1c", 123, "hello", 'w')); -#if !defined(OS_ANDROID) +#if defined(OS_WIN) EXPECT_EQ(L"123hello w", StringPrintf(L"%3d%2ls %1lc", 123, L"hello", 'w')); #endif } @@ -41,7 +41,7 @@ TEST(StringPrintfTest, StringAppendfEmptyString) { StringAppendF(&value, "%s", ""); EXPECT_EQ("Hello", value); -#if !defined(OS_ANDROID) +#if defined(OS_WIN) std::wstring valuew(L"Hello"); StringAppendF(&valuew, L"%ls", L""); EXPECT_EQ(L"Hello", valuew); @@ -53,7 +53,7 @@ TEST(StringPrintfTest, StringAppendfString) { StringAppendF(&value, " %s", "World"); EXPECT_EQ("Hello World", value); -#if !defined(OS_ANDROID) +#if defined(OS_WIN) std::wstring valuew(L"Hello"); StringAppendF(&valuew, L" %ls", L"World"); EXPECT_EQ(L"Hello World", valuew); @@ -65,7 +65,7 @@ TEST(StringPrintfTest, StringAppendfInt) { StringAppendF(&value, " %d", 123); EXPECT_EQ("Hello 123", value); -#if !defined(OS_ANDROID) +#if defined(OS_WIN) std::wstring valuew(L"Hello"); StringAppendF(&valuew, L" %d", 123); EXPECT_EQ(L"Hello 123", valuew); @@ -90,7 +90,7 @@ TEST(StringPrintfTest, StringPrintfBounds) { SStringPrintf(&out, "%s", src); EXPECT_STREQ(src, out.c_str()); -#if !defined(OS_ANDROID) +#if defined(OS_WIN) srcw[kSrcLen - i] = 0; std::wstring outw; SStringPrintf(&outw, L"%ls", srcw); @@ -163,19 +163,6 @@ TEST(StringPrintfTest, Invalid) { } #endif -// Test that the positional parameters work. -TEST(StringPrintfTest, PositionalParameters) { - std::string out; - SStringPrintf(&out, "%1$s %1$s", "test"); - EXPECT_STREQ("test test", out.c_str()); - -#if defined(OS_WIN) - std::wstring wout; - SStringPrintf(&wout, L"%1$ls %1$ls", L"test"); - EXPECT_STREQ(L"test test", wout.c_str()); -#endif -} - // Test that StringPrintf and StringAppendV do not change errno. TEST(StringPrintfTest, StringPrintfErrno) { errno = 1; diff --git a/chromium/base/strings/sys_string_conversions_posix.cc b/chromium/base/strings/sys_string_conversions_posix.cc index 1e926bb5490..3b1845622fd 100644 --- a/chromium/base/strings/sys_string_conversions_posix.cc +++ b/chromium/base/strings/sys_string_conversions_posix.cc @@ -24,11 +24,10 @@ std::wstring SysUTF8ToWide(const StringPiece& utf8) { return out; } -#if defined(OS_CHROMEOS) || defined(OS_ANDROID) +#if defined(SYSTEM_NATIVE_UTF8) || defined(OS_ANDROID) // TODO(port): Consider reverting the OS_ANDROID when we have wcrtomb() // support and a better understanding of what calls these routines. -// ChromeOS always runs in UTF-8 locale. std::string SysWideToNativeMB(const std::wstring& wide) { return WideToUTF8(wide); } diff --git a/chromium/base/strings/sys_string_conversions_unittest.cc b/chromium/base/strings/sys_string_conversions_unittest.cc index d2d38e40cc4..0cdd4281ecf 100644 --- a/chromium/base/strings/sys_string_conversions_unittest.cc +++ b/chromium/base/strings/sys_string_conversions_unittest.cc @@ -75,7 +75,9 @@ TEST(SysStrings, SysUTF8ToWide) { #if defined(OS_LINUX) // Tests depend on setting a specific Linux locale. TEST(SysStrings, SysWideToNativeMB) { +#if !defined(SYSTEM_NATIVE_UTF8) ScopedLocale locale("en_US.utf-8"); +#endif EXPECT_EQ("Hello, world", SysWideToNativeMB(L"Hello, world")); EXPECT_EQ("\xe4\xbd\xa0\xe5\xa5\xbd", SysWideToNativeMB(L"\x4f60\x597d")); @@ -105,7 +107,9 @@ TEST(SysStrings, SysWideToNativeMB) { // We assume the test is running in a UTF8 locale. TEST(SysStrings, SysNativeMBToWide) { +#if !defined(SYSTEM_NATIVE_UTF8) ScopedLocale locale("en_US.utf-8"); +#endif EXPECT_EQ(L"Hello, world", SysNativeMBToWide("Hello, world")); EXPECT_EQ(L"\x4f60\x597d", SysNativeMBToWide("\xe4\xbd\xa0\xe5\xa5\xbd")); // >16 bits @@ -159,7 +163,9 @@ static const wchar_t* const kConvertRoundtripCases[] = { TEST(SysStrings, SysNativeMBAndWide) { +#if !defined(SYSTEM_NATIVE_UTF8) ScopedLocale locale("en_US.utf-8"); +#endif for (size_t i = 0; i < arraysize(kConvertRoundtripCases); ++i) { std::wstring wide = kConvertRoundtripCases[i]; std::wstring trip = SysNativeMBToWide(SysWideToNativeMB(wide)); diff --git a/chromium/base/strings/utf_offset_string_conversions_unittest.cc b/chromium/base/strings/utf_offset_string_conversions_unittest.cc index 529939f6013..9398a56233c 100644 --- a/chromium/base/strings/utf_offset_string_conversions_unittest.cc +++ b/chromium/base/strings/utf_offset_string_conversions_unittest.cc @@ -293,4 +293,4 @@ TEST(UTFOffsetStringConversionsTest, MergeSequentialAdjustments) { EXPECT_EQ(2u, adjustments_on_adjusted_string[5].output_length); } -} // namaspace base +} // namespace base diff --git a/chromium/base/strings/utf_string_conversions.cc b/chromium/base/strings/utf_string_conversions.cc index 9796eec5e15..1480d48086f 100644 --- a/chromium/base/strings/utf_string_conversions.cc +++ b/chromium/base/strings/utf_string_conversions.cc @@ -209,11 +209,6 @@ std::string UTF16ToUTF8(const string16& utf16) { #endif -std::wstring ASCIIToWide(const StringPiece& ascii) { - DCHECK(IsStringASCII(ascii)) << ascii; - return std::wstring(ascii.begin(), ascii.end()); -} - string16 ASCIIToUTF16(const StringPiece& ascii) { DCHECK(IsStringASCII(ascii)) << ascii; return string16(ascii.begin(), ascii.end()); diff --git a/chromium/base/strings/utf_string_conversions.h b/chromium/base/strings/utf_string_conversions.h index 13e0b7193be..06a3bc64767 100644 --- a/chromium/base/strings/utf_string_conversions.h +++ b/chromium/base/strings/utf_string_conversions.h @@ -39,9 +39,8 @@ BASE_EXPORT bool UTF16ToUTF8(const char16* src, size_t src_len, std::string* output); BASE_EXPORT std::string UTF16ToUTF8(const string16& utf16); -// These convert an ASCII string, typically a hardcoded constant, to a -// UTF16/Wide string. -BASE_EXPORT std::wstring ASCIIToWide(const StringPiece& ascii); +// This converts an ASCII string, typically a hardcoded constant, to a UTF16 +// string. BASE_EXPORT string16 ASCIIToUTF16(const StringPiece& ascii); // Converts to 7-bit ASCII by truncating. The result must be known to be ASCII diff --git a/chromium/base/strings/utf_string_conversions_unittest.cc b/chromium/base/strings/utf_string_conversions_unittest.cc index 009af7c4f53..a7b12ffe0e0 100644 --- a/chromium/base/strings/utf_string_conversions_unittest.cc +++ b/chromium/base/strings/utf_string_conversions_unittest.cc @@ -208,4 +208,4 @@ TEST(UTFStringConversionsTest, ConvertMultiString) { EXPECT_EQ(expected, converted); } -} // base +} // namespace base diff --git a/chromium/base/supports_user_data.h b/chromium/base/supports_user_data.h index 6d515d7341e..711ee7d8979 100644 --- a/chromium/base/supports_user_data.h +++ b/chromium/base/supports_user_data.h @@ -61,7 +61,7 @@ class BASE_EXPORT SupportsUserData { template <typename T> class UserDataAdapter : public base::SupportsUserData::Data { public: - static T* Get(SupportsUserData* supports_user_data, const void* key) { + static T* Get(const SupportsUserData* supports_user_data, const void* key) { UserDataAdapter* data = static_cast<UserDataAdapter*>(supports_user_data->GetUserData(key)); return data ? static_cast<T*>(data->object_.get()) : NULL; diff --git a/chromium/base/sync_socket.h b/chromium/base/sync_socket.h index 4da9d3b6b3a..36d6bc1f59b 100644 --- a/chromium/base/sync_socket.h +++ b/chromium/base/sync_socket.h @@ -124,11 +124,11 @@ class BASE_EXPORT CancelableSyncSocket : public SyncSocket { // and there isn't a way to cancel a blocking synchronous Read that is // supported on <Vista. So, for Windows only, we override these // SyncSocket methods in order to support shutting down the 'socket'. - virtual bool Close() override; - virtual size_t Receive(void* buffer, size_t length) override; - virtual size_t ReceiveWithTimeout(void* buffer, - size_t length, - TimeDelta timeout) override; + bool Close() override; + size_t Receive(void* buffer, size_t length) override; + size_t ReceiveWithTimeout(void* buffer, + size_t length, + TimeDelta timeout) override; #endif // Send() is overridden to catch cases where the remote end is not responding diff --git a/chromium/base/synchronization/cancellation_flag.cc b/chromium/base/synchronization/cancellation_flag.cc index ad3b551169d..ca5c0a82837 100644 --- a/chromium/base/synchronization/cancellation_flag.cc +++ b/chromium/base/synchronization/cancellation_flag.cc @@ -19,4 +19,8 @@ bool CancellationFlag::IsSet() const { return base::subtle::Acquire_Load(&flag_) != 0; } +void CancellationFlag::UnsafeResetForTesting() { + base::subtle::Release_Store(&flag_, 0); +} + } // namespace base diff --git a/chromium/base/synchronization/cancellation_flag.h b/chromium/base/synchronization/cancellation_flag.h index 51a4def1eb3..0f0f08ee8f7 100644 --- a/chromium/base/synchronization/cancellation_flag.h +++ b/chromium/base/synchronization/cancellation_flag.h @@ -29,6 +29,11 @@ class BASE_EXPORT CancellationFlag { void Set(); bool IsSet() const; // Returns true iff the flag was set. + // For subtle reasons that may be different on different architectures, + // a different thread testing IsSet() may erroneously read 'true' after + // this method has been called. + void UnsafeResetForTesting(); + private: base::subtle::Atomic32 flag_; #if !defined(NDEBUG) diff --git a/chromium/base/synchronization/cancellation_flag_unittest.cc b/chromium/base/synchronization/cancellation_flag_unittest.cc index 02b08b6a9de..13c74bcbd45 100644 --- a/chromium/base/synchronization/cancellation_flag_unittest.cc +++ b/chromium/base/synchronization/cancellation_flag_unittest.cc @@ -7,8 +7,9 @@ #include "base/synchronization/cancellation_flag.h" #include "base/bind.h" +#include "base/location.h" #include "base/logging.h" -#include "base/message_loop/message_loop.h" +#include "base/single_thread_task_runner.h" #include "base/synchronization/spin_wait.h" #include "base/threading/thread.h" #include "base/time/time.h" @@ -56,7 +57,7 @@ TEST(CancellationFlagTest, SetOnDifferentThreadDeathTest) { ASSERT_TRUE(t.IsRunning()); CancellationFlag flag; - t.message_loop()->PostTask(FROM_HERE, base::Bind(&CancelHelper, &flag)); + t.task_runner()->PostTask(FROM_HERE, base::Bind(&CancelHelper, &flag)); } } // namespace diff --git a/chromium/base/synchronization/condition_variable_unittest.cc b/chromium/base/synchronization/condition_variable_unittest.cc index 5d7a0ab8b60..e63a723d009 100644 --- a/chromium/base/synchronization/condition_variable_unittest.cc +++ b/chromium/base/synchronization/condition_variable_unittest.cc @@ -9,8 +9,10 @@ #include <vector> #include "base/bind.h" +#include "base/location.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" +#include "base/single_thread_task_runner.h" #include "base/synchronization/condition_variable.h" #include "base/synchronization/lock.h" #include "base/synchronization/spin_wait.h" @@ -220,7 +222,7 @@ TEST_F(ConditionVariableTest, DISABLED_TimeoutAcrossSetTimeOfDay) { Thread thread("Helper"); thread.Start(); - thread.message_loop()->PostTask(FROM_HERE, base::Bind(&BackInTime, &lock)); + thread.task_runner()->PostTask(FROM_HERE, base::Bind(&BackInTime, &lock)); TimeTicks start = TimeTicks::Now(); const TimeDelta kWaitTime = TimeDelta::FromMilliseconds(300); diff --git a/chromium/base/synchronization/condition_variable_win.cc b/chromium/base/synchronization/condition_variable_win.cc index 6dc4831a78f..5f165c8a86e 100644 --- a/chromium/base/synchronization/condition_variable_win.cc +++ b/chromium/base/synchronization/condition_variable_win.cc @@ -72,12 +72,12 @@ class ConditionVarImpl { class WinVistaCondVar: public ConditionVarImpl { public: WinVistaCondVar(Lock* user_lock); - ~WinVistaCondVar() {}; + ~WinVistaCondVar() override {} // Overridden from ConditionVarImpl. - virtual void Wait() override; - virtual void TimedWait(const TimeDelta& max_time) override; - virtual void Broadcast() override; - virtual void Signal() override; + void Wait() override; + void TimedWait(const TimeDelta& max_time) override; + void Broadcast() override; + void Signal() override; private: base::Lock& user_lock_; @@ -127,12 +127,12 @@ void WinVistaCondVar::Signal() { class WinXPCondVar : public ConditionVarImpl { public: WinXPCondVar(Lock* user_lock); - ~WinXPCondVar(); + ~WinXPCondVar() override; // Overridden from ConditionVarImpl. - virtual void Wait() override; - virtual void TimedWait(const TimeDelta& max_time) override; - virtual void Broadcast() override; - virtual void Signal() override; + void Wait() override; + void TimedWait(const TimeDelta& max_time) override; + void Broadcast() override; + void Signal() override; // Define Event class that is used to form circularly linked lists. // The list container is an element with NULL as its handle_ value. diff --git a/chromium/base/synchronization/waitable_event.h b/chromium/base/synchronization/waitable_event.h index 39c0d11538c..c35af5467f6 100644 --- a/chromium/base/synchronization/waitable_event.h +++ b/chromium/base/synchronization/waitable_event.h @@ -21,9 +21,6 @@ namespace base { -// This replaces INFINITE from Win32 -static const int kNoTimeout = -1; - class TimeDelta; // A WaitableEvent can be a useful thread synchronization tool when you want to @@ -53,11 +50,7 @@ class BASE_EXPORT WaitableEvent { // Create a WaitableEvent from an Event HANDLE which has already been // created. This objects takes ownership of the HANDLE and will close it when // deleted. - // TODO(rvargas): Pass ScopedHandle instead (and on Release). - explicit WaitableEvent(HANDLE event_handle); - - // Releases ownership of the handle from this object. - HANDLE Release(); + explicit WaitableEvent(win::ScopedHandle event_handle); #endif ~WaitableEvent(); diff --git a/chromium/base/synchronization/waitable_event_unittest.cc b/chromium/base/synchronization/waitable_event_unittest.cc index abba9356bb1..be56cf171a1 100644 --- a/chromium/base/synchronization/waitable_event_unittest.cc +++ b/chromium/base/synchronization/waitable_event_unittest.cc @@ -73,29 +73,27 @@ TEST(WaitableEventTest, WaitManyShortcut) { class WaitableEventSignaler : public PlatformThread::Delegate { public: - WaitableEventSignaler(double seconds, WaitableEvent* ev) - : seconds_(seconds), - ev_(ev) { + WaitableEventSignaler(TimeDelta delay, WaitableEvent* event) + : delay_(delay), + event_(event) { } void ThreadMain() override { - PlatformThread::Sleep(TimeDelta::FromSecondsD(seconds_)); - ev_->Signal(); + PlatformThread::Sleep(delay_); + event_->Signal(); } private: - const double seconds_; - WaitableEvent *const ev_; + const TimeDelta delay_; + WaitableEvent* event_; }; +// Tests that a WaitableEvent can be safely deleted when |Wait| is done without +// additional synchronization. TEST(WaitableEventTest, WaitAndDelete) { - // This test tests that if a WaitableEvent can be safely deleted - // when |Wait| is done without additional synchrnization. - // If this test crashes, it is a bug. - WaitableEvent* ev = new WaitableEvent(false, false); - WaitableEventSignaler signaler(0.01, ev); + WaitableEventSignaler signaler(TimeDelta::FromMilliseconds(10), ev); PlatformThreadHandle thread; PlatformThread::Create(0, &signaler, &thread); @@ -105,16 +103,14 @@ TEST(WaitableEventTest, WaitAndDelete) { PlatformThread::Join(thread); } +// Tests that a WaitableEvent can be safely deleted when |WaitMany| is done +// without additional synchronization. TEST(WaitableEventTest, WaitMany) { - // This test tests that if a WaitableEvent can be safely deleted - // when |WaitMany| is done without additional synchrnization. - // If this test crashes, it is a bug. - WaitableEvent* ev[5]; for (unsigned i = 0; i < 5; ++i) ev[i] = new WaitableEvent(false, false); - WaitableEventSignaler signaler(0.01, ev[2]); + WaitableEventSignaler signaler(TimeDelta::FromMilliseconds(10), ev[2]); PlatformThreadHandle thread; PlatformThread::Create(0, &signaler, &thread); @@ -127,4 +123,28 @@ TEST(WaitableEventTest, WaitMany) { EXPECT_EQ(2u, index); } +// Tests that using TimeDelta::Max() on TimedWait() is not the same as passing +// a timeout of 0. (crbug.com/465948) +#if defined(OS_POSIX) +// crbug.com/465948 not fixed yet. +#define MAYBE_TimedWait DISABLED_TimedWait +#else +#define MAYBE_TimedWait TimedWait +#endif +TEST(WaitableEventTest, MAYBE_TimedWait) { + WaitableEvent* ev = new WaitableEvent(false, false); + + TimeDelta thread_delay = TimeDelta::FromMilliseconds(10); + WaitableEventSignaler signaler(thread_delay, ev); + PlatformThreadHandle thread; + TimeTicks start = TimeTicks::Now(); + PlatformThread::Create(0, &signaler, &thread); + + ev->TimedWait(TimeDelta::Max()); + EXPECT_GE(TimeTicks::Now() - start, thread_delay); + delete ev; + + PlatformThread::Join(thread); +} + } // namespace base diff --git a/chromium/base/synchronization/waitable_event_watcher.h b/chromium/base/synchronization/waitable_event_watcher.h index d4d8e7779bd..eb51effa499 100644 --- a/chromium/base/synchronization/waitable_event_watcher.h +++ b/chromium/base/synchronization/waitable_event_watcher.h @@ -92,7 +92,7 @@ class BASE_EXPORT WaitableEventWatcher private: #if defined(OS_WIN) - virtual void OnObjectSignaled(HANDLE h) override; + void OnObjectSignaled(HANDLE h) override; win::ObjectWatcher watcher_; #else // Implementation of MessageLoop::DestructionObserver diff --git a/chromium/base/synchronization/waitable_event_watcher_posix.cc b/chromium/base/synchronization/waitable_event_watcher_posix.cc index 6fc337cc81b..ad66a4c769a 100644 --- a/chromium/base/synchronization/waitable_event_watcher_posix.cc +++ b/chromium/base/synchronization/waitable_event_watcher_posix.cc @@ -6,7 +6,7 @@ #include "base/bind.h" #include "base/location.h" -#include "base/message_loop/message_loop.h" +#include "base/single_thread_task_runner.h" #include "base/synchronization/lock.h" #include "base/synchronization/waitable_event.h" @@ -68,7 +68,7 @@ class AsyncWaiter : public WaitableEvent::Waiter { bool Fire(WaitableEvent* event) override { // Post the callback if we haven't been cancelled. if (!flag_->value()) { - message_loop_->PostTask(FROM_HERE, callback_); + message_loop_->task_runner()->PostTask(FROM_HERE, callback_); } // We are removed from the wait-list by the WaitableEvent itself. It only @@ -158,7 +158,7 @@ bool WaitableEventWatcher::StartWatching( // No hairpinning - we can't call the delegate directly here. We have to // enqueue a task on the MessageLoop as normal. - current_ml->PostTask(FROM_HERE, internal_callback_); + current_ml->task_runner()->PostTask(FROM_HERE, internal_callback_); return true; } diff --git a/chromium/base/synchronization/waitable_event_watcher_win.cc b/chromium/base/synchronization/waitable_event_watcher_win.cc index f5218f1361d..46d47ac581a 100644 --- a/chromium/base/synchronization/waitable_event_watcher_win.cc +++ b/chromium/base/synchronization/waitable_event_watcher_win.cc @@ -5,7 +5,6 @@ #include "base/synchronization/waitable_event_watcher.h" #include "base/compiler_specific.h" -#include "base/profiler/scoped_tracker.h" #include "base/synchronization/waitable_event.h" #include "base/win/object_watcher.h" @@ -37,10 +36,6 @@ WaitableEvent* WaitableEventWatcher::GetWatchedEvent() { } void WaitableEventWatcher::OnObjectSignaled(HANDLE h) { - // TODO(vadimt): Remove ScopedTracker below once crbug.com/418183 is fixed. - tracked_objects::ScopedTracker tracking_profile( - FROM_HERE_WITH_EXPLICIT_FUNCTION("WaitableEventWatche_OnObjectSignaled")); - WaitableEvent* event = event_; EventCallback callback = callback_; event_ = NULL; diff --git a/chromium/base/synchronization/waitable_event_win.cc b/chromium/base/synchronization/waitable_event_win.cc index ec2d84f2679..4db56277b7c 100644 --- a/chromium/base/synchronization/waitable_event_win.cc +++ b/chromium/base/synchronization/waitable_event_win.cc @@ -4,10 +4,10 @@ #include "base/synchronization/waitable_event.h" -#include <math.h> #include <windows.h> #include "base/logging.h" +#include "base/numerics/safe_conversions.h" #include "base/threading/thread_restrictions.h" #include "base/time/time.h" @@ -20,18 +20,14 @@ WaitableEvent::WaitableEvent(bool manual_reset, bool signaled) CHECK(handle_.IsValid()); } -WaitableEvent::WaitableEvent(HANDLE handle) - : handle_(handle) { +WaitableEvent::WaitableEvent(win::ScopedHandle handle) + : handle_(handle.Pass()) { CHECK(handle_.IsValid()) << "Tried to create WaitableEvent from NULL handle"; } WaitableEvent::~WaitableEvent() { } -HANDLE WaitableEvent::Release() { - return handle_.Take(); -} - void WaitableEvent::Reset() { ResetEvent(handle_.Get()); } @@ -41,7 +37,7 @@ void WaitableEvent::Signal() { } bool WaitableEvent::IsSignaled() { - return TimedWait(TimeDelta::FromMilliseconds(0)); + return TimedWait(TimeDelta()); } void WaitableEvent::Wait() { @@ -54,13 +50,13 @@ void WaitableEvent::Wait() { bool WaitableEvent::TimedWait(const TimeDelta& max_time) { base::ThreadRestrictions::AssertWaitAllowed(); - DCHECK(max_time >= TimeDelta::FromMicroseconds(0)); - // Be careful here. TimeDelta has a precision of microseconds, but this API - // is in milliseconds. If there are 5.5ms left, should the delay be 5 or 6? - // It should be 6 to avoid returning too early. - double timeout = ceil(max_time.InMillisecondsF()); - DWORD result = WaitForSingleObject(handle_.Get(), - static_cast<DWORD>(timeout)); + DCHECK_GE(max_time, TimeDelta()); + // Truncate the timeout to milliseconds. The API specifies that this method + // can return in less than |max_time| (when returning false), as the argument + // is the maximum time that a caller is willing to wait. + DWORD timeout = saturated_cast<DWORD>(max_time.InMilliseconds()); + + DWORD result = WaitForSingleObject(handle_.Get(), timeout); switch (result) { case WAIT_OBJECT_0: return true; diff --git a/chromium/base/sys_info.cc b/chromium/base/sys_info.cc index cb93480b979..8640dc14edc 100644 --- a/chromium/base/sys_info.cc +++ b/chromium/base/sys_info.cc @@ -7,7 +7,9 @@ #include "base/base_switches.h" #include "base/command_line.h" #include "base/lazy_instance.h" +#include "base/metrics/field_trial.h" #include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" #include "base/sys_info_internal.h" #include "base/time/time.h" @@ -19,15 +21,9 @@ static const int kLowMemoryDeviceThresholdMB = 512; bool DetectLowEndDevice() { CommandLine* command_line = CommandLine::ForCurrentProcess(); - int int_value = 0; - if (command_line->HasSwitch(switches::kLowEndDeviceMode)) { - std::string string_value = - command_line->GetSwitchValueASCII(switches::kLowEndDeviceMode); - StringToInt(string_value, &int_value); - } - if (int_value == 1) + if (command_line->HasSwitch(switches::kEnableLowEndDeviceMode)) return true; - if (int_value != 2) + if (command_line->HasSwitch(switches::kDisableLowEndDeviceMode)) return false; int ram_size_mb = SysInfo::AmountOfPhysicalMemoryMB(); @@ -40,6 +36,14 @@ static LazyInstance< // static bool SysInfo::IsLowEndDevice() { + const std::string group_name = + base::FieldTrialList::FindFullName("MemoryReduction"); + + // Low End Device Mode will be enabled if this client is assigned to + // one of those EnabledXXX groups. + if (StartsWithASCII(group_name, "Enabled", true)) + return true; + return g_lazy_low_end_device.Get().value(); } #endif diff --git a/chromium/base/sys_info.h b/chromium/base/sys_info.h index 660343d06f7..654d6945a34 100644 --- a/chromium/base/sys_info.h +++ b/chromium/base/sys_info.h @@ -94,7 +94,7 @@ class BASE_EXPORT SysInfo { #if defined(OS_POSIX) && !defined(OS_MACOSX) // Returns the maximum SysV shared memory segment size, or zero if there is no // limit. - static size_t MaxSharedMemorySize(); + static uint64 MaxSharedMemorySize(); #endif // defined(OS_POSIX) && !defined(OS_MACOSX) #if defined(OS_CHROMEOS) diff --git a/chromium/base/sys_info_android.cc b/chromium/base/sys_info_android.cc index 0d885ee019a..245097ffdc0 100644 --- a/chromium/base/sys_info_android.cc +++ b/chromium/base/sys_info_android.cc @@ -47,7 +47,7 @@ static base::LazyInstance<base::internal::LazySysInfoValue< // from Chrome we work around this by defining a weak stub here, which uses // dlsym to but ensures that Chrome uses the real system // implementatation when loaded. http://crbug.com/392191. -int __system_property_get(const char* name, char* value) { +BASE_EXPORT int __system_property_get(const char* name, char* value) { return g_lazy_real_system_property_get.Get().value()(name, value); } @@ -59,8 +59,8 @@ namespace { // cannot be acquired. Use the latest Android release with a higher bug fix // version to avoid unnecessarily comparison errors with the latest release. // This should be manually kept up-to-date on each Android release. -const int kDefaultAndroidMajorVersion = 4; -const int kDefaultAndroidMinorVersion = 4; +const int kDefaultAndroidMajorVersion = 5; +const int kDefaultAndroidMinorVersion = 1; const int kDefaultAndroidBugfixVersion = 99; // Parse out the OS version numbers from the system properties. diff --git a/chromium/base/sys_info_chromeos.cc b/chromium/base/sys_info_chromeos.cc index ef5f1fedb51..9915055996d 100644 --- a/chromium/base/sys_info_chromeos.cc +++ b/chromium/base/sys_info_chromeos.cc @@ -109,7 +109,7 @@ class ChromeOSVersionInfo { // Parse and cache lsb_release key pairs. There should only be a handful // of entries so the overhead for this will be small, and it can be // useful for debugging. - std::vector<std::pair<std::string, std::string> > pairs; + base::StringPairs pairs; SplitStringIntoKeyValuePairs(lsb_release, '=', '\n', &pairs); for (size_t i = 0; i < pairs.size(); ++i) { std::string key, value; diff --git a/chromium/base/sys_info_freebsd.cc b/chromium/base/sys_info_freebsd.cc index 832b35985be..515b59d6e98 100644 --- a/chromium/base/sys_info_freebsd.cc +++ b/chromium/base/sys_info_freebsd.cc @@ -23,14 +23,14 @@ int64 SysInfo::AmountOfPhysicalMemory() { } // static -size_t SysInfo::MaxSharedMemorySize() { +uint64 SysInfo::MaxSharedMemorySize() { size_t limit; size_t size = sizeof(limit); if (sysctlbyname("kern.ipc.shmmax", &limit, &size, NULL, 0) < 0) { NOTREACHED(); return 0; } - return limit; + return static_cast<uint64>(limit); } } // namespace base diff --git a/chromium/base/sys_info_linux.cc b/chromium/base/sys_info_linux.cc index 2e679ed39f3..1bbfe9c604e 100644 --- a/chromium/base/sys_info_linux.cc +++ b/chromium/base/sys_info_linux.cc @@ -9,6 +9,7 @@ #include "base/files/file_util.h" #include "base/lazy_instance.h" #include "base/logging.h" +#include "base/numerics/safe_conversions.h" #include "base/strings/string_number_conversions.h" #include "base/sys_info_internal.h" @@ -28,7 +29,7 @@ int64 AmountOfPhysicalMemory() { return AmountOfMemory(_SC_PHYS_PAGES); } -size_t MaxSharedMemorySize() { +uint64 MaxSharedMemorySize() { std::string contents; base::ReadFileToString(base::FilePath("/proc/sys/kernel/shmmax"), &contents); DCHECK(!contents.empty()); @@ -40,18 +41,15 @@ size_t MaxSharedMemorySize() { if (!base::StringToUint64(contents, &limit)) { limit = 0; } - if (limit > std::numeric_limits<size_t>::max()) { - limit = 0; - } - DCHECK(limit > 0); - return static_cast<size_t>(limit); + DCHECK_GT(limit, 0u); + return limit; } base::LazyInstance< base::internal::LazySysInfoValue<int64, AmountOfPhysicalMemory> >::Leaky g_lazy_physical_memory = LAZY_INSTANCE_INITIALIZER; base::LazyInstance< - base::internal::LazySysInfoValue<size_t, MaxSharedMemorySize> >::Leaky + base::internal::LazySysInfoValue<uint64, MaxSharedMemorySize> >::Leaky g_lazy_max_shared_memory = LAZY_INSTANCE_INITIALIZER; } // namespace @@ -69,7 +67,7 @@ int64 SysInfo::AmountOfPhysicalMemory() { } // static -size_t SysInfo::MaxSharedMemorySize() { +uint64 SysInfo::MaxSharedMemorySize() { return g_lazy_max_shared_memory.Get().value(); } diff --git a/chromium/base/sys_info_openbsd.cc b/chromium/base/sys_info_openbsd.cc index edbb2c9d86c..595291b0e01 100644 --- a/chromium/base/sys_info_openbsd.cc +++ b/chromium/base/sys_info_openbsd.cc @@ -49,7 +49,7 @@ int64 SysInfo::AmountOfAvailablePhysicalMemory() { } // static -size_t SysInfo::MaxSharedMemorySize() { +uint64 SysInfo::MaxSharedMemorySize() { int mib[] = { CTL_KERN, KERN_SHMINFO, KERN_SHMINFO_SHMMAX }; size_t limit; size_t size = sizeof(limit); @@ -57,7 +57,7 @@ size_t SysInfo::MaxSharedMemorySize() { NOTREACHED(); return 0; } - return limit; + return static_cast<uint64>(limit); } // static diff --git a/chromium/base/sys_info_win.cc b/chromium/base/sys_info_win.cc index 9cc0cfa48a3..c8314c7a6a9 100644 --- a/chromium/base/sys_info_win.cc +++ b/chromium/base/sys_info_win.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2012 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. @@ -24,9 +24,7 @@ int64 AmountOfMemory(DWORDLONG MEMORYSTATUSEX::* memory_field) { } int64 rv = static_cast<int64>(memory_info.*memory_field); - if (rv < 0) - rv = kint64max; - return rv; + return rv < 0 ? kint64max : rv; } } // namespace @@ -55,19 +53,16 @@ int64 SysInfo::AmountOfVirtualMemory() { // static int64 SysInfo::AmountOfFreeDiskSpace(const FilePath& path) { - base::ThreadRestrictions::AssertIOAllowed(); + ThreadRestrictions::AssertIOAllowed(); ULARGE_INTEGER available, total, free; - if (!GetDiskFreeSpaceExW(path.value().c_str(), &available, &total, &free)) { + if (!GetDiskFreeSpaceExW(path.value().c_str(), &available, &total, &free)) return -1; - } + int64 rv = static_cast<int64>(available.QuadPart); - if (rv < 0) - rv = kint64max; - return rv; + return rv < 0 ? kint64max : rv; } -// static std::string SysInfo::OperatingSystemName() { return "Windows NT"; } diff --git a/chromium/base/system_monitor/system_monitor.cc b/chromium/base/system_monitor/system_monitor.cc index 11dd000a16e..99152ab8a85 100644 --- a/chromium/base/system_monitor/system_monitor.cc +++ b/chromium/base/system_monitor/system_monitor.cc @@ -46,7 +46,7 @@ void SystemMonitor::RemoveDevicesChangedObserver(DevicesChangedObserver* obs) { void SystemMonitor::NotifyDevicesChanged(DeviceType device_type) { DVLOG(1) << "DevicesChanged with device type " << device_type; devices_changed_observer_list_->Notify( - &DevicesChangedObserver::OnDevicesChanged, device_type); + FROM_HERE, &DevicesChangedObserver::OnDevicesChanged, device_type); } } // namespace base diff --git a/chromium/base/system_monitor/system_monitor_unittest.cc b/chromium/base/system_monitor/system_monitor_unittest.cc index e49405ec45e..f3db4c77c94 100644 --- a/chromium/base/system_monitor/system_monitor_unittest.cc +++ b/chromium/base/system_monitor/system_monitor_unittest.cc @@ -19,11 +19,11 @@ class SystemMonitorTest : public testing::Test { SystemMonitorTest() { system_monitor_.reset(new SystemMonitor); } - virtual ~SystemMonitorTest() {} MessageLoop message_loop_; scoped_ptr<SystemMonitor> system_monitor_; + private: DISALLOW_COPY_AND_ASSIGN(SystemMonitorTest); }; diff --git a/chromium/base/task/cancelable_task_tracker.cc b/chromium/base/task/cancelable_task_tracker.cc index b6e4b6ac4de..a2e4799f438 100644 --- a/chromium/base/task/cancelable_task_tracker.cc +++ b/chromium/base/task/cancelable_task_tracker.cc @@ -11,9 +11,10 @@ #include "base/compiler_specific.h" #include "base/location.h" #include "base/memory/ref_counted.h" -#include "base/message_loop/message_loop_proxy.h" +#include "base/single_thread_task_runner.h" #include "base/synchronization/cancellation_flag.h" #include "base/task_runner.h" +#include "base/thread_task_runner_handle.h" using base::Bind; using base::CancellationFlag; @@ -85,7 +86,7 @@ CancelableTaskTracker::TaskId CancelableTaskTracker::PostTaskAndReply( DCHECK(thread_checker_.CalledOnValidThread()); // We need a MessageLoop to run reply. - DCHECK(base::MessageLoopProxy::current().get()); + DCHECK(base::ThreadTaskRunnerHandle::IsSet()); // Owned by reply callback below. CancellationFlag* flag = new CancellationFlag(); @@ -113,7 +114,7 @@ CancelableTaskTracker::TaskId CancelableTaskTracker::PostTaskAndReply( CancelableTaskTracker::TaskId CancelableTaskTracker::NewTrackedTaskId( IsCanceledCallback* is_canceled_cb) { DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(base::MessageLoopProxy::current().get()); + DCHECK(base::ThreadTaskRunnerHandle::IsSet()); TaskId id = next_id_; next_id_++; // int64 is big enough that we ignore the potential overflow. @@ -129,7 +130,7 @@ CancelableTaskTracker::TaskId CancelableTaskTracker::NewTrackedTaskId( // Will always run |untrack_and_delete_flag| on current MessageLoop. base::ScopedClosureRunner* untrack_and_delete_flag_runner = new base::ScopedClosureRunner(Bind(&RunOrPostToTaskRunner, - base::MessageLoopProxy::current(), + base::ThreadTaskRunnerHandle::Get(), untrack_and_delete_flag)); *is_canceled_cb = diff --git a/chromium/base/task/cancelable_task_tracker_unittest.cc b/chromium/base/task/cancelable_task_tracker_unittest.cc index e122e8deef0..ff9e40b855e 100644 --- a/chromium/base/task/cancelable_task_tracker_unittest.cc +++ b/chromium/base/task/cancelable_task_tracker_unittest.cc @@ -13,8 +13,8 @@ #include "base/logging.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" -#include "base/message_loop/message_loop.h" #include "base/run_loop.h" +#include "base/single_thread_task_runner.h" #include "base/test/test_simple_task_runner.h" #include "base/threading/thread.h" #include "testing/gtest/include/gtest/gtest.h" @@ -25,7 +25,7 @@ namespace { class CancelableTaskTrackerTest : public testing::Test { protected: - virtual ~CancelableTaskTrackerTest() { RunCurrentLoopUntilIdle(); } + ~CancelableTaskTrackerTest() override { RunCurrentLoopUntilIdle(); } void RunCurrentLoopUntilIdle() { RunLoop run_loop; @@ -84,15 +84,13 @@ TEST_F(CancelableTaskTrackerTest, NoCancel) { Thread worker_thread("worker thread"); ASSERT_TRUE(worker_thread.Start()); - ignore_result(task_tracker_.PostTask(worker_thread.message_loop_proxy().get(), + ignore_result(task_tracker_.PostTask(worker_thread.task_runner().get(), FROM_HERE, MakeExpectedRunClosure(FROM_HERE))); - ignore_result( - task_tracker_.PostTaskAndReply(worker_thread.message_loop_proxy().get(), - FROM_HERE, - MakeExpectedRunClosure(FROM_HERE), - MakeExpectedRunClosure(FROM_HERE))); + ignore_result(task_tracker_.PostTaskAndReply( + worker_thread.task_runner().get(), FROM_HERE, + MakeExpectedRunClosure(FROM_HERE), MakeExpectedRunClosure(FROM_HERE))); CancelableTaskTracker::IsCanceledCallback is_canceled; ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled)); @@ -166,11 +164,9 @@ TEST_F(CancelableTaskTrackerTest, CancelReplyDifferentThread) { Thread worker_thread("worker thread"); ASSERT_TRUE(worker_thread.Start()); - CancelableTaskTracker::TaskId task_id = - task_tracker_.PostTaskAndReply(worker_thread.message_loop_proxy().get(), - FROM_HERE, - Bind(&DoNothing), - MakeExpectedNotRunClosure(FROM_HERE)); + CancelableTaskTracker::TaskId task_id = task_tracker_.PostTaskAndReply( + worker_thread.task_runner().get(), FROM_HERE, Bind(&DoNothing), + MakeExpectedNotRunClosure(FROM_HERE)); EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); task_tracker_.TryCancel(task_id); @@ -196,14 +192,14 @@ TEST_F(CancelableTaskTrackerTest, NewTrackedTaskIdDifferentThread) { Thread other_thread("other thread"); ASSERT_TRUE(other_thread.Start()); - other_thread.message_loop_proxy()->PostTask( + other_thread.task_runner()->PostTask( FROM_HERE, Bind(&ExpectIsCanceled, is_canceled, false)); other_thread.Stop(); task_tracker_.TryCancel(task_id); ASSERT_TRUE(other_thread.Start()); - other_thread.message_loop_proxy()->PostTask( + other_thread.task_runner()->PostTask( FROM_HERE, Bind(&ExpectIsCanceled, is_canceled, true)); other_thread.Stop(); } @@ -346,8 +342,6 @@ class CancelableTaskTrackerDeathTest : public CancelableTaskTrackerTest { // The default style "fast" does not support multi-threaded tests. ::testing::FLAGS_gtest_death_test_style = "threadsafe"; } - - virtual ~CancelableTaskTrackerDeathTest() {} }; // Duplicated from base/threading/thread_checker.h so that we can be @@ -380,11 +374,9 @@ TEST_F(CancelableTaskTrackerDeathTest, PostFromDifferentThread) { Thread bad_thread("bad thread"); ASSERT_TRUE(bad_thread.Start()); - bad_thread.message_loop_proxy()->PostTask( - FROM_HERE, - Bind(&MaybeRunDeadlyTaskTrackerMemberFunction, - Unretained(&task_tracker_), - Bind(&PostDoNothingTask))); + bad_thread.task_runner()->PostTask( + FROM_HERE, Bind(&MaybeRunDeadlyTaskTrackerMemberFunction, + Unretained(&task_tracker_), Bind(&PostDoNothingTask))); } void TryCancel(CancelableTaskTracker::TaskId task_id, @@ -403,11 +395,9 @@ TEST_F(CancelableTaskTrackerDeathTest, CancelOnDifferentThread) { test_task_runner.get(), FROM_HERE, Bind(&DoNothing)); EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); - bad_thread.message_loop_proxy()->PostTask( - FROM_HERE, - Bind(&MaybeRunDeadlyTaskTrackerMemberFunction, - Unretained(&task_tracker_), - Bind(&TryCancel, task_id))); + bad_thread.task_runner()->PostTask( + FROM_HERE, Bind(&MaybeRunDeadlyTaskTrackerMemberFunction, + Unretained(&task_tracker_), Bind(&TryCancel, task_id))); test_task_runner->RunUntilIdle(); } @@ -423,10 +413,9 @@ TEST_F(CancelableTaskTrackerDeathTest, CancelAllOnDifferentThread) { test_task_runner.get(), FROM_HERE, Bind(&DoNothing)); EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); - bad_thread.message_loop_proxy()->PostTask( + bad_thread.task_runner()->PostTask( FROM_HERE, - Bind(&MaybeRunDeadlyTaskTrackerMemberFunction, - Unretained(&task_tracker_), + Bind(&MaybeRunDeadlyTaskTrackerMemberFunction, Unretained(&task_tracker_), Bind(&CancelableTaskTracker::TryCancelAll))); test_task_runner->RunUntilIdle(); diff --git a/chromium/base/task_runner_util_unittest.cc b/chromium/base/task_runner_util_unittest.cc index 481f09ebd19..8245cfcdc50 100644 --- a/chromium/base/task_runner_util_unittest.cc +++ b/chromium/base/task_runner_util_unittest.cc @@ -5,7 +5,7 @@ #include "base/task_runner_util.h" #include "base/bind.h" -#include "base/message_loop/message_loop.h" +#include "base/location.h" #include "base/run_loop.h" #include "testing/gtest/include/gtest/gtest.h" @@ -69,8 +69,7 @@ TEST(TaskRunnerHelpersTest, PostTaskAndReplyWithResult) { int result = 0; MessageLoop message_loop; - PostTaskAndReplyWithResult(message_loop.message_loop_proxy().get(), - FROM_HERE, + PostTaskAndReplyWithResult(message_loop.task_runner().get(), FROM_HERE, Bind(&ReturnFourtyTwo), Bind(&StoreValue, &result)); @@ -83,8 +82,7 @@ TEST(TaskRunnerHelpersTest, PostTaskAndReplyWithResultImplicitConvert) { double result = 0; MessageLoop message_loop; - PostTaskAndReplyWithResult(message_loop.message_loop_proxy().get(), - FROM_HERE, + PostTaskAndReplyWithResult(message_loop.task_runner().get(), FROM_HERE, Bind(&ReturnFourtyTwo), Bind(&StoreDoubleValue, &result)); @@ -98,10 +96,8 @@ TEST(TaskRunnerHelpersTest, PostTaskAndReplyWithResultPassed) { g_foo_free_count = 0; MessageLoop message_loop; - PostTaskAndReplyWithResult(message_loop.message_loop_proxy().get(), - FROM_HERE, - Bind(&CreateFoo), - Bind(&ExpectFoo)); + PostTaskAndReplyWithResult(message_loop.task_runner().get(), FROM_HERE, + Bind(&CreateFoo), Bind(&ExpectFoo)); RunLoop().RunUntilIdle(); @@ -114,10 +110,8 @@ TEST(TaskRunnerHelpersTest, PostTaskAndReplyWithResultPassedFreeProc) { g_foo_free_count = 0; MessageLoop message_loop; - PostTaskAndReplyWithResult(message_loop.message_loop_proxy().get(), - FROM_HERE, - Bind(&CreateScopedFoo), - Bind(&ExpectScopedFoo)); + PostTaskAndReplyWithResult(message_loop.task_runner().get(), FROM_HERE, + Bind(&CreateScopedFoo), Bind(&ExpectScopedFoo)); RunLoop().RunUntilIdle(); diff --git a/chromium/base/third_party/dynamic_annotations/BUILD.gn b/chromium/base/third_party/dynamic_annotations/BUILD.gn index 9a478674f1b..bc324ae4a78 100644 --- a/chromium/base/third_party/dynamic_annotations/BUILD.gn +++ b/chromium/base/third_party/dynamic_annotations/BUILD.gn @@ -2,14 +2,25 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -source_set("dynamic_annotations") { - sources = [ - "dynamic_annotations.c", - "dynamic_annotations.h", - "../valgrind/valgrind.h", - ] - if (is_android && !is_debug) { - configs -= [ "//build/config/compiler:optimize" ] - configs += [ "//build/config/compiler:optimize_max" ] +if (is_nacl) { + # Native client doesn't need dynamic annotations, so we provide a + # dummy target in order for clients to not have to special-case the + # dependency. + source_set("dynamic_annotations") { + sources = [ + "dynamic_annotations.h", + ] + } +} else { + source_set("dynamic_annotations") { + sources = [ + "../valgrind/valgrind.h", + "dynamic_annotations.c", + "dynamic_annotations.h", + ] + if (is_android && !is_debug) { + configs -= [ "//build/config/compiler:optimize" ] + configs += [ "//build/config/compiler:optimize_max" ] + } } } diff --git a/chromium/base/third_party/dynamic_annotations/README.chromium b/chromium/base/third_party/dynamic_annotations/README.chromium index dc8bdef0531..ff21b19e502 100644 --- a/chromium/base/third_party/dynamic_annotations/README.chromium +++ b/chromium/base/third_party/dynamic_annotations/README.chromium @@ -3,6 +3,13 @@ URL: http://code.google.com/p/data-race-test/wiki/DynamicAnnotations Version: 4384 License: BSD +ATTENTION: please avoid using these annotations in Chromium code. +They were mainly intended to instruct the Valgrind-based version of +ThreadSanitizer to handle atomic operations. The new version of ThreadSanitizer +based on compiler instrumentation understands atomic operations out of the box, +so normally you don't need the annotations. +If you still think you do, please consider writing a comment at http://crbug.com/349861 + One header and one source file (dynamic_annotations.h and dynamic_annotations.c) in this directory define runtime macros useful for annotating synchronization utilities and benign data races so data race detectors can handle Chromium code diff --git a/chromium/base/third_party/dynamic_annotations/dynamic_annotations.gyp b/chromium/base/third_party/dynamic_annotations/dynamic_annotations.gyp index 12cfb6588eb..8d2e9ec96c5 100644 --- a/chromium/base/third_party/dynamic_annotations/dynamic_annotations.gyp +++ b/chromium/base/third_party/dynamic_annotations/dynamic_annotations.gyp @@ -12,9 +12,9 @@ '../../../', ], 'sources': [ + '../valgrind/valgrind.h', 'dynamic_annotations.c', 'dynamic_annotations.h', - '../valgrind/valgrind.h', ], 'includes': [ '../../../build/android/increase_size_for_speed.gypi', diff --git a/chromium/base/third_party/nspr/BUILD.gn b/chromium/base/third_party/nspr/BUILD.gn deleted file mode 100644 index 516ca1f60f0..00000000000 --- a/chromium/base/third_party/nspr/BUILD.gn +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) 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. - -source_set("nspr") { - visibility = [ "//base" ] - sources = [ - "prtime.cc", - "prtime.h", - ] - - # In GYP this project is part of base, so it uses the base implementation - # define. TODO(brettw) rename this define. - defines = [ "BASE_IMPLEMENTATION" ] - - if (is_android && !is_debug) { - configs -= [ "//build/config/compiler:optimize" ] - configs += [ "//build/config/compiler:optimize_max" ] - } -} diff --git a/chromium/base/third_party/xdg_mime/README.chromium b/chromium/base/third_party/xdg_mime/README.chromium index 29d239a4ce0..95f3a9658d1 100644 --- a/chromium/base/third_party/xdg_mime/README.chromium +++ b/chromium/base/third_party/xdg_mime/README.chromium @@ -7,5 +7,7 @@ git://anongit.freedesktop.org/xdg/xdgmime @ 2cdd8d36d7930d5a594587286cb1949ff62f7027 on 2012/08/06. In addition, we have the following patch(es): -- compile.patch: small tweaks to make the code compile. -- Added a LICENSE file. + - compile.patch: small tweaks to make the code compile. + - free_pointer_later.patch: small patch that fixes potential crash in + xdg_mime_get_mime_type_for_file() - use of pointer after being freed. + - Added a LICENSE file. diff --git a/chromium/base/third_party/xdg_mime/free_pointer_later.patch b/chromium/base/third_party/xdg_mime/free_pointer_later.patch new file mode 100644 index 00000000000..76687610d52 --- /dev/null +++ b/chromium/base/third_party/xdg_mime/free_pointer_later.patch @@ -0,0 +1,22 @@ +diff --git a/base/third_party/xdg_mime/xdgmime.c b/base/third_party/xdg_mime/xdgmime.c +index c7b16bb..6dc58c2 100644 +--- a/base/third_party/xdg_mime/xdgmime.c ++++ b/base/third_party/xdg_mime/xdgmime.c +@@ -558,13 +558,13 @@ xdg_mime_get_mime_type_for_file (const char *file_name, + mime_type = _xdg_mime_magic_lookup_data (global_magic, data, bytes_read, NULL, + mime_types, n); + +- free (data); + fclose (file); + +- if (mime_type) +- return mime_type; ++ if (!mime_type) ++ mime_type = _xdg_binary_or_text_fallback(data, bytes_read); + +- return _xdg_binary_or_text_fallback(data, bytes_read); ++ free (data); ++ return mime_type; + } + + const char * diff --git a/chromium/base/third_party/xdg_mime/xdgmime.c b/chromium/base/third_party/xdg_mime/xdgmime.c index c7b16bbca76..6dc58c253fa 100644 --- a/chromium/base/third_party/xdg_mime/xdgmime.c +++ b/chromium/base/third_party/xdg_mime/xdgmime.c @@ -558,13 +558,13 @@ xdg_mime_get_mime_type_for_file (const char *file_name, mime_type = _xdg_mime_magic_lookup_data (global_magic, data, bytes_read, NULL, mime_types, n); - free (data); fclose (file); - if (mime_type) - return mime_type; + if (!mime_type) + mime_type = _xdg_binary_or_text_fallback(data, bytes_read); - return _xdg_binary_or_text_fallback(data, bytes_read); + free (data); + return mime_type; } const char * diff --git a/chromium/base/threading/platform_thread.h b/chromium/base/threading/platform_thread.h index 28743145610..d8f06e5884d 100644 --- a/chromium/base/threading/platform_thread.h +++ b/chromium/base/threading/platform_thread.h @@ -110,15 +110,17 @@ class PlatformThreadHandle { const PlatformThreadId kInvalidThreadId(0); -// Valid values for SetThreadPriority() -enum ThreadPriority{ - kThreadPriority_Normal, - // Suitable for low-latency, glitch-resistant audio. - kThreadPriority_RealtimeAudio, - // Suitable for threads which generate data for the display (at ~60Hz). - kThreadPriority_Display, +// Valid values for SetThreadPriority(), listed in increasing order of +// importance. +enum class ThreadPriority { // Suitable for threads that shouldn't disrupt high priority work. - kThreadPriority_Background + BACKGROUND, + // Default priority level. + NORMAL, + // Suitable for threads which generate data for the display (at ~60Hz). + DISPLAY, + // Suitable for low-latency, glitch-resistant audio. + REALTIME_AUDIO, }; // A namespace for low-level thread functions. @@ -141,7 +143,10 @@ class BASE_EXPORT PlatformThread { // we're on the right thread quickly. static PlatformThreadRef CurrentRef(); - // Get the current handle. + // Get the handle representing the current thread. On Windows, this is a + // pseudo handle constant which will always represent the thread using it and + // hence should not be shared with other threads nor be used to differentiate + // the current thread from another. static PlatformThreadHandle CurrentHandle(); // Yield the current thread so another thread can be scheduled. @@ -151,9 +156,8 @@ class BASE_EXPORT PlatformThread { static void Sleep(base::TimeDelta duration); // Sets the thread name visible to debuggers/tools. This has no effect - // otherwise. This name pointer is not copied internally. Thus, it must stay - // valid until the thread ends. - static void SetName(const char* name); + // otherwise. + static void SetName(const std::string& name); // Gets the thread name, if previously set by SetName. static const char* GetName(); @@ -171,9 +175,7 @@ class BASE_EXPORT PlatformThread { // 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(). SetThreadPriority() has not been - // implemented on the Linux platform yet, this is the only way to get a high - // priority thread on Linux. + // followed by SetThreadPriority(). static bool CreateWithPriority(size_t stack_size, Delegate* delegate, PlatformThreadHandle* thread_handle, ThreadPriority priority); @@ -191,6 +193,8 @@ class BASE_EXPORT PlatformThread { static void SetThreadPriority(PlatformThreadHandle handle, ThreadPriority priority); + static ThreadPriority GetThreadPriority(PlatformThreadHandle handle); + private: DISALLOW_IMPLICIT_CONSTRUCTORS(PlatformThread); }; diff --git a/chromium/base/threading/platform_thread_android.cc b/chromium/base/threading/platform_thread_android.cc index f8395e59221..11e5e2e3c87 100644 --- a/chromium/base/threading/platform_thread_android.cc +++ b/chromium/base/threading/platform_thread_android.cc @@ -7,81 +7,64 @@ #include <errno.h> #include <sys/prctl.h> #include <sys/resource.h> +#include <sys/types.h> +#include <unistd.h> #include "base/android/jni_android.h" #include "base/android/thread_utils.h" #include "base/lazy_instance.h" #include "base/logging.h" +#include "base/threading/platform_thread_internal_posix.h" #include "base/threading/thread_id_name_manager.h" #include "base/tracked_objects.h" #include "jni/ThreadUtils_jni.h" namespace base { -namespace { -int ThreadNiceValue(ThreadPriority priority) { - // 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). - static const int threadPriorityAudio = -16; - static const int threadPriorityBackground = 10; - static const int threadPriorityDefault = 0; - static const int threadPriorityDisplay = -6; - switch (priority) { - case kThreadPriority_RealtimeAudio: - return threadPriorityAudio; - case kThreadPriority_Background: - return threadPriorityBackground; - case kThreadPriority_Normal: - return threadPriorityDefault; - case kThreadPriority_Display: - return threadPriorityDisplay; - default: - NOTREACHED() << "Unknown priority."; - return 0; - } -} -} // namespace - -//static -void PlatformThread::SetThreadPriority(PlatformThreadHandle handle, - ThreadPriority priority) { +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). +const ThreadPriorityToNiceValuePair kThreadPriorityToNiceValueMap[4] = { + {ThreadPriority::BACKGROUND, 10}, + {ThreadPriority::NORMAL, 0}, + {ThreadPriority::DISPLAY, -6}, + {ThreadPriority::REALTIME_AUDIO, -16}, +}; + +bool SetThreadPriorityForPlatform(PlatformThreadHandle handle, + 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 == kThreadPriority_RealtimeAudio) { + if (priority == ThreadPriority::REALTIME_AUDIO) { JNIEnv* env = base::android::AttachCurrentThread(); Java_ThreadUtils_setThreadPriorityAudio(env, PlatformThread::CurrentId()); - return; + return true; } + return false; +} - // setpriority(2) should change the whole thread group's (i.e. process) - // priority. however, on linux it will only change the target thread's - // priority. see the bugs section in - // http://man7.org/linux/man-pages/man2/getpriority.2.html. - // we prefer using 0 rather than the current thread id since they are - // equivalent but it makes sandboxing easier (https://crbug.com/399473). - DCHECK_NE(handle.id_, kInvalidThreadId); - int kNiceSetting = ThreadNiceValue(priority); - const PlatformThreadId current_id = PlatformThread::CurrentId(); - if (setpriority(PRIO_PROCESS, - handle.id_ == current_id ? 0 : handle.id_, - kNiceSetting)) { - LOG(ERROR) << "Failed to set nice value of thread to " << kNiceSetting; - } +bool GetThreadPriorityForPlatform(PlatformThreadHandle handle, + ThreadPriority* priority) { + NOTIMPLEMENTED(); + return false; } -void PlatformThread::SetName(const char* name) { +} // namespace internal + +void PlatformThread::SetName(const std::string& name) { ThreadIdNameManager::GetInstance()->SetName(CurrentId(), name); tracked_objects::ThreadData::InitializeThreadContext(name); @@ -93,7 +76,7 @@ void PlatformThread::SetName(const char* name) { return; // Set the name for the LWP (which gets truncated to 15 characters). - int err = prctl(PR_SET_NAME, name); + int err = prctl(PR_SET_NAME, name.c_str()); if (err < 0 && errno != EPERM) DPLOG(ERROR) << "prctl(PR_SET_NAME)"; } @@ -106,7 +89,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(), - kThreadPriority_Normal); + ThreadPriority::NORMAL); } void TerminateOnThread() { diff --git a/chromium/base/threading/platform_thread_freebsd.cc b/chromium/base/threading/platform_thread_freebsd.cc index 7a24f4e055e..f4fded0ebb2 100644 --- a/chromium/base/threading/platform_thread_freebsd.cc +++ b/chromium/base/threading/platform_thread_freebsd.cc @@ -9,42 +9,67 @@ #include "base/lazy_instance.h" #include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "base/safe_strerror_posix.h" #include "base/threading/thread_id_name_manager.h" -#include "base/threading/thread_restrictions.h" #include "base/tracked_objects.h" #if !defined(OS_NACL) -#include <sys/resource.h> -#include <sys/syscall.h> -#include <sys/time.h> +#include <pthread.h> +#include <sys/prctl.h> #include <sys/types.h> #include <unistd.h> #endif namespace base { +namespace internal { + namespace { -int ThreadNiceValue(ThreadPriority priority) { - switch (priority) { - case kThreadPriority_RealtimeAudio: - return -10; - case kThreadPriority_Background: - return 10; - case kThreadPriority_Normal: - return 0; - case kThreadPriority_Display: - return -6; - default: - NOTREACHED() << "Unknown priority."; - return 0; +#if !defined(OS_NACL) +const struct sched_param kRealTimePrio = {8}; +#endif +} // namespace + +const ThreadPriorityToNiceValuePair kThreadPriorityToNiceValueMap[4] = { + {ThreadPriority::BACKGROUND, 10}, + {ThreadPriority::NORMAL, 0}, + {ThreadPriority::DISPLAY, -6}, + {ThreadPriority::REALTIME_AUDIO, -10}, +} + +bool SetThreadPriorityForPlatform(PlatformThreadHandle handle, + 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 + return false; +#endif +} + +bool GetThreadPriorityForPlatform(PlatformThreadHandle handle, + 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, + &maybe_realtime_prio) == 0 && + maybe_sched_rr == SCHED_RR && + maybe_realtime_prio.sched_priority == kRealTimePrio.sched_priority) { + *priority = ThreadPriority::REALTIME_AUDIO; + return true; } +#endif + return false; } -} // namespace + +} // namespace internal // static -void PlatformThread::SetName(const char* name) { +void PlatformThread::SetName(const std::string& name) { ThreadIdNameManager::GetInstance()->SetName(CurrentId(), name); tracked_objects::ThreadData::InitializeThreadContext(name); @@ -55,32 +80,7 @@ void PlatformThread::SetName(const char* name) { // killall to stop working. if (PlatformThread::CurrentId() == getpid()) return; - setproctitle("%s", name); -#endif // !defined(OS_NACL) -} - -// static -void PlatformThread::SetThreadPriority(PlatformThreadHandle handle, - ThreadPriority priority) { -#if !defined(OS_NACL) - if (priority == kThreadPriority_RealtimeAudio) { - const struct sched_param kRealTimePrio = { 8 }; - if (pthread_setschedparam(pthread_self(), SCHED_RR, &kRealTimePrio) == 0) { - // Got real time priority, no need to set nice level. - return; - } - } - - // setpriority(2) will set a thread's priority if it is passed a tid as - // the 'process identifier', not affecting the rest of the threads in the - // process. Setting this priority will only succeed if the user has been - // granted permission to adjust nice values on the system. - DCHECK_NE(handle.id_, kInvalidThreadId); - const int kNiceSetting = ThreadNiceValue(priority); - if (setpriority(PRIO_PROCESS, handle.id_, kNiceSetting)) { - DVPLOG(1) << "Failed to set nice value of thread (" - << handle.id_ << ") to " << kNiceSetting; - } + setproctitle("%s", name.c_str()); #endif // !defined(OS_NACL) } diff --git a/chromium/base/threading/platform_thread_internal_posix.cc b/chromium/base/threading/platform_thread_internal_posix.cc new file mode 100644 index 00000000000..9af02044fce --- /dev/null +++ b/chromium/base/threading/platform_thread_internal_posix.cc @@ -0,0 +1,35 @@ +// 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/threading/platform_thread_internal_posix.h" + +#include "base/logging.h" + +namespace base { + +namespace internal { + +int ThreadPriorityToNiceValue(ThreadPriority priority) { + for (const ThreadPriorityToNiceValuePair& pair : + kThreadPriorityToNiceValueMap) { + if (pair.priority == priority) + return pair.nice_value; + } + NOTREACHED() << "Unknown ThreadPriority"; + return 0; +} + +ThreadPriority NiceValueToThreadPriority(int nice_value) { + for (const ThreadPriorityToNiceValuePair& pair : + kThreadPriorityToNiceValueMap) { + if (pair.nice_value == nice_value) + return pair.priority; + } + NOTREACHED() << "Unknown nice value"; + return ThreadPriority::NORMAL; +} + +} // namespace internal + +} // namespace base diff --git a/chromium/base/threading/platform_thread_internal_posix.h b/chromium/base/threading/platform_thread_internal_posix.h new file mode 100644 index 00000000000..62006ce13c9 --- /dev/null +++ b/chromium/base/threading/platform_thread_internal_posix.h @@ -0,0 +1,45 @@ +// 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_THREADING_PLATFORM_THREAD_INTERNAL_POSIX_H_ +#define BASE_THREADING_PLATFORM_THREAD_INTERNAL_POSIX_H_ + +#include "base/threading/platform_thread.h" + +namespace base { + +namespace internal { + +struct ThreadPriorityToNiceValuePair { + ThreadPriority priority; + int nice_value; +}; +extern const ThreadPriorityToNiceValuePair kThreadPriorityToNiceValueMap[4]; + +// Returns the nice value matching |priority| based on the platform-specific +// implementation of kThreadPriorityToNiceValueMap. +int ThreadPriorityToNiceValue(ThreadPriority priority); + +// Returns the ThreadPrioirty matching |nice_value| based on the platform- +// specific implementation of kThreadPriorityToNiceValueMap. +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); + +// 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); + +} // namespace internal + +} // namespace base + +#endif // BASE_THREADING_PLATFORM_THREAD_INTERNAL_POSIX_H_ diff --git a/chromium/base/threading/platform_thread_linux.cc b/chromium/base/threading/platform_thread_linux.cc index d9e2bd9932f..9f7437418a7 100644 --- a/chromium/base/threading/platform_thread_linux.cc +++ b/chromium/base/threading/platform_thread_linux.cc @@ -9,44 +9,68 @@ #include "base/lazy_instance.h" #include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "base/safe_strerror_posix.h" +#include "base/threading/platform_thread_internal_posix.h" #include "base/threading/thread_id_name_manager.h" -#include "base/threading/thread_restrictions.h" #include "base/tracked_objects.h" #if !defined(OS_NACL) +#include <pthread.h> #include <sys/prctl.h> -#include <sys/resource.h> -#include <sys/syscall.h> -#include <sys/time.h> +#include <sys/types.h> #include <unistd.h> #endif namespace base { +namespace internal { + namespace { +#if !defined(OS_NACL) +const struct sched_param kRealTimePrio = {8}; +#endif +} // namespace + +const ThreadPriorityToNiceValuePair kThreadPriorityToNiceValueMap[4] = { + {ThreadPriority::BACKGROUND, 10}, + {ThreadPriority::NORMAL, 0}, + {ThreadPriority::DISPLAY, -6}, + {ThreadPriority::REALTIME_AUDIO, -10}, +}; -int ThreadNiceValue(ThreadPriority priority) { - switch (priority) { - case kThreadPriority_RealtimeAudio: - return -10; - case kThreadPriority_Background: - return 10; - case kThreadPriority_Normal: - return 0; - case kThreadPriority_Display: - return -6; - default: - NOTREACHED() << "Unknown priority."; - return 0; +bool SetThreadPriorityForPlatform(PlatformThreadHandle handle, + 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 + return false; +#endif +} + +bool GetThreadPriorityForPlatform(PlatformThreadHandle handle, + 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 && + maybe_realtime_prio.sched_priority == kRealTimePrio.sched_priority) { + *priority = ThreadPriority::REALTIME_AUDIO; + return true; } +#endif + return false; } -} // namespace +} // namespace internal // static -void PlatformThread::SetName(const char* name) { +void PlatformThread::SetName(const std::string& name) { ThreadIdNameManager::GetInstance()->SetName(CurrentId(), name); tracked_objects::ThreadData::InitializeThreadContext(name); @@ -63,43 +87,13 @@ void PlatformThread::SetName(const char* name) { // Note that glibc also has a 'pthread_setname_np' api, but it may not be // available everywhere and it's only benefit over using prctl directly is // that it can set the name of threads other than the current thread. - int err = prctl(PR_SET_NAME, name); + int err = prctl(PR_SET_NAME, name.c_str()); // We expect EPERM failures in sandboxed processes, just ignore those. if (err < 0 && errno != EPERM) DPLOG(ERROR) << "prctl(PR_SET_NAME)"; #endif // !defined(OS_NACL) } -// static -void PlatformThread::SetThreadPriority(PlatformThreadHandle handle, - ThreadPriority priority) { -#if !defined(OS_NACL) - if (priority == kThreadPriority_RealtimeAudio) { - const struct sched_param kRealTimePrio = {8}; - if (pthread_setschedparam(pthread_self(), SCHED_RR, &kRealTimePrio) == 0) { - // Got real time priority, no need to set nice level. - return; - } - } - - // setpriority(2) should change the whole thread group's (i.e. process) - // priority. however, on linux it will only change the target thread's - // priority. see the bugs section in - // http://man7.org/linux/man-pages/man2/getpriority.2.html. - // we prefer using 0 rather than the current thread id since they are - // equivalent but it makes sandboxing easier (https://crbug.com/399473). - DCHECK_NE(handle.id_, kInvalidThreadId); - const int kNiceSetting = ThreadNiceValue(priority); - const PlatformThreadId current_id = PlatformThread::CurrentId(); - if (setpriority(PRIO_PROCESS, - handle.id_ == current_id ? 0 : handle.id_, - kNiceSetting)) { - DVPLOG(1) << "Failed to set nice value of thread (" << handle.id_ << ") to " - << kNiceSetting; - } -#endif // !defined(OS_NACL) -} - void InitThreading() {} void InitOnThread() {} diff --git a/chromium/base/threading/platform_thread_mac.mm b/chromium/base/threading/platform_thread_mac.mm index 147e625dbc8..a9c347a5b53 100644 --- a/chromium/base/threading/platform_thread_mac.mm +++ b/chromium/base/threading/platform_thread_mac.mm @@ -42,14 +42,14 @@ void InitThreading() { } // static -void PlatformThread::SetName(const char* name) { +void PlatformThread::SetName(const std::string& name) { ThreadIdNameManager::GetInstance()->SetName(CurrentId(), name); tracked_objects::ThreadData::InitializeThreadContext(name); // Mac OS X does not expose the length limit of the name, so // hardcode it. const int kMaxNameLength = 63; - std::string shortened_name = std::string(name).substr(0, kMaxNameLength); + std::string shortened_name = name.substr(0, kMaxNameLength); // pthread_setname() fails (harmlessly) in the sandbox, ignore when it does. // See http://crbug.com/47058 pthread_setname_np(shortened_name.c_str()); @@ -161,10 +161,10 @@ void PlatformThread::SetThreadPriority(PlatformThreadHandle handle, mach_port_t mach_thread_id = pthread_mach_thread_np(handle.handle_); switch (priority) { - case kThreadPriority_Normal: + case ThreadPriority::NORMAL: SetPriorityNormal(mach_thread_id); break; - case kThreadPriority_RealtimeAudio: + case ThreadPriority::REALTIME_AUDIO: SetPriorityRealtimeAudio(mach_thread_id); break; default: @@ -173,6 +173,12 @@ void PlatformThread::SetThreadPriority(PlatformThreadHandle handle, } } +// static +ThreadPriority PlatformThread::GetThreadPriority(PlatformThreadHandle handle) { + NOTIMPLEMENTED(); + return ThreadPriority::NORMAL; +} + size_t GetDefaultThreadStackSize(const pthread_attr_t& attributes) { #if defined(OS_IOS) return 0; diff --git a/chromium/base/threading/platform_thread_posix.cc b/chromium/base/threading/platform_thread_posix.cc index 42b416d4a38..3dbdc980875 100644 --- a/chromium/base/threading/platform_thread_posix.cc +++ b/chromium/base/threading/platform_thread_posix.cc @@ -5,28 +5,25 @@ #include "base/threading/platform_thread.h" #include <errno.h> +#include <pthread.h> #include <sched.h> +#include <sys/resource.h> +#include <sys/time.h> #include "base/lazy_instance.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/safe_strerror_posix.h" #include "base/synchronization/waitable_event.h" +#include "base/threading/platform_thread_internal_posix.h" #include "base/threading/thread_id_name_manager.h" #include "base/threading/thread_restrictions.h" #include "base/tracked_objects.h" -#if defined(OS_MACOSX) -#include <sys/resource.h> -#include <algorithm> -#endif - #if defined(OS_LINUX) -#include <sys/prctl.h> -#include <sys/resource.h> #include <sys/syscall.h> -#include <sys/time.h> -#include <unistd.h> +#elif defined(OS_ANDROID) +#include <sys/types.h> #endif namespace base { @@ -42,7 +39,7 @@ struct ThreadParams { ThreadParams() : delegate(NULL), joinable(false), - priority(kThreadPriority_Normal), + priority(ThreadPriority::NORMAL), handle(NULL), handle_set(false, false) { } @@ -62,7 +59,7 @@ void* ThreadFunc(void* params) { if (!thread_params->joinable) base::ThreadRestrictions::SetSingletonAllowed(false); - if (thread_params->priority != kThreadPriority_Normal) { + if (thread_params->priority != ThreadPriority::NORMAL) { PlatformThread::SetThreadPriority(PlatformThread::CurrentHandle(), thread_params->priority); } @@ -204,7 +201,7 @@ bool PlatformThread::Create(size_t stack_size, Delegate* delegate, PlatformThreadHandle* thread_handle) { base::ThreadRestrictions::ScopedAllowWait allow_wait; return CreateThread(stack_size, true /* joinable thread */, - delegate, thread_handle, kThreadPriority_Normal); + delegate, thread_handle, ThreadPriority::NORMAL); } // static @@ -222,7 +219,7 @@ bool PlatformThread::CreateNonJoinable(size_t stack_size, Delegate* delegate) { base::ThreadRestrictions::ScopedAllowWait allow_wait; bool result = CreateThread(stack_size, false /* non-joinable thread */, - delegate, &unused, kThreadPriority_Normal); + delegate, &unused, ThreadPriority::NORMAL); return result; } @@ -235,4 +232,64 @@ void PlatformThread::Join(PlatformThreadHandle thread_handle) { CHECK_EQ(0, pthread_join(thread_handle.handle_, NULL)); } +// Mac has its own Set/GetThreadPriority() implementations. +#if !defined(OS_MACOSX) + +// static +void PlatformThread::SetThreadPriority(PlatformThreadHandle handle, + ThreadPriority priority) { +#if defined(OS_NACL) + NOTIMPLEMENTED(); +#else + if (internal::SetThreadPriorityForPlatform(handle, priority)) + return; + + // setpriority(2) should change the whole thread group's (i.e. process) + // priority. However, as stated in the bugs section of + // http://man7.org/linux/man-pages/man2/getpriority.2.html: "under the current + // 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; + } +#endif // defined(OS_NACL) +} + +// static +ThreadPriority PlatformThread::GetThreadPriority(PlatformThreadHandle handle) { +#if defined(OS_NACL) + NOTIMPLEMENTED(); + return ThreadPriority::NORMAL; +#else + // Mirrors SetThreadPriority()'s implementation. + ThreadPriority platform_specific_priority; + if (internal::GetThreadPriorityForPlatform(handle, + &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_); + if (errno != 0) { + DVPLOG(1) << "Failed to get nice value of thread (" << handle.id_ << ")"; + return ThreadPriority::NORMAL; + } + + return internal::NiceValueToThreadPriority(nice_value); +#endif // !defined(OS_NACL) +} + +#endif // !defined(OS_MACOSX) + } // namespace base diff --git a/chromium/base/threading/platform_thread_unittest.cc b/chromium/base/threading/platform_thread_unittest.cc index 21260e5ec36..c4b3d5d7ecc 100644 --- a/chromium/base/threading/platform_thread_unittest.cc +++ b/chromium/base/threading/platform_thread_unittest.cc @@ -3,10 +3,15 @@ // found in the LICENSE file. #include "base/compiler_specific.h" +#include "base/macros.h" +#include "base/synchronization/waitable_event.h" #include "base/threading/platform_thread.h" - #include "testing/gtest/include/gtest/gtest.h" +#if defined(OS_WIN) +#include <windows.h> +#endif + namespace base { // Trivial tests that thread runs and doesn't crash on create and join --------- @@ -51,26 +56,59 @@ TEST(PlatformThreadTest, TrivialTimesTen) { // Tests of basic thread functions --------------------------------------------- -class FunctionTestThread : public TrivialThread { +class FunctionTestThread : public PlatformThread::Delegate { public: - FunctionTestThread() : thread_id_(0) {} + FunctionTestThread() + : thread_id_(kInvalidThreadId), + thread_started_(true, false), + terminate_thread_(true, false), + done_(false) {} + ~FunctionTestThread() override { + EXPECT_TRUE(terminate_thread_.IsSignaled()) + << "Need to mark thread for termination and join the underlying thread " + << "before destroying a FunctionTestThread as it owns the " + << "WaitableEvent blocking the underlying thread's main."; + } + // Grabs |thread_id_|, signals |thread_started_|, and then waits for + // |terminate_thread_| to be signaled before exiting. void ThreadMain() override { thread_id_ = PlatformThread::CurrentId(); - PlatformThread::YieldCurrentThread(); - PlatformThread::Sleep(TimeDelta::FromMilliseconds(50)); + EXPECT_NE(thread_id_, kInvalidThreadId); // Make sure that the thread ID is the same across calls. EXPECT_EQ(thread_id_, PlatformThread::CurrentId()); - TrivialThread::ThreadMain(); + thread_started_.Signal(); + + terminate_thread_.Wait(); + + done_ = true; + } + + PlatformThreadId thread_id() const { + EXPECT_TRUE(thread_started_.IsSignaled()) << "Thread ID still unknown"; + return thread_id_; } - PlatformThreadId thread_id() const { return thread_id_; } + bool IsRunning() const { + return thread_started_.IsSignaled() && !done_; + } + + // Blocks until this thread is started. + void WaitForThreadStart() { thread_started_.Wait(); } + + // Mark this thread for termination (callers must then join this thread to be + // guaranteed of termination). + void MarkForTermination() { terminate_thread_.Signal(); } private: PlatformThreadId thread_id_; + mutable WaitableEvent thread_started_; + WaitableEvent terminate_thread_; + bool done_; + DISALLOW_COPY_AND_ASSIGN(FunctionTestThread); }; @@ -80,12 +118,16 @@ TEST(PlatformThreadTest, Function) { FunctionTestThread thread; PlatformThreadHandle handle; - ASSERT_FALSE(thread.did_run()); + ASSERT_FALSE(thread.IsRunning()); ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle)); - PlatformThread::Join(handle); - ASSERT_TRUE(thread.did_run()); + thread.WaitForThreadStart(); + ASSERT_TRUE(thread.IsRunning()); EXPECT_NE(thread.thread_id(), main_thread_id); + thread.MarkForTermination(); + PlatformThread::Join(handle); + ASSERT_FALSE(thread.IsRunning()); + // Make sure that the thread ID is the same across calls. EXPECT_EQ(main_thread_id, PlatformThread::CurrentId()); } @@ -97,13 +139,15 @@ TEST(PlatformThreadTest, FunctionTimesTen) { PlatformThreadHandle handle[arraysize(thread)]; for (size_t n = 0; n < arraysize(thread); n++) - ASSERT_FALSE(thread[n].did_run()); + ASSERT_FALSE(thread[n].IsRunning()); + 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++) - PlatformThread::Join(handle[n]); + thread[n].WaitForThreadStart(); + for (size_t n = 0; n < arraysize(thread); n++) { - ASSERT_TRUE(thread[n].did_run()); + ASSERT_TRUE(thread[n].IsRunning()); EXPECT_NE(thread[n].thread_id(), main_thread_id); // Make sure no two threads get the same ID. @@ -112,8 +156,120 @@ TEST(PlatformThreadTest, FunctionTimesTen) { } } + for (size_t n = 0; n < arraysize(thread); n++) + thread[n].MarkForTermination(); + for (size_t n = 0; n < arraysize(thread); n++) + PlatformThread::Join(handle[n]); + for (size_t n = 0; n < arraysize(thread); n++) + ASSERT_FALSE(thread[n].IsRunning()); + // Make sure that the thread ID is the same across calls. EXPECT_EQ(main_thread_id, PlatformThread::CurrentId()); } +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, + 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 +}; + +} // 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()); + + // 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 +// 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; + PlatformThreadHandle handle; + 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.MarkForTermination(); + PlatformThread::Join(handle); +} + } // namespace base diff --git a/chromium/base/threading/platform_thread_win.cc b/chromium/base/threading/platform_thread_win.cc index 3df371943f5..4eb2cb2b33d 100644 --- a/chromium/base/threading/platform_thread_win.cc +++ b/chromium/base/threading/platform_thread_win.cc @@ -126,18 +126,17 @@ bool CreateThreadInternal(size_t stack_size, // static PlatformThreadId PlatformThread::CurrentId() { - return GetCurrentThreadId(); + return ::GetCurrentThreadId(); } // static PlatformThreadRef PlatformThread::CurrentRef() { - return PlatformThreadRef(GetCurrentThreadId()); + return PlatformThreadRef(::GetCurrentThreadId()); } // static PlatformThreadHandle PlatformThread::CurrentHandle() { - NOTIMPLEMENTED(); // See OpenThread() - return PlatformThreadHandle(); + return PlatformThreadHandle(::GetCurrentThread()); } // static @@ -155,7 +154,7 @@ void PlatformThread::Sleep(TimeDelta duration) { } // static -void PlatformThread::SetName(const char* name) { +void PlatformThread::SetName(const std::string& name) { ThreadIdNameManager::GetInstance()->SetName(CurrentId(), name); // On Windows only, we don't need to tell the profiler about the "BrokerEvent" @@ -164,7 +163,7 @@ void PlatformThread::SetName(const char* name) { // which would also (as a side effect) initialize the profiler in this unused // context, including setting up thread local storage, etc. The performance // impact is not terrible, but there is no reason to do initialize it. - if (0 != strcmp(name, "BrokerEvent")) + if (name != "BrokerEvent") tracked_objects::ThreadData::InitializeThreadContext(name); // The debugger needs to be around to catch the name in the exception. If @@ -174,7 +173,7 @@ void PlatformThread::SetName(const char* name) { if (!::IsDebuggerPresent() && !base::debug::IsBinaryInstrumented()) return; - SetNameInternal(CurrentId(), name); + SetNameInternal(CurrentId(), name.c_str()); } // static @@ -234,17 +233,56 @@ void PlatformThread::Join(PlatformThreadHandle thread_handle) { // static void PlatformThread::SetThreadPriority(PlatformThreadHandle handle, ThreadPriority priority) { + DCHECK(!handle.is_null()); + + int desired_priority = THREAD_PRIORITY_ERROR_RETURN; switch (priority) { - case kThreadPriority_Normal: - ::SetThreadPriority(handle.handle_, THREAD_PRIORITY_NORMAL); + case ThreadPriority::BACKGROUND: + desired_priority = THREAD_PRIORITY_LOWEST; + break; + case ThreadPriority::NORMAL: + desired_priority = THREAD_PRIORITY_NORMAL; + break; + case ThreadPriority::DISPLAY: + desired_priority = THREAD_PRIORITY_ABOVE_NORMAL; break; - case kThreadPriority_RealtimeAudio: - ::SetThreadPriority(handle.handle_, THREAD_PRIORITY_TIME_CRITICAL); + case ThreadPriority::REALTIME_AUDIO: + desired_priority = THREAD_PRIORITY_TIME_CRITICAL; break; default: NOTREACHED() << "Unknown priority."; break; } + DCHECK_NE(desired_priority, THREAD_PRIORITY_ERROR_RETURN); + +#ifndef NDEBUG + const BOOL success = +#endif + ::SetThreadPriority(handle.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.handle_); + switch (priority) { + case THREAD_PRIORITY_LOWEST: + return ThreadPriority::BACKGROUND; + case THREAD_PRIORITY_NORMAL: + return ThreadPriority::NORMAL; + case THREAD_PRIORITY_ABOVE_NORMAL: + return ThreadPriority::DISPLAY; + case THREAD_PRIORITY_TIME_CRITICAL: + return ThreadPriority::REALTIME_AUDIO; + case THREAD_PRIORITY_ERROR_RETURN: + DPCHECK(false) << "GetThreadPriority error"; // Falls through. + default: + NOTREACHED() << "Unexpected priority: " << priority; + return ThreadPriority::NORMAL; + } } } // namespace base diff --git a/chromium/base/threading/post_task_and_reply_impl.cc b/chromium/base/threading/post_task_and_reply_impl.cc index a82a4fd8043..f3e88abf608 100644 --- a/chromium/base/threading/post_task_and_reply_impl.cc +++ b/chromium/base/threading/post_task_and_reply_impl.cc @@ -25,30 +25,30 @@ namespace { class PostTaskAndReplyRelay { public: PostTaskAndReplyRelay(const tracked_objects::Location& from_here, - const Closure& task, const Closure& reply) + const Closure& task, + const Closure& reply) : from_here_(from_here), - origin_loop_(ThreadTaskRunnerHandle::Get()) { + origin_task_runner_(ThreadTaskRunnerHandle::Get()) { task_ = task; reply_ = reply; } ~PostTaskAndReplyRelay() { - DCHECK(origin_loop_->BelongsToCurrentThread()); + DCHECK(origin_task_runner_->BelongsToCurrentThread()); task_.Reset(); reply_.Reset(); } void Run() { task_.Run(); - origin_loop_->PostTask( - from_here_, - Bind(&PostTaskAndReplyRelay::RunReplyAndSelfDestruct, - base::Unretained(this))); + origin_task_runner_->PostTask( + from_here_, Bind(&PostTaskAndReplyRelay::RunReplyAndSelfDestruct, + base::Unretained(this))); } private: void RunReplyAndSelfDestruct() { - DCHECK(origin_loop_->BelongsToCurrentThread()); + DCHECK(origin_task_runner_->BelongsToCurrentThread()); // Force |task_| to be released before |reply_| is to ensure that no one // accidentally depends on |task_| keeping one of its arguments alive while @@ -62,7 +62,7 @@ class PostTaskAndReplyRelay { } tracked_objects::Location from_here_; - scoped_refptr<SingleThreadTaskRunner> origin_loop_; + scoped_refptr<SingleThreadTaskRunner> origin_task_runner_; Closure reply_; Closure task_; }; diff --git a/chromium/base/threading/post_task_and_reply_impl.h b/chromium/base/threading/post_task_and_reply_impl.h index 076a46d69e8..a5b9580e6dc 100644 --- a/chromium/base/threading/post_task_and_reply_impl.h +++ b/chromium/base/threading/post_task_and_reply_impl.h @@ -3,7 +3,7 @@ // found in the LICENSE file. // This file contains the implementation shared by -// MessageLoopProxy::PostTaskAndReply and WorkerPool::PostTaskAndReply. +// TaskRunner::PostTaskAndReply and WorkerPool::PostTaskAndReply. #ifndef BASE_THREADING_POST_TASK_AND_REPLY_IMPL_H_ #define BASE_THREADING_POST_TASK_AND_REPLY_IMPL_H_ @@ -21,11 +21,11 @@ namespace internal { // MessageLoop. // // If you're looking for a concrete implementation of -// PostTaskAndReply, you probably want base::MessageLoopProxy, or you +// PostTaskAndReply, you probably want base::SingleThreadTaskRunner, or you // may want base::WorkerPool. class PostTaskAndReplyImpl { public: - // Implementation for MessageLoopProxy::PostTaskAndReply and + // Implementation for TaskRunner::PostTaskAndReply and // WorkerPool::PostTaskAndReply. bool PostTaskAndReply(const tracked_objects::Location& from_here, const Closure& task, diff --git a/chromium/base/threading/sequenced_worker_pool.cc b/chromium/base/threading/sequenced_worker_pool.cc index 4c37320b5bc..7bbca92a2fb 100644 --- a/chromium/base/threading/sequenced_worker_pool.cc +++ b/chromium/base/threading/sequenced_worker_pool.cc @@ -14,20 +14,20 @@ #include "base/callback.h" #include "base/compiler_specific.h" #include "base/critical_closure.h" -#include "base/debug/trace_event.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "base/memory/linked_ptr.h" -#include "base/message_loop/message_loop_proxy.h" #include "base/stl_util.h" #include "base/strings/stringprintf.h" #include "base/synchronization/condition_variable.h" #include "base/synchronization/lock.h" +#include "base/thread_task_runner_handle.h" #include "base/threading/platform_thread.h" #include "base/threading/simple_thread.h" #include "base/threading/thread_local.h" #include "base/threading/thread_restrictions.h" #include "base/time/time.h" +#include "base/trace_event/trace_event.h" #include "base/tracked_objects.h" #if defined(OS_MACOSX) @@ -239,24 +239,41 @@ class SequencedWorkerPool::Worker : public SimpleThread { // SimpleThread implementation. This actually runs the background thread. void Run() override; + // Indicates that a task is about to be run. The parameters provide + // additional metainformation about the task being run. void set_running_task_info(SequenceToken token, WorkerShutdown shutdown_behavior) { - running_sequence_ = token; - running_shutdown_behavior_ = shutdown_behavior; + is_processing_task_ = true; + task_sequence_token_ = token; + task_shutdown_behavior_ = shutdown_behavior; } - SequenceToken running_sequence() const { - return running_sequence_; + // Indicates that the task has finished running. + void reset_running_task_info() { is_processing_task_ = false; } + + // Whether the worker is processing a task. + bool is_processing_task() { return is_processing_task_; } + + SequenceToken task_sequence_token() const { + DCHECK(is_processing_task_); + return task_sequence_token_; } - WorkerShutdown running_shutdown_behavior() const { - return running_shutdown_behavior_; + WorkerShutdown task_shutdown_behavior() const { + DCHECK(is_processing_task_); + return task_shutdown_behavior_; } private: scoped_refptr<SequencedWorkerPool> worker_pool_; - SequenceToken running_sequence_; - WorkerShutdown running_shutdown_behavior_; + // The sequence token of the task being processed. Only valid when + // is_processing_task_ is true. + SequenceToken task_sequence_token_; + // The shutdown behavior of the task being processed. Only valid when + // is_processing_task_ is true. + WorkerShutdown task_shutdown_behavior_; + // Whether the Worker is processing a task. + bool is_processing_task_; DISALLOW_COPY_AND_ASSIGN(Worker); }; @@ -326,11 +343,6 @@ class SequencedWorkerPool::Inner { // Called from within the lock, this returns the next sequence task number. int64 LockedGetNextSequenceTaskNumber(); - // Called from within the lock, returns the shutdown behavior of the task - // running on the currently executing worker thread. If invoked from a thread - // that is not one of the workers, returns CONTINUE_ON_SHUTDOWN. - WorkerShutdown LockedCurrentThreadShutdownBehavior() const; - // Gets new task. There are 3 cases depending on the return value: // // 1) If the return value is |GET_WORK_FOUND|, |task| is filled in and should @@ -483,7 +495,8 @@ SequencedWorkerPool::Worker::Worker( const std::string& prefix) : SimpleThread(prefix + StringPrintf("Worker%d", thread_number)), worker_pool_(worker_pool), - running_shutdown_behavior_(CONTINUE_ON_SHUTDOWN) { + task_shutdown_behavior_(BLOCK_SHUTDOWN), + is_processing_task_(false) { Start(); } @@ -497,7 +510,7 @@ void SequencedWorkerPool::Worker::Run() { // Store a pointer to the running sequence in thread local storage for // static function access. - g_lazy_tls_ptr.Get().Set(&running_sequence_); + g_lazy_tls_ptr.Get().Set(&task_sequence_token_); // Just jump back to the Inner object to run the thread, since it has all the // tracking information and queues. It might be more natural to implement @@ -583,10 +596,19 @@ bool SequencedWorkerPool::Inner::PostTask( { AutoLock lock(lock_); if (shutdown_called_) { - if (shutdown_behavior != BLOCK_SHUTDOWN || - LockedCurrentThreadShutdownBehavior() == CONTINUE_ON_SHUTDOWN) { + // Don't allow a new task to be posted if it doesn't block shutdown. + if (shutdown_behavior != BLOCK_SHUTDOWN) + return false; + + // If the current thread is running a task, and that task doesn't block + // shutdown, then it shouldn't be allowed to post any more tasks. + ThreadMap::const_iterator found = + threads_.find(PlatformThread::CurrentId()); + if (found != threads_.end() && found->second->is_processing_task() && + found->second->task_shutdown_behavior() != BLOCK_SHUTDOWN) { return false; } + if (max_blocking_tasks_after_shutdown_ <= 0) { DLOG(WARNING) << "BLOCK_SHUTDOWN task disallowed"; return false; @@ -635,7 +657,8 @@ bool SequencedWorkerPool::Inner::IsRunningSequenceOnCurrentThread( ThreadMap::const_iterator found = threads_.find(PlatformThread::CurrentId()); if (found == threads_.end()) return false; - return sequence_token.Equals(found->second->running_sequence()); + return found->second->is_processing_task() && + sequence_token.Equals(found->second->task_sequence_token()); } // See https://code.google.com/p/chromium/issues/detail?id=168415 @@ -754,7 +777,6 @@ void SequencedWorkerPool::Inner::ThreadLoop(Worker* this_worker) { this_worker->set_running_task_info( SequenceToken(task.sequence_token_id), task.shutdown_behavior); - tracked_objects::ThreadData::PrepareForStartOfRun(task.birth_tally); tracked_objects::TaskStopwatch stopwatch; stopwatch.Start(); task.task.Run(); @@ -765,13 +787,12 @@ void SequencedWorkerPool::Inner::ThreadLoop(Worker* this_worker) { // Make sure our task is erased outside the lock for the // same reason we do this with delete_these_oustide_lock. - // Also, do it before calling set_running_task_info() so + // Also, do it before calling reset_running_task_info() so // that sequence-checking from within the task's destructor // still works. task.task = Closure(); - this_worker->set_running_task_info( - SequenceToken(), CONTINUE_ON_SHUTDOWN); + this_worker->reset_running_task_info(); } DidRunWorkerTask(task); // Must be done inside the lock. } else if (cleanup_state_ == CLEANUP_RUNNING) { @@ -798,9 +819,25 @@ void SequencedWorkerPool::Inner::ThreadLoop(Worker* this_worker) { // to run them. Also, there may be some tasks stuck behind running // ones with the same sequence token, but additional threads won't // help this case. - if (shutdown_called_ && - blocking_shutdown_pending_task_count_ == 0) + if (shutdown_called_ && blocking_shutdown_pending_task_count_ == 0) { + AutoUnlock unlock(lock_); + delete_these_outside_lock.clear(); break; + } + + // No work was found, but there are tasks that need deletion. The + // deletion must happen outside of the lock. + if (delete_these_outside_lock.size()) { + AutoUnlock unlock(lock_); + delete_these_outside_lock.clear(); + + // Since the lock has been released, |status| may no longer be + // accurate. It might read GET_WORK_WAIT even if there are tasks + // ready to perform work. Jump to the top of the loop to recalculate + // |status|. + continue; + } + waiting_thread_count_++; switch (status) { @@ -888,15 +925,6 @@ int64 SequencedWorkerPool::Inner::LockedGetNextSequenceTaskNumber() { return next_sequence_task_number_++; } -SequencedWorkerPool::WorkerShutdown -SequencedWorkerPool::Inner::LockedCurrentThreadShutdownBehavior() const { - lock_.AssertAcquired(); - ThreadMap::const_iterator found = threads_.find(PlatformThread::CurrentId()); - if (found == threads_.end()) - return CONTINUE_ON_SHUTDOWN; - return found->second->running_shutdown_behavior(); -} - SequencedWorkerPool::Inner::GetWorkStatus SequencedWorkerPool::Inner::GetWork( SequencedTask* task, TimeDelta* wait_time, @@ -1089,7 +1117,7 @@ int SequencedWorkerPool::Inner::PrepareToStartAdditionalThreadIfHelpful() { void SequencedWorkerPool::Inner::FinishStartingAdditionalThread( int thread_number) { // Called outside of the lock. - DCHECK(thread_number > 0); + DCHECK_GT(thread_number, 0); // The worker is assigned to the list when the thread actually starts, which // will manage the memory of the pointer. @@ -1129,29 +1157,26 @@ SequencedWorkerPool::GetSequenceTokenForCurrentThread() { return *token; } -SequencedWorkerPool::SequencedWorkerPool( - size_t max_threads, - const std::string& thread_name_prefix) - : constructor_message_loop_(MessageLoopProxy::current()), +SequencedWorkerPool::SequencedWorkerPool(size_t max_threads, + const std::string& thread_name_prefix) + : constructor_task_runner_(ThreadTaskRunnerHandle::Get()), inner_(new Inner(this, max_threads, thread_name_prefix, NULL)) { } -SequencedWorkerPool::SequencedWorkerPool( - size_t max_threads, - const std::string& thread_name_prefix, - TestingObserver* observer) - : constructor_message_loop_(MessageLoopProxy::current()), +SequencedWorkerPool::SequencedWorkerPool(size_t max_threads, + const std::string& thread_name_prefix, + TestingObserver* observer) + : constructor_task_runner_(ThreadTaskRunnerHandle::Get()), inner_(new Inner(this, max_threads, thread_name_prefix, observer)) { } SequencedWorkerPool::~SequencedWorkerPool() {} void SequencedWorkerPool::OnDestruct() const { - DCHECK(constructor_message_loop_.get()); // Avoid deleting ourselves on a worker thread (which would // deadlock). if (RunsTasksOnCurrentThread()) { - constructor_message_loop_->DeleteSoon(FROM_HERE, this); + constructor_task_runner_->DeleteSoon(FROM_HERE, this); } else { delete this; } @@ -1271,7 +1296,7 @@ void SequencedWorkerPool::SignalHasWorkForTesting() { } void SequencedWorkerPool::Shutdown(int max_new_blocking_tasks_after_shutdown) { - DCHECK(constructor_message_loop_->BelongsToCurrentThread()); + DCHECK(constructor_task_runner_->BelongsToCurrentThread()); inner_->Shutdown(max_new_blocking_tasks_after_shutdown); } diff --git a/chromium/base/threading/sequenced_worker_pool.h b/chromium/base/threading/sequenced_worker_pool.h index 63c6204ebbb..ee282bc2312 100644 --- a/chromium/base/threading/sequenced_worker_pool.h +++ b/chromium/base/threading/sequenced_worker_pool.h @@ -13,6 +13,7 @@ #include "base/callback_forward.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "base/single_thread_task_runner.h" #include "base/task_runner.h" namespace tracked_objects { @@ -21,7 +22,7 @@ class Location; namespace base { -class MessageLoopProxy; +class SingleThreadTaskRunner; template <class T> class DeleteHelper; @@ -320,12 +321,10 @@ class BASE_EXPORT SequencedWorkerPool : public TaskRunner { // Must be called from the same thread this object was constructed on. void Shutdown() { Shutdown(0); } - // A variant that allows an arbitrary number of new blocking tasks to - // be posted during shutdown from within tasks that execute during shutdown. - // Only tasks designated as BLOCKING_SHUTDOWN will be allowed, and only if - // posted by tasks that are not designated as CONTINUE_ON_SHUTDOWN. Once + // A variant that allows an arbitrary number of new blocking tasks to be + // posted during shutdown. The tasks cannot be posted within the execution + // context of tasks whose shutdown behavior is not BLOCKING_SHUTDOWN. Once // the limit is reached, subsequent calls to post task fail in all cases. - // // Must be called from the same thread this object was constructed on. void Shutdown(int max_new_blocking_tasks_after_shutdown); @@ -347,7 +346,7 @@ class BASE_EXPORT SequencedWorkerPool : public TaskRunner { class Inner; class Worker; - const scoped_refptr<MessageLoopProxy> constructor_message_loop_; + const scoped_refptr<SingleThreadTaskRunner> constructor_task_runner_; // Avoid pulling in too many headers by putting (almost) everything // into |inner_|. diff --git a/chromium/base/threading/sequenced_worker_pool_unittest.cc b/chromium/base/threading/sequenced_worker_pool_unittest.cc index b1fe2764442..05989a5487a 100644 --- a/chromium/base/threading/sequenced_worker_pool_unittest.cc +++ b/chromium/base/threading/sequenced_worker_pool_unittest.cc @@ -11,7 +11,6 @@ #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" -#include "base/message_loop/message_loop_proxy.h" #include "base/synchronization/condition_variable.h" #include "base/synchronization/lock.h" #include "base/test/sequenced_task_runner_test_template.h" @@ -54,7 +53,7 @@ class ThreadBlocker { void Unblock(size_t count) { { base::AutoLock lock(lock_); - DCHECK(unblock_counter_ == 0); + DCHECK_EQ(unblock_counter_, 0u); unblock_counter_ = count; } cond_var_.Signal(); @@ -67,6 +66,23 @@ class ThreadBlocker { size_t unblock_counter_; }; +class DestructionDeadlockChecker + : public base::RefCountedThreadSafe<DestructionDeadlockChecker> { + public: + DestructionDeadlockChecker(const scoped_refptr<SequencedWorkerPool>& pool) + : pool_(pool) {} + + protected: + virtual ~DestructionDeadlockChecker() { + // This method should not deadlock. + pool_->RunsTasksOnCurrentThread(); + } + + private: + scoped_refptr<SequencedWorkerPool> pool_; + friend class base::RefCountedThreadSafe<DestructionDeadlockChecker>; +}; + class TestTracker : public base::RefCountedThreadSafe<TestTracker> { public: TestTracker() @@ -117,6 +133,41 @@ class TestTracker : public base::RefCountedThreadSafe<TestTracker> { SignalWorkerDone(id); } + // This task posts itself back onto the SequencedWorkerPool before it + // finishes running. Each instance of the task maintains a strong reference + // to a DestructionDeadlockChecker. The DestructionDeadlockChecker is only + // destroyed when the task is destroyed without being run, which only happens + // during destruction of the SequencedWorkerPool. + void PostRepostingTask( + const scoped_refptr<SequencedWorkerPool>& pool, + const scoped_refptr<DestructionDeadlockChecker>& checker) { + Closure reposting_task = + base::Bind(&TestTracker::PostRepostingTask, this, pool, checker); + pool->PostWorkerTaskWithShutdownBehavior( + FROM_HERE, reposting_task, SequencedWorkerPool::SKIP_ON_SHUTDOWN); + } + + // This task reposts itself back onto the SequencedWorkerPool before it + // finishes running. + void PostRepostingBlockingTask( + const scoped_refptr<SequencedWorkerPool>& pool, + const SequencedWorkerPool::SequenceToken& token) { + Closure reposting_task = + base::Bind(&TestTracker::PostRepostingBlockingTask, this, pool, token); + pool->PostSequencedWorkerTaskWithShutdownBehavior(token, + FROM_HERE, reposting_task, SequencedWorkerPool::BLOCK_SHUTDOWN); + } + + void PostBlockingTaskThenUnblockThreads( + const scoped_refptr<SequencedWorkerPool>& pool, + ThreadBlocker* blocker, + size_t threads_to_wake) { + Closure arbitrary_task = base::Bind(&TestTracker::FastTask, this, 0); + pool->PostWorkerTaskWithShutdownBehavior( + FROM_HERE, arbitrary_task, SequencedWorkerPool::BLOCK_SHUTDOWN); + blocker->Unblock(threads_to_wake); + } + // Waits until the given number of tasks have started executing. void WaitUntilTasksBlocked(size_t count) { { @@ -183,13 +234,7 @@ class SequencedWorkerPoolTest : public testing::Test { ResetPool(); } - virtual ~SequencedWorkerPoolTest() {} - - virtual void SetUp() override {} - - virtual void TearDown() override { - pool()->Shutdown(); - } + void TearDown() override { pool()->Shutdown(); } const scoped_refptr<SequencedWorkerPool>& pool() { return pool_owner_->pool(); @@ -251,7 +296,7 @@ class SequencedWorkerPoolTest : public testing::Test { // Checks that the given number of entries are in the tasks to complete of // the given tracker, and then signals the given event the given number of -// times. This is used to wakt up blocked background threads before blocking +// times. This is used to wake up blocked background threads before blocking // on shutdown. void EnsureTasksToCompleteCountAndUnblock(scoped_refptr<TestTracker> tracker, size_t expected_tasks_to_complete, @@ -547,6 +592,34 @@ TEST_F(SequencedWorkerPoolTest, AllowsAfterShutdown) { tracker()->ClearCompleteSequence(); } +// Tests that blocking tasks can still be posted during shutdown, as long as +// the task is not being posted within the context of a running task. +TEST_F(SequencedWorkerPoolTest, + AllowsBlockingTasksDuringShutdownOutsideOfRunningTask) { + EnsureAllWorkersCreated(); + ThreadBlocker blocker; + + // Start tasks to take all the threads and block them. + const int kNumBlockTasks = static_cast<int>(kNumWorkerThreads); + for (int i = 0; i < kNumBlockTasks; ++i) { + EXPECT_TRUE(pool()->PostWorkerTask( + FROM_HERE, + base::Bind(&TestTracker::BlockTask, tracker(), i, &blocker))); + } + tracker()->WaitUntilTasksBlocked(kNumWorkerThreads); + + // Setup to open the floodgates from within Shutdown(). + SetWillWaitForShutdownCallback( + base::Bind(&TestTracker::PostBlockingTaskThenUnblockThreads, + scoped_refptr<TestTracker>(tracker()), pool(), &blocker, + kNumWorkerThreads)); + pool()->Shutdown(kNumWorkerThreads + 1); + + // Ensure that the correct number of tasks actually got run. + tracker()->WaitUntilTasksComplete(static_cast<size_t>(kNumWorkerThreads + 1)); + tracker()->ClearCompleteSequence(); +} + // Tests that unrun tasks are discarded properly according to their shutdown // mode. TEST_F(SequencedWorkerPoolTest, DiscardOnShutdown) { @@ -755,6 +828,41 @@ TEST_F(SequencedWorkerPoolTest, IsRunningOnCurrentThread) { unused_pool->Shutdown(); } +// Checks that tasks are destroyed in the right context during shutdown. If a +// task is destroyed while SequencedWorkerPool's global lock is held, +// SequencedWorkerPool might deadlock. +TEST_F(SequencedWorkerPoolTest, AvoidsDeadlockOnShutdown) { + for (int i = 0; i < 4; ++i) { + scoped_refptr<DestructionDeadlockChecker> checker( + new DestructionDeadlockChecker(pool())); + tracker()->PostRepostingTask(pool(), checker); + } + + // Shutting down the pool should destroy the DestructionDeadlockCheckers, + // which in turn should not deadlock in their destructors. + pool()->Shutdown(); +} + +// Similar to the test AvoidsDeadlockOnShutdown, but there are now also +// sequenced, blocking tasks in the queue during shutdown. +TEST_F(SequencedWorkerPoolTest, + AvoidsDeadlockOnShutdownWithSequencedBlockingTasks) { + const std::string sequence_token_name("name"); + for (int i = 0; i < 4; ++i) { + scoped_refptr<DestructionDeadlockChecker> checker( + new DestructionDeadlockChecker(pool())); + tracker()->PostRepostingTask(pool(), checker); + + SequencedWorkerPool::SequenceToken token1 = + pool()->GetNamedSequenceToken(sequence_token_name); + tracker()->PostRepostingBlockingTask(pool(), token1); + } + + // Shutting down the pool should destroy the DestructionDeadlockCheckers, + // which in turn should not deadlock in their destructors. + pool()->Shutdown(); +} + // Verify that FlushForTesting works as intended. TEST_F(SequencedWorkerPoolTest, FlushForTesting) { // Should be fine to call on a new instance. diff --git a/chromium/base/threading/simple_thread.cc b/chromium/base/threading/simple_thread.cc index 028d4f4ab23..1bc31135941 100644 --- a/chromium/base/threading/simple_thread.cc +++ b/chromium/base/threading/simple_thread.cc @@ -52,7 +52,7 @@ void SimpleThread::ThreadMain() { // Construct our full name of the form "name_prefix_/TID". name_.push_back('/'); name_.append(IntToString(tid_)); - PlatformThread::SetName(name_.c_str()); + PlatformThread::SetName(name_); // We've initialized our new thread, signal that we're done to Start(). event_.Signal(); diff --git a/chromium/base/threading/thread.cc b/chromium/base/threading/thread.cc index ad1360be3e4..0e4aab2c354 100644 --- a/chromium/base/threading/thread.cc +++ b/chromium/base/threading/thread.cc @@ -6,11 +6,13 @@ #include "base/bind.h" #include "base/lazy_instance.h" +#include "base/location.h" +#include "base/profiler/scoped_tracker.h" +#include "base/synchronization/waitable_event.h" #include "base/third_party/dynamic_annotations/dynamic_annotations.h" #include "base/threading/thread_id_name_manager.h" #include "base/threading/thread_local.h" #include "base/threading/thread_restrictions.h" -#include "base/synchronization/waitable_event.h" #if defined(OS_WIN) #include "base/win/scoped_com_initializer.h" @@ -111,6 +113,11 @@ bool Thread::StartWithOptions(const Options& options) { return false; } + // TODO(kinuko): Remove once crbug.com/465458 is solved. + tracked_objects::ScopedTracker tracking_profile_wait( + FROM_HERE_WITH_EXPLICIT_FUNCTION( + "465458 base::Thread::StartWithOptions (Wait)")); + // Wait for the thread to start and initialize message_loop_ base::ThreadRestrictions::ScopedAllowWait allow_wait; startup_data.event.Wait(); @@ -156,7 +163,7 @@ void Thread::StopSoon() { return; stopping_ = true; - message_loop_->PostTask(FROM_HERE, base::Bind(&ThreadQuitHelper)); + task_runner()->PostTask(FROM_HERE, base::Bind(&ThreadQuitHelper)); } bool Thread::IsRunning() const { @@ -201,7 +208,7 @@ void Thread::ThreadMain() { // Complete the initialization of our Thread object. thread_id_ = PlatformThread::CurrentId(); - PlatformThread::SetName(name_.c_str()); + PlatformThread::SetName(name_); ANNOTATE_THREAD_NAME(name_.c_str()); // Tell the name to race detector. message_loop->set_thread_name(name_); message_loop->SetTimerSlack(startup_data_->options.timer_slack); diff --git a/chromium/base/threading/thread.h b/chromium/base/threading/thread.h index 5010f0ec428..49156063929 100644 --- a/chromium/base/threading/thread.h +++ b/chromium/base/threading/thread.h @@ -11,8 +11,8 @@ #include "base/callback.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" -#include "base/message_loop/message_loop_proxy.h" #include "base/message_loop/timer_slack.h" +#include "base/single_thread_task_runner.h" #include "base/threading/platform_thread.h" namespace base { @@ -156,7 +156,7 @@ class BASE_EXPORT Thread : PlatformThread::Delegate { // 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_proxy(); + return message_loop_->task_runner(); } // Returns the name of this thread (for display in debugger too). diff --git a/chromium/base/threading/thread_checker.h b/chromium/base/threading/thread_checker.h index 4d71f798d4d..449247af9dd 100644 --- a/chromium/base/threading/thread_checker.h +++ b/chromium/base/threading/thread_checker.h @@ -30,7 +30,7 @@ namespace base { // right version for your build configuration. class ThreadCheckerDoNothing { public: - bool CalledOnValidThread() const { + bool CalledOnValidThread() const WARN_UNUSED_RESULT { return true; } diff --git a/chromium/base/threading/thread_checker_impl.h b/chromium/base/threading/thread_checker_impl.h index 879ac3ab354..c92e143db0c 100644 --- a/chromium/base/threading/thread_checker_impl.h +++ b/chromium/base/threading/thread_checker_impl.h @@ -6,6 +6,7 @@ #define BASE_THREADING_THREAD_CHECKER_IMPL_H_ #include "base/base_export.h" +#include "base/compiler_specific.h" #include "base/synchronization/lock.h" #include "base/threading/platform_thread.h" @@ -22,7 +23,7 @@ class BASE_EXPORT ThreadCheckerImpl { ThreadCheckerImpl(); ~ThreadCheckerImpl(); - bool CalledOnValidThread() const; + bool CalledOnValidThread() const WARN_UNUSED_RESULT; // Changes the thread that is checked for in CalledOnValidThread. This may // be useful when an object may be created on one thread and then used diff --git a/chromium/base/threading/thread_collision_warner_unittest.cc b/chromium/base/threading/thread_collision_warner_unittest.cc index 26faff404f6..d7ce79ec378 100644 --- a/chromium/base/threading/thread_collision_warner_unittest.cc +++ b/chromium/base/threading/thread_collision_warner_unittest.cc @@ -146,24 +146,23 @@ TEST(ThreadCollisionTest, MTBookCriticalSectionTest) { class QueueUser : public base::DelegateSimpleThread::Delegate { public: - explicit QueueUser(NonThreadSafeQueue& queue) - : queue_(queue) {} + explicit QueueUser(NonThreadSafeQueue* queue) : queue_(queue) {} void Run() override { - queue_.push(0); - queue_.pop(); + queue_->push(0); + queue_->pop(); } private: - NonThreadSafeQueue& queue_; + NonThreadSafeQueue* queue_; }; AssertReporter* local_reporter = new AssertReporter(); NonThreadSafeQueue queue(local_reporter); - QueueUser queue_user_a(queue); - QueueUser queue_user_b(queue); + QueueUser queue_user_a(&queue); + QueueUser queue_user_b(&queue); base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a"); base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b"); @@ -204,24 +203,23 @@ TEST(ThreadCollisionTest, MTScopedBookCriticalSectionTest) { class QueueUser : public base::DelegateSimpleThread::Delegate { public: - explicit QueueUser(NonThreadSafeQueue& queue) - : queue_(queue) {} + explicit QueueUser(NonThreadSafeQueue* queue) : queue_(queue) {} void Run() override { - queue_.push(0); - queue_.pop(); + queue_->push(0); + queue_->pop(); } private: - NonThreadSafeQueue& queue_; + NonThreadSafeQueue* queue_; }; AssertReporter* local_reporter = new AssertReporter(); NonThreadSafeQueue queue(local_reporter); - QueueUser queue_user_a(queue); - QueueUser queue_user_b(queue); + QueueUser queue_user_a(&queue); + QueueUser queue_user_b(&queue); base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a"); base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b"); @@ -264,23 +262,22 @@ TEST(ThreadCollisionTest, MTSynchedScopedBookCriticalSectionTest) { // a lock. class QueueUser : public base::DelegateSimpleThread::Delegate { public: - QueueUser(NonThreadSafeQueue& queue, base::Lock& lock) - : queue_(queue), - lock_(lock) {} + QueueUser(NonThreadSafeQueue* queue, base::Lock* lock) + : queue_(queue), lock_(lock) {} void Run() override { { - base::AutoLock auto_lock(lock_); - queue_.push(0); + base::AutoLock auto_lock(*lock_); + queue_->push(0); } { - base::AutoLock auto_lock(lock_); - queue_.pop(); + base::AutoLock auto_lock(*lock_); + queue_->pop(); } } private: - NonThreadSafeQueue& queue_; - base::Lock& lock_; + NonThreadSafeQueue* queue_; + base::Lock* lock_; }; AssertReporter* local_reporter = new AssertReporter(); @@ -289,8 +286,8 @@ TEST(ThreadCollisionTest, MTSynchedScopedBookCriticalSectionTest) { base::Lock lock; - QueueUser queue_user_a(queue, lock); - QueueUser queue_user_b(queue, lock); + QueueUser queue_user_a(&queue, &lock); + QueueUser queue_user_b(&queue, &lock); base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a"); base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b"); @@ -338,27 +335,26 @@ TEST(ThreadCollisionTest, MTSynchedScopedRecursiveBookCriticalSectionTest) { // a lock. class QueueUser : public base::DelegateSimpleThread::Delegate { public: - QueueUser(NonThreadSafeQueue& queue, base::Lock& lock) - : queue_(queue), - lock_(lock) {} + QueueUser(NonThreadSafeQueue* queue, base::Lock* lock) + : queue_(queue), lock_(lock) {} void Run() override { { - base::AutoLock auto_lock(lock_); - queue_.push(0); + base::AutoLock auto_lock(*lock_); + queue_->push(0); } { - base::AutoLock auto_lock(lock_); - queue_.bar(); + base::AutoLock auto_lock(*lock_); + queue_->bar(); } { - base::AutoLock auto_lock(lock_); - queue_.pop(); + base::AutoLock auto_lock(*lock_); + queue_->pop(); } } private: - NonThreadSafeQueue& queue_; - base::Lock& lock_; + NonThreadSafeQueue* queue_; + base::Lock* lock_; }; AssertReporter* local_reporter = new AssertReporter(); @@ -367,8 +363,8 @@ TEST(ThreadCollisionTest, MTSynchedScopedRecursiveBookCriticalSectionTest) { base::Lock lock; - QueueUser queue_user_a(queue, lock); - QueueUser queue_user_b(queue, lock); + QueueUser queue_user_a(&queue, &lock); + QueueUser queue_user_b(&queue, &lock); base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a"); base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b"); diff --git a/chromium/base/threading/thread_id_name_manager.cc b/chromium/base/threading/thread_id_name_manager.cc index 7c85c1b5e99..56cfa273a87 100644 --- a/chromium/base/threading/thread_id_name_manager.cc +++ b/chromium/base/threading/thread_id_name_manager.cc @@ -48,17 +48,16 @@ void ThreadIdNameManager::RegisterThread(PlatformThreadHandle::Handle handle, name_to_interned_name_[kDefaultName]; } -void ThreadIdNameManager::SetName(PlatformThreadId id, const char* name) { - std::string str_name(name); - +void ThreadIdNameManager::SetName(PlatformThreadId id, + const std::string& name) { AutoLock locked(lock_); - NameToInternedNameMap::iterator iter = name_to_interned_name_.find(str_name); + NameToInternedNameMap::iterator iter = name_to_interned_name_.find(name); std::string* leaked_str = NULL; if (iter != name_to_interned_name_.end()) { leaked_str = iter->second; } else { - leaked_str = new std::string(str_name); - name_to_interned_name_[str_name] = leaked_str; + leaked_str = new std::string(name); + name_to_interned_name_[name] = leaked_str; } ThreadIdToHandleMap::iterator id_to_handle_iter = diff --git a/chromium/base/threading/thread_id_name_manager.h b/chromium/base/threading/thread_id_name_manager.h index 0ea59df6572..927d25fe1e8 100644 --- a/chromium/base/threading/thread_id_name_manager.h +++ b/chromium/base/threading/thread_id_name_manager.h @@ -27,7 +27,7 @@ class BASE_EXPORT ThreadIdNameManager { void RegisterThread(PlatformThreadHandle::Handle handle, PlatformThreadId id); // Set the name for the given id. - void SetName(PlatformThreadId id, const char* name); + void SetName(PlatformThreadId id, const std::string& name); // Get the name for the given id. const char* GetName(PlatformThreadId id); diff --git a/chromium/base/threading/thread_local_android.cc b/chromium/base/threading/thread_local_android.cc index c890237f67d..813dd78b5ec 100644 --- a/chromium/base/threading/thread_local_android.cc +++ b/chromium/base/threading/thread_local_android.cc @@ -4,15 +4,12 @@ #include "base/threading/thread_local.h" -#include "base/logging.h" - namespace base { namespace internal { // static void ThreadLocalPlatform::AllocateSlot(SlotType* slot) { - bool succeed = slot->Initialize(NULL); - CHECK(succeed); + slot->Initialize(nullptr); } // static diff --git a/chromium/base/threading/thread_local_storage.cc b/chromium/base/threading/thread_local_storage.cc index 4f4169fe718..0bb396cfd79 100644 --- a/chromium/base/threading/thread_local_storage.cc +++ b/chromium/base/threading/thread_local_storage.cc @@ -140,8 +140,8 @@ void OnThreadExitInternal(void* value) { base::subtle::Atomic32 last_used_tls_key = base::subtle::NoBarrier_Load(&g_last_used_tls_key); for (int slot = last_used_tls_key; slot > 0; --slot) { - void* value = stack_allocated_tls_data[slot]; - if (value == NULL) + void* tls_value = stack_allocated_tls_data[slot]; + if (tls_value == NULL) continue; base::ThreadLocalStorage::TLSDestructorFunc destructor = @@ -149,7 +149,7 @@ void OnThreadExitInternal(void* value) { if (destructor == NULL) continue; stack_allocated_tls_data[slot] = NULL; // pre-clear the slot. - destructor(value); + destructor(tls_value); // Any destructor might have called a different service, which then set // a different slot to a non-NULL value. Hence we need to check // the whole vector again. This is a pthread standard. @@ -197,7 +197,7 @@ ThreadLocalStorage::Slot::Slot(TLSDestructorFunc destructor) { Initialize(destructor); } -bool ThreadLocalStorage::StaticSlot::Initialize(TLSDestructorFunc destructor) { +void ThreadLocalStorage::StaticSlot::Initialize(TLSDestructorFunc destructor) { PlatformThreadLocalStorage::TLSKey key = base::subtle::NoBarrier_Load(&g_native_tls_key); if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES || @@ -212,7 +212,6 @@ bool ThreadLocalStorage::StaticSlot::Initialize(TLSDestructorFunc destructor) { // Setup our destructor. g_tls_destructors[slot_] = destructor; initialized_ = true; - return true; } void ThreadLocalStorage::StaticSlot::Free() { diff --git a/chromium/base/threading/thread_local_storage.h b/chromium/base/threading/thread_local_storage.h index 53ebe55e51c..50f88685a57 100644 --- a/chromium/base/threading/thread_local_storage.h +++ b/chromium/base/threading/thread_local_storage.h @@ -98,8 +98,7 @@ class BASE_EXPORT ThreadLocalStorage { // Set up the TLS slot. Called by the constructor. // 'destructor' is a pointer to a function to perform per-thread cleanup of // this object. If set to NULL, no cleanup is done for this TLS slot. - // Returns false on error. - bool Initialize(TLSDestructorFunc destructor); + void Initialize(TLSDestructorFunc destructor); // Free a previously allocated TLS 'slot'. // If a destructor was set for this slot, removes @@ -136,6 +135,7 @@ class BASE_EXPORT ThreadLocalStorage { DISALLOW_COPY_AND_ASSIGN(Slot); }; + private: DISALLOW_COPY_AND_ASSIGN(ThreadLocalStorage); }; diff --git a/chromium/base/threading/thread_local_storage_unittest.cc b/chromium/base/threading/thread_local_storage_unittest.cc index b53f5777158..bcc1d1b9db5 100644 --- a/chromium/base/threading/thread_local_storage_unittest.cc +++ b/chromium/base/threading/thread_local_storage_unittest.cc @@ -79,17 +79,16 @@ TEST(ThreadLocalStorageTest, Basics) { EXPECT_EQ(value, 123); } -#if defined(THREAD_SANITIZER) || \ - (defined(OS_WIN) && defined(ARCH_CPU_X86_64) && \ - defined(INCREMENTAL_LINKING)) +#if defined(THREAD_SANITIZER) || \ + (defined(OS_WIN) && defined(ARCH_CPU_X86_64) && !defined(NDEBUG)) // Do not run the test under ThreadSanitizer. Because this test iterates its // own TSD destructor for the maximum possible number of times, TSan can't jump // in after the last destructor invocation, therefore the destructor remains // unsynchronized with the following users of the same TSD slot. This results // in race reports between the destructor and functions in other tests. // -// It is disabled on Win x64 with incremental linking pending resolution of -// http://crbug.com/251251. +// It is disabled on Win x64 with incremental linking (i.e. "Debug") pending +// resolution of http://crbug.com/251251. #define MAYBE_TLSDestructors DISABLED_TLSDestructors #else #define MAYBE_TLSDestructors TLSDestructors diff --git a/chromium/base/threading/thread_local_unittest.cc b/chromium/base/threading/thread_local_unittest.cc index 8dc7cd2bdde..e94c1db1c8d 100644 --- a/chromium/base/threading/thread_local_unittest.cc +++ b/chromium/base/threading/thread_local_unittest.cc @@ -14,7 +14,7 @@ namespace { class ThreadLocalTesterBase : public base::DelegateSimpleThreadPool::Delegate { public: - typedef base::ThreadLocalPointer<ThreadLocalTesterBase> TLPType; + typedef base::ThreadLocalPointer<char> TLPType; ThreadLocalTesterBase(TLPType* tlp, base::WaitableEvent* done) : tlp_(tlp), @@ -35,7 +35,7 @@ class SetThreadLocal : public ThreadLocalTesterBase { } ~SetThreadLocal() override {} - void set_value(ThreadLocalTesterBase* val) { val_ = val; } + void set_value(char* val) { val_ = val; } void Run() override { DCHECK(!done_->IsSignaled()); @@ -44,7 +44,7 @@ class SetThreadLocal : public ThreadLocalTesterBase { } private: - ThreadLocalTesterBase* val_; + char* val_; }; class GetThreadLocal : public ThreadLocalTesterBase { @@ -55,7 +55,7 @@ class GetThreadLocal : public ThreadLocalTesterBase { } ~GetThreadLocal() override {} - void set_ptr(ThreadLocalTesterBase** ptr) { ptr_ = ptr; } + void set_ptr(char** ptr) { ptr_ = ptr; } void Run() override { DCHECK(!done_->IsSignaled()); @@ -64,7 +64,7 @@ class GetThreadLocal : public ThreadLocalTesterBase { } private: - ThreadLocalTesterBase** ptr_; + char** ptr_; }; } // namespace @@ -77,12 +77,11 @@ TEST(ThreadLocalTest, Pointer) { tp1.Start(); tp2.Start(); - base::ThreadLocalPointer<ThreadLocalTesterBase> tlp; + base::ThreadLocalPointer<char> tlp; - static ThreadLocalTesterBase* const kBogusPointer = - reinterpret_cast<ThreadLocalTesterBase*>(0x1234); + static char* const kBogusPointer = reinterpret_cast<char*>(0x1234); - ThreadLocalTesterBase* tls_val; + char* tls_val; base::WaitableEvent done(true, false); GetThreadLocal getter(&tlp, &done); @@ -93,13 +92,13 @@ TEST(ThreadLocalTest, Pointer) { done.Reset(); tp1.AddWork(&getter); done.Wait(); - EXPECT_EQ(static_cast<ThreadLocalTesterBase*>(NULL), tls_val); + EXPECT_EQ(static_cast<char*>(NULL), tls_val); tls_val = kBogusPointer; done.Reset(); tp2.AddWork(&getter); done.Wait(); - EXPECT_EQ(static_cast<ThreadLocalTesterBase*>(NULL), tls_val); + EXPECT_EQ(static_cast<char*>(NULL), tls_val); SetThreadLocal setter(&tlp, &done); @@ -121,7 +120,7 @@ TEST(ThreadLocalTest, Pointer) { done.Reset(); tp2.AddWork(&getter); done.Wait(); - EXPECT_EQ(static_cast<ThreadLocalTesterBase*>(NULL), tls_val); + EXPECT_EQ(static_cast<char*>(NULL), tls_val); // Set thread 2 to kBogusPointer + 1. setter.set_value(kBogusPointer + 1); diff --git a/chromium/base/threading/thread_perftest.cc b/chromium/base/threading/thread_perftest.cc index 9fbc844ec2e..3bc9fb409ce 100644 --- a/chromium/base/threading/thread_perftest.cc +++ b/chromium/base/threading/thread_perftest.cc @@ -5,7 +5,9 @@ #include "base/base_switches.h" #include "base/bind.h" #include "base/command_line.h" +#include "base/location.h" #include "base/memory/scoped_vector.h" +#include "base/single_thread_task_runner.h" #include "base/strings/stringprintf.h" #include "base/synchronization/condition_variable.h" #include "base/synchronization/lock.h" @@ -54,12 +56,9 @@ class ThreadPerfTest : public testing::Test { base::TimeTicks ThreadNow(base::Thread* thread) { base::WaitableEvent done(false, false); base::TimeTicks ticks; - thread->message_loop_proxy()->PostTask( - FROM_HERE, - base::Bind(&ThreadPerfTest::TimeOnThread, - base::Unretained(this), - &ticks, - &done)); + thread->task_runner()->PostTask( + FROM_HERE, base::Bind(&ThreadPerfTest::TimeOnThread, + base::Unretained(this), &ticks, &done)); done.Wait(); return ticks; } @@ -76,10 +75,10 @@ class ThreadPerfTest : public testing::Test { Init(); - base::TimeTicks start = base::TimeTicks::HighResNow(); + base::TimeTicks start = base::TimeTicks::Now(); PingPong(kNumRuns); done_.Wait(); - base::TimeTicks end = base::TimeTicks::HighResNow(); + base::TimeTicks end = base::TimeTicks::Now(); // Gather the cpu-time spent on each thread. This does one extra tasks, // but that should be in the noise given enough runs. @@ -128,10 +127,9 @@ class TaskPerfTest : public ThreadPerfTest { FinishMeasurement(); return; } - NextThread(hops)->message_loop_proxy()->PostTask( - FROM_HERE, - base::Bind( - &ThreadPerfTest::PingPong, base::Unretained(this), hops - 1)); + NextThread(hops)->task_runner()->PostTask( + FROM_HERE, base::Bind(&ThreadPerfTest::PingPong, base::Unretained(this), + hops - 1)); } }; @@ -174,12 +172,12 @@ TEST_F(TaskObserverPerfTest, TaskPingPong) { template <typename WaitableEventType> class EventPerfTest : public ThreadPerfTest { public: - virtual void Init() override { + void Init() override { for (size_t i = 0; i < threads_.size(); i++) events_.push_back(new WaitableEventType(false, false)); } - virtual void Reset() override { events_.clear(); } + void Reset() override { events_.clear(); } void WaitAndSignalOnThread(size_t event) { size_t next_event = (event + 1) % events_.size(); @@ -195,14 +193,12 @@ class EventPerfTest : public ThreadPerfTest { FinishMeasurement(); } - virtual void PingPong(int hops) override { + void PingPong(int hops) override { remaining_hops_ = hops; for (size_t i = 0; i < threads_.size(); i++) { - threads_[i]->message_loop_proxy()->PostTask( - FROM_HERE, - base::Bind(&EventPerfTest::WaitAndSignalOnThread, - base::Unretained(this), - i)); + threads_[i]->task_runner()->PostTask( + FROM_HERE, base::Bind(&EventPerfTest::WaitAndSignalOnThread, + base::Unretained(this), i)); } // Kick off the Signal ping-ponging. diff --git a/chromium/base/threading/thread_restrictions.h b/chromium/base/threading/thread_restrictions.h index 7c46fd2bdb4..54f50ebca87 100644 --- a/chromium/base/threading/thread_restrictions.h +++ b/chromium/base/threading/thread_restrictions.h @@ -22,6 +22,7 @@ class ScopedAllowWaitForLegacyWebViewApi; namespace cc { class CompletionEvent; +class TaskGraphRunner; } namespace chromeos { class BlockingMethodCaller; @@ -41,8 +42,9 @@ class GpuChannelHost; class NestedMessagePumpAndroid; class RenderWidgetResizeHelper; class ScopedAllowWaitForAndroidLayoutTests; +class ScopedAllowWaitForDebugURL; class TextInputClientMac; -} +} // namespace content namespace dbus { class Bus; } @@ -174,9 +176,11 @@ class BASE_EXPORT ThreadRestrictions { friend class content::NestedMessagePumpAndroid; friend class content::RenderWidgetResizeHelper; friend class content::ScopedAllowWaitForAndroidLayoutTests; + friend class content::ScopedAllowWaitForDebugURL; friend class ::HistogramSynchronizer; friend class ::ScopedAllowWaitForLegacyWebViewApi; friend class cc::CompletionEvent; + friend class cc::TaskGraphRunner; friend class mojo::common::WatcherThreadManager; friend class remoting::AutoThread; friend class MessagePumpDefault; diff --git a/chromium/base/threading/thread_unittest.cc b/chromium/base/threading/thread_unittest.cc index f3fb3343e9a..a89768e9d0a 100644 --- a/chromium/base/threading/thread_unittest.cc +++ b/chromium/base/threading/thread_unittest.cc @@ -7,7 +7,8 @@ #include <vector> #include "base/bind.h" -#include "base/message_loop/message_loop.h" +#include "base/location.h" +#include "base/single_thread_task_runner.h" #include "base/third_party/dynamic_annotations/dynamic_annotations.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/platform_test.h" @@ -133,13 +134,18 @@ TEST_F(ThreadTest, StartWithOptions_StackSize) { // Ensure that the thread can work with only 12 kb and still process a // message. Thread::Options options; +#if defined(ADDRESS_SANITIZER) && defined(OS_MACOSX) + // ASan bloats the stack variables and overflows the 12 kb stack on OSX. + options.stack_size = 24*1024; +#else options.stack_size = 12*1024; +#endif EXPECT_TRUE(a.StartWithOptions(options)); EXPECT_TRUE(a.message_loop()); EXPECT_TRUE(a.IsRunning()); bool was_invoked = false; - a.message_loop()->PostTask(FROM_HERE, base::Bind(&ToggleValue, &was_invoked)); + a.task_runner()->PostTask(FROM_HERE, base::Bind(&ToggleValue, &was_invoked)); // wait for the task to run (we could use a kernel event here // instead to avoid busy waiting, but this is sufficient for @@ -160,14 +166,12 @@ TEST_F(ThreadTest, TwoTasks) { // Test that all events are dispatched before the Thread object is // destroyed. We do this by dispatching a sleep event before the // event that will toggle our sentinel value. - a.message_loop()->PostTask( - FROM_HERE, - base::Bind( - static_cast<void (*)(base::TimeDelta)>( - &base::PlatformThread::Sleep), - base::TimeDelta::FromMilliseconds(20))); - a.message_loop()->PostTask(FROM_HERE, base::Bind(&ToggleValue, - &was_invoked)); + a.task_runner()->PostTask( + FROM_HERE, base::Bind(static_cast<void (*)(base::TimeDelta)>( + &base::PlatformThread::Sleep), + base::TimeDelta::FromMilliseconds(20))); + a.task_runner()->PostTask(FROM_HERE, + base::Bind(&ToggleValue, &was_invoked)); } EXPECT_TRUE(was_invoked); } @@ -216,7 +220,7 @@ TEST_F(ThreadTest, CleanUp) { // Register an observer that writes into |captured_events| once the // thread's message loop is destroyed. - t.message_loop()->PostTask( + t.task_runner()->PostTask( FROM_HERE, base::Bind(&RegisterDestructionObserver, base::Unretained(&loop_destruction_observer))); diff --git a/chromium/base/threading/watchdog.cc b/chromium/base/threading/watchdog.cc index 769119efc4b..c0637998a4b 100644 --- a/chromium/base/threading/watchdog.cc +++ b/chromium/base/threading/watchdog.cc @@ -169,7 +169,7 @@ void Watchdog::ThreadDelegate::ThreadMain() { void Watchdog::ThreadDelegate::SetThreadName() const { std::string name = watchdog_->thread_watched_name_ + " Watchdog"; - PlatformThread::SetName(name.c_str()); + PlatformThread::SetName(name); DVLOG(1) << "Watchdog active: " << name; } diff --git a/chromium/base/threading/watchdog_unittest.cc b/chromium/base/threading/watchdog_unittest.cc index b8cc7b7e004..627f46d8a9b 100644 --- a/chromium/base/threading/watchdog_unittest.cc +++ b/chromium/base/threading/watchdog_unittest.cc @@ -43,9 +43,7 @@ class WatchdogCounter : public Watchdog { class WatchdogTest : public testing::Test { public: - virtual void SetUp() override { - Watchdog::ResetStaticData(); - } + void SetUp() override { Watchdog::ResetStaticData(); } }; } // namespace diff --git a/chromium/base/threading/worker_pool.h b/chromium/base/threading/worker_pool.h index 333b4950f6b..a52a41428b3 100644 --- a/chromium/base/threading/worker_pool.h +++ b/chromium/base/threading/worker_pool.h @@ -36,7 +36,7 @@ class BASE_EXPORT WorkerPool { static bool PostTask(const tracked_objects::Location& from_here, const base::Closure& task, bool task_is_slow); - // Just like MessageLoopProxy::PostTaskAndReply, except the destination + // Just like TaskRunner::PostTaskAndReply, except the destination // for |task| is a worker thread and you can specify |task_is_slow| just // like you can for PostTask above. static bool PostTaskAndReply(const tracked_objects::Location& from_here, diff --git a/chromium/base/threading/worker_pool_posix.cc b/chromium/base/threading/worker_pool_posix.cc index cd3c9dc7543..349b5d751c1 100644 --- a/chromium/base/threading/worker_pool_posix.cc +++ b/chromium/base/threading/worker_pool_posix.cc @@ -6,7 +6,6 @@ #include "base/bind.h" #include "base/callback.h" -#include "base/debug/trace_event.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "base/memory/ref_counted.h" @@ -14,6 +13,7 @@ #include "base/threading/platform_thread.h" #include "base/threading/thread_local.h" #include "base/threading/worker_pool.h" +#include "base/trace_event/trace_event.h" #include "base/tracked_objects.h" using tracked_objects::TrackedTime; @@ -27,14 +27,6 @@ base::LazyInstance<ThreadLocalBoolean>::Leaky const int kIdleSecondsBeforeExit = 10 * 60; -#ifdef ADDRESS_SANITIZER -const int kWorkerThreadStackSize = 256 * 1024; -#else -// A stack size of 64 KB is too small for the CERT_PKIXVerifyCert -// function of NSS because of NSS bug 439169. -const int kWorkerThreadStackSize = 128 * 1024; -#endif - class WorkerPoolImpl { public: WorkerPoolImpl(); @@ -85,7 +77,7 @@ void WorkerThread::ThreadMain() { const std::string name = base::StringPrintf( "%s/%d", name_prefix_.c_str(), PlatformThread::CurrentId()); // Note |name.c_str()| must remain valid for for the whole life of the thread. - PlatformThread::SetName(name.c_str()); + PlatformThread::SetName(name); for (;;) { PendingTask pending_task = pool_->WaitForTask(); @@ -95,15 +87,13 @@ void WorkerThread::ThreadMain() { "src_file", pending_task.posted_from.file_name(), "src_func", pending_task.posted_from.function_name()); - tracked_objects::ThreadData::PrepareForStartOfRun(pending_task.birth_tally); tracked_objects::TaskStopwatch stopwatch; stopwatch.Start(); pending_task.task.Run(); stopwatch.Stop(); tracked_objects::ThreadData::TallyRunOnWorkerThreadIfTracking( - pending_task.birth_tally, TrackedTime(pending_task.time_posted), - stopwatch); + pending_task.birth_tally, pending_task.time_posted, stopwatch); } // The WorkerThread is non-joinable, so it deletes itself. @@ -169,7 +159,7 @@ void PosixDynamicThreadPool::AddTask(PendingTask* pending_task) { // which will delete itself on exit. WorkerThread* worker = new WorkerThread(name_prefix_, this); - PlatformThread::CreateNonJoinable(kWorkerThreadStackSize, worker); + PlatformThread::CreateNonJoinable(0, worker); } } diff --git a/chromium/base/threading/worker_pool_posix_unittest.cc b/chromium/base/threading/worker_pool_posix_unittest.cc index b694155fdfd..354a99c538d 100644 --- a/chromium/base/threading/worker_pool_posix_unittest.cc +++ b/chromium/base/threading/worker_pool_posix_unittest.cc @@ -97,11 +97,11 @@ class PosixDynamicThreadPoolTest : public testing::Test { num_waiting_to_start_cv_(&num_waiting_to_start_lock_), start_(true, false) {} - virtual void SetUp() override { + void SetUp() override { peer_.set_num_idle_threads_cv(new ConditionVariable(peer_.lock())); } - virtual void TearDown() override { + void TearDown() override { // Wake up the idle threads so they can terminate. if (pool_.get()) pool_->Terminate(); } diff --git a/chromium/base/threading/worker_pool_win.cc b/chromium/base/threading/worker_pool_win.cc index ff3cc832dc5..1b0ade5e244 100644 --- a/chromium/base/threading/worker_pool_win.cc +++ b/chromium/base/threading/worker_pool_win.cc @@ -6,10 +6,10 @@ #include "base/bind.h" #include "base/callback.h" -#include "base/debug/trace_event.h" #include "base/logging.h" #include "base/pending_task.h" #include "base/threading/thread_local.h" +#include "base/trace_event/trace_event.h" #include "base/tracked_objects.h" namespace base { @@ -25,8 +25,6 @@ DWORD CALLBACK WorkItemCallback(void* param) { "src_file", pending_task->posted_from.file_name(), "src_func", pending_task->posted_from.function_name()); - tracked_objects::ThreadData::PrepareForStartOfRun(pending_task->birth_tally); - g_worker_pool_running_on_this_thread.Get().Set(true); tracked_objects::TaskStopwatch stopwatch; @@ -37,9 +35,7 @@ DWORD CALLBACK WorkItemCallback(void* param) { g_worker_pool_running_on_this_thread.Get().Set(false); tracked_objects::ThreadData::TallyRunOnWorkerThreadIfTracking( - pending_task->birth_tally, - tracked_objects::TrackedTime(pending_task->time_posted), - stopwatch); + pending_task->birth_tally, pending_task->time_posted, stopwatch); delete pending_task; return 0; diff --git a/chromium/base/time/clock.h b/chromium/base/time/clock.h index be389be999d..507a850ff0a 100644 --- a/chromium/base/time/clock.h +++ b/chromium/base/time/clock.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_CLOCK_H_ -#define BASE_CLOCK_H_ +#ifndef BASE_TIME_CLOCK_H_ +#define BASE_TIME_CLOCK_H_ #include "base/base_export.h" #include "base/time/time.h" @@ -37,4 +37,4 @@ class BASE_EXPORT Clock { } // namespace base -#endif // BASE_CLOCK_H_ +#endif // BASE_TIME_CLOCK_H_ diff --git a/chromium/base/time/default_clock.h b/chromium/base/time/default_clock.h index 3d2e9473c18..0b8250e539b 100644 --- a/chromium/base/time/default_clock.h +++ b/chromium/base/time/default_clock.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_DEFAULT_CLOCK_H_ -#define BASE_DEFAULT_CLOCK_H_ +#ifndef BASE_TIME_DEFAULT_CLOCK_H_ +#define BASE_TIME_DEFAULT_CLOCK_H_ #include "base/base_export.h" #include "base/compiler_specific.h" @@ -22,4 +22,4 @@ class BASE_EXPORT DefaultClock : public Clock { } // namespace base -#endif // BASE_DEFAULT_CLOCK_H_ +#endif // BASE_TIME_DEFAULT_CLOCK_H_ diff --git a/chromium/base/time/default_tick_clock.h b/chromium/base/time/default_tick_clock.h index a6d6b15d02c..cb041e6124a 100644 --- a/chromium/base/time/default_tick_clock.h +++ b/chromium/base/time/default_tick_clock.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_DEFAULT_TICK_CLOCK_H_ -#define BASE_DEFAULT_TICK_CLOCK_H_ +#ifndef BASE_TIME_DEFAULT_TICK_CLOCK_H_ +#define BASE_TIME_DEFAULT_TICK_CLOCK_H_ #include "base/base_export.h" #include "base/compiler_specific.h" @@ -22,4 +22,4 @@ class BASE_EXPORT DefaultTickClock : public TickClock { } // namespace base -#endif // BASE_DEFAULT_CLOCK_H_ +#endif // BASE_TIME_DEFAULT_TICK_CLOCK_H_ diff --git a/chromium/base/time/pr_time_unittest.cc b/chromium/base/time/pr_time_unittest.cc index 2853e964802..06043a5b8eb 100644 --- a/chromium/base/time/pr_time_unittest.cc +++ b/chromium/base/time/pr_time_unittest.cc @@ -25,7 +25,7 @@ PRTime comparison_time_2 = INT64_C(1373275692441381); // represented as GMT // tested by comparing them to a known time in the local zone. class PRTimeTest : public testing::Test { protected: - virtual void SetUp() override { + void SetUp() override { // Use mktime to get a time_t, and turn it into a PRTime by converting // seconds to microseconds. Use 15th Oct 2007 12:45:00 local. This // must be a time guaranteed to be outside of a DST fallback hour in diff --git a/chromium/base/time/tick_clock.h b/chromium/base/time/tick_clock.h index 3e53d832857..f7aba537430 100644 --- a/chromium/base/time/tick_clock.h +++ b/chromium/base/time/tick_clock.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_TICK_CLOCK_H_ -#define BASE_TICK_CLOCK_H_ +#ifndef BASE_TIME_TICK_CLOCK_H_ +#define BASE_TIME_TICK_CLOCK_H_ #include "base/base_export.h" #include "base/time/time.h" @@ -17,8 +17,8 @@ namespace base { // See DefaultTickClock (base/time/default_tick_clock.h) for the default // implementation that simply uses TimeTicks::Now(). // -// (Other implementations that use TimeTicks::HighResNow() or -// TimeTicks::NowFromSystemTime() should be added as needed.) +// (Other implementations that use TimeTicks::NowFromSystemTime() should +// be added as needed.) // // See SimpleTestTickClock (base/test/simple_test_tick_clock.h) for a // simple test implementation. @@ -37,4 +37,4 @@ class BASE_EXPORT TickClock { } // namespace base -#endif // BASE_TICK_CLOCK_H_ +#endif // BASE_TIME_TICK_CLOCK_H_ diff --git a/chromium/base/time/time.cc b/chromium/base/time/time.cc index ce9d12c0c2e..9834188597d 100644 --- a/chromium/base/time/time.cc +++ b/chromium/base/time/time.cc @@ -4,12 +4,12 @@ #include "base/time/time.h" +#include <cmath> #include <ios> #include <limits> #include <ostream> #include <sstream> -#include "base/float_util.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "base/strings/stringprintf.h" @@ -97,6 +97,36 @@ int64 TimeDelta::InMicroseconds() const { return delta_; } +namespace time_internal { + +int64 SaturatedAdd(TimeDelta delta, int64 value) { + CheckedNumeric<int64> rv(delta.delta_); + rv += value; + return FromCheckedNumeric(rv); +} + +int64 SaturatedSub(TimeDelta delta, int64 value) { + CheckedNumeric<int64> rv(delta.delta_); + rv -= value; + return FromCheckedNumeric(rv); +} + +int64 FromCheckedNumeric(const CheckedNumeric<int64> value) { + if (value.IsValid()) + return value.ValueUnsafe(); + + // We could return max/min but we don't really expose what the maximum delta + // is. Instead, return max/(-max), which is something that clients can reason + // about. + // TODO(rvargas) crbug.com/332611: don't use internal values. + int64 limit = std::numeric_limits<int64>::max(); + if (value.validity() == internal::RANGE_UNDERFLOW) + limit = -limit; + return value.ValueOrDefault(limit); +} + +} // namespace time_internal + std::ostream& operator<<(std::ostream& os, TimeDelta time_delta) { return os << time_delta.InSecondsF() << "s"; } @@ -134,7 +164,7 @@ time_t Time::ToTimeT() const { // static Time Time::FromDoubleT(double dt) { - if (dt == 0 || IsNaN(dt)) + if (dt == 0 || std::isnan(dt)) return Time(); // Preserve 0 so we can tell it doesn't exist. if (dt == std::numeric_limits<double>::infinity()) return Max(); @@ -274,6 +304,19 @@ TimeTicks TimeTicks::UnixEpoch() { return leaky_unix_epoch_singleton_instance.Get().unix_epoch(); } +TimeTicks TimeTicks::SnappedToNextTick(TimeTicks tick_phase, + TimeDelta tick_interval) const { + // |interval_offset| is the offset from |this| to the next multiple of + // |tick_interval| after |tick_phase|, possibly negative if in the past. + TimeDelta interval_offset = (tick_phase - *this) % tick_interval; + // If |this| is exactly on the interval (i.e. offset==0), don't adjust. + // Otherwise, if |tick_phase| was in the past, adjust forward to the next + // tick after |this|. + if (!interval_offset.is_zero() && tick_phase < *this) + interval_offset += tick_interval; + return *this + interval_offset; +} + std::ostream& operator<<(std::ostream& os, TimeTicks time_ticks) { // This function formats a TimeTicks object as "bogo-microseconds". // The origin and granularity of the count are platform-specific, and may very diff --git a/chromium/base/time/time.h b/chromium/base/time/time.h index 9cb007ec222..0a267783861 100644 --- a/chromium/base/time/time.h +++ b/chromium/base/time/time.h @@ -33,6 +33,7 @@ #include "base/base_export.h" #include "base/basictypes.h" +#include "base/numerics/safe_math.h" #include "build/build_config.h" #if defined(OS_MACOSX) @@ -56,8 +57,23 @@ namespace base { -class Time; -class TimeTicks; +class TimeDelta; + +// The functions in the time_internal namespace are meant to be used only by the +// time classes and functions. Please use the math operators defined in the +// time classes instead. +namespace time_internal { + +// Add or subtract |value| from a TimeDelta. The int64 argument and return value +// are in terms of a microsecond timebase. +BASE_EXPORT int64 SaturatedAdd(TimeDelta delta, int64 value); +BASE_EXPORT int64 SaturatedSub(TimeDelta delta, int64 value); + +// Clamp |value| on overflow and underflow conditions. The int64 argument and +// return value are in terms of a microsecond timebase. +BASE_EXPORT int64 FromCheckedNumeric(const CheckedNumeric<int64> value); + +} // namespace time_internal // TimeDelta ------------------------------------------------------------------ @@ -100,6 +116,20 @@ class BASE_EXPORT TimeDelta { return delta_; } + // Returns the magnitude (absolute value) of this TimeDelta. + TimeDelta magnitude() const { + // Some toolchains provide an incomplete C++11 implementation and lack an + // int64 overload for std::abs(). The following is a simple branchless + // implementation: + const int64 mask = delta_ >> (sizeof(delta_) * 8 - 1); + return TimeDelta((delta_ + mask) ^ mask); + } + + // Returns true if the time delta is zero. + bool is_zero() const { + return delta_ == 0; + } + // Returns true if the time delta is the maximum time delta. bool is_max() const { return delta_ == std::numeric_limits<int64>::max(); @@ -131,47 +161,50 @@ class BASE_EXPORT TimeDelta { // Computations with other deltas. TimeDelta operator+(TimeDelta other) const { - return TimeDelta(delta_ + other.delta_); + return TimeDelta(time_internal::SaturatedAdd(*this, other.delta_)); } TimeDelta operator-(TimeDelta other) const { - return TimeDelta(delta_ - other.delta_); + return TimeDelta(time_internal::SaturatedSub(*this, other.delta_)); } TimeDelta& operator+=(TimeDelta other) { - delta_ += other.delta_; - return *this; + return *this = (*this + other); } TimeDelta& operator-=(TimeDelta other) { - delta_ -= other.delta_; - return *this; + return *this = (*this - other); } TimeDelta operator-() const { return TimeDelta(-delta_); } - // Computations with ints, note that we only allow multiplicative operations - // with ints, and additive operations with other deltas. - TimeDelta operator*(int64 a) const { - return TimeDelta(delta_ * a); + // Computations with numeric types. + template<typename T> + TimeDelta operator*(T a) const { + CheckedNumeric<int64> rv(delta_); + rv *= a; + return TimeDelta(time_internal::FromCheckedNumeric(rv)); } - TimeDelta operator/(int64 a) const { - return TimeDelta(delta_ / a); + template<typename T> + TimeDelta operator/(T a) const { + CheckedNumeric<int64> rv(delta_); + rv /= a; + return TimeDelta(time_internal::FromCheckedNumeric(rv)); } - TimeDelta& operator*=(int64 a) { - delta_ *= a; - return *this; + template<typename T> + TimeDelta& operator*=(T a) { + return *this = (*this * a); } - TimeDelta& operator/=(int64 a) { - delta_ /= a; - return *this; + template<typename T> + TimeDelta& operator/=(T a) { + return *this = (*this / a); } + int64 operator/(TimeDelta a) const { return delta_ / a.delta_; } - - // Defined below because it depends on the definition of the other classes. - Time operator+(Time t) const; - TimeTicks operator+(TimeTicks t) const; + TimeDelta operator%(TimeDelta a) const { + return TimeDelta(delta_ % a.delta_); + } // Comparison operators. bool operator==(TimeDelta other) const { @@ -194,9 +227,8 @@ class BASE_EXPORT TimeDelta { } private: - friend class Time; - friend class TimeTicks; - friend TimeDelta operator*(int64 a, TimeDelta td); + friend int64 time_internal::SaturatedAdd(TimeDelta delta, int64 value); + friend int64 time_internal::SaturatedSub(TimeDelta delta, int64 value); // Constructs a delta given the duration in microseconds. This is private // to avoid confusion by callers with an integer constructor. Use @@ -208,30 +240,139 @@ class BASE_EXPORT TimeDelta { int64 delta_; }; -inline TimeDelta operator*(int64 a, TimeDelta td) { - return TimeDelta(a * td.delta_); +template<typename T> +inline TimeDelta operator*(T a, TimeDelta td) { + return td * a; } // For logging use only. BASE_EXPORT std::ostream& operator<<(std::ostream& os, TimeDelta time_delta); -// Time ----------------------------------------------------------------------- +// Do not reference the time_internal::TimeBase template class directly. Please +// use one of the time subclasses instead, and only reference the public +// TimeBase members via those classes. +namespace time_internal { + +// TimeBase-------------------------------------------------------------------- -// Represents a wall clock time in UTC. -class BASE_EXPORT Time { +// Provides value storage and comparison/math operations common to all time +// classes. Each subclass provides for strong type-checking to ensure +// semantically meaningful comparison/math of time values from the same clock +// source or timeline. +template<class TimeClass> +class TimeBase { public: + static const int64 kHoursPerDay = 24; static const int64 kMillisecondsPerSecond = 1000; + static const int64 kMillisecondsPerDay = kMillisecondsPerSecond * 60 * 60 * + kHoursPerDay; static const int64 kMicrosecondsPerMillisecond = 1000; static const int64 kMicrosecondsPerSecond = kMicrosecondsPerMillisecond * kMillisecondsPerSecond; static const int64 kMicrosecondsPerMinute = kMicrosecondsPerSecond * 60; static const int64 kMicrosecondsPerHour = kMicrosecondsPerMinute * 60; - static const int64 kMicrosecondsPerDay = kMicrosecondsPerHour * 24; + static const int64 kMicrosecondsPerDay = kMicrosecondsPerHour * kHoursPerDay; static const int64 kMicrosecondsPerWeek = kMicrosecondsPerDay * 7; static const int64 kNanosecondsPerMicrosecond = 1000; static const int64 kNanosecondsPerSecond = kNanosecondsPerMicrosecond * kMicrosecondsPerSecond; + // Returns true if this object has not been initialized. + // + // Warning: Be careful when writing code that performs math on time values, + // since it's possible to produce a valid "zero" result that should not be + // interpreted as a "null" value. + bool is_null() const { + return us_ == 0; + } + + // Returns true if this object represents the maximum time. + bool is_max() const { + return us_ == std::numeric_limits<int64>::max(); + } + + // For serializing only. Use FromInternalValue() to reconstitute. Please don't + // use this and do arithmetic on it, as it is more error prone than using the + // provided operators. + int64 ToInternalValue() const { + return us_; + } + + TimeClass& operator=(TimeClass other) { + us_ = other.us_; + return *(static_cast<TimeClass*>(this)); + } + + // Compute the difference between two times. + TimeDelta operator-(TimeClass other) const { + return TimeDelta::FromMicroseconds(us_ - other.us_); + } + + // Return a new time modified by some delta. + TimeClass operator+(TimeDelta delta) const { + return TimeClass(time_internal::SaturatedAdd(delta, us_)); + } + TimeClass operator-(TimeDelta delta) const { + return TimeClass(-time_internal::SaturatedSub(delta, us_)); + } + + // Modify by some time delta. + TimeClass& operator+=(TimeDelta delta) { + return static_cast<TimeClass&>(*this = (*this + delta)); + } + TimeClass& operator-=(TimeDelta delta) { + return static_cast<TimeClass&>(*this = (*this - delta)); + } + + // Comparison operators + bool operator==(TimeClass other) const { + return us_ == other.us_; + } + bool operator!=(TimeClass other) const { + return us_ != other.us_; + } + bool operator<(TimeClass other) const { + return us_ < other.us_; + } + bool operator<=(TimeClass other) const { + return us_ <= other.us_; + } + bool operator>(TimeClass other) const { + return us_ > other.us_; + } + bool operator>=(TimeClass other) const { + return us_ >= other.us_; + } + + // Converts an integer value representing TimeClass to a class. This is used + // when deserializing a |TimeClass| structure, using a value known to be + // compatible. It is not provided as a constructor because the integer type + // may be unclear from the perspective of a caller. + static TimeClass FromInternalValue(int64 us) { + return TimeClass(us); + } + + protected: + explicit TimeBase(int64 us) : us_(us) { + } + + // Time value in a microsecond timebase. + int64 us_; +}; + +} // namespace time_internal + +template<class TimeClass> +inline TimeClass operator+(TimeDelta delta, TimeClass t) { + return t + delta; +} + +// Time ----------------------------------------------------------------------- + +// Represents a wall clock time in UTC. Values are not guaranteed to be +// monotonically non-decreasing and are subject to large amounts of skew. +class BASE_EXPORT Time : public time_internal::TimeBase<Time> { + public: // The representation of Jan 1, 1970 UTC in microseconds since the // platform-dependent epoch. static const int64 kTimeTToMicrosecondsOffset; @@ -271,17 +412,7 @@ class BASE_EXPORT Time { }; // Contains the NULL time. Use Time::Now() to get the current time. - Time() : us_(0) { - } - - // Returns true if the time object has not been initialized. - bool is_null() const { - return us_ == 0; - } - - // Returns true if the time object is the maximum time. - bool is_max() const { - return us_ == std::numeric_limits<int64>::max(); + Time() : TimeBase(0) { } // Returns the time for epoch in Unix-like system (Jan 1, 1970). @@ -380,14 +511,6 @@ class BASE_EXPORT Time { return FromExploded(true, exploded); } - // Converts an integer value representing Time to a class. This is used - // when deserializing a |Time| structure, using a value known to be - // compatible. It is not provided as a constructor because the integer type - // may be unclear from the perspective of a caller. - static Time FromInternalValue(int64 us) { - return Time(us); - } - // Converts a string representation of time to a Time object. // An example of a time string which is converted is as below:- // "Tue, 15 Nov 1994 12:45:26 GMT". If the timezone is not specified @@ -403,13 +526,6 @@ class BASE_EXPORT Time { return FromStringInternal(time_string, false, parsed_time); } - // For serializing, use FromInternalValue to reconstitute. Please don't use - // this and do arithmetic on it, as it is more error prone than using the - // provided operators. - int64 ToInternalValue() const { - return us_; - } - // Fills the given exploded structure with either the local time or UTC from // this time structure (containing UTC). void UTCExplode(Exploded* exploded) const { @@ -423,58 +539,10 @@ class BASE_EXPORT Time { // midnight on that day. Time LocalMidnight() const; - Time& operator=(Time other) { - us_ = other.us_; - return *this; - } - - // Compute the difference between two times. - TimeDelta operator-(Time other) const { - return TimeDelta(us_ - other.us_); - } - - // Modify by some time delta. - Time& operator+=(TimeDelta delta) { - us_ += delta.delta_; - return *this; - } - Time& operator-=(TimeDelta delta) { - us_ -= delta.delta_; - return *this; - } - - // Return a new time modified by some delta. - Time operator+(TimeDelta delta) const { - return Time(us_ + delta.delta_); - } - Time operator-(TimeDelta delta) const { - return Time(us_ - delta.delta_); - } - - // Comparison operators - bool operator==(Time other) const { - return us_ == other.us_; - } - bool operator!=(Time other) const { - return us_ != other.us_; - } - bool operator<(Time other) const { - return us_ < other.us_; - } - bool operator<=(Time other) const { - return us_ <= other.us_; - } - bool operator>(Time other) const { - return us_ > other.us_; - } - bool operator>=(Time other) const { - return us_ >= other.us_; - } - private: - friend class TimeDelta; + friend class time_internal::TimeBase<Time>; - explicit Time(int64 us) : us_(us) { + explicit Time(int64 us) : TimeBase(us) { } // Explodes the given time to either local time |is_local = true| or UTC @@ -495,9 +563,6 @@ class BASE_EXPORT Time { static bool FromStringInternal(const char* time_string, bool is_local, Time* parsed_time); - - // Time in microseconds in UTC. - int64 us_; }; // Inline the TimeDelta factory methods, for fast TimeDelta construction. @@ -566,16 +631,13 @@ inline TimeDelta TimeDelta::FromMicroseconds(int64 us) { return TimeDelta(us); } -inline Time TimeDelta::operator+(Time t) const { - return Time(t.us_ + delta_); -} - // For logging use only. BASE_EXPORT std::ostream& operator<<(std::ostream& os, Time time); // TimeTicks ------------------------------------------------------------------ -class BASE_EXPORT TimeTicks { +// Represents monotonically non-decreasing clock time. +class BASE_EXPORT TimeTicks : public time_internal::TimeBase<TimeTicks> { public: // We define this even without OS_CHROMEOS for seccomp sandbox testing. #if defined(OS_LINUX) @@ -585,21 +647,20 @@ class BASE_EXPORT TimeTicks { static const clockid_t kClockSystemTrace = 11; #endif - TimeTicks() : ticks_(0) { + TimeTicks() : TimeBase(0) { } - // Platform-dependent tick count representing "right now." - // The resolution of this clock is ~1-15ms. Resolution varies depending - // on hardware/operating system configuration. + // Platform-dependent tick count representing "right now." When + // IsHighResolution() returns false, the resolution of the clock could be + // as coarse as ~15.6ms. Otherwise, the resolution should be no worse than one + // microsecond. static TimeTicks Now(); - // Returns a platform-dependent high-resolution tick count. Implementation - // is hardware dependent and may or may not return sub-millisecond - // resolution. THIS CALL IS GENERALLY MUCH MORE EXPENSIVE THAN Now() AND - // SHOULD ONLY BE USED WHEN IT IS REALLY NEEDED. - static TimeTicks HighResNow(); - - static bool IsHighResNowFastAndReliable(); + // Returns true if the high resolution clock is working on this system and + // Now() will return high resolution values. Note that, on systems where the + // high resolution clock works but is deemed inefficient, the low resolution + // clock will be used instead. + static bool IsHighResolution(); // Returns true if ThreadNow() is supported on this system. static bool IsThreadNowSupported() { @@ -616,42 +677,35 @@ class BASE_EXPORT TimeTicks { // to (approximately) measure how much time the calling thread spent doing // actual work vs. being de-scheduled. May return bogus results if the thread // migrates to another CPU between two calls. + // + // WARNING: The returned value might NOT have the same origin as Now(). Do not + // perform math between TimeTicks values returned by Now() and ThreadNow() and + // expect meaningful results. + // TODO(miu): Since the timeline of these values is different, the values + // should be of a different type. static TimeTicks ThreadNow(); - // Returns the current system trace time or, if none is defined, the current - // high-res time (i.e. HighResNow()). On systems where a global trace clock - // is defined, timestamping TraceEvents's with this value guarantees - // synchronization between events collected inside chrome and events - // collected outside (e.g. kernel, X server). + // Returns the current system trace time or, if not available on this + // platform, a high-resolution time value; or a low-resolution time value if + // neither are avalable. On systems where a global trace clock is defined, + // timestamping TraceEvents's with this value guarantees synchronization + // between events collected inside chrome and events collected outside + // (e.g. kernel, X server). + // + // WARNING: The returned value might NOT have the same origin as Now(). Do not + // perform math between TimeTicks values returned by Now() and + // NowFromSystemTraceTime() and expect meaningful results. + // TODO(miu): Since the timeline of these values is different, the values + // should be of a different type. static TimeTicks NowFromSystemTraceTime(); #if defined(OS_WIN) - // Get the absolute value of QPC time drift. For testing. - static int64 GetQPCDriftMicroseconds(); - + // Translates an absolute QPC timestamp into a TimeTicks value. The returned + // value has the same origin as Now(). Do NOT attempt to use this if + // IsHighResolution() returns false. static TimeTicks FromQPCValue(LONGLONG qpc_value); - - // Returns true if the high resolution clock is working on this system. - // This is only for testing. - static bool IsHighResClockWorking(); - - // Returns a time value that is NOT rollover protected. - static TimeTicks UnprotectedNow(); #endif - // Returns true if this object has not been initialized. - bool is_null() const { - return ticks_ == 0; - } - - // Converts an integer value representing TimeTicks to a class. This is used - // when deserializing a |TimeTicks| structure, using a value known to be - // compatible. It is not provided as a constructor because the integer type - // may be unclear from the perspective of a caller. - static TimeTicks FromInternalValue(int64 ticks) { - return TimeTicks(ticks); - } - // Get the TimeTick value at the time of the UnixEpoch. This is useful when // you need to relate the value of TimeTicks to a real time and date. // Note: Upon first invocation, this function takes a snapshot of the realtime @@ -660,80 +714,26 @@ class BASE_EXPORT TimeTicks { // application runs. static TimeTicks UnixEpoch(); - // Returns the internal numeric value of the TimeTicks object. - // For serializing, use FromInternalValue to reconstitute. - int64 ToInternalValue() const { - return ticks_; - } - - TimeTicks& operator=(TimeTicks other) { - ticks_ = other.ticks_; - return *this; - } - - // Compute the difference between two times. - TimeDelta operator-(TimeTicks other) const { - return TimeDelta(ticks_ - other.ticks_); - } - - // Modify by some time delta. - TimeTicks& operator+=(TimeDelta delta) { - ticks_ += delta.delta_; - return *this; - } - TimeTicks& operator-=(TimeDelta delta) { - ticks_ -= delta.delta_; - return *this; - } - - // Return a new TimeTicks modified by some delta. - TimeTicks operator+(TimeDelta delta) const { - return TimeTicks(ticks_ + delta.delta_); - } - TimeTicks operator-(TimeDelta delta) const { - return TimeTicks(ticks_ - delta.delta_); - } - - // Comparison operators - bool operator==(TimeTicks other) const { - return ticks_ == other.ticks_; - } - bool operator!=(TimeTicks other) const { - return ticks_ != other.ticks_; - } - bool operator<(TimeTicks other) const { - return ticks_ < other.ticks_; - } - bool operator<=(TimeTicks other) const { - return ticks_ <= other.ticks_; - } - bool operator>(TimeTicks other) const { - return ticks_ > other.ticks_; - } - bool operator>=(TimeTicks other) const { - return ticks_ >= other.ticks_; - } - - protected: - friend class TimeDelta; - - // Please use Now() to create a new object. This is for internal use - // and testing. Ticks is in microseconds. - explicit TimeTicks(int64 ticks) : ticks_(ticks) { - } - - // Tick count in microseconds. - int64 ticks_; + // Returns |this| snapped to the next tick, given a |tick_phase| and + // repeating |tick_interval| in both directions. |this| may be before, + // after, or equal to the |tick_phase|. + TimeTicks SnappedToNextTick(TimeTicks tick_phase, + TimeDelta tick_interval) const; #if defined(OS_WIN) + protected: typedef DWORD (*TickFunctionType)(void); static TickFunctionType SetMockTickFunction(TickFunctionType ticker); #endif -}; -inline TimeTicks TimeDelta::operator+(TimeTicks t) const { - return TimeTicks(t.ticks_ + delta_); -} + private: + friend class time_internal::TimeBase<TimeTicks>; + + // Please use Now() to create a new object. This is for internal use + // and testing. + explicit TimeTicks(int64 us) : TimeBase(us) { + } +}; // For logging use only. BASE_EXPORT std::ostream& operator<<(std::ostream& os, TimeTicks time_ticks); diff --git a/chromium/base/time/time_mac.cc b/chromium/base/time/time_mac.cc index 26c5d7a715b..e263d079459 100644 --- a/chromium/base/time/time_mac.cc +++ b/chromium/base/time/time_mac.cc @@ -219,12 +219,7 @@ TimeTicks TimeTicks::Now() { } // static -TimeTicks TimeTicks::HighResNow() { - return Now(); -} - -// static -bool TimeTicks::IsHighResNowFastAndReliable() { +bool TimeTicks::IsHighResolution() { return true; } @@ -235,7 +230,7 @@ TimeTicks TimeTicks::ThreadNow() { // static TimeTicks TimeTicks::NowFromSystemTraceTime() { - return HighResNow(); + return Now(); } } // namespace base diff --git a/chromium/base/time/time_posix.cc b/chromium/base/time/time_posix.cc index ad00c513685..8b207eb36a9 100644 --- a/chromium/base/time/time_posix.cc +++ b/chromium/base/time/time_posix.cc @@ -314,12 +314,7 @@ TimeTicks TimeTicks::Now() { } // static -TimeTicks TimeTicks::HighResNow() { - return Now(); -} - -// static -bool TimeTicks::IsHighResNowFastAndReliable() { +bool TimeTicks::IsHighResolution() { return true; } @@ -343,7 +338,7 @@ TimeTicks TimeTicks::NowFromSystemTraceTime() { struct timespec ts; if (clock_gettime(kClockSystemTrace, &ts) != 0) { // NB: fall-back for a chrome os build running on linux - return HighResNow(); + return Now(); } absolute_micro = @@ -357,7 +352,7 @@ TimeTicks TimeTicks::NowFromSystemTraceTime() { // static TimeTicks TimeTicks::NowFromSystemTraceTime() { - return HighResNow(); + return Now(); } #endif // defined(OS_CHROMEOS) diff --git a/chromium/base/time/time_unittest.cc b/chromium/base/time/time_unittest.cc index ef80e438593..456782c6452 100644 --- a/chromium/base/time/time_unittest.cc +++ b/chromium/base/time/time_unittest.cc @@ -6,6 +6,7 @@ #include <time.h> +#include <limits> #include <string> #include "base/compiler_specific.h" @@ -24,7 +25,7 @@ namespace { // See also pr_time_unittests.cc class TimeTest : public testing::Test { protected: - virtual void SetUp() override { + void SetUp() override { // Use mktime to get a time_t, and turn it into a PRTime by converting // seconds to microseconds. Use 15th Oct 2007 12:45:00 local. This // must be a time guaranteed to be outside of a DST fallback hour in @@ -642,12 +643,10 @@ TEST(TimeTicks, Deltas) { } static void HighResClockTest(TimeTicks (*GetTicks)()) { -#if defined(OS_WIN) - // HighResNow doesn't work on some systems. Since the product still works - // even if it doesn't work, it makes this entire test questionable. - if (!TimeTicks::IsHighResClockWorking()) + // IsHighResolution() is false on some systems. Since the product still works + // even if it's false, it makes this entire test questionable. + if (!TimeTicks::IsHighResolution()) return; -#endif // Why do we loop here? // We're trying to measure that intervals increment in a VERY small amount @@ -680,8 +679,8 @@ static void HighResClockTest(TimeTicks (*GetTicks)()) { EXPECT_TRUE(success); } -TEST(TimeTicks, HighResNow) { - HighResClockTest(&TimeTicks::HighResNow); +TEST(TimeTicks, HighRes) { + HighResClockTest(&TimeTicks::Now); } // Fails frequently on Android http://crbug.com/352633 with: @@ -712,10 +711,66 @@ TEST(TimeTicks, MAYBE_ThreadNow) { } TEST(TimeTicks, NowFromSystemTraceTime) { - // Re-use HighResNow test for now since clock properties are identical. + // Re-use HighRes test for now since clock properties are identical. HighResClockTest(&TimeTicks::NowFromSystemTraceTime); } +TEST(TimeTicks, SnappedToNextTickBasic) { + base::TimeTicks phase = base::TimeTicks::FromInternalValue(4000); + base::TimeDelta interval = base::TimeDelta::FromMicroseconds(1000); + base::TimeTicks timestamp; + + // Timestamp in previous interval. + timestamp = base::TimeTicks::FromInternalValue(3500); + EXPECT_EQ(4000, + timestamp.SnappedToNextTick(phase, interval).ToInternalValue()); + + // Timestamp in next interval. + timestamp = base::TimeTicks::FromInternalValue(4500); + EXPECT_EQ(5000, + timestamp.SnappedToNextTick(phase, interval).ToInternalValue()); + + // Timestamp multiple intervals before. + timestamp = base::TimeTicks::FromInternalValue(2500); + EXPECT_EQ(3000, + timestamp.SnappedToNextTick(phase, interval).ToInternalValue()); + + // Timestamp multiple intervals after. + timestamp = base::TimeTicks::FromInternalValue(6500); + EXPECT_EQ(7000, + timestamp.SnappedToNextTick(phase, interval).ToInternalValue()); + + // Timestamp on previous interval. + timestamp = base::TimeTicks::FromInternalValue(3000); + EXPECT_EQ(3000, + timestamp.SnappedToNextTick(phase, interval).ToInternalValue()); + + // Timestamp on next interval. + timestamp = base::TimeTicks::FromInternalValue(5000); + EXPECT_EQ(5000, + timestamp.SnappedToNextTick(phase, interval).ToInternalValue()); + + // Timestamp equal to phase. + timestamp = base::TimeTicks::FromInternalValue(4000); + EXPECT_EQ(4000, + timestamp.SnappedToNextTick(phase, interval).ToInternalValue()); +} + +TEST(TimeTicks, SnappedToNextTickOverflow) { + // int(big_timestamp / interval) < 0, so this causes a crash if the number of + // intervals elapsed is attempted to be stored in an int. + base::TimeTicks phase = base::TimeTicks::FromInternalValue(0); + base::TimeDelta interval = base::TimeDelta::FromMicroseconds(4000); + base::TimeTicks big_timestamp = + base::TimeTicks::FromInternalValue(8635916564000); + + EXPECT_EQ(8635916564000, + big_timestamp.SnappedToNextTick(phase, interval).ToInternalValue()); + EXPECT_EQ(8635916564000, + big_timestamp.SnappedToNextTick(big_timestamp, interval) + .ToInternalValue()); +} + TEST(TimeDelta, FromAndIn) { EXPECT_TRUE(TimeDelta::FromDays(2) == TimeDelta::FromHours(48)); EXPECT_TRUE(TimeDelta::FromHours(3) == TimeDelta::FromMinutes(180)); @@ -792,6 +847,172 @@ std::string AnyToString(Any any) { return oss.str(); } +TEST(TimeDelta, Magnitude) { + const int64 zero = 0; + EXPECT_EQ(TimeDelta::FromMicroseconds(zero), + TimeDelta::FromMicroseconds(zero).magnitude()); + + const int64 one = 1; + const int64 negative_one = -1; + EXPECT_EQ(TimeDelta::FromMicroseconds(one), + TimeDelta::FromMicroseconds(one).magnitude()); + EXPECT_EQ(TimeDelta::FromMicroseconds(one), + TimeDelta::FromMicroseconds(negative_one).magnitude()); + + const int64 max_int64_minus_one = std::numeric_limits<int64>::max() - 1; + const int64 min_int64_plus_two = std::numeric_limits<int64>::min() + 2; + EXPECT_EQ(TimeDelta::FromMicroseconds(max_int64_minus_one), + TimeDelta::FromMicroseconds(max_int64_minus_one).magnitude()); + EXPECT_EQ(TimeDelta::FromMicroseconds(max_int64_minus_one), + TimeDelta::FromMicroseconds(min_int64_plus_two).magnitude()); +} + + +TEST(TimeDelta, NumericOperators) { + double d = 0.5; + EXPECT_EQ(TimeDelta::FromMilliseconds(500), + TimeDelta::FromMilliseconds(1000) * d); + EXPECT_EQ(TimeDelta::FromMilliseconds(2000), + TimeDelta::FromMilliseconds(1000) / d); + EXPECT_EQ(TimeDelta::FromMilliseconds(500), + TimeDelta::FromMilliseconds(1000) *= d); + EXPECT_EQ(TimeDelta::FromMilliseconds(2000), + TimeDelta::FromMilliseconds(1000) /= d); + EXPECT_EQ(TimeDelta::FromMilliseconds(500), + d * TimeDelta::FromMilliseconds(1000)); + + float f = 0.5; + EXPECT_EQ(TimeDelta::FromMilliseconds(500), + TimeDelta::FromMilliseconds(1000) * f); + EXPECT_EQ(TimeDelta::FromMilliseconds(2000), + TimeDelta::FromMilliseconds(1000) / f); + EXPECT_EQ(TimeDelta::FromMilliseconds(500), + TimeDelta::FromMilliseconds(1000) *= f); + EXPECT_EQ(TimeDelta::FromMilliseconds(2000), + TimeDelta::FromMilliseconds(1000) /= f); + EXPECT_EQ(TimeDelta::FromMilliseconds(500), + f * TimeDelta::FromMilliseconds(1000)); + + + int i = 2; + EXPECT_EQ(TimeDelta::FromMilliseconds(2000), + TimeDelta::FromMilliseconds(1000) * i); + EXPECT_EQ(TimeDelta::FromMilliseconds(500), + TimeDelta::FromMilliseconds(1000) / i); + EXPECT_EQ(TimeDelta::FromMilliseconds(2000), + TimeDelta::FromMilliseconds(1000) *= i); + EXPECT_EQ(TimeDelta::FromMilliseconds(500), + TimeDelta::FromMilliseconds(1000) /= i); + EXPECT_EQ(TimeDelta::FromMilliseconds(2000), + i * TimeDelta::FromMilliseconds(1000)); + + int64_t i64 = 2; + EXPECT_EQ(TimeDelta::FromMilliseconds(2000), + TimeDelta::FromMilliseconds(1000) * i64); + EXPECT_EQ(TimeDelta::FromMilliseconds(500), + TimeDelta::FromMilliseconds(1000) / i64); + EXPECT_EQ(TimeDelta::FromMilliseconds(2000), + TimeDelta::FromMilliseconds(1000) *= i64); + EXPECT_EQ(TimeDelta::FromMilliseconds(500), + TimeDelta::FromMilliseconds(1000) /= i64); + EXPECT_EQ(TimeDelta::FromMilliseconds(2000), + i64 * TimeDelta::FromMilliseconds(1000)); + + + EXPECT_EQ(TimeDelta::FromMilliseconds(500), + TimeDelta::FromMilliseconds(1000) * 0.5); + EXPECT_EQ(TimeDelta::FromMilliseconds(2000), + TimeDelta::FromMilliseconds(1000) / 0.5); + EXPECT_EQ(TimeDelta::FromMilliseconds(500), + TimeDelta::FromMilliseconds(1000) *= 0.5); + EXPECT_EQ(TimeDelta::FromMilliseconds(2000), + TimeDelta::FromMilliseconds(1000) /= 0.5); + EXPECT_EQ(TimeDelta::FromMilliseconds(500), + 0.5 * TimeDelta::FromMilliseconds(1000)); + + EXPECT_EQ(TimeDelta::FromMilliseconds(2000), + TimeDelta::FromMilliseconds(1000) * 2); + EXPECT_EQ(TimeDelta::FromMilliseconds(500), + TimeDelta::FromMilliseconds(1000) / 2); + EXPECT_EQ(TimeDelta::FromMilliseconds(2000), + TimeDelta::FromMilliseconds(1000) *= 2); + EXPECT_EQ(TimeDelta::FromMilliseconds(500), + TimeDelta::FromMilliseconds(1000) /= 2); + EXPECT_EQ(TimeDelta::FromMilliseconds(2000), + 2 * TimeDelta::FromMilliseconds(1000)); +} + +bool IsMin(TimeDelta delta) { + return (-delta).is_max(); +} + +TEST(TimeDelta, Overflows) { + // Some sanity checks. + EXPECT_TRUE(TimeDelta::Max().is_max()); + EXPECT_TRUE(IsMin(-TimeDelta::Max())); + EXPECT_GT(TimeDelta(), -TimeDelta::Max()); + + TimeDelta large_delta = TimeDelta::Max() - TimeDelta::FromMilliseconds(1); + TimeDelta large_negative = -large_delta; + EXPECT_GT(TimeDelta(), large_negative); + EXPECT_FALSE(large_delta.is_max()); + EXPECT_FALSE(IsMin(-large_negative)); + TimeDelta one_second = TimeDelta::FromSeconds(1); + + // Test +, -, * and / operators. + EXPECT_TRUE((large_delta + one_second).is_max()); + EXPECT_TRUE(IsMin(large_negative + (-one_second))); + EXPECT_TRUE(IsMin(large_negative - one_second)); + EXPECT_TRUE((large_delta - (-one_second)).is_max()); + EXPECT_TRUE((large_delta * 2).is_max()); + EXPECT_TRUE(IsMin(large_delta * -2)); + EXPECT_TRUE((large_delta / 0.5).is_max()); + EXPECT_TRUE(IsMin(large_delta / -0.5)); + + // Test +=, -=, *= and /= operators. + TimeDelta delta = large_delta; + delta += one_second; + EXPECT_TRUE(delta.is_max()); + delta = large_negative; + delta += -one_second; + EXPECT_TRUE(IsMin(delta)); + + delta = large_negative; + delta -= one_second; + EXPECT_TRUE(IsMin(delta)); + delta = large_delta; + delta -= -one_second; + EXPECT_TRUE(delta.is_max()); + + delta = large_delta; + delta *= 2; + EXPECT_TRUE(delta.is_max()); + delta = large_negative; + delta *= 1.5; + EXPECT_TRUE(IsMin(delta)); + + delta = large_delta; + delta /= 0.5; + EXPECT_TRUE(delta.is_max()); + delta = large_negative; + delta /= 0.5; + EXPECT_TRUE(IsMin(delta)); + + // Test operations with Time and TimeTicks. + EXPECT_TRUE((large_delta + Time::Now()).is_max()); + EXPECT_TRUE((large_delta + TimeTicks::Now()).is_max()); + EXPECT_TRUE((Time::Now() + large_delta).is_max()); + EXPECT_TRUE((TimeTicks::Now() + large_delta).is_max()); + + Time time_now = Time::Now(); + EXPECT_EQ(one_second, (time_now + one_second) - time_now); + EXPECT_EQ(-one_second, (time_now - one_second) - time_now); + + TimeTicks ticks_now = TimeTicks::Now(); + EXPECT_EQ(-one_second, (ticks_now - one_second) - ticks_now); + EXPECT_EQ(one_second, (ticks_now + one_second) - ticks_now); +} + TEST(TimeDeltaLogging, DCheckEqCompiles) { DCHECK_EQ(TimeDelta(), TimeDelta()); } diff --git a/chromium/base/time/time_win.cc b/chromium/base/time/time_win.cc index 006118091fc..d2403f21b7f 100644 --- a/chromium/base/time/time_win.cc +++ b/chromium/base/time/time_win.cc @@ -203,12 +203,12 @@ bool Time::ActivateHighResolutionTimer(bool activating) { UINT period = g_high_res_timer_enabled ? kMinTimerIntervalHighResMs : kMinTimerIntervalLowResMs; if (activating) { - DCHECK(g_high_res_timer_count != max); + DCHECK_NE(g_high_res_timer_count, max); ++g_high_res_timer_count; if (g_high_res_timer_count == 1) timeBeginPeriod(period); } else { - DCHECK(g_high_res_timer_count != 0); + DCHECK_NE(g_high_res_timer_count, 0u); --g_high_res_timer_count; if (g_high_res_timer_count == 0) timeEndPeriod(period); @@ -307,13 +307,13 @@ DWORD timeGetTimeWrapper() { return timeGetTime(); } -DWORD (*tick_function)(void) = &timeGetTimeWrapper; +DWORD (*g_tick_function)(void) = &timeGetTimeWrapper; // Accumulation of time lost due to rollover (in milliseconds). -int64 rollover_ms = 0; +int64 g_rollover_ms = 0; // The last timeGetTime value we saw, to detect rollover. -DWORD last_seen_now = 0; +DWORD g_last_seen_now = 0; // Lock protecting rollover_ms and last_seen_now. // Note: this is a global object, and we usually avoid these. However, the time @@ -321,169 +321,161 @@ DWORD last_seen_now = 0; // easy to use a Singleton without even knowing it, and that may lead to many // gotchas). Its impact on startup time should be negligible due to low-level // nature of time code. -base::Lock rollover_lock; +base::Lock g_rollover_lock; // We use timeGetTime() to implement TimeTicks::Now(). This can be problematic // because it returns the number of milliseconds since Windows has started, // which will roll over the 32-bit value every ~49 days. We try to track // rollover ourselves, which works if TimeTicks::Now() is called at least every // 49 days. -TimeDelta RolloverProtectedNow() { - base::AutoLock locked(rollover_lock); +TimeTicks RolloverProtectedNow() { + base::AutoLock locked(g_rollover_lock); // We should hold the lock while calling tick_function to make sure that // we keep last_seen_now stay correctly in sync. - DWORD now = tick_function(); - if (now < last_seen_now) - rollover_ms += 0x100000000I64; // ~49.7 days. - last_seen_now = now; - return TimeDelta::FromMilliseconds(now + rollover_ms); + DWORD now = g_tick_function(); + if (now < g_last_seen_now) + g_rollover_ms += 0x100000000I64; // ~49.7 days. + g_last_seen_now = now; + return TimeTicks() + TimeDelta::FromMilliseconds(now + g_rollover_ms); } -bool IsBuggyAthlon(const base::CPU& cpu) { - // On Athlon X2 CPUs (e.g. model 15) QueryPerformanceCounter is - // unreliable. Fallback to low-res clock. - return cpu.vendor_name() == "AuthenticAMD" && cpu.family() == 15; -} - -// Overview of time counters: +// Discussion of tick counter options on Windows: +// // (1) CPU cycle counter. (Retrieved via RDTSC) // The CPU counter provides the highest resolution time stamp and is the least -// expensive to retrieve. However, the CPU counter is unreliable and should not -// be used in production. Its biggest issue is that it is per processor and it -// is not synchronized between processors. Also, on some computers, the counters -// will change frequency due to thermal and power changes, and stop in some -// states. +// expensive to retrieve. However, on older CPUs, two issues can affect its +// reliability: First it is maintained per processor and not synchronized +// between processors. Also, the counters will change frequency due to thermal +// and power changes, and stop in some states. // // (2) QueryPerformanceCounter (QPC). The QPC counter provides a high- -// resolution (100 nanoseconds) time stamp but is comparatively more expensive -// to retrieve. What QueryPerformanceCounter actually does is up to the HAL. -// (with some help from ACPI). -// According to http://blogs.msdn.com/oldnewthing/archive/2005/09/02/459952.aspx -// in the worst case, it gets the counter from the rollover interrupt on the +// resolution (<1 microsecond) time stamp. On most hardware running today, it +// auto-detects and uses the constant-rate RDTSC counter to provide extremely +// efficient and reliable time stamps. +// +// On older CPUs where RDTSC is unreliable, it falls back to using more +// expensive (20X to 40X more costly) alternate clocks, such as HPET or the ACPI +// PM timer, and can involve system calls; and all this is up to the HAL (with +// some help from ACPI). According to +// http://blogs.msdn.com/oldnewthing/archive/2005/09/02/459952.aspx, in the +// worst case, it gets the counter from the rollover interrupt on the // programmable interrupt timer. In best cases, the HAL may conclude that the // RDTSC counter runs at a constant frequency, then it uses that instead. On // multiprocessor machines, it will try to verify the values returned from // RDTSC on each processor are consistent with each other, and apply a handful // of workarounds for known buggy hardware. In other words, QPC is supposed to -// give consistent result on a multiprocessor computer, but it is unreliable in -// reality due to bugs in BIOS or HAL on some, especially old computers. -// With recent updates on HAL and newer BIOS, QPC is getting more reliable but -// it should be used with caution. +// give consistent results on a multiprocessor computer, but for older CPUs it +// can be unreliable due bugs in BIOS or HAL. // -// (3) System time. The system time provides a low-resolution (typically 10ms -// to 55 milliseconds) time stamp but is comparatively less expensive to -// retrieve and more reliable. -class HighResNowSingleton { - public: - HighResNowSingleton() - : ticks_per_second_(0), - skew_(0) { - - base::CPU cpu; - if (IsBuggyAthlon(cpu)) - return; - - // Synchronize the QPC clock with GetSystemTimeAsFileTime. - LARGE_INTEGER ticks_per_sec = {0}; - if (!QueryPerformanceFrequency(&ticks_per_sec)) - return; // QPC is not available. - ticks_per_second_ = ticks_per_sec.QuadPart; - - skew_ = UnreliableNow() - ReliableNow(); +// (3) System time. The system time provides a low-resolution (from ~1 to ~15.6 +// milliseconds) time stamp but is comparatively less expensive to retrieve and +// more reliable. Time::EnableHighResolutionTimer() and +// Time::ActivateHighResolutionTimer() can be called to alter the resolution of +// this timer; and also other Windows applications can alter it, affecting this +// one. + +using NowFunction = TimeTicks (*)(void); + +TimeTicks InitialNowFunction(); +TimeTicks InitialSystemTraceNowFunction(); + +// See "threading notes" in InitializeNowFunctionPointers() for details on how +// concurrent reads/writes to these globals has been made safe. +NowFunction g_now_function = &InitialNowFunction; +NowFunction g_system_trace_now_function = &InitialSystemTraceNowFunction; +int64 g_qpc_ticks_per_second = 0; + +// As of January 2015, use of <atomic> is forbidden in Chromium code. This is +// what std::atomic_thread_fence does on Windows on all Intel architectures when +// the memory_order argument is anything but std::memory_order_seq_cst: +#define ATOMIC_THREAD_FENCE(memory_order) _ReadWriteBarrier(); + +TimeDelta QPCValueToTimeDelta(LONGLONG qpc_value) { + // Ensure that the assignment to |g_qpc_ticks_per_second|, made in + // InitializeNowFunctionPointers(), has happened by this point. + ATOMIC_THREAD_FENCE(memory_order_acquire); + + DCHECK_GT(g_qpc_ticks_per_second, 0); + + // If the QPC Value is below the overflow threshold, we proceed with + // simple multiply and divide. + if (qpc_value < Time::kQPCOverflowThreshold) { + return TimeDelta::FromMicroseconds( + qpc_value * Time::kMicrosecondsPerSecond / g_qpc_ticks_per_second); } + // Otherwise, calculate microseconds in a round about manner to avoid + // overflow and precision issues. + int64 whole_seconds = qpc_value / g_qpc_ticks_per_second; + int64 leftover_ticks = qpc_value - (whole_seconds * g_qpc_ticks_per_second); + return TimeDelta::FromMicroseconds( + (whole_seconds * Time::kMicrosecondsPerSecond) + + ((leftover_ticks * Time::kMicrosecondsPerSecond) / + g_qpc_ticks_per_second)); +} - bool IsUsingHighResClock() { - return ticks_per_second_ != 0; - } - - TimeDelta Now() { - if (IsUsingHighResClock()) - return TimeDelta::FromMicroseconds(UnreliableNow()); - - // Just fallback to the slower clock. - return RolloverProtectedNow(); - } - - int64 GetQPCDriftMicroseconds() { - if (!IsUsingHighResClock()) - return 0; - return abs((UnreliableNow() - ReliableNow()) - skew_); - } - - int64 QPCValueToMicroseconds(LONGLONG qpc_value) { - if (!ticks_per_second_) - return 0; - // If the QPC Value is below the overflow threshold, we proceed with - // simple multiply and divide. - if (qpc_value < Time::kQPCOverflowThreshold) - return qpc_value * Time::kMicrosecondsPerSecond / ticks_per_second_; - // Otherwise, calculate microseconds in a round about manner to avoid - // overflow and precision issues. - int64 whole_seconds = qpc_value / ticks_per_second_; - int64 leftover_ticks = qpc_value - (whole_seconds * ticks_per_second_); - int64 microseconds = (whole_seconds * Time::kMicrosecondsPerSecond) + - ((leftover_ticks * Time::kMicrosecondsPerSecond) / - ticks_per_second_); - return microseconds; - } - - private: - // Get the number of microseconds since boot in an unreliable fashion. - int64 UnreliableNow() { - LARGE_INTEGER now; - QueryPerformanceCounter(&now); - return QPCValueToMicroseconds(now.QuadPart); - } - - // Get the number of microseconds since boot in a reliable fashion. - int64 ReliableNow() { - return RolloverProtectedNow().InMicroseconds(); - } - - int64 ticks_per_second_; // 0 indicates QPF failed and we're broken. - int64 skew_; // Skew between lo-res and hi-res clocks (for debugging). -}; - -static base::LazyInstance<HighResNowSingleton>::Leaky - leaky_high_res_now_singleton = LAZY_INSTANCE_INITIALIZER; - -HighResNowSingleton* GetHighResNowSingleton() { - return leaky_high_res_now_singleton.Pointer(); +TimeTicks QPCNow() { + LARGE_INTEGER now; + QueryPerformanceCounter(&now); + return TimeTicks() + QPCValueToTimeDelta(now.QuadPart); } -TimeDelta HighResNowWrapper() { - return GetHighResNowSingleton()->Now(); +bool IsBuggyAthlon(const base::CPU& cpu) { + // On Athlon X2 CPUs (e.g. model 15) QueryPerformanceCounter is unreliable. + return cpu.vendor_name() == "AuthenticAMD" && cpu.family() == 15; } -typedef TimeDelta (*NowFunction)(void); +void InitializeNowFunctionPointers() { + LARGE_INTEGER ticks_per_sec = {0}; + if (!QueryPerformanceFrequency(&ticks_per_sec)) + ticks_per_sec.QuadPart = 0; -bool CPUReliablySupportsHighResTime() { + // If Windows cannot provide a QPC implementation, both Now() and + // NowFromSystemTraceTime() must use the low-resolution clock. + // + // If the QPC implementation is expensive and/or unreliable, Now() will use + // the low-resolution clock, but NowFromSystemTraceTime() will use the QPC (in + // the hope that it is still useful for tracing purposes). A CPU lacking a + // non-stop time counter will cause Windows to provide an alternate QPC + // implementation that works, but is expensive to use. Certain Athlon CPUs are + // known to make the QPC implementation unreliable. + // + // Otherwise, both Now functions can use the high-resolution QPC clock. As of + // 4 January 2015, ~68% of users fall within this category. + NowFunction now_function; + NowFunction system_trace_now_function; base::CPU cpu; - if (!cpu.has_non_stop_time_stamp_counter() || - !GetHighResNowSingleton()->IsUsingHighResClock()) - return false; - - if (IsBuggyAthlon(cpu)) - return false; + if (ticks_per_sec.QuadPart <= 0) { + now_function = system_trace_now_function = &RolloverProtectedNow; + } else if (!cpu.has_non_stop_time_stamp_counter() || IsBuggyAthlon(cpu)) { + now_function = &RolloverProtectedNow; + system_trace_now_function = &QPCNow; + } else { + now_function = system_trace_now_function = &QPCNow; + } - return true; + // Threading note 1: In an unlikely race condition, it's possible for two or + // more threads to enter InitializeNowFunctionPointers() in parallel. This is + // not a problem since all threads should end up writing out the same values + // to the global variables. + // + // Threading note 2: A release fence is placed here to ensure, from the + // perspective of other threads using the function pointers, that the + // assignment to |g_qpc_ticks_per_second| happens before the function pointers + // are changed. + g_qpc_ticks_per_second = ticks_per_sec.QuadPart; + ATOMIC_THREAD_FENCE(memory_order_release); + g_now_function = now_function; + g_system_trace_now_function = system_trace_now_function; } -TimeDelta InitialNowFunction(); - -volatile NowFunction now_function = InitialNowFunction; +TimeTicks InitialNowFunction() { + InitializeNowFunctionPointers(); + return g_now_function(); +} -TimeDelta InitialNowFunction() { - if (!CPUReliablySupportsHighResTime()) { - InterlockedExchangePointer( - reinterpret_cast<void* volatile*>(&now_function), - &RolloverProtectedNow); - return RolloverProtectedNow(); - } - InterlockedExchangePointer( - reinterpret_cast<void* volatile*>(&now_function), - &HighResNowWrapper); - return HighResNowWrapper(); +TimeTicks InitialSystemTraceNowFunction() { + InitializeNowFunctionPointers(); + return g_system_trace_now_function(); } } // namespace @@ -491,27 +483,24 @@ TimeDelta InitialNowFunction() { // static TimeTicks::TickFunctionType TimeTicks::SetMockTickFunction( TickFunctionType ticker) { - base::AutoLock locked(rollover_lock); - TickFunctionType old = tick_function; - tick_function = ticker; - rollover_ms = 0; - last_seen_now = 0; + base::AutoLock locked(g_rollover_lock); + TickFunctionType old = g_tick_function; + g_tick_function = ticker; + g_rollover_ms = 0; + g_last_seen_now = 0; return old; } // static TimeTicks TimeTicks::Now() { - return TimeTicks() + now_function(); + return g_now_function(); } // static -TimeTicks TimeTicks::HighResNow() { - return TimeTicks() + HighResNowWrapper(); -} - -// static -bool TimeTicks::IsHighResNowFastAndReliable() { - return CPUReliablySupportsHighResTime(); +bool TimeTicks::IsHighResolution() { + if (g_now_function == &InitialNowFunction) + InitializeNowFunctionPointers(); + return g_now_function == &QPCNow; } // static @@ -522,35 +511,17 @@ TimeTicks TimeTicks::ThreadNow() { // static TimeTicks TimeTicks::NowFromSystemTraceTime() { - return HighResNow(); -} - -// static -int64 TimeTicks::GetQPCDriftMicroseconds() { - return GetHighResNowSingleton()->GetQPCDriftMicroseconds(); + return g_system_trace_now_function(); } // static TimeTicks TimeTicks::FromQPCValue(LONGLONG qpc_value) { - return TimeTicks(GetHighResNowSingleton()->QPCValueToMicroseconds(qpc_value)); -} - -// static -bool TimeTicks::IsHighResClockWorking() { - return GetHighResNowSingleton()->IsUsingHighResClock(); -} - -TimeTicks TimeTicks::UnprotectedNow() { - if (now_function == HighResNowWrapper) { - return Now(); - } else { - return TimeTicks() + TimeDelta::FromMilliseconds(timeGetTime()); - } + return TimeTicks() + QPCValueToTimeDelta(qpc_value); } // TimeDelta ------------------------------------------------------------------ // static TimeDelta TimeDelta::FromQPCValue(LONGLONG qpc_value) { - return TimeDelta(GetHighResNowSingleton()->QPCValueToMicroseconds(qpc_value)); + return QPCValueToTimeDelta(qpc_value); } diff --git a/chromium/base/time/time_win_unittest.cc b/chromium/base/time/time_win_unittest.cc index 058dfd79d14..82be8c5254d 100644 --- a/chromium/base/time/time_win_unittest.cc +++ b/chromium/base/time/time_win_unittest.cc @@ -6,6 +6,10 @@ #include <mmsystem.h> #include <process.h> +#include <cmath> +#include <limits> +#include <vector> + #include "base/threading/platform_thread.h" #include "base/time/time.h" #include "testing/gtest/include/gtest/gtest.h" @@ -112,9 +116,9 @@ TEST(TimeTicks, WinRollover) { } TEST(TimeTicks, SubMillisecondTimers) { - // HighResNow doesn't work on some systems. Since the product still works - // even if it doesn't work, it makes this entire test questionable. - if (!TimeTicks::IsHighResClockWorking()) + // IsHighResolution() is false on some systems. Since the product still works + // even if it's false, it makes this entire test questionable. + if (!TimeTicks::IsHighResolution()) return; const int kRetries = 1000; @@ -122,11 +126,11 @@ TEST(TimeTicks, SubMillisecondTimers) { // Run kRetries attempts to see a sub-millisecond timer. for (int index = 0; index < kRetries; index++) { - TimeTicks last_time = TimeTicks::HighResNow(); + TimeTicks last_time = TimeTicks::Now(); TimeDelta delta; // Spin until the clock has detected a change. do { - delta = TimeTicks::HighResNow() - last_time; + delta = TimeTicks::Now() - last_time; } while (delta.InMicroseconds() == 0); if (delta.InMicroseconds() < 1000) { saw_submillisecond_timer = true; @@ -183,16 +187,16 @@ TEST(TimeTicks, TimerPerformance) { TestCase cases[] = { { reinterpret_cast<TestFunc>(Time::Now), "Time::Now" }, { TimeTicks::Now, "TimeTicks::Now" }, - { TimeTicks::HighResNow, "TimeTicks::HighResNow" }, + { TimeTicks::NowFromSystemTraceTime, "TimeTicks::NowFromSystemTraceTime" }, { NULL, "" } }; int test_case = 0; while (cases[test_case].func) { - TimeTicks start = TimeTicks::HighResNow(); + TimeTicks start = TimeTicks::Now(); for (int index = 0; index < kLoops; index++) cases[test_case].func(); - TimeTicks stop = TimeTicks::HighResNow(); + TimeTicks stop = TimeTicks::Now(); // Turning off the check for acceptible delays. Without this check, // the test really doesn't do much other than measure. But the // measurements are still useful for testing timers on various platforms. @@ -207,65 +211,57 @@ TEST(TimeTicks, TimerPerformance) { } } -// http://crbug.com/396384 -TEST(TimeTicks, DISABLED_Drift) { - // If QPC is disabled, this isn't measuring anything. - if (!TimeTicks::IsHighResClockWorking()) - return; - - const int kIterations = 100; - int64 total_drift = 0; - - for (int i = 0; i < kIterations; ++i) { - int64 drift_microseconds = TimeTicks::GetQPCDriftMicroseconds(); - - // Make sure the drift never exceeds our limit. - EXPECT_LT(drift_microseconds, 50000); - - // Sleep for a few milliseconds (note that it means 1000 microseconds). - // If we check the drift too frequently, it's going to increase - // monotonically, making our measurement less realistic. - base::PlatformThread::Sleep( - base::TimeDelta::FromMilliseconds((i % 2 == 0) ? 1 : 2)); - - total_drift += drift_microseconds; - } - - // Sanity check. We expect some time drift to occur, especially across - // the number of iterations we do. - EXPECT_LT(0, total_drift); - - printf("average time drift in microseconds: %lld\n", - total_drift / kIterations); -} - -int64 QPCValueToMicrosecondsSafely(LONGLONG qpc_value, - int64 ticks_per_second) { - int64 whole_seconds = qpc_value / ticks_per_second; - int64 leftover_ticks = qpc_value % ticks_per_second; - int64 microseconds = (whole_seconds * Time::kMicrosecondsPerSecond) + - ((leftover_ticks * Time::kMicrosecondsPerSecond) / - ticks_per_second); - return microseconds; -} - TEST(TimeTicks, FromQPCValue) { - if (!TimeTicks::IsHighResClockWorking()) + if (!TimeTicks::IsHighResolution()) return; + LARGE_INTEGER frequency; - QueryPerformanceFrequency(&frequency); - int64 ticks_per_second = frequency.QuadPart; - LONGLONG qpc_value = Time::kQPCOverflowThreshold; - TimeTicks expected_value = TimeTicks::FromInternalValue( - QPCValueToMicrosecondsSafely(qpc_value + 1, ticks_per_second)); - EXPECT_EQ(expected_value, - TimeTicks::FromQPCValue(qpc_value + 1)); - expected_value = TimeTicks::FromInternalValue( - QPCValueToMicrosecondsSafely(qpc_value, ticks_per_second)); - EXPECT_EQ(expected_value, - TimeTicks::FromQPCValue(qpc_value)); - expected_value = TimeTicks::FromInternalValue( - QPCValueToMicrosecondsSafely(qpc_value - 1, ticks_per_second)); - EXPECT_EQ(expected_value, - TimeTicks::FromQPCValue(qpc_value - 1)); + ASSERT_TRUE(QueryPerformanceFrequency(&frequency)); + const int64 ticks_per_second = frequency.QuadPart; + ASSERT_GT(ticks_per_second, 0); + + // Generate the tick values to convert, advancing the tick count by varying + // amounts. These values will ensure that both the fast and overflow-safe + // conversion logic in FromQPCValue() is tested, and across the entire range + // of possible QPC tick values. + std::vector<int64> test_cases; + test_cases.push_back(0); + const int kNumAdvancements = 100; + int64 ticks = 0; + int64 ticks_increment = 10; + for (int i = 0; i < kNumAdvancements; ++i) { + test_cases.push_back(ticks); + ticks += ticks_increment; + ticks_increment = ticks_increment * 6 / 5; + } + test_cases.push_back(Time::kQPCOverflowThreshold - 1); + test_cases.push_back(Time::kQPCOverflowThreshold); + test_cases.push_back(Time::kQPCOverflowThreshold + 1); + ticks = Time::kQPCOverflowThreshold + 10; + ticks_increment = 10; + for (int i = 0; i < kNumAdvancements; ++i) { + test_cases.push_back(ticks); + ticks += ticks_increment; + ticks_increment = ticks_increment * 6 / 5; + } + test_cases.push_back(std::numeric_limits<int64>::max()); + + // Test that the conversions using FromQPCValue() match those computed here + // using simple floating-point arithmetic. The floating-point math provides + // enough precision to confirm the implementation is correct to the + // microsecond for all |test_cases| (though it would be insufficient to + // confirm many "very large" tick values which are not being tested here). + for (int64 ticks : test_cases) { + const double expected_microseconds_since_origin = + (static_cast<double>(ticks) * Time::kMicrosecondsPerSecond) / + ticks_per_second; + const TimeTicks converted_value = TimeTicks::FromQPCValue(ticks); + const double converted_microseconds_since_origin = + static_cast<double>((converted_value - TimeTicks()).InMicroseconds()); + EXPECT_NEAR(expected_microseconds_since_origin, + converted_microseconds_since_origin, + 1.0) + << "ticks=" << ticks << ", to be converted via logic path: " + << (ticks < Time::kQPCOverflowThreshold ? "FAST" : "SAFE"); + } } diff --git a/chromium/base/timer/elapsed_timer.h b/chromium/base/timer/elapsed_timer.h index 1cb9f1193d8..592858a6e50 100644 --- a/chromium/base/timer/elapsed_timer.h +++ b/chromium/base/timer/elapsed_timer.h @@ -6,7 +6,7 @@ #define BASE_TIMER_ELAPSED_TIMER_H_ #include "base/base_export.h" -#include "base/basictypes.h" +#include "base/macros.h" #include "base/time/time.h" namespace base { @@ -15,10 +15,9 @@ namespace base { class BASE_EXPORT ElapsedTimer { public: ElapsedTimer(); - virtual ~ElapsedTimer() {} // Returns the time elapsed since object construction. - virtual TimeDelta Elapsed() const; + TimeDelta Elapsed() const; private: TimeTicks begin_; diff --git a/chromium/base/timer/mock_timer.h b/chromium/base/timer/mock_timer.h index b07c9c06337..e18a5c0489b 100644 --- a/chromium/base/timer/mock_timer.h +++ b/chromium/base/timer/mock_timer.h @@ -38,4 +38,4 @@ class BASE_EXPORT MockTimer : public Timer { } // namespace base -#endif // !BASE_TIMER_MOCK_TIMER_H_ +#endif // BASE_TIMER_MOCK_TIMER_H_ diff --git a/chromium/base/timer/timer.cc b/chromium/base/timer/timer.cc index 11f73ca4290..fa6b8cd2724 100644 --- a/chromium/base/timer/timer.cc +++ b/chromium/base/timer/timer.cc @@ -163,8 +163,10 @@ void Timer::PostNewScheduledTask(TimeDelta delay) { } // Remember the thread ID that posts the first task -- this will be verified // later when the task is abandoned to detect misuse from multiple threads. - if (!thread_id_) + if (!thread_id_) { + DCHECK(GetTaskRunner()->BelongsToCurrentThread()); thread_id_ = static_cast<int>(PlatformThread::CurrentId()); + } } scoped_refptr<SingleThreadTaskRunner> Timer::GetTaskRunner() { diff --git a/chromium/base/timer/timer.h b/chromium/base/timer/timer.h index 7e2c1d41f13..1ef58a3ea0b 100644 --- a/chromium/base/timer/timer.h +++ b/chromium/base/timer/timer.h @@ -89,7 +89,8 @@ class BASE_EXPORT Timer { virtual TimeDelta GetCurrentDelay() const; // Set the task runner on which the task should be scheduled. This method can - // only be called before any tasks have been scheduled. + // only be called before any tasks have been scheduled. The task runner must + // run tasks on the same thread the timer is used on. virtual void SetTaskRunner(scoped_refptr<SingleThreadTaskRunner> task_runner); // Start the timer to run at the given |delay| from now. If the timer is @@ -259,7 +260,7 @@ class DelayTimer : protected Timer { base::Bind(method, base::Unretained(receiver)), false) {} - void Reset() { Timer::Reset(); } + void Reset() override { Timer::Reset(); } }; } // namespace base diff --git a/chromium/base/timer/timer_unittest.cc b/chromium/base/timer/timer_unittest.cc index 1cbccd1626d..7213b809b7b 100644 --- a/chromium/base/timer/timer_unittest.cc +++ b/chromium/base/timer/timer_unittest.cc @@ -189,10 +189,6 @@ void RunTest_RepeatingTimer_Cancel(base::MessageLoop::Type message_loop_type, class DelayTimerTarget { public: - DelayTimerTarget() - : signaled_(false) { - } - bool signaled() const { return signaled_; } void Signal() { @@ -201,7 +197,7 @@ class DelayTimerTarget { } private: - bool signaled_; + bool signaled_ = false; }; void RunTest_DelayTimer_NoCall(base::MessageLoop::Type message_loop_type) { diff --git a/chromium/base/trace_event/BUILD.gn b/chromium/base/trace_event/BUILD.gn new file mode 100644 index 00000000000..633ab90ca7c --- /dev/null +++ b/chromium/base/trace_event/BUILD.gn @@ -0,0 +1,107 @@ +# 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. + +source_set("trace_event") { + sources = [ + "java_heap_dump_provider_android.cc", + "java_heap_dump_provider_android.h", + "memory_allocator_dump.cc", + "memory_allocator_dump.h", + "memory_dump_manager.cc", + "memory_dump_manager.h", + "memory_dump_provider.h", + "memory_dump_request_args.h", + "memory_dump_session_state.cc", + "memory_dump_session_state.h", + "process_memory_dump.cc", + "process_memory_dump.h", + "process_memory_maps.cc", + "process_memory_maps.h", + "process_memory_maps_dump_provider.cc", + "process_memory_maps_dump_provider.h", + "process_memory_totals.cc", + "process_memory_totals.h", + "process_memory_totals_dump_provider.cc", + "process_memory_totals_dump_provider.h", + "trace_event.h", + "trace_event_android.cc", + "trace_event_argument.cc", + "trace_event_argument.h", + "trace_event_etw_export_win.cc", + "trace_event_etw_export_win.h", + "trace_event_impl.cc", + "trace_event_impl.h", + "trace_event_impl_constants.cc", + "trace_event_memory.cc", + "trace_event_memory.h", + "trace_event_synthetic_delay.cc", + "trace_event_synthetic_delay.h", + "trace_event_system_stats_monitor.cc", + "trace_event_system_stats_monitor.h", + "trace_event_win.cc", + "trace_event_win.h", + "winheap_dump_provider_win.cc", + "winheap_dump_provider_win.h", + ] + + if (is_nacl) { + sources -= [ + "process_memory_totals_dump_provider.cc", + "trace_event_system_stats_monitor.cc", + ] + } + + if (is_linux || is_android) { + sources += [ + "malloc_dump_provider.cc", + "malloc_dump_provider.h", + ] + } + + configs += [ "//base:base_implementation" ] + + deps = [ + "//base/debug", + "//base/json", + "//base/memory", + "//base/process", + "//base/third_party/dynamic_annotations", + ] + + if (is_win) { + deps += [ "//base/trace_event/etw_manifest:chrome_events_win" ] + } + + allow_circular_includes_from = [ + "//base/debug", + "//base/memory", + "//base/process", + ] + + visibility = [ "//base/*" ] +} + +source_set("trace_event_unittests") { + testonly = true + sources = [ + "java_heap_dump_provider_android_unittest.cc", + "memory_allocator_dump_unittest.cc", + "memory_dump_manager_unittest.cc", + "process_memory_maps_dump_provider_unittest.cc", + "process_memory_totals_dump_provider_unittest.cc", + "trace_event_argument_unittest.cc", + "trace_event_memory_unittest.cc", + "trace_event_synthetic_delay_unittest.cc", + "trace_event_system_stats_monitor_unittest.cc", + "trace_event_unittest.cc", + "trace_event_win_unittest.cc", + "winheap_dump_provider_win_unittest.cc", + ] + + deps = [ + "//base/test:test_support", + "//testing/gmock", + "//testing/gtest", + ] +} diff --git a/chromium/base/trace_event/OWNERS b/chromium/base/trace_event/OWNERS new file mode 100644 index 00000000000..aa1d675f75a --- /dev/null +++ b/chromium/base/trace_event/OWNERS @@ -0,0 +1,4 @@ +nduca@chromium.org +dsinclair@chromium.org +primiano@chromium.org +per-file trace_event_android.cc=wangxianzhu@chromium.org diff --git a/chromium/base/trace_event/etw_manifest/BUILD.gn b/chromium/base/trace_event/etw_manifest/BUILD.gn new file mode 100644 index 00000000000..f62e356b118 --- /dev/null +++ b/chromium/base/trace_event/etw_manifest/BUILD.gn @@ -0,0 +1,48 @@ +# 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. + +assert(is_win, "This only runs on Windows.") + +# Makes the .h/.rc files from the .man file. +action("chrome_events_win_generate") { + visibility = [ ":*" ] + script = "build/message_compiler.py" + + sources = [ + "chrome_events_win.man", + ] + + outputs = [ + "$target_gen_dir/chrome_events_win.h", + "$target_gen_dir/chrome_events_win.rc", + ] + + args = [ + # Where to put the header. + "-h", + rebase_path("$target_gen_dir", root_build_dir), + + # Where to put the .rc file. + "-r", + rebase_path("$target_gen_dir", root_build_dir), + + # Generate the user-mode code. + "-um", + rebase_path("chrome_events_win.man", root_build_dir), + ] +} + +# Compile the generated files. +source_set("chrome_events_win") { + visibility = [ + "//base/trace_event/*", + "//chrome:main_dll", + ] + + sources = get_target_outputs(":chrome_events_win_generate") + + deps = [ + ":chrome_events_win_generate", + ] +} diff --git a/chromium/base/trace_event/etw_manifest/BUILD/message_compiler.py b/chromium/base/trace_event/etw_manifest/BUILD/message_compiler.py new file mode 100644 index 00000000000..be5927d9bec --- /dev/null +++ b/chromium/base/trace_event/etw_manifest/BUILD/message_compiler.py @@ -0,0 +1,16 @@ +# 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. + +# Runs the Microsoft Message Compiler (mc.exe). This Python adapter is for the +# GN build, which can only run Python and not native binaries. + +import subprocess +import sys + +# mc writes to stderr, so this explicily redirects to stdout and eats it. +try: + subprocess.check_output(["mc.exe"] + sys.argv[1:], stderr=subprocess.STDOUT) +except subprocess.CalledProcessError as e: + print e.output + sys.exit(e.returncode) diff --git a/chromium/base/trace_event/etw_manifest/chrome_events_win.man b/chromium/base/trace_event/etw_manifest/chrome_events_win.man new file mode 100644 index 00000000000..10a8ddf16f6 --- /dev/null +++ b/chromium/base/trace_event/etw_manifest/chrome_events_win.man @@ -0,0 +1,84 @@ +<?xml version='1.0' encoding='utf-8' standalone='yes'?> +<instrumentationManifest + xmlns="http://schemas.microsoft.com/win/2004/08/events" + xmlns:win="http://manifests.microsoft.com/win/2004/08/windows/events" + xmlns:xs="http://www.w3.org/2001/XMLSchema" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://schemas.microsoft.com/win/2004/08/events eventman.xsd" + > + <instrumentation> + <events> + <provider + guid="{D2D578D9-2936-45B6-A09f-30E32715F42D}" + messageFileName="chrome.dll" + name="Chrome" + resourceFileName="chrome.dll" + symbol="CHROME" + > + <channels> + <importChannel + chid="SYSTEM" + name="System" + /> + </channels> + <templates> + <template tid="tid_chrome_event"> + <data + inType="win:AnsiString" + name="Name" + /> + <data + inType="win:AnsiString" + name="Phase" + /> + <data + inType="win:AnsiString" + name="Arg Name 1" + /> + <data + inType="win:AnsiString" + name="Arg Value 1" + /> + <data + inType="win:AnsiString" + name="Arg Name 2" + /> + <data + inType="win:AnsiString" + name="Arg Value 2" + /> + <data + inType="win:AnsiString" + name="Arg Name 3" + /> + <data + inType="win:AnsiString" + name="Arg Value 3" + /> + </template> + </templates> + <events> + <event + channel="SYSTEM" + level="win:Informational" + message="$(string.ChromeEvent.EventMessage)" + opcode="win:Info" + symbol="ChromeEvent" + template="tid_chrome_event" + value="1" + /> + </events> + </provider> + </events> + </instrumentation> + <localization xmlns="http://schemas.microsoft.com/win/2004/08/events"> + <resources culture="en-US"> + <stringTable> + <string + id="ChromeEvent.EventMessage" + value="Chrome Event: %1 (%2)" + /> + </stringTable> + </resources> + </localization> +</instrumentationManifest> diff --git a/chromium/base/trace_event/etw_manifest/etw_manifest.gyp b/chromium/base/trace_event/etw_manifest/etw_manifest.gyp new file mode 100644 index 00000000000..b2f0eb8ea11 --- /dev/null +++ b/chromium/base/trace_event/etw_manifest/etw_manifest.gyp @@ -0,0 +1,41 @@ +# 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. +{ + 'targets': [ + { + # GN version: //base/trace_event/etw_manifest/BUILD.gn + 'target_name': 'etw_manifest', + 'type': 'none', + 'toolsets': ['host', 'target'], + 'hard_dependency': 1, + 'conditions': [ + ['OS=="win"', { + 'sources': [ + 'chrome_events_win.man', + ], + 'variables': { + 'man_output_dir': '<(SHARED_INTERMEDIATE_DIR)/base/trace_event/etw_manifest', + }, + 'rules': [{ + # Rule to run the message compiler. + 'rule_name': 'message_compiler', + 'extension': 'man', + 'outputs': [ + '<(man_output_dir)/chrome_events_win.h', + '<(man_output_dir)/chrome_events_win.rc', + ], + 'action': [ + 'mc.exe', + '-h', '<(man_output_dir)', + '-r', '<(man_output_dir)/.', + '-um', + '<(RULE_INPUT_PATH)', + ], + 'message': 'Running message compiler on <(RULE_INPUT_PATH)', + }], + }], + ], + } + ] +} diff --git a/chromium/base/trace_event/java_heap_dump_provider_android.cc b/chromium/base/trace_event/java_heap_dump_provider_android.cc new file mode 100644 index 00000000000..2a84b81b8db --- /dev/null +++ b/chromium/base/trace_event/java_heap_dump_provider_android.cc @@ -0,0 +1,43 @@ +// 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/java_heap_dump_provider_android.h" + +#include "base/android/java_runtime.h" +#include "base/trace_event/process_memory_dump.h" + +namespace base { +namespace trace_event { + +// static +JavaHeapDumpProvider* JavaHeapDumpProvider::GetInstance() { + return Singleton<JavaHeapDumpProvider, + LeakySingletonTraits<JavaHeapDumpProvider>>::get(); +} + +JavaHeapDumpProvider::JavaHeapDumpProvider() { +} + +JavaHeapDumpProvider::~JavaHeapDumpProvider() { +} + +// Called at trace dump point time. Creates a snapshot with the memory counters +// for the current process. +bool JavaHeapDumpProvider::OnMemoryDump(ProcessMemoryDump* pmd) { + MemoryAllocatorDump* dump = pmd->CreateAllocatorDump("java_heap"); + + // These numbers come from java.lang.Runtime stats. + long total_heap_size = 0; + long free_heap_size = 0; + android::JavaRuntime::GetMemoryUsage(&total_heap_size, &free_heap_size); + dump->AddScalar(MemoryAllocatorDump::kNameOuterSize, + MemoryAllocatorDump::kUnitsBytes, total_heap_size); + dump->AddScalar(MemoryAllocatorDump::kNameInnerSize, + MemoryAllocatorDump::kUnitsBytes, + total_heap_size - free_heap_size); + return true; +} + +} // namespace trace_event +} // namespace base diff --git a/chromium/base/trace_event/java_heap_dump_provider_android.h b/chromium/base/trace_event/java_heap_dump_provider_android.h new file mode 100644 index 00000000000..2f31047b50b --- /dev/null +++ b/chromium/base/trace_event/java_heap_dump_provider_android.h @@ -0,0 +1,34 @@ +// 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_JAVA_HEAP_DUMP_PROVIDER_ANDROID_H_ +#define BASE_TRACE_EVENT_JAVA_HEAP_DUMP_PROVIDER_ANDROID_H_ + +#include "base/memory/singleton.h" +#include "base/trace_event/memory_dump_provider.h" + +namespace base { +namespace trace_event { + +// Dump provider which collects process-wide memory stats. +class BASE_EXPORT JavaHeapDumpProvider : public MemoryDumpProvider { + public: + static JavaHeapDumpProvider* GetInstance(); + + // MemoryDumpProvider implementation. + bool OnMemoryDump(ProcessMemoryDump* pmd) override; + + private: + friend struct DefaultSingletonTraits<JavaHeapDumpProvider>; + + JavaHeapDumpProvider(); + ~JavaHeapDumpProvider() override; + + DISALLOW_COPY_AND_ASSIGN(JavaHeapDumpProvider); +}; + +} // namespace trace_event +} // namespace base + +#endif // BASE_TRACE_EVENT_JAVA_HEAP_DUMP_PROVIDER_ANDROID_H_ diff --git a/chromium/base/trace_event/java_heap_dump_provider_android_unittest.cc b/chromium/base/trace_event/java_heap_dump_provider_android_unittest.cc new file mode 100644 index 00000000000..bbefba518d0 --- /dev/null +++ b/chromium/base/trace_event/java_heap_dump_provider_android_unittest.cc @@ -0,0 +1,21 @@ +// 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/java_heap_dump_provider_android.h" + +#include "base/trace_event/process_memory_dump.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { +namespace trace_event { + +TEST(JavaHeapDumpProviderTest, JavaHeapDump) { + auto jhdp = JavaHeapDumpProvider::GetInstance(); + scoped_ptr<ProcessMemoryDump> pmd(new ProcessMemoryDump(nullptr)); + + jhdp->OnMemoryDump(pmd.get()); +} + +} // namespace trace_event +} // namespace base diff --git a/chromium/base/trace_event/malloc_dump_provider.cc b/chromium/base/trace_event/malloc_dump_provider.cc new file mode 100644 index 00000000000..c04b8588c27 --- /dev/null +++ b/chromium/base/trace_event/malloc_dump_provider.cc @@ -0,0 +1,52 @@ +// 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/malloc_dump_provider.h" + +#include <malloc.h> + +#include "base/trace_event/process_memory_dump.h" + +namespace base { +namespace trace_event { + +// static +MallocDumpProvider* MallocDumpProvider::GetInstance() { + return Singleton<MallocDumpProvider, + LeakySingletonTraits<MallocDumpProvider>>::get(); +} + +MallocDumpProvider::MallocDumpProvider() { +} + +MallocDumpProvider::~MallocDumpProvider() { +} + +// Called at trace dump point time. Creates a snapshot the memory counters for +// the current process. +bool MallocDumpProvider::OnMemoryDump(ProcessMemoryDump* pmd) { + struct mallinfo info = mallinfo(); + DCHECK_GE(info.arena + info.hblkhd, info.uordblks); + + MemoryAllocatorDump* dump = pmd->CreateAllocatorDump("malloc"); + if (!dump) + return false; + + // When the system allocator is implemented by tcmalloc, the total physical + // 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. + dump->AddScalar(MemoryAllocatorDump::kNameOuterSize, + MemoryAllocatorDump::kUnitsBytes, info.arena + info.hblkhd); + + // Total allocated space is given by |uordblks|. + dump->AddScalar(MemoryAllocatorDump::kNameInnerSize, + MemoryAllocatorDump::kUnitsBytes, info.uordblks); + + return true; +} + +} // namespace trace_event +} // namespace base diff --git a/chromium/base/trace_event/malloc_dump_provider.h b/chromium/base/trace_event/malloc_dump_provider.h new file mode 100644 index 00000000000..ec8683a2e20 --- /dev/null +++ b/chromium/base/trace_event/malloc_dump_provider.h @@ -0,0 +1,36 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_TRACE_EVENT_MALLOC_DUMP_PROVIDER_H_ +#define BASE_TRACE_EVENT_MALLOC_DUMP_PROVIDER_H_ + +#include <istream> + +#include "base/memory/singleton.h" +#include "base/trace_event/memory_dump_provider.h" + +namespace base { +namespace trace_event { + +// Dump provider which collects process-wide memory stats. +class BASE_EXPORT MallocDumpProvider : public MemoryDumpProvider { + public: + static MallocDumpProvider* GetInstance(); + + // MemoryDumpProvider implementation. + bool OnMemoryDump(ProcessMemoryDump* pmd) override; + + private: + friend struct DefaultSingletonTraits<MallocDumpProvider>; + + MallocDumpProvider(); + ~MallocDumpProvider() override; + + DISALLOW_COPY_AND_ASSIGN(MallocDumpProvider); +}; + +} // namespace trace_event +} // namespace base + +#endif // BASE_TRACE_EVENT_MALLOC_DUMP_PROVIDER_H_ diff --git a/chromium/base/trace_event/memory_allocator_dump.cc b/chromium/base/trace_event/memory_allocator_dump.cc new file mode 100644 index 00000000000..edec31b1fdb --- /dev/null +++ b/chromium/base/trace_event/memory_allocator_dump.cc @@ -0,0 +1,119 @@ +// 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_allocator_dump.h" + +#include "base/format_macros.h" +#include "base/strings/stringprintf.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_argument.h" +#include "base/values.h" + +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 + +const char MemoryAllocatorDump::kNameOuterSize[] = "outer_size"; +const char MemoryAllocatorDump::kNameInnerSize[] = "inner_size"; +const char MemoryAllocatorDump::kNameObjectsCount[] = "objects_count"; +const char MemoryAllocatorDump::kTypeScalar[] = "scalar"; +const char MemoryAllocatorDump::kTypeString[] = "string"; +const char MemoryAllocatorDump::kUnitsBytes[] = "bytes"; +const char MemoryAllocatorDump::kUnitsObjects[] = "objects"; + +MemoryAllocatorDump::MemoryAllocatorDump(const std::string& absolute_name, + ProcessMemoryDump* process_memory_dump) + : absolute_name_(absolute_name), process_memory_dump_(process_memory_dump) { + // The |absolute_name| cannot be empty. + DCHECK(!absolute_name.empty()); + + // The |absolute_name| can contain slash separator, but not leading or + // trailing ones. + DCHECK(absolute_name[0] != '/' && *absolute_name.rbegin() != '/'); + + // Dots are not allowed anywhere as the underlying base::DictionaryValue + // would treat them magically and split in sub-nodes, which is not intended. + DCHECK_EQ(std::string::npos, absolute_name.find_first_of('.')); +} + +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, + const char* units, + uint64 value) { + scoped_ptr<Value> hex_value(new StringValue(StringPrintf("%" PRIx64, value))); + Add(name, kTypeScalar, units, hex_value.Pass()); +} + +void MemoryAllocatorDump::AddString(const std::string& name, + const char* units, + const std::string& value) { + scoped_ptr<Value> str_value(new StringValue(value)); + Add(name, kTypeString, units, str_value.Pass()); +} + +void MemoryAllocatorDump::AsValueInto(TracedValue* value) const { + value->BeginDictionary(absolute_name_.c_str()); + 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->EndDictionary(); // "allocator_name/heap_subheap": { ... } +} + +} // namespace trace_event +} // namespace base diff --git a/chromium/base/trace_event/memory_allocator_dump.h b/chromium/base/trace_event/memory_allocator_dump.h new file mode 100644 index 00000000000..1bb27f952e1 --- /dev/null +++ b/chromium/base/trace_event/memory_allocator_dump.h @@ -0,0 +1,82 @@ +// 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_MEMORY_ALLOCATOR_DUMP_H_ +#define BASE_TRACE_EVENT_MEMORY_ALLOCATOR_DUMP_H_ + +#include "base/base_export.h" +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/values.h" + +namespace base { +namespace trace_event { + +class MemoryDumpManager; +class ProcessMemoryDump; +class TracedValue; + +// Data model for user-land memory allocator dumps. +class BASE_EXPORT MemoryAllocatorDump { + public: + // MemoryAllocatorDump is owned by ProcessMemoryDump. + MemoryAllocatorDump(const std::string& absolute_name, + ProcessMemoryDump* process_memory_dump); + ~MemoryAllocatorDump(); + + // 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. + static const char kUnitsBytes[]; // Unit name to represent bytes. + static const char kUnitsObjects[]; // Unit name to represent #objects. + + // 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 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; + + // Get the ProcessMemoryDump instance that owns this. + ProcessMemoryDump* process_memory_dump() const { + return process_memory_dump_; + } + + private: + const std::string absolute_name_; + ProcessMemoryDump* const process_memory_dump_; // Not owned (PMD owns this). + DictionaryValue attributes_; + + DISALLOW_COPY_AND_ASSIGN(MemoryAllocatorDump); +}; + +} // namespace trace_event +} // namespace base + +#endif // BASE_TRACE_EVENT_MEMORY_ALLOCATOR_DUMP_H_ diff --git a/chromium/base/trace_event/memory_allocator_dump_unittest.cc b/chromium/base/trace_event/memory_allocator_dump_unittest.cc new file mode 100644 index 00000000000..0b2cbdf513d --- /dev/null +++ b/chromium/base/trace_event/memory_allocator_dump_unittest.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/memory_allocator_dump.h" + +#include "base/format_macros.h" +#include "base/strings/stringprintf.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" +#include "base/trace_event/trace_event_argument.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { +namespace trace_event { + +namespace { + +class FakeMemoryAllocatorDumpProvider : public MemoryDumpProvider { + public: + bool OnMemoryDump(ProcessMemoryDump* pmd) override { + MemoryAllocatorDump* root_heap = + pmd->CreateAllocatorDump("foobar_allocator"); + + root_heap->AddScalar(MemoryAllocatorDump::kNameOuterSize, + MemoryAllocatorDump::kUnitsBytes, 4096); + root_heap->AddScalar(MemoryAllocatorDump::kNameInnerSize, + MemoryAllocatorDump::kUnitsBytes, 1000); + root_heap->AddScalar(MemoryAllocatorDump::kNameObjectsCount, + MemoryAllocatorDump::kUnitsObjects, 42); + root_heap->AddScalar("attr1", "units1", 1234); + root_heap->AddString("attr2", "units2", "string_value"); + + MemoryAllocatorDump* sub_heap = + pmd->CreateAllocatorDump("foobar_allocator/sub_heap"); + sub_heap->AddScalar(MemoryAllocatorDump::kNameOuterSize, + MemoryAllocatorDump::kUnitsBytes, 1); + sub_heap->AddScalar(MemoryAllocatorDump::kNameInnerSize, + MemoryAllocatorDump::kUnitsBytes, 2); + sub_heap->AddScalar(MemoryAllocatorDump::kNameObjectsCount, + MemoryAllocatorDump::kUnitsObjects, 3); + + pmd->CreateAllocatorDump("foobar_allocator/sub_heap/empty"); + // Leave the rest of sub heap deliberately uninitialized, to check that + // CreateAllocatorDump returns a properly zero-initialized object. + + return true; + } +}; + +void CheckAttribute(const MemoryAllocatorDump* dump, + const std::string& name, + const char* expected_type, + const char* expected_units, + const std::string& expected_value) { + const char* attr_type; + const char* attr_units; + const Value* attr_value; + std::string attr_str_value; + bool res = dump->Get(name, &attr_type, &attr_units, &attr_value); + EXPECT_TRUE(res); + if (!res) + return; + EXPECT_EQ(expected_type, std::string(attr_type)); + EXPECT_EQ(expected_units, std::string(attr_units)); + EXPECT_TRUE(attr_value->GetAsString(&attr_str_value)); + EXPECT_EQ(expected_value, attr_str_value); +} + +void CheckAttribute(const MemoryAllocatorDump* dump, + const std::string& name, + const char* expected_type, + const char* expected_units, + uint64 expected_value) { + CheckAttribute(dump, name, expected_type, expected_units, + StringPrintf("%" PRIx64, expected_value)); +} +} // namespace + +TEST(MemoryAllocatorDumpTest, DumpIntoProcessMemoryDump) { + FakeMemoryAllocatorDumpProvider fmadp; + ProcessMemoryDump pmd(make_scoped_refptr(new MemoryDumpSessionState())); + + fmadp.OnMemoryDump(&pmd); + + ASSERT_EQ(3u, pmd.allocator_dumps().size()); + + const MemoryAllocatorDump* root_heap = + pmd.GetAllocatorDump("foobar_allocator"); + ASSERT_NE(nullptr, root_heap); + EXPECT_EQ("foobar_allocator", root_heap->absolute_name()); + CheckAttribute(root_heap, MemoryAllocatorDump::kNameOuterSize, + MemoryAllocatorDump::kTypeScalar, + MemoryAllocatorDump::kUnitsBytes, 4096); + CheckAttribute(root_heap, MemoryAllocatorDump::kNameInnerSize, + MemoryAllocatorDump::kTypeScalar, + MemoryAllocatorDump::kUnitsBytes, 1000); + CheckAttribute(root_heap, MemoryAllocatorDump::kNameObjectsCount, + MemoryAllocatorDump::kTypeScalar, + MemoryAllocatorDump::kUnitsObjects, 42); + CheckAttribute(root_heap, "attr1", MemoryAllocatorDump::kTypeScalar, "units1", + 1234); + CheckAttribute(root_heap, "attr2", MemoryAllocatorDump::kTypeString, "units2", + "string_value"); + + const MemoryAllocatorDump* sub_heap = + pmd.GetAllocatorDump("foobar_allocator/sub_heap"); + ASSERT_NE(nullptr, sub_heap); + EXPECT_EQ("foobar_allocator/sub_heap", sub_heap->absolute_name()); + CheckAttribute(sub_heap, MemoryAllocatorDump::kNameOuterSize, + MemoryAllocatorDump::kTypeScalar, + MemoryAllocatorDump::kUnitsBytes, 1); + CheckAttribute(sub_heap, MemoryAllocatorDump::kNameInnerSize, + MemoryAllocatorDump::kTypeScalar, + MemoryAllocatorDump::kUnitsBytes, 2); + CheckAttribute(sub_heap, MemoryAllocatorDump::kNameObjectsCount, + MemoryAllocatorDump::kTypeScalar, + 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::kNameOuterSize, nullptr, + nullptr, nullptr)); + ASSERT_FALSE(empty_sub_heap->Get(MemoryAllocatorDump::kNameInnerSize, nullptr, + nullptr, nullptr)); + ASSERT_FALSE(empty_sub_heap->Get(MemoryAllocatorDump::kNameObjectsCount, + nullptr, nullptr, nullptr)); + + // Check that the AsValueInfo doesn't hit any DCHECK. + scoped_refptr<TracedValue> traced_value(new TracedValue()); + pmd.AsValueInto(traced_value.get()); +} + +// DEATH tests are not supported in Android / iOS. +#if !defined(NDEBUG) && !defined(OS_ANDROID) && !defined(OS_IOS) +TEST(MemoryAllocatorDumpTest, ForbidDuplicatesDeathTest) { + FakeMemoryAllocatorDumpProvider fmadp; + ProcessMemoryDump pmd(make_scoped_refptr(new MemoryDumpSessionState())); + pmd.CreateAllocatorDump("foo_allocator"); + pmd.CreateAllocatorDump("bar_allocator/heap"); + ASSERT_DEATH(pmd.CreateAllocatorDump("foo_allocator"), ""); + ASSERT_DEATH(pmd.CreateAllocatorDump("bar_allocator/heap"), ""); + ASSERT_DEATH(pmd.CreateAllocatorDump(""), ""); +} +#endif + +} // namespace trace_event +} // namespace base diff --git a/chromium/base/trace_event/memory_dump_manager.cc b/chromium/base/trace_event/memory_dump_manager.cc new file mode 100644 index 00000000000..1e4d8229aef --- /dev/null +++ b/chromium/base/trace_event/memory_dump_manager.cc @@ -0,0 +1,405 @@ +// 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_manager.h" + +#include <algorithm> + +#include "base/atomic_sequence_num.h" +#include "base/compiler_specific.h" +#include "base/trace_event/memory_dump_provider.h" +#include "base/trace_event/memory_dump_session_state.h" +#include "base/trace_event/process_memory_dump.h" +#include "base/trace_event/trace_event_argument.h" +#include "build/build_config.h" + +#if !defined(OS_NACL) +#include "base/trace_event/process_memory_totals_dump_provider.h" +#endif + +#if defined(OS_LINUX) || defined(OS_ANDROID) +#include "base/trace_event/malloc_dump_provider.h" +#include "base/trace_event/process_memory_maps_dump_provider.h" +#endif + +#if defined(OS_ANDROID) +#include "base/trace_event/java_heap_dump_provider_android.h" +#endif + +#if defined(OS_WIN) +#include "base/trace_event/winheap_dump_provider_win.h" +#endif + +namespace base { +namespace trace_event { + +namespace { + +// TODO(primiano): this should be smarter and should do something similar to +// trace event synthetic delays. +const char kTraceCategory[] = TRACE_DISABLED_BY_DEFAULT("memory-infra"); + +MemoryDumpManager* g_instance_for_testing = nullptr; +const int kDumpIntervalSeconds = 2; +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(); + } +} + +void RequestPeriodicGlobalDump() { + MemoryDumpManager::GetInstance()->RequestGlobalDump( + MemoryDumpType::PERIODIC_INTERVAL); +} + +} // namespace + +// static +const char* const MemoryDumpManager::kTraceCategoryForTesting = kTraceCategory; + +// static +MemoryDumpManager* MemoryDumpManager::GetInstance() { + if (g_instance_for_testing) + return g_instance_for_testing; + + return Singleton<MemoryDumpManager, + LeakySingletonTraits<MemoryDumpManager>>::get(); +} + +// static +void MemoryDumpManager::SetInstanceForTesting(MemoryDumpManager* instance) { + if (instance) + instance->skip_core_dumpers_auto_registration_for_testing_ = true; + g_instance_for_testing = instance; +} + +MemoryDumpManager::MemoryDumpManager() + : delegate_(nullptr), + memory_tracing_enabled_(0), + skip_core_dumpers_auto_registration_for_testing_(false) { + g_next_guid.GetNext(); // Make sure that first guid is not zero. +} + +MemoryDumpManager::~MemoryDumpManager() { + base::trace_event::TraceLog::GetInstance()->RemoveEnabledStateObserver(this); +} + +void MemoryDumpManager::Initialize() { + TRACE_EVENT0(kTraceCategory, "init"); // Add to trace-viewer category list. + trace_event::TraceLog::GetInstance()->AddEnabledStateObserver(this); + + if (skip_core_dumpers_auto_registration_for_testing_) + return; + + // Enable the core dump providers. +#if !defined(OS_NACL) + RegisterDumpProvider(ProcessMemoryTotalsDumpProvider::GetInstance()); +#endif + +#if defined(OS_LINUX) || defined(OS_ANDROID) + RegisterDumpProvider(ProcessMemoryMapsDumpProvider::GetInstance()); + RegisterDumpProvider(MallocDumpProvider::GetInstance()); +#endif + +#if defined(OS_ANDROID) + RegisterDumpProvider(JavaHeapDumpProvider::GetInstance()); +#endif + +#if defined(OS_WIN) + RegisterDumpProvider(WinHeapDumpProvider::GetInstance()); +#endif +} + +void MemoryDumpManager::SetDelegate(MemoryDumpManagerDelegate* delegate) { + AutoLock lock(lock_); + DCHECK_EQ(static_cast<MemoryDumpManagerDelegate*>(nullptr), delegate_); + delegate_ = delegate; +} + +void MemoryDumpManager::RegisterDumpProvider( + MemoryDumpProvider* mdp, + const scoped_refptr<SingleThreadTaskRunner>& task_runner) { + MemoryDumpProviderInfo mdp_info(task_runner); + AutoLock lock(lock_); + dump_providers_.insert(std::make_pair(mdp, mdp_info)); +} + +void MemoryDumpManager::RegisterDumpProvider(MemoryDumpProvider* mdp) { + RegisterDumpProvider(mdp, nullptr); +} + +void MemoryDumpManager::UnregisterDumpProvider(MemoryDumpProvider* mdp) { + AutoLock lock(lock_); + + auto it = dump_providers_.find(mdp); + if (it == 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 + // and OnMemoryDump() at the same time). + // Otherwise, it is not possible to guarantee that its unregistration is + // 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()) + << "The MemoryDumpProvider attempted to unregister itself in a racy way. " + << " 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); +} + +void MemoryDumpManager::RequestGlobalDump( + MemoryDumpType dump_type, + const MemoryDumpCallback& callback) { + // Bail out immediately if tracing is not enabled at all. + if (!UNLIKELY(subtle::NoBarrier_Load(&memory_tracing_enabled_))) + return; + + const uint64 guid = + TraceLog::GetInstance()->MangleEventId(g_next_guid.GetNext()); + + // The delegate_ is supposed to be thread safe, immutable and long lived. + // No need to keep the lock after we ensure that a delegate has been set. + MemoryDumpManagerDelegate* delegate; + { + AutoLock lock(lock_); + delegate = delegate_; + } + + if (delegate) { + // The delegate is in charge to coordinate the request among all the + // processes and call the CreateLocalDumpPoint on the local process. + MemoryDumpRequestArgs args = {guid, dump_type}; + delegate->RequestGlobalMemoryDump(args, callback); + } else if (!callback.is_null()) { + callback.Run(guid, false /* success */); + } +} + +void MemoryDumpManager::RequestGlobalDump(MemoryDumpType dump_type) { + 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. + { + 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; + } + return dump_successful; +} + +// 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. +void MemoryDumpManager::ContinueAsyncProcessDump( + MemoryDumpProvider* mdp, + scoped_refptr<ProcessMemoryDumpHolder> pmd_holder) { + bool should_finalize_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; + + // 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); + + // 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; + } // AutoLock(lock_) + + if (should_finalize_dump) + FinalizeDumpAndAddToTrace(pmd_holder); +} + +void MemoryDumpManager::OnTraceLogEnabled() { + // TODO(primiano): at this point we query TraceLog::GetCurrentCategoryFilter + // to figure out (and cache) which dumpers should be enabled or not. + // For the moment piggy back everything on the generic "memory" category. + bool enabled; + TRACE_EVENT_CATEGORY_GROUP_ENABLED(kTraceCategory, &enabled); + + 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; + return; + } + + session_state_ = new MemoryDumpSessionState(); + for (auto it = dump_providers_.begin(); it != dump_providers_.end(); ++it) + it->second.disabled = false; + + subtle::NoBarrier_Store(&memory_tracing_enabled_, 1); + + if (delegate_->IsCoordinatorProcess()) { + periodic_dump_timer_.Start(FROM_HERE, + TimeDelta::FromSeconds(kDumpIntervalSeconds), + base::Bind(&RequestPeriodicGlobalDump)); + } +} + +void MemoryDumpManager::OnTraceLogDisabled() { + AutoLock lock(lock_); + periodic_dump_timer_.Stop(); + subtle::NoBarrier_Store(&memory_tracing_enabled_, 0); + session_state_ = nullptr; +} + +MemoryDumpManager::MemoryDumpProviderInfo::MemoryDumpProviderInfo( + const scoped_refptr<SingleThreadTaskRunner>& task_runner) + : task_runner(task_runner), disabled(false) { +} +MemoryDumpManager::MemoryDumpProviderInfo::~MemoryDumpProviderInfo() { +} + +} // namespace trace_event +} // namespace base diff --git a/chromium/base/trace_event/memory_dump_manager.h b/chromium/base/trace_event/memory_dump_manager.h new file mode 100644 index 00000000000..3645ac18ba4 --- /dev/null +++ b/chromium/base/trace_event/memory_dump_manager.h @@ -0,0 +1,170 @@ +// 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_MEMORY_DUMP_MANAGER_H_ +#define BASE_TRACE_EVENT_MEMORY_DUMP_MANAGER_H_ + +#include <vector> + +#include "base/atomicops.h" +#include "base/containers/hash_tables.h" +#include "base/memory/ref_counted.h" +#include "base/memory/singleton.h" +#include "base/synchronization/lock.h" +#include "base/timer/timer.h" +#include "base/trace_event/memory_dump_request_args.h" +#include "base/trace_event/trace_event.h" + +namespace base { + +class SingleThreadTaskRunner; + +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 +// memory tracing. The main entry point for clients is represented by +// RequestDumpPoint(). The extension by Un(RegisterDumpProvider). +class BASE_EXPORT MemoryDumpManager : public TraceLog::EnabledStateObserver { + public: + static const char* const kTraceCategoryForTesting; + + static MemoryDumpManager* GetInstance(); + + // Invoked once per process to register the TraceLog observer. + void Initialize(); + + // See the lifetime and thread-safety requirements on the delegate below in + // the |MemoryDumpManagerDelegate| docstring. + void SetDelegate(MemoryDumpManagerDelegate* delegate); + + // MemoryDumpManager does NOT take memory ownership of |mdp|, which is + // expected to either be a singleton or unregister itself. + // If the optional |task_runner| argument is non-null, all the calls to the + // |mdp| will be issues on the given thread. Otherwise, the |mdp| should be + // able to handle calls on arbitrary threads. + void RegisterDumpProvider( + MemoryDumpProvider* mdp, + const scoped_refptr<SingleThreadTaskRunner>& task_runner); + void RegisterDumpProvider(MemoryDumpProvider* mdp); + void UnregisterDumpProvider(MemoryDumpProvider* mdp); + + // Requests a memory dump. The dump might happen or not depending on the + // filters and categories specified when enabling tracing. + // The optional |callback| is executed asynchronously, on an arbitrary thread, + // to notify about the completion of the global dump (i.e. after all the + // processes have dumped) and its success (true iff all the dumps were + // successful). + void RequestGlobalDump(MemoryDumpType dump_type, + const MemoryDumpCallback& callback); + + // Same as above (still asynchronous), but without callback. + void RequestGlobalDump(MemoryDumpType dump_type); + + // TraceLog::EnabledStateObserver implementation. + void OnTraceLogEnabled() override; + void OnTraceLogDisabled() override; + + // Returns the MemoryDumpSessionState object, which is shared by all the + // ProcessMemoryDump and MemoryAllocatorDump instances through all the tracing + // session lifetime. + const scoped_refptr<MemoryDumpSessionState>& session_state() const { + return session_state_; + } + + 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; + + static void SetInstanceForTesting(MemoryDumpManager* instance); + + MemoryDumpManager(); + virtual ~MemoryDumpManager(); + + // Internal, used only by MemoryDumpManagerDelegate. + // Creates a memory dump for the current process and appends it to the trace. + // |callback| will be invoked asynchronously upon completion on the same + // thread on which CreateProcessDump() was called. + void CreateProcessDump(const MemoryDumpRequestArgs& args, + const MemoryDumpCallback& callback); + + bool InvokeDumpProviderLocked(MemoryDumpProvider* mdp, + ProcessMemoryDump* pmd); + void ContinueAsyncProcessDump( + MemoryDumpProvider* mdp, + scoped_refptr<ProcessMemoryDumpHolder> pmd_holder); + + hash_map<MemoryDumpProvider*, MemoryDumpProviderInfo> dump_providers_; + + // Shared among all the PMDs to keep state scoped to the tracing session. + scoped_refptr<MemoryDumpSessionState> session_state_; + + MemoryDumpManagerDelegate* delegate_; // Not owned. + + // Protects from concurrent accesses to the |dump_providers_*| and |delegate_| + // to guard against disabling logging while dumping on another thread. + Lock lock_; + + // Optimization to avoid attempting any memory dump (i.e. to not walk an empty + // dump_providers_enabled_ list) when tracing is not enabled. + subtle::AtomicWord memory_tracing_enabled_; + + // For time-triggered periodic dumps. + RepeatingTimer<MemoryDumpManager> periodic_dump_timer_; + + // Skips the auto-registration of the core dumpers during Initialize(). + bool skip_core_dumpers_auto_registration_for_testing_; + + DISALLOW_COPY_AND_ASSIGN(MemoryDumpManager); +}; + +// The delegate is supposed to be long lived (read: a Singleton) and thread +// safe (i.e. should expect calls from any thread and handle thread hopping). +class BASE_EXPORT MemoryDumpManagerDelegate { + public: + virtual void RequestGlobalMemoryDump(const MemoryDumpRequestArgs& args, + const MemoryDumpCallback& callback) = 0; + + // Determines whether the MemoryDumpManager instance should be the master + // (the ones which initiates and coordinates the multiprocess dumps) or not. + virtual bool IsCoordinatorProcess() const = 0; + + protected: + MemoryDumpManagerDelegate() {} + virtual ~MemoryDumpManagerDelegate() {} + + void CreateProcessDump(const MemoryDumpRequestArgs& args, + const MemoryDumpCallback& callback) { + MemoryDumpManager::GetInstance()->CreateProcessDump(args, callback); + } + + private: + DISALLOW_COPY_AND_ASSIGN(MemoryDumpManagerDelegate); +}; + +} // namespace trace_event +} // namespace base + +#endif // BASE_TRACE_EVENT_MEMORY_DUMP_MANAGER_H_ diff --git a/chromium/base/trace_event/memory_dump_manager_unittest.cc b/chromium/base/trace_event/memory_dump_manager_unittest.cc new file mode 100644 index 00000000000..1da9429588e --- /dev/null +++ b/chromium/base/trace_event/memory_dump_manager_unittest.cc @@ -0,0 +1,276 @@ +// 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_manager.h" + +#include "base/bind_helpers.h" +#include "base/memory/scoped_vector.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "base/threading/thread.h" +#include "base/trace_event/memory_dump_provider.h" +#include "base/trace_event/process_memory_dump.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; +using testing::Invoke; +using testing::Return; + +namespace base { +namespace trace_event { + +// Testing MemoryDumpManagerDelegate which short-circuits dump requests locally +// instead of performing IPC dances. +class MemoryDumpManagerDelegateForTesting : public MemoryDumpManagerDelegate { + public: + void RequestGlobalMemoryDump( + const base::trace_event::MemoryDumpRequestArgs& args, + const MemoryDumpCallback& callback) override { + CreateProcessDump(args, callback); + } + + bool IsCoordinatorProcess() const override { return false; } +}; + +class MemoryDumpManagerTest : public testing::Test { + public: + void SetUp() override { + message_loop_.reset(new MessageLoop()); + mdm_.reset(new MemoryDumpManager()); + MemoryDumpManager::SetInstanceForTesting(mdm_.get()); + ASSERT_EQ(mdm_, MemoryDumpManager::GetInstance()); + MemoryDumpManager::GetInstance()->Initialize(); + MemoryDumpManager::GetInstance()->SetDelegate(&delegate_); + } + + void TearDown() override { + MemoryDumpManager::SetInstanceForTesting(nullptr); + mdm_.reset(); + message_loop_.reset(); + TraceLog::DeleteForTesting(); + } + + void DumpCallbackAdapter(scoped_refptr<SingleThreadTaskRunner> task_runner, + Closure closure, + uint64 dump_guid, + bool success) { + task_runner->PostTask(FROM_HERE, closure); + } + + protected: + const char* kTraceCategory = MemoryDumpManager::kTraceCategoryForTesting; + + void EnableTracing(const char* category) { + TraceLog::GetInstance()->SetEnabled( + CategoryFilter(category), TraceLog::RECORDING_MODE, TraceOptions()); + } + + void DisableTracing() { TraceLog::GetInstance()->SetDisabled(); } + + scoped_ptr<MemoryDumpManager> mdm_; + + private: + scoped_ptr<MessageLoop> message_loop_; + MemoryDumpManagerDelegateForTesting delegate_; + + // We want our singleton torn down after each test. + ShadowingAtExitManager at_exit_manager_; +}; + +class MockDumpProvider : public MemoryDumpProvider { + public: + MockDumpProvider() : last_session_state_(nullptr) {} + + // Ctor used by the RespectTaskRunnerAffinity test. + explicit MockDumpProvider( + const scoped_refptr<SingleThreadTaskRunner>& task_runner) + : last_session_state_(nullptr), task_runner_(task_runner) {} + + virtual ~MockDumpProvider() {} + + MOCK_METHOD1(OnMemoryDump, bool(ProcessMemoryDump* pmd)); + + // OnMemoryDump() override for the RespectTaskRunnerAffinity test. + bool OnMemoryDump_CheckTaskRunner(ProcessMemoryDump* pmd) { + EXPECT_TRUE(task_runner_->RunsTasksOnCurrentThread()); + return true; + } + + // OnMemoryDump() override for the SharedSessionState test. + bool OnMemoryDump_CheckSessionState(ProcessMemoryDump* pmd) { + MemoryDumpSessionState* cur_session_state = pmd->session_state().get(); + if (last_session_state_) + EXPECT_EQ(last_session_state_, cur_session_state); + last_session_state_ = cur_session_state; + return true; + } + + private: + MemoryDumpSessionState* last_session_state_; + scoped_refptr<SingleThreadTaskRunner> task_runner_; +}; + +TEST_F(MemoryDumpManagerTest, SingleDumper) { + MockDumpProvider mdp; + mdm_->RegisterDumpProvider(&mdp); + + // Check that the dumper is not called if the memory category is not enabled. + EnableTracing("foo-and-bar-but-not-memory"); + EXPECT_CALL(mdp, OnMemoryDump(_)).Times(0); + mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED); + DisableTracing(); + + // Now repeat enabling the memory category and check that the dumper is + // invoked this time. + EnableTracing(kTraceCategory); + EXPECT_CALL(mdp, OnMemoryDump(_)).Times(3).WillRepeatedly(Return(true)); + for (int i = 0; i < 3; ++i) + mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED); + DisableTracing(); + + mdm_->UnregisterDumpProvider(&mdp); + + // Finally check the unregister logic (no calls to the mdp after unregister). + EnableTracing(kTraceCategory); + EXPECT_CALL(mdp, OnMemoryDump(_)).Times(0); + mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED); + TraceLog::GetInstance()->SetDisabled(); +} + +TEST_F(MemoryDumpManagerTest, SharedSessionState) { + MockDumpProvider mdp1; + MockDumpProvider mdp2; + mdm_->RegisterDumpProvider(&mdp1); + mdm_->RegisterDumpProvider(&mdp2); + + EnableTracing(kTraceCategory); + EXPECT_CALL(mdp1, OnMemoryDump(_)) + .Times(2) + .WillRepeatedly( + Invoke(&mdp1, &MockDumpProvider::OnMemoryDump_CheckSessionState)); + EXPECT_CALL(mdp2, OnMemoryDump(_)) + .Times(2) + .WillRepeatedly( + Invoke(&mdp2, &MockDumpProvider::OnMemoryDump_CheckSessionState)); + + for (int i = 0; i < 2; ++i) + mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED); + + DisableTracing(); +} + +TEST_F(MemoryDumpManagerTest, MultipleDumpers) { + MockDumpProvider mdp1; + MockDumpProvider mdp2; + + // Enable only mdp1. + mdm_->RegisterDumpProvider(&mdp1); + EnableTracing(kTraceCategory); + EXPECT_CALL(mdp1, OnMemoryDump(_)).Times(1).WillRepeatedly(Return(true)); + EXPECT_CALL(mdp2, OnMemoryDump(_)).Times(0); + mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED); + DisableTracing(); + + // Invert: enable mdp1 and disable mdp2. + mdm_->UnregisterDumpProvider(&mdp1); + mdm_->RegisterDumpProvider(&mdp2); + EnableTracing(kTraceCategory); + EXPECT_CALL(mdp1, OnMemoryDump(_)).Times(0); + EXPECT_CALL(mdp2, OnMemoryDump(_)).Times(1).WillRepeatedly(Return(true)); + mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED); + DisableTracing(); + + // Enable both mdp1 and mdp2. + mdm_->RegisterDumpProvider(&mdp1); + EnableTracing(kTraceCategory); + EXPECT_CALL(mdp1, OnMemoryDump(_)).Times(1).WillRepeatedly(Return(true)); + EXPECT_CALL(mdp2, OnMemoryDump(_)).Times(1).WillRepeatedly(Return(true)); + mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED); + DisableTracing(); +} + +// Checks that the MemoryDumpManager respects the thread affinity when a +// MemoryDumpProvider specifies a task_runner(). The test starts creating 8 +// threads and registering a MemoryDumpProvider on each of them. At each +// iteration, one thread is removed, to check the live unregistration logic. +TEST_F(MemoryDumpManagerTest, RespectTaskRunnerAffinity) { + const uint32 kNumInitialThreads = 8; + + ScopedVector<Thread> threads; + ScopedVector<MockDumpProvider> mdps; + + // Create the threads and setup the expectations. Given that at each iteration + // we will pop out one thread/MemoryDumpProvider, each MDP is supposed to be + // invoked a number of times equal to its index. + for (uint32 i = kNumInitialThreads; i > 0; --i) { + threads.push_back(new Thread("test thread")); + threads.back()->Start(); + mdps.push_back(new MockDumpProvider(threads.back()->task_runner())); + MockDumpProvider* mdp = mdps.back(); + mdm_->RegisterDumpProvider(mdp, threads.back()->task_runner()); + EXPECT_CALL(*mdp, OnMemoryDump(_)) + .Times(i) + .WillRepeatedly( + Invoke(mdp, &MockDumpProvider::OnMemoryDump_CheckTaskRunner)); + } + + EnableTracing(kTraceCategory); + + while (!threads.empty()) { + { + RunLoop run_loop; + MemoryDumpCallback callback = + Bind(&MemoryDumpManagerTest::DumpCallbackAdapter, Unretained(this), + MessageLoop::current()->task_runner(), run_loop.QuitClosure()); + mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED, callback); + // This nested message loop (|run_loop|) will be quit if and only if + // the RequestGlobalDump callback is invoked. + run_loop.Run(); + } + + // Unregister a MDP and destroy one thread at each iteration to check the + // live unregistration logic. The unregistration needs to happen on the same + // thread the MDP belongs to. + { + RunLoop run_loop; + Closure unregistration = + Bind(&MemoryDumpManager::UnregisterDumpProvider, + Unretained(mdm_.get()), Unretained(mdps.back())); + threads.back()->task_runner()->PostTaskAndReply(FROM_HERE, unregistration, + run_loop.QuitClosure()); + run_loop.Run(); + } + mdps.pop_back(); + threads.back()->Stop(); + threads.pop_back(); + } + + DisableTracing(); +} + +// Enable both dump providers, make mdp1 fail and assert that only mdp2 is +// invoked the 2nd time. +// FIXME(primiano): remove once crbug.com/461788 gets fixed. +TEST_F(MemoryDumpManagerTest, DisableFailingDumpers) { + MockDumpProvider mdp1; + MockDumpProvider mdp2; + + mdm_->RegisterDumpProvider(&mdp1); + mdm_->RegisterDumpProvider(&mdp2); + EnableTracing(kTraceCategory); + + EXPECT_CALL(mdp1, 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(0); + EXPECT_CALL(mdp2, OnMemoryDump(_)).Times(1).WillRepeatedly(Return(false)); + mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED); + + DisableTracing(); +} + +} // namespace trace_event +} // namespace base diff --git a/chromium/base/trace_event/memory_dump_provider.h b/chromium/base/trace_event/memory_dump_provider.h new file mode 100644 index 00000000000..6e6551cc039 --- /dev/null +++ b/chromium/base/trace_event/memory_dump_provider.h @@ -0,0 +1,36 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_TRACE_EVENT_MEMORY_DUMP_PROVIDER_H_ +#define BASE_TRACE_EVENT_MEMORY_DUMP_PROVIDER_H_ + +#include "base/base_export.h" +#include "base/macros.h" + +namespace base { +namespace trace_event { + +class ProcessMemoryDump; + +// The contract interface that memory dump providers must implement. +class BASE_EXPORT MemoryDumpProvider { + public: + // Called by the MemoryDumpManager when generating memory dumps. + // The embedder should return true if the |pmd| was successfully populated, + // false if something went wrong and the dump should be considered invalid. + // (Note, the MemoryDumpManager has a fail-safe logic which will disable the + // MemoryDumpProvider for the entire trace session if it fails consistently). + virtual bool OnMemoryDump(ProcessMemoryDump* pmd) = 0; + + protected: + MemoryDumpProvider() {} + virtual ~MemoryDumpProvider() {} + + DISALLOW_COPY_AND_ASSIGN(MemoryDumpProvider); +}; + +} // namespace trace_event +} // namespace base + +#endif // BASE_TRACE_EVENT_MEMORY_DUMP_PROVIDER_H_ diff --git a/chromium/base/trace_event/memory_dump_request_args.h b/chromium/base/trace_event/memory_dump_request_args.h new file mode 100644 index 00000000000..4d3763acf16 --- /dev/null +++ b/chromium/base/trace_event/memory_dump_request_args.h @@ -0,0 +1,41 @@ +// 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_MEMORY_DUMP_REQUEST_ARGS_H_ +#define BASE_TRACE_EVENT_MEMORY_DUMP_REQUEST_ARGS_H_ + +// This file defines the types and structs used to issue memory dump requests. +// These are also used in the IPCs for coordinating inter-process memory dumps. + +#include "base/base_export.h" +#include "base/callback.h" + +namespace base { +namespace trace_event { + +// Captures the reason why a memory dump is being requested. This is to allow +// selective enabling of dumps, filtering and post-processing. +enum class MemoryDumpType { + 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. +}; + +using MemoryDumpCallback = Callback<void(uint64 dump_guid, bool success)>; + +struct BASE_EXPORT MemoryDumpRequestArgs { + // Globally unique identifier. In multi-process dumps, all processes issue a + // local dump with the same guid. This allows the trace importers to + // reconstruct the global dump. + uint64 dump_guid; + + MemoryDumpType dump_type; +}; + +} // namespace trace_event +} // namespace base + +#endif // BASE_TRACE_EVENT_MEMORY_DUMP_REQUEST_ARGS_H_ diff --git a/chromium/base/trace_event/memory_dump_session_state.cc b/chromium/base/trace_event/memory_dump_session_state.cc new file mode 100644 index 00000000000..433ac14cbfa --- /dev/null +++ b/chromium/base/trace_event/memory_dump_session_state.cc @@ -0,0 +1,17 @@ +// 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_session_state.h" + +namespace base { +namespace trace_event { + +MemoryDumpSessionState::MemoryDumpSessionState() { +} + +MemoryDumpSessionState::~MemoryDumpSessionState() { +} + +} // namespace trace_event +} // namespace base diff --git a/chromium/base/trace_event/memory_dump_session_state.h b/chromium/base/trace_event/memory_dump_session_state.h new file mode 100644 index 00000000000..cf29b85559c --- /dev/null +++ b/chromium/base/trace_event/memory_dump_session_state.h @@ -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. + +#ifndef BASE_TRACE_EVENT_MEMORY_DUMP_SESSION_STATE_H_ +#define BASE_TRACE_EVENT_MEMORY_DUMP_SESSION_STATE_H_ + +#include <string> + +#include "base/base_export.h" +#include "base/memory/ref_counted.h" + +namespace base { +namespace trace_event { + +// Container for state variables that should be shared across all the memory +// dumps in a tracing session. +class BASE_EXPORT MemoryDumpSessionState + : public RefCountedThreadSafe<MemoryDumpSessionState> { + public: + MemoryDumpSessionState(); + + private: + friend class RefCountedThreadSafe<MemoryDumpSessionState>; + ~MemoryDumpSessionState(); +}; + +} // namespace trace_event +} // namespace base + +#endif // BASE_TRACE_EVENT_MEMORY_DUMP_SESSION_STATE_H_ diff --git a/chromium/base/trace_event/process_memory_dump.cc b/chromium/base/trace_event/process_memory_dump.cc new file mode 100644 index 00000000000..54fcad6f231 --- /dev/null +++ b/chromium/base/trace_event/process_memory_dump.cc @@ -0,0 +1,59 @@ +// 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/process_memory_dump.h" + +#include "base/trace_event/process_memory_totals.h" +#include "base/trace_event/trace_event_argument.h" + +namespace base { +namespace trace_event { + +ProcessMemoryDump::ProcessMemoryDump( + const scoped_refptr<MemoryDumpSessionState>& session_state) + : has_process_totals_(false), + has_process_mmaps_(false), + session_state_(session_state) { +} + +ProcessMemoryDump::~ProcessMemoryDump() { +} + +MemoryAllocatorDump* ProcessMemoryDump::CreateAllocatorDump( + const std::string& absolute_name) { + MemoryAllocatorDump* mad = new MemoryAllocatorDump(absolute_name, this); + DCHECK_EQ(0ul, allocator_dumps_.count(absolute_name)); + allocator_dumps_storage_.push_back(mad); + allocator_dumps_[absolute_name] = mad; + return mad; +} + +MemoryAllocatorDump* ProcessMemoryDump::GetAllocatorDump( + const std::string& absolute_name) const { + auto it = allocator_dumps_.find(absolute_name); + return it == allocator_dumps_.end() ? nullptr : it->second; +} + +void ProcessMemoryDump::AsValueInto(TracedValue* value) const { + // Build up the [dumper name] -> [value] dictionary. + if (has_process_totals_) { + value->BeginDictionary("process_totals"); + process_totals_.AsValueInto(value); + value->EndDictionary(); + } + if (has_process_mmaps_) { + value->BeginDictionary("process_mmaps"); + process_mmaps_.AsValueInto(value); + value->EndDictionary(); + } + if (allocator_dumps_storage_.size() > 0) { + value->BeginDictionary("allocators"); + for (const MemoryAllocatorDump* allocator_dump : allocator_dumps_storage_) + allocator_dump->AsValueInto(value); + value->EndDictionary(); + } +} + +} // namespace trace_event +} // namespace base diff --git a/chromium/base/trace_event/process_memory_dump.h b/chromium/base/trace_event/process_memory_dump.h new file mode 100644 index 00000000000..889356d65aa --- /dev/null +++ b/chromium/base/trace_event/process_memory_dump.h @@ -0,0 +1,94 @@ +// 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_PROCESS_MEMORY_DUMP_H_ +#define BASE_TRACE_EVENT_PROCESS_MEMORY_DUMP_H_ + +#include "base/base_export.h" +#include "base/containers/hash_tables.h" +#include "base/containers/small_map.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_vector.h" +#include "base/trace_event/memory_allocator_dump.h" +#include "base/trace_event/memory_dump_session_state.h" +#include "base/trace_event/process_memory_maps.h" +#include "base/trace_event/process_memory_totals.h" + +namespace base { +namespace trace_event { + +class ConvertableToTraceFormat; +class MemoryDumpManager; +class MemoryDumpSessionState; + +// ProcessMemoryDump is as a strongly typed container which enforces the data +// model for each memory dump and holds the dumps produced by the +// MemoryDumpProvider(s) for a specific process. +// At trace generation time (i.e. when AsValue() is called), ProcessMemoryDump +// will compose a key-value dictionary of the various dumps obtained at trace +// dump point time. +class BASE_EXPORT ProcessMemoryDump { + public: + // Maps allocator dumps absolute names (allocator_name/heap/subheap) to + // MemoryAllocatorDump instances. + using AllocatorDumpsMap = + SmallMap<hash_map<std::string, MemoryAllocatorDump*>>; + + ProcessMemoryDump(const scoped_refptr<MemoryDumpSessionState>& session_state); + ~ProcessMemoryDump(); + + // 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; } + + // Creates a new MemoryAllocatorDump with the given name and returns the + // empty object back to the caller. + // Arguments: + // absolute_name: a name that uniquely identifies allocator dumps produced + // by this provider. It is possible to specify nesting by using a + // path-like string (e.g., v8/isolate1/heap1, v8/isolate1/heap2). + // Leading or trailing slashes are not allowed. + // ProcessMemoryDump handles the memory ownership of its MemoryAllocatorDumps. + MemoryAllocatorDump* CreateAllocatorDump(const std::string& absolute_name); + + // Looks up a MemoryAllocatorDump given its allocator and heap names, or + // nullptr if not found. + MemoryAllocatorDump* GetAllocatorDump(const std::string& absolute_name) const; + + // Returns the map of the MemoryAllocatorDumps added to this dump. + const AllocatorDumpsMap& allocator_dumps() const { return allocator_dumps_; } + + const scoped_refptr<MemoryDumpSessionState>& session_state() const { + return session_state_; + } + + private: + ProcessMemoryTotals process_totals_; + bool has_process_totals_; + + ProcessMemoryMaps process_mmaps_; + bool has_process_mmaps_; + + AllocatorDumpsMap allocator_dumps_; + + // ProcessMemoryDump handles the memory ownership of all its belongings. + ScopedVector<MemoryAllocatorDump> allocator_dumps_storage_; + + // State shared among all PMDs instances created in a given trace session. + scoped_refptr<MemoryDumpSessionState> session_state_; + + DISALLOW_COPY_AND_ASSIGN(ProcessMemoryDump); +}; + +} // namespace trace_event +} // namespace base + +#endif // BASE_TRACE_EVENT_PROCESS_MEMORY_DUMP_H_ diff --git a/chromium/base/trace_event/process_memory_maps.cc b/chromium/base/trace_event/process_memory_maps.cc new file mode 100644 index 00000000000..d553ee88e80 --- /dev/null +++ b/chromium/base/trace_event/process_memory_maps.cc @@ -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. + +#include "base/trace_event/process_memory_maps.h" + +#include "base/format_macros.h" +#include "base/strings/stringprintf.h" +#include "base/trace_event/trace_event_argument.h" + +namespace base { +namespace trace_event { + +// static +const uint32 ProcessMemoryMaps::VMRegion::kProtectionFlagsRead = 4; +const uint32 ProcessMemoryMaps::VMRegion::kProtectionFlagsWrite = 2; +const uint32 ProcessMemoryMaps::VMRegion::kProtectionFlagsExec = 1; + +ProcessMemoryMaps::ProcessMemoryMaps() { +} + +ProcessMemoryMaps::~ProcessMemoryMaps() { +} + +void ProcessMemoryMaps::AsValueInto(TracedValue* value) const { + static const char kHexFmt[] = "%" PRIx64; + + // Refer to the design doc goo.gl/sxfFY8 for the semantic of these fields. + value->BeginArray("vm_regions"); + for (const auto& region : vm_regions_) { + value->BeginDictionary(); + + value->SetString("sa", StringPrintf(kHexFmt, region.start_address)); + value->SetString("sz", StringPrintf(kHexFmt, region.size_in_bytes)); + value->SetInteger("pf", region.protection_flags); + value->SetString("mf", region.mapped_file); + + value->BeginDictionary("bs"); // byte stats + value->SetString( + "pss", StringPrintf(kHexFmt, region.byte_stats_proportional_resident)); + value->SetString("prv", + StringPrintf(kHexFmt, region.byte_stats_private_resident)); + value->SetString("shr", + StringPrintf(kHexFmt, region.byte_stats_shared_resident)); + value->EndDictionary(); + + value->EndDictionary(); + } + value->EndArray(); +} + +} // namespace trace_event +} // namespace base diff --git a/chromium/base/trace_event/process_memory_maps.h b/chromium/base/trace_event/process_memory_maps.h new file mode 100644 index 00000000000..dc1892fd622 --- /dev/null +++ b/chromium/base/trace_event/process_memory_maps.h @@ -0,0 +1,58 @@ +// 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_PROCESS_MEMORY_MAPS_H_ +#define BASE_TRACE_EVENT_PROCESS_MEMORY_MAPS_H_ + +#include <string> +#include <vector> + +#include "base/base_export.h" +#include "base/basictypes.h" + +namespace base { +namespace trace_event { + +class TracedValue; + +// Data model for process-wide memory stats. +class BASE_EXPORT ProcessMemoryMaps { + public: + struct VMRegion { + static const uint32 kProtectionFlagsRead; + static const uint32 kProtectionFlagsWrite; + static const uint32 kProtectionFlagsExec; + + uint64 start_address; + uint64 size_in_bytes; + uint32 protection_flags; + std::string mapped_file; + + // private_resident + shared_resident = resident set size. + uint64 byte_stats_private_resident; + uint64 byte_stats_shared_resident; + + // For multiprocess accounting. + uint64 byte_stats_proportional_resident; + }; + + ProcessMemoryMaps(); + ~ProcessMemoryMaps(); + + void AddVMRegion(const VMRegion& region) { vm_regions_.push_back(region); } + const std::vector<VMRegion>& vm_regions() const { return vm_regions_; } + + // Called at trace generation time to populate the TracedValue. + void AsValueInto(TracedValue* value) const; + + private: + std::vector<VMRegion> vm_regions_; + + DISALLOW_COPY_AND_ASSIGN(ProcessMemoryMaps); +}; + +} // namespace trace_event +} // namespace base + +#endif // BASE_TRACE_EVENT_PROCESS_MEMORY_MAPS_H_ diff --git a/chromium/base/trace_event/process_memory_maps_dump_provider.cc b/chromium/base/trace_event/process_memory_maps_dump_provider.cc new file mode 100644 index 00000000000..680fa29609e --- /dev/null +++ b/chromium/base/trace_event/process_memory_maps_dump_provider.cc @@ -0,0 +1,189 @@ +// 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/process_memory_maps_dump_provider.h" + +#include <cctype> +#include <fstream> + +#include "base/logging.h" +#include "base/process/process_metrics.h" +#include "base/trace_event/process_memory_dump.h" +#include "base/trace_event/process_memory_maps.h" + +namespace base { +namespace trace_event { + +#if defined(OS_LINUX) || defined(OS_ANDROID) +// static +std::istream* ProcessMemoryMapsDumpProvider::proc_smaps_for_testing = nullptr; + +namespace { + +const uint32 kMaxLineSize = 4096; + +bool ParseSmapsHeader(std::istream* smaps, + ProcessMemoryMaps::VMRegion* region) { + // e.g., "00400000-00421000 r-xp 00000000 fc:01 1234 /foo.so\n" + bool res = true; // Whether this region should be appended or skipped. + uint64 end_addr; + std::string protection_flags; + std::string ignored; + *smaps >> std::hex >> region->start_address; + smaps->ignore(1); + *smaps >> std::hex >> end_addr; + if (end_addr > region->start_address) { + region->size_in_bytes = end_addr - region->start_address; + } else { + // This is not just paranoia, it can actually happen (See crbug.com/461237). + region->size_in_bytes = 0; + res = false; + } + + region->protection_flags = 0; + *smaps >> protection_flags; + CHECK_EQ(4UL, protection_flags.size()); + if (protection_flags[0] == 'r') { + region->protection_flags |= + ProcessMemoryMaps::VMRegion::kProtectionFlagsRead; + } + if (protection_flags[1] == 'w') { + region->protection_flags |= + ProcessMemoryMaps::VMRegion::kProtectionFlagsWrite; + } + if (protection_flags[2] == 'x') { + region->protection_flags |= + ProcessMemoryMaps::VMRegion::kProtectionFlagsExec; + } + *smaps >> ignored; // Ignore mapped file offset. + *smaps >> ignored; // Ignore device maj-min (fc:01 in the example above). + *smaps >> ignored; // Ignore inode number (1234 in the example above). + + while (smaps->peek() == ' ') + smaps->ignore(1); + char mapped_file[kMaxLineSize]; + smaps->getline(mapped_file, sizeof(mapped_file)); + region->mapped_file = mapped_file; + + return res; +} + +uint64 ReadCounterBytes(std::istream* smaps) { + uint64 counter_value = 0; + *smaps >> std::dec >> counter_value; + return counter_value * 1024; +} + +uint32 ParseSmapsCounter(std::istream* smaps, + ProcessMemoryMaps::VMRegion* region) { + // A smaps counter lines looks as follows: "RSS: 0 Kb\n" + uint32 res = 0; + std::string counter_name; + *smaps >> counter_name; + + // TODO(primiano): "Swap" should also be accounted as resident. Check + // whether Rss isn't already counting swapped and fix below if that is + // the case. + if (counter_name == "Pss:") { + region->byte_stats_proportional_resident = ReadCounterBytes(smaps); + res = 1; + } else if (counter_name == "Private_Dirty:" || + counter_name == "Private_Clean:") { + // For Private and Shared counters keep the sum of the dirty + clean stats. + region->byte_stats_private_resident += ReadCounterBytes(smaps); + res = 1; + } else if (counter_name == "Shared_Dirty:" || + counter_name == "Shared_Clean:") { + region->byte_stats_shared_resident += ReadCounterBytes(smaps); + res = 1; + } + +#ifndef NDEBUG + // Paranoid check against changes of the Kernel /proc interface. + if (res) { + std::string unit; + *smaps >> unit; + DCHECK_EQ("kB", unit); + } +#endif + + smaps->ignore(kMaxLineSize, '\n'); + + return res; +} + +uint32 ReadLinuxProcSmapsFile(std::istream* smaps, ProcessMemoryMaps* pmm) { + if (!smaps->good()) + return 0; + + const uint32 kNumExpectedCountersPerRegion = 5; + uint32 counters_parsed_for_current_region = 0; + uint32 num_valid_regions = 0; + ProcessMemoryMaps::VMRegion region; + bool should_add_current_region = false; + for (;;) { + int next = smaps->peek(); + if (next == std::ifstream::traits_type::eof() || next == '\n') + break; + if (isxdigit(next) && !isupper(next)) { + region = {0}; + counters_parsed_for_current_region = 0; + should_add_current_region = ParseSmapsHeader(smaps, ®ion); + } else { + counters_parsed_for_current_region += ParseSmapsCounter(smaps, ®ion); + DCHECK_LE(counters_parsed_for_current_region, + kNumExpectedCountersPerRegion); + if (counters_parsed_for_current_region == kNumExpectedCountersPerRegion) { + if (should_add_current_region) { + pmm->AddVMRegion(region); + ++num_valid_regions; + should_add_current_region = false; + } + } + } + } + return num_valid_regions; +} + +} // namespace +#endif // defined(OS_LINUX) || defined(OS_ANDROID) + +// static +ProcessMemoryMapsDumpProvider* ProcessMemoryMapsDumpProvider::GetInstance() { + return Singleton<ProcessMemoryMapsDumpProvider, + LeakySingletonTraits<ProcessMemoryMapsDumpProvider>>::get(); +} + +ProcessMemoryMapsDumpProvider::ProcessMemoryMapsDumpProvider() { +} + +ProcessMemoryMapsDumpProvider::~ProcessMemoryMapsDumpProvider() { +} + +// Called at trace dump point time. Creates a snapshot the memory maps for the +// current process. +bool ProcessMemoryMapsDumpProvider::OnMemoryDump(ProcessMemoryDump* pmd) { + uint32 res = 0; + +#if defined(OS_LINUX) || defined(OS_ANDROID) + if (UNLIKELY(proc_smaps_for_testing)) { + res = ReadLinuxProcSmapsFile(proc_smaps_for_testing, pmd->process_mmaps()); + } else { + std::ifstream proc_self_smaps("/proc/self/smaps"); + res = ReadLinuxProcSmapsFile(&proc_self_smaps, pmd->process_mmaps()); + } +#else + LOG(ERROR) << "ProcessMemoryMaps dump provider is supported only on Linux"; +#endif + + if (res > 0) { + pmd->set_has_process_mmaps(); + return true; + } + + return false; +} + +} // namespace trace_event +} // namespace base diff --git a/chromium/base/trace_event/process_memory_maps_dump_provider.h b/chromium/base/trace_event/process_memory_maps_dump_provider.h new file mode 100644 index 00000000000..c73c4d2be66 --- /dev/null +++ b/chromium/base/trace_event/process_memory_maps_dump_provider.h @@ -0,0 +1,42 @@ +// 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_PROCESS_MEMORY_MAPS_DUMP_PROVIDER_H_ +#define BASE_TRACE_EVENT_PROCESS_MEMORY_MAPS_DUMP_PROVIDER_H_ + +#include <istream> + +#include "base/gtest_prod_util.h" +#include "base/memory/singleton.h" +#include "base/trace_event/memory_dump_provider.h" + +namespace base { +namespace trace_event { + +// Dump provider which collects process-wide memory stats. +class BASE_EXPORT ProcessMemoryMapsDumpProvider : public MemoryDumpProvider { + public: + static ProcessMemoryMapsDumpProvider* GetInstance(); + + // MemoryDumpProvider implementation. + bool OnMemoryDump(ProcessMemoryDump* pmd) override; + + private: + friend struct DefaultSingletonTraits<ProcessMemoryMapsDumpProvider>; + FRIEND_TEST_ALL_PREFIXES(ProcessMemoryMapsDumpProviderTest, ParseProcSmaps); + +#if defined(OS_LINUX) || defined(OS_ANDROID) + static std::istream* proc_smaps_for_testing; +#endif + + ProcessMemoryMapsDumpProvider(); + ~ProcessMemoryMapsDumpProvider() override; + + DISALLOW_COPY_AND_ASSIGN(ProcessMemoryMapsDumpProvider); +}; + +} // namespace trace_event +} // namespace base + +#endif // BASE_TRACE_EVENT_PROCESS_MEMORY_MAPS_DUMP_PROVIDER_H_ diff --git a/chromium/base/trace_event/process_memory_maps_dump_provider_unittest.cc b/chromium/base/trace_event/process_memory_maps_dump_provider_unittest.cc new file mode 100644 index 00000000000..e45d30a6507 --- /dev/null +++ b/chromium/base/trace_event/process_memory_maps_dump_provider_unittest.cc @@ -0,0 +1,175 @@ +// 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/process_memory_maps_dump_provider.h" + +#include <fstream> +#include <sstream> + +#include "base/trace_event/process_memory_dump.h" +#include "base/trace_event/process_memory_maps.h" +#include "base/trace_event/trace_event_argument.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { +namespace trace_event { + +#if defined(OS_LINUX) || defined(OS_ANDROID) +namespace { +const char kTestSmaps1[] = + "00400000-004be000 r-xp 00000000 fc:01 1234 /file/1\n" + "Size: 760 kB\n" + "Rss: 296 kB\n" + "Pss: 162 kB\n" + "Shared_Clean: 228 kB\n" + "Shared_Dirty: 0 kB\n" + "Private_Clean: 0 kB\n" + "Private_Dirty: 68 kB\n" + "Referenced: 296 kB\n" + "Anonymous: 68 kB\n" + "AnonHugePages: 0 kB\n" + "Swap: 0 kB\n" + "KernelPageSize: 4 kB\n" + "MMUPageSize: 4 kB\n" + "Locked: 0 kB\n" + "VmFlags: rd ex mr mw me dw sd\n" + "ff000000-ff800000 -w-p 00001080 fc:01 0 /file/name with space\n" + "Size: 0 kB\n" + "Rss: 192 kB\n" + "Pss: 128 kB\n" + "Shared_Clean: 120 kB\n" + "Shared_Dirty: 4 kB\n" + "Private_Clean: 60 kB\n" + "Private_Dirty: 8 kB\n" + "Referenced: 296 kB\n" + "Anonymous: 0 kB\n" + "AnonHugePages: 0 kB\n" + "Swap: 0 kB\n" + "KernelPageSize: 4 kB\n" + "MMUPageSize: 4 kB\n" + "Locked: 0 kB\n" + "VmFlags: rd ex mr mw me dw sd"; + +const char kTestSmaps2[] = + // An invalid region, with zero size and overlapping with the last one + // (See crbug.com/461237). + "7fe7ce79c000-7fe7ce79c000 ---p 00000000 00:00 0 \n" + "Size: 4 kB\n" + "Rss: 0 kB\n" + "Pss: 0 kB\n" + "Shared_Clean: 0 kB\n" + "Shared_Dirty: 0 kB\n" + "Private_Clean: 0 kB\n" + "Private_Dirty: 0 kB\n" + "Referenced: 0 kB\n" + "Anonymous: 0 kB\n" + "AnonHugePages: 0 kB\n" + "Swap: 0 kB\n" + "KernelPageSize: 4 kB\n" + "MMUPageSize: 4 kB\n" + "Locked: 0 kB\n" + "VmFlags: rd ex mr mw me dw sd\n" + // A invalid region with its range going backwards. + "00400000-00200000 ---p 00000000 00:00 0 \n" + "Size: 4 kB\n" + "Rss: 0 kB\n" + "Pss: 0 kB\n" + "Shared_Clean: 0 kB\n" + "Shared_Dirty: 0 kB\n" + "Private_Clean: 0 kB\n" + "Private_Dirty: 0 kB\n" + "Referenced: 0 kB\n" + "Anonymous: 0 kB\n" + "AnonHugePages: 0 kB\n" + "Swap: 0 kB\n" + "KernelPageSize: 4 kB\n" + "MMUPageSize: 4 kB\n" + "Locked: 0 kB\n" + "VmFlags: rd ex mr mw me dw sd\n" + // A good anonymous region at the end. + "7fe7ce79c000-7fe7ce7a8000 ---p 00000000 00:00 0 \n" + "Size: 48 kB\n" + "Rss: 40 kB\n" + "Pss: 32 kB\n" + "Shared_Clean: 16 kB\n" + "Shared_Dirty: 12 kB\n" + "Private_Clean: 8 kB\n" + "Private_Dirty: 4 kB\n" + "Referenced: 40 kB\n" + "Anonymous: 16 kB\n" + "AnonHugePages: 0 kB\n" + "Swap: 0 kB\n" + "KernelPageSize: 4 kB\n" + "MMUPageSize: 4 kB\n" + "Locked: 0 kB\n" + "VmFlags: rd wr mr mw me ac sd\n"; +} // namespace + +TEST(ProcessMemoryMapsDumpProviderTest, ParseProcSmaps) { + const uint32 kProtR = ProcessMemoryMaps::VMRegion::kProtectionFlagsRead; + const uint32 kProtW = ProcessMemoryMaps::VMRegion::kProtectionFlagsWrite; + const uint32 kProtX = ProcessMemoryMaps::VMRegion::kProtectionFlagsExec; + + auto pmmdp = ProcessMemoryMapsDumpProvider::GetInstance(); + + // Emulate a non-existent /proc/self/smaps. + ProcessMemoryDump pmd_invalid(nullptr /* session_state */); + std::ifstream non_existent_file("/tmp/does-not-exist"); + ProcessMemoryMapsDumpProvider::proc_smaps_for_testing = &non_existent_file; + CHECK_EQ(false, non_existent_file.good()); + pmmdp->OnMemoryDump(&pmd_invalid); + ASSERT_FALSE(pmd_invalid.has_process_mmaps()); + + // Emulate an empty /proc/self/smaps. + std::ifstream empty_file("/dev/null"); + ProcessMemoryMapsDumpProvider::proc_smaps_for_testing = &empty_file; + CHECK_EQ(true, empty_file.good()); + pmmdp->OnMemoryDump(&pmd_invalid); + ASSERT_FALSE(pmd_invalid.has_process_mmaps()); + + // Parse the 1st smaps file. + ProcessMemoryDump pmd_1(nullptr /* session_state */); + std::istringstream test_smaps_1(kTestSmaps1); + ProcessMemoryMapsDumpProvider::proc_smaps_for_testing = &test_smaps_1; + pmmdp->OnMemoryDump(&pmd_1); + ASSERT_TRUE(pmd_1.has_process_mmaps()); + const auto& regions_1 = pmd_1.process_mmaps()->vm_regions(); + ASSERT_EQ(2UL, regions_1.size()); + + EXPECT_EQ(0x00400000UL, regions_1[0].start_address); + EXPECT_EQ(0x004be000UL - 0x00400000UL, regions_1[0].size_in_bytes); + EXPECT_EQ(kProtR | kProtX, regions_1[0].protection_flags); + EXPECT_EQ("/file/1", regions_1[0].mapped_file); + EXPECT_EQ(162 * 1024UL, regions_1[0].byte_stats_proportional_resident); + EXPECT_EQ((228 + 0) * 1024UL, regions_1[0].byte_stats_shared_resident); + EXPECT_EQ((0 + 68) * 1024UL, regions_1[0].byte_stats_private_resident); + + EXPECT_EQ(0xff000000UL, regions_1[1].start_address); + EXPECT_EQ(0xff800000UL - 0xff000000UL, regions_1[1].size_in_bytes); + EXPECT_EQ(kProtW, regions_1[1].protection_flags); + EXPECT_EQ("/file/name with space", regions_1[1].mapped_file); + EXPECT_EQ(128 * 1024UL, regions_1[1].byte_stats_proportional_resident); + EXPECT_EQ((120 + 4) * 1024UL, regions_1[1].byte_stats_shared_resident); + EXPECT_EQ((60 + 8) * 1024UL, regions_1[1].byte_stats_private_resident); + + // Parse the 2nd smaps file. + ProcessMemoryDump pmd_2(nullptr /* session_state */); + std::istringstream test_smaps_2(kTestSmaps2); + ProcessMemoryMapsDumpProvider::proc_smaps_for_testing = &test_smaps_2; + pmmdp->OnMemoryDump(&pmd_2); + ASSERT_TRUE(pmd_2.has_process_mmaps()); + const auto& regions_2 = pmd_2.process_mmaps()->vm_regions(); + ASSERT_EQ(1UL, regions_2.size()); + EXPECT_EQ(0x7fe7ce79c000UL, regions_2[0].start_address); + EXPECT_EQ(0x7fe7ce7a8000UL - 0x7fe7ce79c000UL, regions_2[0].size_in_bytes); + EXPECT_EQ(0U, regions_2[0].protection_flags); + EXPECT_EQ("", regions_2[0].mapped_file); + EXPECT_EQ(32 * 1024UL, regions_2[0].byte_stats_proportional_resident); + EXPECT_EQ((16 + 12) * 1024UL, regions_2[0].byte_stats_shared_resident); + EXPECT_EQ((8 + 4) * 1024UL, regions_2[0].byte_stats_private_resident); +} +#endif // defined(OS_LINUX) || defined(OS_ANDROID) + +} // namespace trace_event +} // namespace base diff --git a/chromium/base/trace_event/process_memory_totals.cc b/chromium/base/trace_event/process_memory_totals.cc new file mode 100644 index 00000000000..9b0c3776eea --- /dev/null +++ b/chromium/base/trace_event/process_memory_totals.cc @@ -0,0 +1,20 @@ +// 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/process_memory_totals.h" + +#include "base/format_macros.h" +#include "base/strings/stringprintf.h" +#include "base/trace_event/trace_event_argument.h" + +namespace base { +namespace trace_event { + +void ProcessMemoryTotals::AsValueInto(TracedValue* value) const { + value->SetString("resident_set_bytes", + StringPrintf("%" PRIx64, resident_set_bytes_)); +} + +} // namespace trace_event +} // namespace base diff --git a/chromium/base/trace_event/process_memory_totals.h b/chromium/base/trace_event/process_memory_totals.h new file mode 100644 index 00000000000..29c94c9aacc --- /dev/null +++ b/chromium/base/trace_event/process_memory_totals.h @@ -0,0 +1,36 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_TRACE_EVENT_PROCESS_MEMORY_TOTALS_H_ +#define BASE_TRACE_EVENT_PROCESS_MEMORY_TOTALS_H_ + +#include "base/base_export.h" +#include "base/basictypes.h" + +namespace base { +namespace trace_event { + +class TracedValue; + +// Data model for process-wide memory stats. +class BASE_EXPORT ProcessMemoryTotals { + public: + ProcessMemoryTotals() : resident_set_bytes_(0) {} + + // Called at trace generation time to populate the TracedValue. + void AsValueInto(TracedValue* value) const; + + uint64 resident_set_bytes() const { return resident_set_bytes_; } + void set_resident_set_bytes(uint64 value) { resident_set_bytes_ = value; } + + private: + uint64 resident_set_bytes_; + + DISALLOW_COPY_AND_ASSIGN(ProcessMemoryTotals); +}; + +} // namespace trace_event +} // namespace base + +#endif // BASE_TRACE_EVENT_PROCESS_MEMORY_TOTALS_H_ diff --git a/chromium/base/trace_event/process_memory_totals_dump_provider.cc b/chromium/base/trace_event/process_memory_totals_dump_provider.cc new file mode 100644 index 00000000000..06b537c4188 --- /dev/null +++ b/chromium/base/trace_event/process_memory_totals_dump_provider.cc @@ -0,0 +1,60 @@ +// 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/process_memory_totals_dump_provider.h" + +#include "base/process/process_metrics.h" +#include "base/trace_event/process_memory_dump.h" +#include "base/trace_event/process_memory_totals.h" + +namespace base { +namespace trace_event { + +// static +uint64 ProcessMemoryTotalsDumpProvider::rss_bytes_for_testing = 0; + +namespace { + +ProcessMetrics* CreateProcessMetricsForCurrentProcess() { +#if !defined(OS_MACOSX) || defined(OS_IOS) + return ProcessMetrics::CreateProcessMetrics(GetCurrentProcessHandle()); +#else + return ProcessMetrics::CreateProcessMetrics(GetCurrentProcessHandle(), NULL); +#endif +} +} // namespace + +// static +ProcessMemoryTotalsDumpProvider* +ProcessMemoryTotalsDumpProvider::GetInstance() { + return Singleton< + ProcessMemoryTotalsDumpProvider, + LeakySingletonTraits<ProcessMemoryTotalsDumpProvider>>::get(); +} + +ProcessMemoryTotalsDumpProvider::ProcessMemoryTotalsDumpProvider() + : process_metrics_(CreateProcessMetricsForCurrentProcess()) { +} + +ProcessMemoryTotalsDumpProvider::~ProcessMemoryTotalsDumpProvider() { +} + +// Called at trace dump point time. Creates a snapshot the memory counters for +// the current process. +bool ProcessMemoryTotalsDumpProvider::OnMemoryDump(ProcessMemoryDump* pmd) { + const uint64 rss_bytes = rss_bytes_for_testing + ? rss_bytes_for_testing + : process_metrics_->GetWorkingSetSize(); + + if (rss_bytes > 0) { + pmd->process_totals()->set_resident_set_bytes(rss_bytes); + pmd->set_has_process_totals(); + return true; + } + + return false; +} + +} // namespace trace_event +} // namespace base diff --git a/chromium/base/trace_event/process_memory_totals_dump_provider.h b/chromium/base/trace_event/process_memory_totals_dump_provider.h new file mode 100644 index 00000000000..6c86eb6b4a1 --- /dev/null +++ b/chromium/base/trace_event/process_memory_totals_dump_provider.h @@ -0,0 +1,44 @@ +// 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_PROCESS_MEMORY_TOTALS_DUMP_PROVIDER_H_ +#define BASE_TRACE_EVENT_PROCESS_MEMORY_TOTALS_DUMP_PROVIDER_H_ + +#include "base/gtest_prod_util.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/singleton.h" +#include "base/trace_event/memory_dump_provider.h" + +namespace base { + +class ProcessMetrics; + +namespace trace_event { + +// Dump provider which collects process-wide memory stats. +class BASE_EXPORT ProcessMemoryTotalsDumpProvider : public MemoryDumpProvider { + public: + static ProcessMemoryTotalsDumpProvider* GetInstance(); + + // MemoryDumpProvider implementation. + bool OnMemoryDump(ProcessMemoryDump* pmd) override; + + private: + friend struct DefaultSingletonTraits<ProcessMemoryTotalsDumpProvider>; + FRIEND_TEST_ALL_PREFIXES(ProcessMemoryTotalsDumpProviderTest, DumpRSS); + + static uint64 rss_bytes_for_testing; + + ProcessMemoryTotalsDumpProvider(); + ~ProcessMemoryTotalsDumpProvider() override; + + scoped_ptr<ProcessMetrics> process_metrics_; + + DISALLOW_COPY_AND_ASSIGN(ProcessMemoryTotalsDumpProvider); +}; + +} // namespace trace_event +} // namespace base + +#endif // BASE_TRACE_EVENT_PROCESS_MEMORY_TOTALS_DUMP_PROVIDER_H_ diff --git a/chromium/base/trace_event/process_memory_totals_dump_provider_unittest.cc b/chromium/base/trace_event/process_memory_totals_dump_provider_unittest.cc new file mode 100644 index 00000000000..f9bb6c071ce --- /dev/null +++ b/chromium/base/trace_event/process_memory_totals_dump_provider_unittest.cc @@ -0,0 +1,43 @@ +// 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/process_memory_totals_dump_provider.h" + +#include "base/trace_event/process_memory_dump.h" +#include "base/trace_event/process_memory_totals.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { +namespace trace_event { + +TEST(ProcessMemoryTotalsDumpProviderTest, DumpRSS) { + auto pmtdp = ProcessMemoryTotalsDumpProvider::GetInstance(); + scoped_ptr<ProcessMemoryDump> pmd_before(new ProcessMemoryDump(nullptr)); + scoped_ptr<ProcessMemoryDump> pmd_after(new ProcessMemoryDump(nullptr)); + + ProcessMemoryTotalsDumpProvider::rss_bytes_for_testing = 1024; + pmtdp->OnMemoryDump(pmd_before.get()); + + // Pretend that the RSS of the process increased of +1M. + const size_t kAllocSize = 1048576; + ProcessMemoryTotalsDumpProvider::rss_bytes_for_testing += kAllocSize; + + pmtdp->OnMemoryDump(pmd_after.get()); + + ProcessMemoryTotalsDumpProvider::rss_bytes_for_testing = 0; + + ASSERT_TRUE(pmd_before->has_process_totals()); + ASSERT_TRUE(pmd_after->has_process_totals()); + + const uint64 rss_before = pmd_before->process_totals()->resident_set_bytes(); + const uint64 rss_after = pmd_after->process_totals()->resident_set_bytes(); + + EXPECT_NE(0U, rss_before); + EXPECT_NE(0U, rss_after); + + EXPECT_EQ(rss_after - rss_before, kAllocSize); +} + +} // namespace trace_event +} // namespace base diff --git a/chromium/base/trace_event/trace_event.gypi b/chromium/base/trace_event/trace_event.gypi new file mode 100644 index 00000000000..9a072e49003 --- /dev/null +++ b/chromium/base/trace_event/trace_event.gypi @@ -0,0 +1,70 @@ +# 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. +{ + 'variables': { + 'trace_event_sources' : [ + 'trace_event/java_heap_dump_provider_android.cc', + 'trace_event/java_heap_dump_provider_android.h', + 'trace_event/memory_allocator_dump.cc', + 'trace_event/memory_allocator_dump.h', + 'trace_event/memory_dump_manager.cc', + 'trace_event/memory_dump_manager.h', + 'trace_event/memory_dump_provider.h', + 'trace_event/memory_dump_request_args.h', + 'trace_event/memory_dump_session_state.cc', + 'trace_event/memory_dump_session_state.h', + 'trace_event/process_memory_dump.cc', + 'trace_event/process_memory_dump.h', + 'trace_event/process_memory_maps.cc', + 'trace_event/process_memory_maps.h', + 'trace_event/process_memory_maps_dump_provider.cc', + 'trace_event/process_memory_maps_dump_provider.h', + 'trace_event/process_memory_totals.cc', + 'trace_event/process_memory_totals.h', + 'trace_event/process_memory_totals_dump_provider.cc', + 'trace_event/process_memory_totals_dump_provider.h', + 'trace_event/trace_event.h', + 'trace_event/trace_event_android.cc', + 'trace_event/trace_event_argument.cc', + 'trace_event/trace_event_argument.h', + 'trace_event/trace_event_etw_export_win.cc', + 'trace_event/trace_event_etw_export_win.h', + 'trace_event/trace_event_impl.cc', + 'trace_event/trace_event_impl.h', + 'trace_event/trace_event_impl_constants.cc', + 'trace_event/trace_event_memory.cc', + 'trace_event/trace_event_memory.h', + 'trace_event/trace_event_synthetic_delay.cc', + 'trace_event/trace_event_synthetic_delay.h', + 'trace_event/trace_event_system_stats_monitor.cc', + 'trace_event/trace_event_system_stats_monitor.h', + 'trace_event/trace_event_win.cc', + 'trace_event/trace_event_win.h', + 'trace_event/winheap_dump_provider_win.cc', + 'trace_event/winheap_dump_provider_win.h', + ], + 'conditions': [ + ['OS == "linux" or OS == "android"', { + 'trace_event_sources': [ + 'trace_event/malloc_dump_provider.cc', + 'trace_event/malloc_dump_provider.h', + ], + }], + ], + 'trace_event_test_sources' : [ + 'trace_event/java_heap_dump_provider_android_unittest.cc', + 'trace_event/memory_allocator_dump_unittest.cc', + 'trace_event/memory_dump_manager_unittest.cc', + 'trace_event/process_memory_maps_dump_provider_unittest.cc', + 'trace_event/process_memory_totals_dump_provider_unittest.cc', + 'trace_event/trace_event_argument_unittest.cc', + 'trace_event/trace_event_memory_unittest.cc', + 'trace_event/trace_event_synthetic_delay_unittest.cc', + 'trace_event/trace_event_system_stats_monitor_unittest.cc', + 'trace_event/trace_event_unittest.cc', + 'trace_event/trace_event_win_unittest.cc', + 'trace_event/winheap_dump_provider_win_unittest.cc', + ], + }, +} diff --git a/chromium/base/debug/trace_event.h b/chromium/base/trace_event/trace_event.h index 62a9b1f047a..2c30b3340a2 100644 --- a/chromium/base/debug/trace_event.h +++ b/chromium/base/trace_event/trace_event.h @@ -141,14 +141,14 @@ // means, if the category for the event is disabled, the conversion will not // happen. // -// class MyData : public base::debug::ConvertableToTraceFormat { +// class MyData : public base::trace_event::ConvertableToTraceFormat { // public: // MyData() {} -// virtual void AppendAsTraceFormat(std::string* out) const override { +// void AppendAsTraceFormat(std::string* out) const override { // out->append("{\"foo\":1}"); // } // private: -// virtual ~MyData() {} +// ~MyData() override {} // DISALLOW_COPY_AND_ASSIGN(MyData); // }; // @@ -186,16 +186,16 @@ // trace points would carry a significant performance cost of acquiring a lock // and resolving the category. -#ifndef BASE_DEBUG_TRACE_EVENT_H_ -#define BASE_DEBUG_TRACE_EVENT_H_ +#ifndef BASE_TRACE_EVENT_TRACE_EVENT_H_ +#define BASE_TRACE_EVENT_TRACE_EVENT_H_ #include <string> #include "base/atomicops.h" -#include "base/debug/trace_event_impl.h" -#include "base/debug/trace_event_memory.h" -#include "base/debug/trace_event_system_stats_monitor.h" #include "base/time/time.h" +#include "base/trace_event/trace_event_impl.h" +#include "base/trace_event/trace_event_memory.h" +#include "base/trace_event/trace_event_system_stats_monitor.h" #include "build/build_config.h" // By default, const char* argument values are assumed to have long-lived scope @@ -395,6 +395,18 @@ INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group, name, id, thread_id, \ timestamp, TRACE_EVENT_FLAG_COPY) +#define TRACE_EVENT_COPY_BEGIN_WITH_ID_TID_AND_TIMESTAMP1( \ + category_group, name, id, thread_id, timestamp, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group, name, id, thread_id, \ + timestamp, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val) +#define TRACE_EVENT_COPY_BEGIN_WITH_ID_TID_AND_TIMESTAMP2( \ + category_group, name, id, thread_id, timestamp, arg1_name, arg1_val, \ + arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group, name, id, thread_id, \ + timestamp, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val, arg2_name, \ + arg2_val) // Records a single END event for "name" immediately. If the category // is not enabled, then this does nothing. @@ -439,6 +451,18 @@ INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ TRACE_EVENT_PHASE_ASYNC_END, category_group, name, id, thread_id, \ timestamp, TRACE_EVENT_FLAG_COPY) +#define TRACE_EVENT_COPY_END_WITH_ID_TID_AND_TIMESTAMP1( \ + category_group, name, id, thread_id, timestamp, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + TRACE_EVENT_PHASE_ASYNC_END, category_group, name, id, thread_id, \ + timestamp, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val) +#define TRACE_EVENT_COPY_END_WITH_ID_TID_AND_TIMESTAMP2( \ + category_group, name, id, thread_id, timestamp, arg1_name, arg1_val, \ + arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + TRACE_EVENT_PHASE_ASYNC_END, category_group, name, id, thread_id, \ + timestamp, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val, arg2_name, \ + arg2_val) // Records the value of a counter called "name" immediately. Value // must be representable as a 32 bit integer. @@ -510,6 +534,27 @@ value1_name, static_cast<int>(value1_val), \ value2_name, static_cast<int>(value2_val)) +// TRACE_EVENT_SAMPLE_* events are injected by the sampling profiler. +#define TRACE_EVENT_SAMPLE_WITH_TID_AND_TIMESTAMP0(category_group, name, \ + thread_id, timestamp) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + TRACE_EVENT_PHASE_SAMPLE, category_group, name, 0, thread_id, timestamp, \ + TRACE_EVENT_FLAG_NONE) + +#define TRACE_EVENT_SAMPLE_WITH_TID_AND_TIMESTAMP1( \ + category_group, name, thread_id, timestamp, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + TRACE_EVENT_PHASE_SAMPLE, category_group, name, 0, thread_id, timestamp, \ + TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val) + +#define TRACE_EVENT_SAMPLE_WITH_TID_AND_TIMESTAMP2(category_group, name, \ + thread_id, timestamp, \ + arg1_name, arg1_val, \ + arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + TRACE_EVENT_PHASE_SAMPLE, category_group, name, 0, thread_id, timestamp, \ + TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, arg2_name, arg2_val) + // ASYNC_STEP_* APIs should be only used by legacy code. New code should // consider using NESTABLE_ASYNC_* APIs to describe substeps within an async // event. @@ -570,6 +615,12 @@ TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group, name, id, \ static_cast<int>(base::PlatformThread::CurrentId()), \ timestamp, TRACE_EVENT_FLAG_NONE) +#define TRACE_EVENT_COPY_ASYNC_BEGIN_WITH_TIMESTAMP0(category_group, \ + name, id, timestamp) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group, name, id, \ + static_cast<int>(base::PlatformThread::CurrentId()), \ + timestamp, TRACE_EVENT_FLAG_COPY) // Records a single ASYNC_STEP_INTO event for |step| immediately. If the // category is not enabled, then this does nothing. The |name| and |id| must @@ -586,6 +637,15 @@ category_group, name, id, TRACE_EVENT_FLAG_NONE, "step", step, \ arg1_name, arg1_val) +// Similar to TRACE_EVENT_ASYNC_STEP_INTOx but with a custom |at| timestamp +// provided. +#define TRACE_EVENT_ASYNC_STEP_INTO_WITH_TIMESTAMP0(category_group, name, \ + id, step, timestamp) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + TRACE_EVENT_PHASE_ASYNC_STEP_INTO, category_group, name, id, \ + static_cast<int>(base::PlatformThread::CurrentId()), \ + timestamp, TRACE_EVENT_FLAG_NONE, "step", step) + // Records a single ASYNC_STEP_PAST event for |step| immediately. If the // category is not enabled, then this does nothing. The |name| and |id| must // match the ASYNC_BEGIN event above. The |step| param identifies this step @@ -641,31 +701,65 @@ // events. // - category and name strings must have application lifetime (statics or // literals). They may not include " chars. -// - |id| is used to match the NESTABLE_ASYNC_BEGIN event with the -// NESTABLE_ASYNC_END event. Events are considered to match if their -// category_group, name and id values all match. |id| must either be a -// pointer or an integer value up to 64 bits. If it's a pointer, the bits -// will be xored with a hash of the process ID so that the same pointer on two -// different processes will not collide. +// - A pair of NESTABLE_ASYNC_BEGIN event and NESTABLE_ASYNC_END event is +// considered as a match if their category_group, name and id all match. +// - |id| must either be a pointer or an integer value up to 64 bits. +// If it's a pointer, the bits will be xored with a hash of the process ID so +// that the same pointer on two different processes will not collide. +// - |id| is used to match a child NESTABLE_ASYNC event with its parent +// NESTABLE_ASYNC event. Therefore, events in the same nested event tree must +// be logged using the same id and category_group. // -// Unmatched NESTABLE_ASYNC_END event will be parsed as an instant event, -// and unmatched NESTABLE_ASYNC_BEGIN event will be parsed as an event that -// ends at the last NESTABLE_ASYNC_END event of that |id|. - -// Records a single NESTABLE_ASYNC_BEGIN event called "name" immediately, with 2 -// associated arguments. If the category is not enabled, then this does nothing. +// Unmatched NESTABLE_ASYNC_END event will be parsed as an event that starts +// at the first NESTABLE_ASYNC event of that id, and unmatched +// NESTABLE_ASYNC_BEGIN event will be parsed as an event that ends at the last +// NESTABLE_ASYNC event of that id. Corresponding warning messages for +// unmatched events will be shown in the analysis view. + +// Records a single NESTABLE_ASYNC_BEGIN event called "name" immediately, with +// 0, 1 or 2 associated arguments. If the category is not enabled, then this +// does nothing. +#define TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(category_group, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN, \ + category_group, name, id, TRACE_EVENT_FLAG_NONE) +#define TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(category_group, name, id, arg1_name, \ + arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN, \ + category_group, name, id, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val) #define TRACE_EVENT_NESTABLE_ASYNC_BEGIN2(category_group, name, id, arg1_name, \ arg1_val, arg2_name, arg2_val) \ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN, \ category_group, name, id, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, \ arg2_name, arg2_val) -// Records a single NESTABLE_ASYNC_END event called "name" immediately, with 2 -// associated arguments. If the category is not enabled, then this does nothing. +// Records a single NESTABLE_ASYNC_END event called "name" immediately, with 0, +// 1, or 2 associated arguments. If the category is not enabled, then this does +// nothing. +#define TRACE_EVENT_NESTABLE_ASYNC_END0(category_group, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_NESTABLE_ASYNC_END, \ + category_group, name, id, TRACE_EVENT_FLAG_NONE) +#define TRACE_EVENT_NESTABLE_ASYNC_END1(category_group, name, id, arg1_name, \ + arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_NESTABLE_ASYNC_END, \ + category_group, name, id, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val) #define TRACE_EVENT_NESTABLE_ASYNC_END2(category_group, name, id, arg1_name, \ arg1_val, arg2_name, arg2_val) \ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_NESTABLE_ASYNC_END, \ category_group, name, id, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, \ arg2_name, arg2_val) + +#define TRACE_EVENT_COPY_NESTABLE_ASYNC_BEGIN_WITH_TTS2(category_group, name, \ + id, arg1_name, arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN, \ + category_group, name, id, \ + TRACE_EVENT_FLAG_ASYNC_TTS | TRACE_EVENT_FLAG_COPY, \ + arg1_name, arg1_val, arg2_name, arg2_val) +#define TRACE_EVENT_COPY_NESTABLE_ASYNC_END_WITH_TTS2(category_group, name, \ + id, arg1_name, arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_NESTABLE_ASYNC_END, \ + category_group, name, id, \ + TRACE_EVENT_FLAG_ASYNC_TTS | TRACE_EVENT_FLAG_COPY, \ + arg1_name, arg1_val, arg2_name, arg2_val) + // Records a single NESTABLE_ASYNC_INSTANT event called "name" immediately, // with 2 associated arguments. If the category is not enabled, then this // does nothing. @@ -784,9 +878,10 @@ category_group, name, TRACE_ID_DONT_MANGLE(id), TRACE_EVENT_FLAG_NONE) #define INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE() \ - UNLIKELY(*INTERNAL_TRACE_EVENT_UID(category_group_enabled) & \ - (base::debug::TraceLog::ENABLED_FOR_RECORDING | \ - base::debug::TraceLog::ENABLED_FOR_EVENT_CALLBACK)) + UNLIKELY(*INTERNAL_TRACE_EVENT_UID(category_group_enabled) & \ + (base::trace_event::TraceLog::ENABLED_FOR_RECORDING | \ + base::trace_event::TraceLog::ENABLED_FOR_EVENT_CALLBACK | \ + base::trace_event::TraceLog::ENABLED_FOR_ETW_EXPORT)) // Macro to efficiently determine if a given category group is enabled. #define TRACE_EVENT_CATEGORY_GROUP_ENABLED(category_group, ret) \ @@ -829,16 +924,16 @@ // const unsigned char* // TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(const char* category_group) #define TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED \ - base::debug::TraceLog::GetCategoryGroupEnabled + base::trace_event::TraceLog::GetCategoryGroupEnabled // Get the number of times traces have been recorded. This is used to implement // the TRACE_EVENT_IS_NEW_TRACE facility. // unsigned int TRACE_EVENT_API_GET_NUM_TRACES_RECORDED() #define TRACE_EVENT_API_GET_NUM_TRACES_RECORDED \ - base::debug::TraceLog::GetInstance()->GetNumTracesRecorded + base::trace_event::TraceLog::GetInstance()->GetNumTracesRecorded // Add a trace event to the platform tracing system. -// base::debug::TraceEventHandle TRACE_EVENT_API_ADD_TRACE_EVENT( +// base::trace_event::TraceEventHandle TRACE_EVENT_API_ADD_TRACE_EVENT( // char phase, // const unsigned char* category_group_enabled, // const char* name, @@ -849,10 +944,11 @@ // const unsigned long long* arg_values, // unsigned char flags) #define TRACE_EVENT_API_ADD_TRACE_EVENT \ - base::debug::TraceLog::GetInstance()->AddTraceEvent + base::trace_event::TraceLog::GetInstance()->AddTraceEvent // Add a trace event to the platform tracing system. -// base::debug::TraceEventHandle TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_TIMESTAMP( +// base::trace_event::TraceEventHandle +// TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_TIMESTAMP( // char phase, // const unsigned char* category_group_enabled, // const char* name, @@ -865,15 +961,16 @@ // const unsigned long long* arg_values, // unsigned char flags) #define TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_THREAD_ID_AND_TIMESTAMP \ - base::debug::TraceLog::GetInstance()->AddTraceEventWithThreadIdAndTimestamp + base::trace_event::TraceLog::GetInstance() \ + ->AddTraceEventWithThreadIdAndTimestamp // Set the duration field of a COMPLETE trace event. // void TRACE_EVENT_API_UPDATE_TRACE_EVENT_DURATION( // const unsigned char* category_group_enabled, // const char* name, -// base::debug::TraceEventHandle id) +// base::trace_event::TraceEventHandle id) #define TRACE_EVENT_API_UPDATE_TRACE_EVENT_DURATION \ - base::debug::TraceLog::GetInstance()->UpdateTraceEventDuration + base::trace_event::TraceLog::GetInstance()->UpdateTraceEventDuration // Defines atomic operations used internally by the tracing system. #define TRACE_EVENT_API_ATOMIC_WORD base::subtle::AtomicWord @@ -946,11 +1043,12 @@ TRACE_EVENT_API_CLASS_EXPORT extern \ INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \ trace_event_internal::ScopedTracer INTERNAL_TRACE_EVENT_UID(tracer); \ if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \ - base::debug::TraceEventHandle h = trace_event_internal::AddTraceEvent( \ - TRACE_EVENT_PHASE_COMPLETE, \ - INTERNAL_TRACE_EVENT_UID(category_group_enabled), \ - name, trace_event_internal::kNoEventId, \ - TRACE_EVENT_FLAG_NONE, ##__VA_ARGS__); \ + base::trace_event::TraceEventHandle h = \ + trace_event_internal::AddTraceEvent( \ + TRACE_EVENT_PHASE_COMPLETE, \ + INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \ + trace_event_internal::kNoEventId, TRACE_EVENT_FLAG_NONE, \ + ##__VA_ARGS__); \ INTERNAL_TRACE_EVENT_UID(tracer).Initialize( \ INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, h); \ } @@ -986,7 +1084,8 @@ TRACE_EVENT_API_CLASS_EXPORT extern \ phase, INTERNAL_TRACE_EVENT_UID(category_group_enabled), \ name, trace_event_trace_id.data(), \ thread_id, base::TimeTicks::FromInternalValue(timestamp), \ - trace_event_flags, ##__VA_ARGS__); \ + trace_event_flags | TRACE_EVENT_FLAG_EXPLICIT_TIMESTAMP, \ + ##__VA_ARGS__); \ } \ } while (0) @@ -1016,6 +1115,7 @@ TRACE_EVENT_API_CLASS_EXPORT extern \ #define TRACE_EVENT_PHASE_CREATE_OBJECT ('N') #define TRACE_EVENT_PHASE_SNAPSHOT_OBJECT ('O') #define TRACE_EVENT_PHASE_DELETE_OBJECT ('D') +#define TRACE_EVENT_PHASE_MEMORY_DUMP ('v') // Flags for changing the behavior of TRACE_EVENT_API_ADD_TRACE_EVENT. #define TRACE_EVENT_FLAG_NONE (static_cast<unsigned char>(0)) @@ -1023,9 +1123,12 @@ TRACE_EVENT_API_CLASS_EXPORT extern \ #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_SCOPE_MASK (static_cast<unsigned char>( \ - TRACE_EVENT_FLAG_SCOPE_OFFSET | (TRACE_EVENT_FLAG_SCOPE_OFFSET << 1))) + 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)) @@ -1243,7 +1346,7 @@ static inline void SetTraceValue(const base::TimeTicks arg, // pointers to the internal c_str and pass through to the tracing API, // the arg_values must live throughout these procedures. -static inline base::debug::TraceEventHandle +static inline base::trace_event::TraceEventHandle AddTraceEventWithThreadIdAndTimestamp( char phase, const unsigned char* category_group_enabled, @@ -1253,7 +1356,8 @@ AddTraceEventWithThreadIdAndTimestamp( const base::TimeTicks& timestamp, unsigned char flags, const char* arg1_name, - const scoped_refptr<base::debug::ConvertableToTraceFormat>& arg1_val) { + const scoped_refptr<base::trace_event::ConvertableToTraceFormat>& + arg1_val) { const int num_args = 1; unsigned char arg_types[1] = { TRACE_VALUE_TYPE_CONVERTABLE }; return TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_THREAD_ID_AND_TIMESTAMP( @@ -1262,7 +1366,7 @@ AddTraceEventWithThreadIdAndTimestamp( } template<class ARG1_TYPE> -static inline base::debug::TraceEventHandle +static inline base::trace_event::TraceEventHandle AddTraceEventWithThreadIdAndTimestamp( char phase, const unsigned char* category_group_enabled, @@ -1274,7 +1378,8 @@ AddTraceEventWithThreadIdAndTimestamp( const char* arg1_name, const ARG1_TYPE& arg1_val, const char* arg2_name, - const scoped_refptr<base::debug::ConvertableToTraceFormat>& arg2_val) { + const scoped_refptr<base::trace_event::ConvertableToTraceFormat>& + arg2_val) { const int num_args = 2; const char* arg_names[2] = { arg1_name, arg2_name }; @@ -1283,7 +1388,8 @@ AddTraceEventWithThreadIdAndTimestamp( SetTraceValue(arg1_val, &arg_types[0], &arg_values[0]); arg_types[1] = TRACE_VALUE_TYPE_CONVERTABLE; - scoped_refptr<base::debug::ConvertableToTraceFormat> convertable_values[2]; + scoped_refptr<base::trace_event::ConvertableToTraceFormat> + convertable_values[2]; convertable_values[1] = arg2_val; return TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_THREAD_ID_AND_TIMESTAMP( @@ -1292,7 +1398,7 @@ AddTraceEventWithThreadIdAndTimestamp( } template<class ARG2_TYPE> -static inline base::debug::TraceEventHandle +static inline base::trace_event::TraceEventHandle AddTraceEventWithThreadIdAndTimestamp( char phase, const unsigned char* category_group_enabled, @@ -1302,7 +1408,7 @@ AddTraceEventWithThreadIdAndTimestamp( const base::TimeTicks& timestamp, unsigned char flags, const char* arg1_name, - const scoped_refptr<base::debug::ConvertableToTraceFormat>& arg1_val, + const scoped_refptr<base::trace_event::ConvertableToTraceFormat>& arg1_val, const char* arg2_name, const ARG2_TYPE& arg2_val) { const int num_args = 2; @@ -1314,7 +1420,8 @@ AddTraceEventWithThreadIdAndTimestamp( arg_values[0] = 0; SetTraceValue(arg2_val, &arg_types[1], &arg_values[1]); - scoped_refptr<base::debug::ConvertableToTraceFormat> convertable_values[2]; + scoped_refptr<base::trace_event::ConvertableToTraceFormat> + convertable_values[2]; convertable_values[0] = arg1_val; return TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_THREAD_ID_AND_TIMESTAMP( @@ -1322,7 +1429,7 @@ AddTraceEventWithThreadIdAndTimestamp( num_args, arg_names, arg_types, arg_values, convertable_values, flags); } -static inline base::debug::TraceEventHandle +static inline base::trace_event::TraceEventHandle AddTraceEventWithThreadIdAndTimestamp( char phase, const unsigned char* category_group_enabled, @@ -1332,22 +1439,23 @@ AddTraceEventWithThreadIdAndTimestamp( const base::TimeTicks& timestamp, unsigned char flags, const char* arg1_name, - const scoped_refptr<base::debug::ConvertableToTraceFormat>& arg1_val, + const scoped_refptr<base::trace_event::ConvertableToTraceFormat>& arg1_val, const char* arg2_name, - const scoped_refptr<base::debug::ConvertableToTraceFormat>& arg2_val) { + const scoped_refptr<base::trace_event::ConvertableToTraceFormat>& + arg2_val) { const int num_args = 2; const char* arg_names[2] = { arg1_name, arg2_name }; unsigned char arg_types[2] = { TRACE_VALUE_TYPE_CONVERTABLE, TRACE_VALUE_TYPE_CONVERTABLE }; - scoped_refptr<base::debug::ConvertableToTraceFormat> convertable_values[2] = - { arg1_val, arg2_val }; + scoped_refptr<base::trace_event::ConvertableToTraceFormat> + convertable_values[2] = {arg1_val, arg2_val}; return TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_THREAD_ID_AND_TIMESTAMP( phase, category_group_enabled, name, id, thread_id, timestamp, num_args, arg_names, arg_types, NULL, convertable_values, flags); } -static inline base::debug::TraceEventHandle +static inline base::trace_event::TraceEventHandle AddTraceEventWithThreadIdAndTimestamp( char phase, const unsigned char* category_group_enabled, @@ -1361,20 +1469,20 @@ AddTraceEventWithThreadIdAndTimestamp( kZeroNumArgs, NULL, NULL, NULL, NULL, flags); } -static inline base::debug::TraceEventHandle AddTraceEvent( +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) { - int thread_id = static_cast<int>(base::PlatformThread::CurrentId()); - base::TimeTicks now = base::TimeTicks::NowFromSystemTraceTime(); + const int thread_id = static_cast<int>(base::PlatformThread::CurrentId()); + const base::TimeTicks now = base::TimeTicks::NowFromSystemTraceTime(); return AddTraceEventWithThreadIdAndTimestamp(phase, category_group_enabled, name, id, thread_id, now, flags); } template<class ARG1_TYPE> -static inline base::debug::TraceEventHandle +static inline base::trace_event::TraceEventHandle AddTraceEventWithThreadIdAndTimestamp( char phase, const unsigned char* category_group_enabled, @@ -1395,7 +1503,7 @@ AddTraceEventWithThreadIdAndTimestamp( } template<class ARG1_TYPE> -static inline base::debug::TraceEventHandle AddTraceEvent( +static inline base::trace_event::TraceEventHandle AddTraceEvent( char phase, const unsigned char* category_group_enabled, const char* name, @@ -1411,7 +1519,7 @@ static inline base::debug::TraceEventHandle AddTraceEvent( } template<class ARG1_TYPE, class ARG2_TYPE> -static inline base::debug::TraceEventHandle +static inline base::trace_event::TraceEventHandle AddTraceEventWithThreadIdAndTimestamp( char phase, const unsigned char* category_group_enabled, @@ -1436,7 +1544,7 @@ AddTraceEventWithThreadIdAndTimestamp( } template<class ARG1_TYPE, class ARG2_TYPE> -static inline base::debug::TraceEventHandle AddTraceEvent( +static inline base::trace_event::TraceEventHandle AddTraceEvent( char phase, const unsigned char* category_group_enabled, const char* name, @@ -1468,7 +1576,7 @@ class TRACE_EVENT_API_CLASS_EXPORT ScopedTracer { void Initialize(const unsigned char* category_group_enabled, const char* name, - base::debug::TraceEventHandle event_handle) { + base::trace_event::TraceEventHandle event_handle) { data_.category_group_enabled = category_group_enabled; data_.name = name; data_.event_handle = event_handle; @@ -1484,7 +1592,7 @@ class TRACE_EVENT_API_CLASS_EXPORT ScopedTracer { struct Data { const unsigned char* category_group_enabled; const char* name; - base::debug::TraceEventHandle event_handle; + base::trace_event::TraceEventHandle event_handle; }; Data* p_data_; Data data_; @@ -1499,7 +1607,7 @@ class TRACE_EVENT_API_CLASS_EXPORT ScopedTraceBinaryEfficient { private: const unsigned char* category_group_enabled_; const char* name_; - base::debug::TraceEventHandle event_handle_; + base::trace_event::TraceEventHandle event_handle_; }; // This macro generates less code then TRACE_EVENT0 but is also @@ -1545,7 +1653,7 @@ class TraceEventSamplingStateScope { } // namespace trace_event_internal namespace base { -namespace debug { +namespace trace_event { template<typename IDType> class TraceScopedTrackableObject { public: @@ -1573,7 +1681,7 @@ template<typename IDType> class TraceScopedTrackableObject { DISALLOW_COPY_AND_ASSIGN(TraceScopedTrackableObject); }; -} // namespace debug -} // namespace base +} // namespace trace_event +} // namespace base -#endif /* BASE_DEBUG_TRACE_EVENT_H_ */ +#endif // BASE_TRACE_EVENT_TRACE_EVENT_H_ diff --git a/chromium/base/debug/trace_event_android.cc b/chromium/base/trace_event/trace_event_android.cc index f08fffca35f..26bd0c777d4 100644 --- a/chromium/base/debug/trace_event_android.cc +++ b/chromium/base/trace_event/trace_event_android.cc @@ -2,15 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/debug/trace_event_impl.h" +#include "base/trace_event/trace_event_impl.h" #include <fcntl.h> -#include "base/debug/trace_event.h" #include "base/format_macros.h" #include "base/logging.h" #include "base/strings/stringprintf.h" #include "base/synchronization/waitable_event.h" +#include "base/trace_event/trace_event.h" namespace { @@ -24,8 +24,8 @@ void WriteEvent( unsigned long long id, const char** arg_names, const unsigned char* arg_types, - const base::debug::TraceEvent::TraceValue* arg_values, - const scoped_refptr<base::debug::ConvertableToTraceFormat>* + 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); @@ -33,7 +33,8 @@ void WriteEvent( base::StringAppendF(&out, "-%" PRIx64, static_cast<uint64>(id)); out += '|'; - for (int i = 0; i < base::debug::kTraceMaxNumArgs && arg_names[i]; ++i) { + for (int i = 0; i < base::trace_event::kTraceMaxNumArgs && arg_names[i]; + ++i) { if (i) out += ';'; out += arg_names[i]; @@ -42,8 +43,8 @@ void WriteEvent( if (arg_types[i] == TRACE_VALUE_TYPE_CONVERTABLE) { convertable_values[i]->AppendAsTraceFormat(&out); } else { - base::debug::TraceEvent::AppendValueAsJSON( - arg_types[i], arg_values[i], &out); + base::trace_event::TraceEvent::AppendValueAsJSON(arg_types[i], + arg_values[i], &out); } // Remove the quotes which may confuse the atrace script. ReplaceSubstringsAfterOffset(&out, value_start, "\\\"", "'"); @@ -65,7 +66,7 @@ void NoOpOutputCallback(base::WaitableEvent* complete_event, complete_event->Signal(); } -void EndChromeTracing(base::debug::TraceLog* trace_log, +void EndChromeTracing(base::trace_event::TraceLog* trace_log, base::WaitableEvent* complete_event) { trace_log->SetDisabled(); // Delete the buffered trace events as they have been sent to atrace. @@ -75,7 +76,7 @@ void EndChromeTracing(base::debug::TraceLog* trace_log, } // namespace namespace base { -namespace debug { +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: @@ -113,7 +114,7 @@ void TraceLog::StopATrace() { Thread end_chrome_tracing_thread("end_chrome_tracing"); WaitableEvent complete_event(false, false); end_chrome_tracing_thread.Start(); - end_chrome_tracing_thread.message_loop()->PostTask( + end_chrome_tracing_thread.task_runner()->PostTask( FROM_HERE, base::Bind(&EndChromeTracing, Unretained(this), Unretained(&complete_event))); complete_event.Wait(); @@ -195,5 +196,5 @@ void TraceLog::AddClockSyncMetadataEvent() { close(atrace_fd); } -} // namespace debug +} // namespace trace_event } // namespace base diff --git a/chromium/base/debug/trace_event_argument.cc b/chromium/base/trace_event/trace_event_argument.cc index 90e924f119e..88b1879b8b3 100644 --- a/chromium/base/debug/trace_event_argument.cc +++ b/chromium/base/trace_event/trace_event_argument.cc @@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/debug/trace_event_argument.h" +#include "base/trace_event/trace_event_argument.h" #include "base/json/json_writer.h" #include "base/values.h" namespace base { -namespace debug { +namespace trace_event { TracedValue::TracedValue() : root_(new DictionaryValue()) { stack_.push_back(root_.get()); @@ -34,19 +34,19 @@ void TracedValue::SetString(const char* name, const std::string& value) { GetCurrentDictionary()->SetString(name, value); } -void TracedValue::SetValue(const char* name, Value* value) { - GetCurrentDictionary()->Set(name, value); +void TracedValue::SetValue(const char* name, scoped_ptr<Value> value) { + GetCurrentDictionary()->Set(name, value.Pass()); } void TracedValue::BeginDictionary(const char* name) { DictionaryValue* dictionary = new DictionaryValue(); - GetCurrentDictionary()->Set(name, dictionary); + GetCurrentDictionary()->Set(name, make_scoped_ptr(dictionary)); stack_.push_back(dictionary); } void TracedValue::BeginArray(const char* name) { ListValue* array = new ListValue(); - GetCurrentDictionary()->Set(name, array); + GetCurrentDictionary()->Set(name, make_scoped_ptr(array)); stack_.push_back(array); } @@ -113,5 +113,5 @@ void TracedValue::AppendAsTraceFormat(std::string* out) const { DCHECK_EQ(1u, stack_.size()) << tmp; } -} // namespace debug +} // namespace trace_event } // namespace base diff --git a/chromium/base/debug/trace_event_argument.h b/chromium/base/trace_event/trace_event_argument.h index 98f1bcf2fc9..d86cfd1f82a 100644 --- a/chromium/base/debug/trace_event_argument.h +++ b/chromium/base/trace_event/trace_event_argument.h @@ -2,21 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_DEBUG_TRACE_EVENT_ARGUMENT_H_ -#define BASE_DEBUG_TRACE_EVENT_ARGUMENT_H_ +#ifndef BASE_TRACE_EVENT_TRACE_EVENT_ARGUMENT_H_ +#define BASE_TRACE_EVENT_TRACE_EVENT_ARGUMENT_H_ #include <string> #include <vector> -#include "base/debug/trace_event.h" #include "base/memory/scoped_ptr.h" +#include "base/trace_event/trace_event.h" namespace base { class DictionaryValue; class ListValue; class Value; -namespace debug { +namespace trace_event { class BASE_EXPORT TracedValue : public ConvertableToTraceFormat { public: @@ -29,7 +29,7 @@ class BASE_EXPORT TracedValue : public ConvertableToTraceFormat { void SetDouble(const char* name, double); void SetBoolean(const char* name, bool value); void SetString(const char* name, const std::string& value); - void SetValue(const char* name, Value* value); + void SetValue(const char* name, scoped_ptr<Value> value); void BeginDictionary(const char* name); void BeginArray(const char* name); @@ -49,11 +49,11 @@ class BASE_EXPORT TracedValue : public ConvertableToTraceFormat { ListValue* GetCurrentArray(); scoped_ptr<base::Value> root_; - std::vector<Value*> stack_; + std::vector<Value*> stack_; // Weak references. DISALLOW_COPY_AND_ASSIGN(TracedValue); }; -} // namespace debug +} // namespace trace_event } // namespace base -#endif // BASE_DEBUG_TRACE_EVENT_ARGUMENT_H_ +#endif // BASE_TRACE_EVENT_TRACE_EVENT_ARGUMENT_H_ diff --git a/chromium/base/debug/trace_event_argument_unittest.cc b/chromium/base/trace_event/trace_event_argument_unittest.cc index 06a7697f082..c59910e4465 100644 --- a/chromium/base/debug/trace_event_argument_unittest.cc +++ b/chromium/base/trace_event/trace_event_argument_unittest.cc @@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/debug/trace_event_argument.h" +#include "base/trace_event/trace_event_argument.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { -namespace debug { +namespace trace_event { TEST(TraceEventArgumentTest, FlatDictionary) { scoped_refptr<TracedValue> value = new TracedValue(); @@ -49,5 +49,5 @@ TEST(TraceEventArgumentTest, Hierarchy) { json); } -} // namespace debug +} // namespace trace_event } // namespace base diff --git a/chromium/base/trace_event/trace_event_etw_export_win.cc b/chromium/base/trace_event/trace_event_etw_export_win.cc new file mode 100644 index 00000000000..28c154c1647 --- /dev/null +++ b/chromium/base/trace_event/trace_event_etw_export_win.cc @@ -0,0 +1,251 @@ +// 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_etw_export_win.h" + +#include "base/command_line.h" +#include "base/logging.h" +#include "base/memory/singleton.h" +#include "base/strings/utf_string_conversions.h" +#include "base/trace_event/trace_event.h" +#include "base/trace_event/trace_event_impl.h" + +// The GetProcAddress technique is borrowed from +// https://github.com/randomascii/main/tree/master/xperf/ETWProviders +// +// EVNTAPI is used in evntprov.h which is included by chrome_events_win.h. +// We define EVNTAPI without the DECLSPEC_IMPORT specifier so that we can +// implement these functions locally instead of using the import library, and +// can therefore still run on Windows XP. +#define EVNTAPI __stdcall +// Include the event register/write/unregister macros compiled from the manifest +// file. Note that this includes evntprov.h which requires a Vista+ Windows SDK. +// +// In SHARED_INTERMEDIATE_DIR. +#include "base/trace_event/etw_manifest/chrome_events_win.h" // NOLINT + +namespace { +// Typedefs for use with GetProcAddress +typedef ULONG(__stdcall* tEventRegister)(LPCGUID ProviderId, + PENABLECALLBACK EnableCallback, + PVOID CallbackContext, + PREGHANDLE RegHandle); +typedef ULONG(__stdcall* tEventWrite)(REGHANDLE RegHandle, + PCEVENT_DESCRIPTOR EventDescriptor, + ULONG UserDataCount, + PEVENT_DATA_DESCRIPTOR UserData); +typedef ULONG(__stdcall* tEventUnregister)(REGHANDLE RegHandle); + +tEventRegister EventRegisterProc = nullptr; +tEventWrite EventWriteProc = nullptr; +tEventUnregister EventUnregisterProc = nullptr; +} // namespace + +// Redirector function for EventRegister. Called by macros in +// chrome_events_win.h +ULONG EVNTAPI EventRegister(LPCGUID ProviderId, + PENABLECALLBACK EnableCallback, + PVOID CallbackContext, + PREGHANDLE RegHandle) { + if (EventRegisterProc) + return EventRegisterProc(ProviderId, EnableCallback, CallbackContext, + RegHandle); + return 0; +} + +// Redirector function for EventWrite. Called by macros in +// chrome_events_win.h +ULONG EVNTAPI EventWrite(REGHANDLE RegHandle, + PCEVENT_DESCRIPTOR EventDescriptor, + ULONG UserDataCount, + PEVENT_DATA_DESCRIPTOR UserData) { + if (EventWriteProc) + return EventWriteProc(RegHandle, EventDescriptor, UserDataCount, UserData); + return 0; +} + +// Redirector function for EventUnregister. Called by macros in +// chrome_events_win.h +ULONG EVNTAPI EventUnregister(REGHANDLE RegHandle) { + if (EventUnregisterProc) + return EventUnregisterProc(RegHandle); + return 0; +} + +namespace base { +namespace trace_event { + +TraceEventETWExport::TraceEventETWExport() : ETWExportEnabled_(false) { + // Find Advapi32.dll. This should always succeed. + HMODULE AdvapiDLL = ::LoadLibraryW(L"Advapi32.dll"); + if (AdvapiDLL) { + // Try to find the ETW functions. This will fail on XP. + EventRegisterProc = reinterpret_cast<tEventRegister>( + ::GetProcAddress(AdvapiDLL, "EventRegister")); + EventWriteProc = reinterpret_cast<tEventWrite>( + ::GetProcAddress(AdvapiDLL, "EventWrite")); + EventUnregisterProc = reinterpret_cast<tEventUnregister>( + ::GetProcAddress(AdvapiDLL, "EventUnregister")); + + // Register the ETW provider. If registration fails then the event logging + // calls will fail (on XP this call will do nothing). + EventRegisterChrome(); + } +} + +TraceEventETWExport::~TraceEventETWExport() { + EventUnregisterChrome(); +} + +// static +TraceEventETWExport* TraceEventETWExport::GetInstance() { + return Singleton<TraceEventETWExport, + StaticMemorySingletonTraits<TraceEventETWExport>>::get(); +} + +// static +void TraceEventETWExport::EnableETWExport() { + if (GetInstance()) + GetInstance()->ETWExportEnabled_ = true; +} + +// static +void TraceEventETWExport::DisableETWExport() { + if (GetInstance()) + GetInstance()->ETWExportEnabled_ = false; +} + +// static +void TraceEventETWExport::AddEvent( + char phase, + const unsigned char* category_group_enabled, + const char* name, + unsigned long long id, + int num_args, + const char** arg_names, + const unsigned char* arg_types, + 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_ || + !EventEnabledChromeEvent()) + return; + + const char* phase_string = nullptr; + // Space to store the phase identifier and null-terminator, when needed. + char phase_buffer[2]; + switch (phase) { + case TRACE_EVENT_PHASE_BEGIN: + phase_string = "Begin"; + break; + case TRACE_EVENT_PHASE_END: + phase_string = "End"; + break; + case TRACE_EVENT_PHASE_COMPLETE: + phase_string = "Complete"; + break; + case TRACE_EVENT_PHASE_INSTANT: + phase_string = "Instant"; + break; + case TRACE_EVENT_PHASE_ASYNC_BEGIN: + phase_string = "Async Begin"; + break; + case TRACE_EVENT_PHASE_ASYNC_STEP_INTO: + phase_string = "Async Step Into"; + break; + case TRACE_EVENT_PHASE_ASYNC_STEP_PAST: + phase_string = "Async Step Past"; + break; + case TRACE_EVENT_PHASE_ASYNC_END: + phase_string = "Async End"; + break; + case TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN: + phase_string = "Nestable Async Begin"; + break; + case TRACE_EVENT_PHASE_NESTABLE_ASYNC_END: + phase_string = "Nestable Async End"; + break; + case TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT: + phase_string = "Nestable Async Instant"; + break; + case TRACE_EVENT_PHASE_FLOW_BEGIN: + phase_string = "Phase Flow Begin"; + break; + case TRACE_EVENT_PHASE_FLOW_STEP: + phase_string = "Phase Flow Step"; + break; + case TRACE_EVENT_PHASE_FLOW_END: + phase_string = "Phase Flow End"; + break; + case TRACE_EVENT_PHASE_METADATA: + phase_string = "Phase Metadata"; + break; + case TRACE_EVENT_PHASE_COUNTER: + phase_string = "Phase Counter"; + break; + case TRACE_EVENT_PHASE_SAMPLE: + phase_string = "Phase Sample"; + break; + case TRACE_EVENT_PHASE_CREATE_OBJECT: + phase_string = "Phase Create Object"; + break; + case TRACE_EVENT_PHASE_SNAPSHOT_OBJECT: + phase_string = "Phase Snapshot Object"; + break; + case TRACE_EVENT_PHASE_DELETE_OBJECT: + phase_string = "Phase Delete Object"; + break; + default: + phase_buffer[0] = phase; + phase_buffer[1] = 0; + phase_string = phase_buffer; + break; + } + + std::string arg_values_string[3]; + for (int i = 0; i < num_args; i++) { + if (arg_types[i] == TRACE_VALUE_TYPE_CONVERTABLE) { + // Temporarily do nothing here. This function consumes 1/3 to 1/2 of + // *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); + } else { + TraceEvent::TraceValue trace_event; + trace_event.as_uint = arg_values[i]; + TraceEvent::AppendValueAsJSON(arg_types[i], trace_event, + arg_values_string + i); + } + } + + EventWriteChromeEvent( + name, phase_string, num_args > 0 ? arg_names[0] : "", + arg_values_string[0].c_str(), num_args > 1 ? arg_names[1] : "", + arg_values_string[1].c_str(), num_args > 2 ? arg_names[2] : "", + arg_values_string[2].c_str()); +} + +// static +void TraceEventETWExport::AddCustomEvent(const char* name, + char const* phase, + const char* arg_name_1, + const char* arg_value_1, + const char* arg_name_2, + const char* arg_value_2, + const char* arg_name_3, + const char* arg_value_3) { + if (!GetInstance() || !GetInstance()->ETWExportEnabled_ || + !EventEnabledChromeEvent()) + return; + + EventWriteChromeEvent(name, phase, arg_name_1, arg_value_1, arg_name_2, + arg_value_2, arg_name_3, arg_value_3); +} + +void TraceEventETWExport::Resurrect() { + StaticMemorySingletonTraits<TraceEventETWExport>::Resurrect(); +} + +} // namespace trace_event +} // namespace base diff --git a/chromium/base/trace_event/trace_event_etw_export_win.h b/chromium/base/trace_event/trace_event_etw_export_win.h new file mode 100644 index 00000000000..eefe8204812 --- /dev/null +++ b/chromium/base/trace_event/trace_event_etw_export_win.h @@ -0,0 +1,75 @@ +// 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. + +// This file contains the Windows-specific exporting to ETW. +#ifndef BASE_TRACE_EVENT_TRACE_EVENT_ETW_EXPORT_WIN_H_ +#define BASE_TRACE_EVENT_TRACE_EVENT_ETW_EXPORT_WIN_H_ + +#include "base/base_export.h" +#include "base/trace_event/trace_event_impl.h" + +// Fwd. +template <typename Type> +struct StaticMemorySingletonTraits; + +namespace base { +namespace trace_event { + +class BASE_EXPORT TraceEventETWExport { + public: + ~TraceEventETWExport(); + + // Retrieves the singleton. + // Note that this may return NULL post-AtExit processing. + static TraceEventETWExport* GetInstance(); + + // Enables/disables exporting of events to ETW. If disabled, + // AddEvent and AddCustomEvent will simply return when called. + static void EnableETWExport(); + static void DisableETWExport(); + + static bool isETWExportEnabled() { + return (GetInstance() && GetInstance()->ETWExportEnabled_); + } + + // Exports an event to ETW. This is mainly used in + // TraceLog::AddTraceEventWithThreadIdAndTimestamp to export internal events. + static void AddEvent( + char phase, + const unsigned char* category_group_enabled, + const char* name, + unsigned long long id, + int num_args, + const char** arg_names, + const unsigned char* arg_types, + const unsigned long long* arg_values, + const scoped_refptr<ConvertableToTraceFormat>* convertable_values); + + // Exports an event to ETW. This should be used when exporting an event only + // 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* arg_name_1, + const char* arg_value_1, + const char* arg_name_2, + const char* arg_value_2, + const char* arg_name_3, + const char* arg_value_3); + + void Resurrect(); + + private: + bool ETWExportEnabled_; + // Ensure only the provider can construct us. + friend struct StaticMemorySingletonTraits<TraceEventETWExport>; + TraceEventETWExport(); + + DISALLOW_COPY_AND_ASSIGN(TraceEventETWExport); +}; + +} // namespace trace_event +} // namespace base + +#endif // BASE_TRACE_EVENT_TRACE_EVENT_ETW_EXPORT_WIN_H_ diff --git a/chromium/base/debug/trace_event_impl.cc b/chromium/base/trace_event/trace_event_impl.cc index ce62766da3d..5ae7fb2572f 100644 --- a/chromium/base/debug/trace_event_impl.cc +++ b/chromium/base/trace_event/trace_event_impl.cc @@ -2,22 +2,20 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/debug/trace_event_impl.h" +#include "base/trace_event/trace_event_impl.h" #include <algorithm> +#include <cmath> #include "base/base_switches.h" #include "base/bind.h" #include "base/command_line.h" #include "base/debug/leak_annotations.h" -#include "base/debug/trace_event.h" -#include "base/debug/trace_event_synthetic_delay.h" -#include "base/float_util.h" #include "base/format_macros.h" #include "base/json/string_escape.h" #include "base/lazy_instance.h" +#include "base/location.h" #include "base/memory/singleton.h" -#include "base/message_loop/message_loop.h" #include "base/process/process_metrics.h" #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" @@ -30,19 +28,24 @@ #include "base/synchronization/waitable_event.h" #include "base/sys_info.h" #include "base/third_party/dynamic_annotations/dynamic_annotations.h" +#include "base/thread_task_runner_handle.h" #include "base/threading/platform_thread.h" #include "base/threading/thread_id_name_manager.h" +#include "base/threading/worker_pool.h" #include "base/time/time.h" +#include "base/trace_event/trace_event.h" +#include "base/trace_event/trace_event_synthetic_delay.h" #if defined(OS_WIN) -#include "base/debug/trace_event_win.h" +#include "base/trace_event/trace_event_etw_export_win.h" +#include "base/trace_event/trace_event_win.h" #endif class DeleteTraceLogForTesting { public: static void Delete() { - Singleton<base::debug::TraceLog, - LeakySingletonTraits<base::debug::TraceLog> >::OnExit(0); + Singleton<base::trace_event::TraceLog, + LeakySingletonTraits<base::trace_event::TraceLog>>::OnExit(0); } }; @@ -50,7 +53,7 @@ class DeleteTraceLogForTesting { BASE_EXPORT TRACE_EVENT_API_ATOMIC_WORD g_trace_state[3]; namespace base { -namespace debug { +namespace trace_event { namespace { @@ -73,7 +76,7 @@ const size_t kTraceEventVectorBigBufferChunks = 512000000 / kTraceBufferChunkSize; const size_t kTraceEventVectorBufferChunks = 256000 / kTraceBufferChunkSize; const size_t kTraceEventRingBufferChunks = kTraceEventVectorBufferChunks / 4; -const size_t kTraceEventBatchChunks = 1000 / kTraceBufferChunkSize; +const size_t kTraceEventBufferSizeInBytes = 100 * 1024; // Can store results for 30 seconds with 1 ms sampling interval. const size_t kMonitorTraceEventBufferChunks = 30000 / kTraceBufferChunkSize; // ECHO_TO_CONSOLE needs a small buffer to hold the unfinished COMPLETE events. @@ -430,25 +433,22 @@ scoped_ptr<TraceBufferChunk> TraceBufferChunk::Clone() const { // and unlocks at the end of scope if locked. class TraceLog::OptionalAutoLock { public: - explicit OptionalAutoLock(Lock& lock) - : lock_(lock), - locked_(false) { - } + explicit OptionalAutoLock(Lock* lock) : lock_(lock), locked_(false) {} ~OptionalAutoLock() { if (locked_) - lock_.Release(); + lock_->Release(); } void EnsureAcquired() { if (!locked_) { - lock_.Acquire(); + lock_->Acquire(); locked_ = true; } } private: - Lock& lock_; + Lock* lock_; bool locked_; DISALLOW_COPY_AND_ASSIGN(OptionalAutoLock); }; @@ -624,7 +624,7 @@ void TraceEvent::Reset() { void TraceEvent::UpdateDuration(const TimeTicks& now, const TimeTicks& thread_now) { - DCHECK(duration_.ToInternalValue() == -1); + DCHECK_EQ(duration_.ToInternalValue(), -1); duration_ = now - timestamp_; thread_duration_ = thread_now - thread_timestamp_; } @@ -648,7 +648,7 @@ void TraceEvent::AppendValueAsJSON(unsigned char type, // should be made into a common method. std::string real; double val = value.as_double; - if (IsFinite(val)) { + if (std::isfinite(val)) { real = DoubleToString(val); // Ensure that the number has a .0 if there's no decimal or 'e'. This // makes sure that when we read the JSON back, it's interpreted as a @@ -666,7 +666,7 @@ void TraceEvent::AppendValueAsJSON(unsigned char type, // "-.1" bad "-0.1" good real.insert(1, "0"); } - } else if (IsNaN(val)){ + } else if (std::isnan(val)){ // The JSON spec doesn't allow NaN and Infinity (since these are // objects in EcmaScript). Use strings instead. real = "\"NaN\""; @@ -701,13 +701,13 @@ void TraceEvent::AppendAsJSON(std::string* out) const { // Category group checked at category creation time. DCHECK(!strchr(name_, '"')); StringAppendF(out, - "{\"cat\":\"%s\",\"pid\":%i,\"tid\":%i,\"ts\":%" PRId64 "," - "\"ph\":\"%c\",\"name\":\"%s\",\"args\":{", - TraceLog::GetCategoryGroupName(category_group_enabled_), + "{\"pid\":%i,\"tid\":%i,\"ts\":%" PRId64 "," + "\"ph\":\"%c\",\"cat\":\"%s\",\"name\":\"%s\",\"args\":{", process_id, thread_id_, time_int64, phase_, + TraceLog::GetCategoryGroupName(category_group_enabled_), name_); // Output argument names and values, stop at first NULL argument name. @@ -742,6 +742,11 @@ void TraceEvent::AppendAsJSON(std::string* out) const { StringAppendF(out, ",\"tts\":%" PRId64, thread_time_int64); } + // Output async tts marker field if flag is set. + if (flags_ & TRACE_EVENT_FLAG_ASYNC_TTS) { + StringAppendF(out, ", \"use_async_tts\":1"); + } + // If id_ is set, print it out as a hex string so we don't loose any // bits (it might be a 64-bit pointer). if (flags_ & TRACE_EVENT_FLAG_HAS_ID) @@ -1185,6 +1190,12 @@ void TraceLog::ThreadLocalEventBuffer::FlushWhileLocked() { // find the generation mismatch and delete this buffer soon. } +TraceLogStatus::TraceLogStatus() : event_capacity(0), event_count(0) { +} + +TraceLogStatus::~TraceLogStatus() { +} + // static TraceLog* TraceLog::GetInstance() { return Singleton<TraceLog, LeakySingletonTraits<TraceLog> >::get(); @@ -1205,7 +1216,8 @@ TraceLog::TraceLog() event_callback_category_filter_( CategoryFilter::kDefaultCategoryFilterString), thread_shared_chunk_index_(0), - generation_(0) { + generation_(0), + use_worker_thread_(false) { // Trace is enabled or disabled on one thread while other threads are // accessing the enabled flag. We don't care whether edge-case events are // traced or not, so we allow races on the enabled flag to keep the trace @@ -1287,6 +1299,11 @@ void TraceLog::UpdateCategoryGroupEnabledFlag(size_t category_index) { if (event_callback_ && event_callback_category_filter_.IsCategoryGroupEnabled(category_group)) enabled_flag |= ENABLED_FOR_EVENT_CALLBACK; +#if defined(OS_WIN) + if (base::trace_event::TraceEventETWExport::isETWExportEnabled()) + enabled_flag |= ENABLED_FOR_ETW_EXPORT; +#endif + g_category_group_enabled[category_index] = enabled_flag; } @@ -1395,7 +1412,7 @@ void TraceLog::SetEnabled(const CategoryFilter& category_filter, AutoLock lock(lock_); // Can't enable tracing when Flush() is in progress. - DCHECK(!flush_message_loop_proxy_.get()); + DCHECK(!flush_task_runner_); InternalTraceOptions new_options = GetInternalOptionsFromTraceOptions(options); @@ -1586,10 +1603,12 @@ bool TraceLog::HasEnabledStateObserver(EnabledStateObserver* listener) const { return it != enabled_state_observer_list_.end(); } -float TraceLog::GetBufferPercentFull() const { +TraceLogStatus TraceLog::GetStatus() const { AutoLock lock(lock_); - return static_cast<float>(static_cast<double>(logged_events_->Size()) / - logged_events_->Capacity()); + TraceLogStatus result; + result.event_capacity = logged_events_->Capacity(); + result.event_count = logged_events_->Size(); + return result; } bool TraceLog::BufferIsFull() const { @@ -1667,16 +1686,18 @@ void TraceLog::SetEventCallbackDisabled() { } // Flush() works as the following: -// 1. Flush() is called in threadA whose message loop is saved in -// flush_message_loop_proxy_; -// 2. If thread_message_loops_ is not empty, threadA posts task to each message +// 1. Flush() is called in thread A whose task runner is saved in +// flush_task_runner_; +// 2. If thread_message_loops_ is not empty, thread A posts task to each message // loop to flush the thread local buffers; otherwise finish the flush; // 3. FlushCurrentThread() deletes the thread local event buffer: // - The last batch of events of the thread are flushed into the main buffer; // - The message loop will be removed from thread_message_loops_; // If this is the last message loop, finish the flush; // 4. If any thread hasn't finish its flush in time, finish the flush. -void TraceLog::Flush(const TraceLog::OutputCallback& cb) { +void TraceLog::Flush(const TraceLog::OutputCallback& cb, + bool use_worker_thread) { + use_worker_thread_ = use_worker_thread; if (IsEnabled()) { // Can't flush when tracing is enabled because otherwise PostTask would // - generate more trace events; @@ -1695,9 +1716,11 @@ void TraceLog::Flush(const TraceLog::OutputCallback& cb) { thread_message_loop_task_runners; { AutoLock lock(lock_); - DCHECK(!flush_message_loop_proxy_.get()); - flush_message_loop_proxy_ = MessageLoopProxy::current(); - DCHECK(!thread_message_loops_.size() || flush_message_loop_proxy_.get()); + DCHECK(!flush_task_runner_); + flush_task_runner_ = ThreadTaskRunnerHandle::IsSet() + ? ThreadTaskRunnerHandle::Get() + : nullptr; + DCHECK_IMPLIES(thread_message_loops_.size(), flush_task_runner_); flush_output_callback_ = cb; if (thread_shared_chunk_) { @@ -1720,7 +1743,7 @@ void TraceLog::Flush(const TraceLog::OutputCallback& cb) { FROM_HERE, Bind(&TraceLog::FlushCurrentThread, Unretained(this), generation)); } - flush_message_loop_proxy_->PostDelayedTask( + flush_task_runner_->PostDelayedTask( FROM_HERE, Bind(&TraceLog::OnFlushTimeout, Unretained(this), generation), TimeDelta::FromMilliseconds(kThreadFlushTimeoutMs)); @@ -1730,6 +1753,7 @@ void TraceLog::Flush(const TraceLog::OutputCallback& cb) { FinishFlush(generation); } +// Usually it runs on a different thread. void TraceLog::ConvertTraceEventsToTraceFormat( scoped_ptr<TraceBuffer> logged_events, const TraceLog::OutputCallback& flush_output_callback) { @@ -1744,19 +1768,17 @@ void TraceLog::ConvertTraceEventsToTraceFormat( scoped_refptr<RefCountedString> json_events_str_ptr = new RefCountedString(); - for (size_t i = 0; i < kTraceEventBatchChunks; ++i) { + while (json_events_str_ptr->size() < kTraceEventBufferSizeInBytes) { const TraceBufferChunk* chunk = logged_events->NextChunk(); - if (!chunk) { - has_more_events = false; + has_more_events = chunk != NULL; + if (!chunk) break; - } for (size_t j = 0; j < chunk->size(); ++j) { - if (i > 0 || j > 0) - json_events_str_ptr->data().append(","); + if (json_events_str_ptr->size()) + json_events_str_ptr->data().append(",\n"); chunk->GetEventAt(j)->AppendAsJSON(&(json_events_str_ptr->data())); } } - flush_output_callback.Run(json_events_str_ptr, has_more_events); } while (has_more_events); } @@ -1775,11 +1797,21 @@ void TraceLog::FinishFlush(int generation) { UseNextTraceBuffer(); thread_message_loops_.clear(); - flush_message_loop_proxy_ = NULL; + flush_task_runner_ = NULL; flush_output_callback = flush_output_callback_; flush_output_callback_.Reset(); } + if (use_worker_thread_ && + WorkerPool::PostTask( + FROM_HERE, + Bind(&TraceLog::ConvertTraceEventsToTraceFormat, + Passed(&previous_logged_events), + flush_output_callback), + true)) { + return; + } + ConvertTraceEventsToTraceFormat(previous_logged_events.Pass(), flush_output_callback); } @@ -1788,7 +1820,7 @@ void TraceLog::FinishFlush(int generation) { void TraceLog::FlushCurrentThread(int generation) { { AutoLock lock(lock_); - if (!CheckGeneration(generation) || !flush_message_loop_proxy_.get()) { + if (!CheckGeneration(generation) || !flush_task_runner_) { // This is late. The corresponding flush has finished. return; } @@ -1798,19 +1830,18 @@ void TraceLog::FlushCurrentThread(int generation) { delete thread_local_event_buffer_.Get(); AutoLock lock(lock_); - if (!CheckGeneration(generation) || !flush_message_loop_proxy_.get() || + if (!CheckGeneration(generation) || !flush_task_runner_ || thread_message_loops_.size()) return; - flush_message_loop_proxy_->PostTask( - FROM_HERE, - Bind(&TraceLog::FinishFlush, Unretained(this), generation)); + flush_task_runner_->PostTask( + FROM_HERE, Bind(&TraceLog::FinishFlush, Unretained(this), generation)); } void TraceLog::OnFlushTimeout(int generation) { { AutoLock lock(lock_); - if (!CheckGeneration(generation) || !flush_message_loop_proxy_.get()) { + if (!CheckGeneration(generation) || !flush_task_runner_) { // Flush has finished before timeout. return; } @@ -1900,11 +1931,14 @@ TraceEventHandle TraceLog::AddTraceEventWithThreadIdAndTimestamp( AutoThreadLocalBoolean thread_is_in_trace_event(&thread_is_in_trace_event_); DCHECK(name); + DCHECK(!timestamp.is_null()); if (flags & TRACE_EVENT_FLAG_MANGLE_ID) - id ^= process_id_hash_; + id = MangleEventId(id); - TimeTicks now = OffsetTimestamp(timestamp); + TimeTicks offset_event_timestamp = OffsetTimestamp(timestamp); + TimeTicks now = flags & TRACE_EVENT_FLAG_EXPLICIT_TIMESTAMP ? + OffsetNow() : offset_event_timestamp; TimeTicks thread_now = ThreadNow(); ThreadLocalEventBuffer* thread_local_event_buffer = NULL; @@ -1963,10 +1997,19 @@ TraceEventHandle TraceLog::AddTraceEventWithThreadIdAndTimestamp( } } +#if defined(OS_WIN) + // This is done sooner rather than later, to avoid creating the event and + // acquiring the lock, which is not needed for ETW as it's already threadsafe. + if (*category_group_enabled & ENABLED_FOR_ETW_EXPORT) + TraceEventETWExport::AddEvent(phase, category_group_enabled, name, id, + num_args, arg_names, arg_types, arg_values, + convertable_values); +#endif // OS_WIN + std::string console_message; if (*category_group_enabled & (ENABLED_FOR_RECORDING | ENABLED_FOR_MONITORING)) { - OptionalAutoLock lock(lock_); + OptionalAutoLock lock(&lock_); TraceEvent* trace_event = NULL; if (thread_local_event_buffer) { @@ -1977,8 +2020,8 @@ TraceEventHandle TraceLog::AddTraceEventWithThreadIdAndTimestamp( } if (trace_event) { - trace_event->Initialize(thread_id, now, thread_now, phase, - category_group_enabled, name, id, + trace_event->Initialize(thread_id, offset_event_timestamp, thread_now, + phase, category_group_enabled, name, id, num_args, arg_names, arg_types, arg_values, convertable_values, flags); @@ -2016,7 +2059,7 @@ TraceEventHandle TraceLog::AddTraceEventWithThreadIdAndTimestamp( EventCallback event_callback = reinterpret_cast<EventCallback>( subtle::NoBarrier_Load(&event_callback_)); if (event_callback) { - event_callback(now, + event_callback(offset_event_timestamp, phase == TRACE_EVENT_PHASE_COMPLETE ? TRACE_EVENT_PHASE_BEGIN : phase, category_group_enabled, name, id, @@ -2119,7 +2162,7 @@ void TraceLog::UpdateTraceEventDuration( std::string console_message; if (*category_group_enabled & ENABLED_FOR_RECORDING) { - OptionalAutoLock lock(lock_); + OptionalAutoLock lock(&lock_); TraceEvent* trace_event = GetEventByHandleInternal(handle, &lock); if (trace_event) { @@ -2169,6 +2212,10 @@ void TraceLog::CancelWatchEvent() { watch_event_callback_.Reset(); } +uint64 TraceLog::MangleEventId(uint64 id) { + return id ^ process_id_hash_; +} + void TraceLog::AddMetadataEventsWhileLocked() { lock_.AssertAcquired(); @@ -2350,24 +2397,6 @@ bool CategoryFilter::IsEmptyOrContainsLeadingOrTrailingWhitespace( str.at(str.length() - 1) == ' '; } -bool CategoryFilter::DoesCategoryGroupContainCategory( - const char* category_group, - const char* category) const { - DCHECK(category); - CStringTokenizer category_group_tokens(category_group, - category_group + strlen(category_group), ","); - while (category_group_tokens.GetNext()) { - std::string category_group_token = category_group_tokens.token(); - // Don't allow empty tokens, nor tokens with leading or trailing space. - DCHECK(!CategoryFilter::IsEmptyOrContainsLeadingOrTrailingWhitespace( - category_group_token)) - << "Disallowed category string"; - if (MatchPattern(category_group_token.c_str(), category)) - return true; - } - return false; -} - CategoryFilter::CategoryFilter(const std::string& filter_string) { if (!filter_string.empty()) Initialize(filter_string); @@ -2475,30 +2504,78 @@ bool CategoryFilter::IsCategoryGroupEnabled( const char* category_group_name) const { // TraceLog should call this method only as part of enabling/disabling // categories. + + bool had_enabled_by_default = false; + DCHECK(category_group_name); + 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(); + // Don't allow empty tokens, nor tokens with leading or trailing space. + DCHECK(!CategoryFilter::IsEmptyOrContainsLeadingOrTrailingWhitespace( + category_group_token)) + << "Disallowed category string"; + if (IsCategoryEnabled(category_group_token.c_str())) { + return true; + } + if (!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 + // (those explicitly enabled have priority due to first pass). + category_group_tokens.Reset(); + bool category_group_disabled = false; + while (category_group_tokens.GetNext()) { + std::string category_group_token = category_group_tokens.token(); + for (StringList::const_iterator ci = excluded_.begin(); + ci != excluded_.end(); ++ci) { + if (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 + // excluded_ list. + category_group_disabled = true; + break; + } + // One of the category of category_group_name is not present in + // excluded_ list. So, it has to be included_ list. Enable the + // category_group_name for recording. + category_group_disabled = false; + } + // One of the categories present in category_group_name is not present in + // excluded_ list. Implies this category_group_name group can be enabled + // for recording, since one of its groups is enabled for recording. + if (!category_group_disabled) + break; + } + // If the category group is not excluded, and there are no included patterns + // we consider this category group enabled, as long as it had categories + // other than disabled-by-default. + return !category_group_disabled && + included_.empty() && had_enabled_by_default; +} + +bool CategoryFilter::IsCategoryEnabled(const char* category_name) const { StringList::const_iterator ci; // Check the disabled- filters and the disabled-* wildcard first so that a // "*" filter does not include the disabled. for (ci = disabled_.begin(); ci != disabled_.end(); ++ci) { - if (DoesCategoryGroupContainCategory(category_group_name, ci->c_str())) + if (MatchPattern(category_name, ci->c_str())) return true; } - if (DoesCategoryGroupContainCategory(category_group_name, - TRACE_DISABLED_BY_DEFAULT("*"))) + + if (MatchPattern(category_name, TRACE_DISABLED_BY_DEFAULT("*"))) return false; for (ci = included_.begin(); ci != included_.end(); ++ci) { - if (DoesCategoryGroupContainCategory(category_group_name, ci->c_str())) + if (MatchPattern(category_name, ci->c_str())) return true; } - for (ci = excluded_.begin(); ci != excluded_.end(); ++ci) { - if (DoesCategoryGroupContainCategory(category_group_name, ci->c_str())) - return false; - } - // If the category group is not excluded, and there are no included patterns - // we consider this pattern enabled. - return included_.empty(); + return false; } bool CategoryFilter::HasIncludedPatterns() const { @@ -2539,7 +2616,7 @@ const CategoryFilter::StringList& return delays_; } -} // namespace debug +} // namespace trace_event } // namespace base namespace trace_event_internal { @@ -2547,7 +2624,7 @@ namespace trace_event_internal { ScopedTraceBinaryEfficient::ScopedTraceBinaryEfficient( const char* category_group, const char* name) { // The single atom works because for now the category_group can only be "gpu". - DCHECK(strcmp(category_group, "gpu") == 0); + DCHECK_EQ(strcmp(category_group, "gpu"), 0); static TRACE_EVENT_API_ATOMIC_WORD atomic = 0; INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO_CUSTOM_VARIABLES( category_group, atomic, category_group_enabled_); diff --git a/chromium/base/debug/trace_event_impl.h b/chromium/base/trace_event/trace_event_impl.h index 6075e2dee78..50d33ca7da9 100644 --- a/chromium/base/debug/trace_event_impl.h +++ b/chromium/base/trace_event/trace_event_impl.h @@ -3,8 +3,8 @@ // found in the LICENSE file. -#ifndef BASE_DEBUG_TRACE_EVENT_IMPL_H_ -#define BASE_DEBUG_TRACE_EVENT_IMPL_H_ +#ifndef BASE_TRACE_EVENT_TRACE_EVENT_IMPL_H_ +#define BASE_TRACE_EVENT_TRACE_EVENT_IMPL_H_ #include <stack> #include <string> @@ -18,6 +18,7 @@ #include "base/memory/ref_counted_memory.h" #include "base/memory/scoped_vector.h" #include "base/observer_list.h" +#include "base/single_thread_task_runner.h" #include "base/strings/string_util.h" #include "base/synchronization/condition_variable.h" #include "base/synchronization/lock.h" @@ -26,18 +27,19 @@ // Older style trace macros with explicit id and extra data // Only these macros result in publishing data to ETW as currently implemented. +// TODO(georgesak): Update/replace these with new ETW macros. #define TRACE_EVENT_BEGIN_ETW(name, id, extra) \ - base::debug::TraceLog::AddTraceEventEtw( \ + base::trace_event::TraceLog::AddTraceEventEtw( \ TRACE_EVENT_PHASE_BEGIN, \ name, reinterpret_cast<const void*>(id), extra) #define TRACE_EVENT_END_ETW(name, id, extra) \ - base::debug::TraceLog::AddTraceEventEtw( \ + base::trace_event::TraceLog::AddTraceEventEtw( \ TRACE_EVENT_PHASE_END, \ name, reinterpret_cast<const void*>(id), extra) #define TRACE_EVENT_INSTANT_ETW(name, id, extra) \ - base::debug::TraceLog::AddTraceEventEtw( \ + base::trace_event::TraceLog::AddTraceEventEtw( \ TRACE_EVENT_PHASE_INSTANT, \ name, reinterpret_cast<const void*>(id), extra) @@ -49,7 +51,7 @@ namespace base { class WaitableEvent; class MessageLoop; -namespace debug { +namespace trace_event { // For any argument of type TRACE_VALUE_TYPE_CONVERTABLE the provided // class must implement this interface. @@ -320,8 +322,8 @@ class BASE_EXPORT CategoryFilter { // categories are distinguished from included categories by the prefix '-'. std::string ToString() const; - // Determines whether category group would be enabled or - // disabled by this category filter. + // Returns true if at least one category in the list is enabled by this + // category filter. bool IsCategoryGroupEnabled(const char* category_group) const; // Return a list of the synthetic delays specified in this category filter. @@ -341,6 +343,9 @@ class BASE_EXPORT CategoryFilter { private: FRIEND_TEST_ALL_PREFIXES(TraceEventTestFixture, CategoryFilter); + // Returns true if category is enable according to this filter. + bool IsCategoryEnabled(const char* category_name) const; + static bool IsEmptyOrContainsLeadingOrTrailingWhitespace( const std::string& str); @@ -351,9 +356,6 @@ class BASE_EXPORT CategoryFilter { void WriteString(const StringList& delays, std::string* out) const; bool HasIncludedPatterns() const; - bool DoesCategoryGroupContainCategory(const char* category_group, - const char* category) const; - StringList included_; StringList disabled_; StringList excluded_; @@ -420,6 +422,13 @@ struct BASE_EXPORT TraceOptions { bool enable_systrace; }; +struct BASE_EXPORT TraceLogStatus { + TraceLogStatus(); + ~TraceLogStatus(); + size_t event_capacity; + size_t event_count; +}; + class BASE_EXPORT TraceLog { public: enum Mode { @@ -439,6 +448,8 @@ class BASE_EXPORT TraceLog { ENABLED_FOR_MONITORING = 1 << 1, // Category group enabled by SetEventCallbackEnabled(). ENABLED_FOR_EVENT_CALLBACK = 1 << 2, + // Category group enabled to export events to ETW. + ENABLED_FOR_ETW_EXPORT = 1 << 3 }; static TraceLog* GetInstance(); @@ -495,7 +506,7 @@ class BASE_EXPORT TraceLog { void RemoveEnabledStateObserver(EnabledStateObserver* listener); bool HasEnabledStateObserver(EnabledStateObserver* listener) const; - float GetBufferPercentFull() const; + TraceLogStatus GetStatus() const; bool BufferIsFull() const; // Not using base::Callback because of its limited by 7 parameters. @@ -531,10 +542,11 @@ class BASE_EXPORT TraceLog { // Due to the implementation of thread-local buffers, flush can't be // done when tracing is enabled. If called when tracing is enabled, the // callback will be called directly with (empty_string, false) to indicate - // the end of this unsuccessful flush. + // the end of this unsuccessful flush. Flush does the serialization + // on the same thread if the caller doesn't set use_worker_thread explicitly. typedef base::Callback<void(const scoped_refptr<base::RefCountedString>&, bool has_more_events)> OutputCallback; - void Flush(const OutputCallback& cb); + void Flush(const OutputCallback& cb, bool use_worker_thread = false); void FlushButLeaveBufferIntact(const OutputCallback& flush_output_callback); // Called by TRACE_EVENT* macros, don't call this directly. @@ -595,6 +607,8 @@ class BASE_EXPORT TraceLog { int process_id() const { return process_id_; } + uint64 MangleEventId(uint64 id); + // Exposed for unittesting: void WaitSamplingEventForTesting(); @@ -603,7 +617,6 @@ class BASE_EXPORT TraceLog { static void DeleteForTesting(); // Allow tests to inspect TraceEvents. - size_t GetEventsSize() const { return logged_events_->Size(); } TraceEvent* GetEventByHandle(TraceEventHandle handle); void SetProcessID(int process_id); @@ -704,7 +717,9 @@ class BASE_EXPORT TraceLog { // |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 ConvertTraceEventsToTraceFormat(scoped_ptr<TraceBuffer> logged_events, + // Usually it runs on a different thread. + static void ConvertTraceEventsToTraceFormat( + scoped_ptr<TraceBuffer> logged_events, const TraceLog::OutputCallback& flush_output_callback); void FinishFlush(int generation); void OnFlushTimeout(int generation); @@ -739,7 +754,6 @@ class BASE_EXPORT TraceLog { // This lock protects accesses to thread_names_, thread_event_start_times_ // and thread_colors_. Lock thread_info_lock_; - int locked_line_; Mode mode_; int num_traces_recorded_; scoped_ptr<TraceBuffer> logged_events_; @@ -785,8 +799,8 @@ class BASE_EXPORT TraceLog { ThreadLocalBoolean thread_is_in_trace_event_; // Contains the message loops of threads that have had at least one event - // added into the local event buffer. Not using MessageLoopProxy because we - // need to know the life time of the message loops. + // added into the local event buffer. Not using SingleThreadTaskRunner + // because we need to know the life time of the message loops. hash_set<MessageLoop*> thread_message_loops_; // For events which can't be added into the thread local buffer, e.g. events @@ -796,13 +810,14 @@ class BASE_EXPORT TraceLog { // Set when asynchronous Flush is in progress. OutputCallback flush_output_callback_; - scoped_refptr<MessageLoopProxy> flush_message_loop_proxy_; + scoped_refptr<SingleThreadTaskRunner> flush_task_runner_; subtle::AtomicWord generation_; + bool use_worker_thread_; DISALLOW_COPY_AND_ASSIGN(TraceLog); }; -} // namespace debug +} // namespace trace_event } // namespace base -#endif // BASE_DEBUG_TRACE_EVENT_IMPL_H_ +#endif // BASE_TRACE_EVENT_TRACE_EVENT_IMPL_H_ diff --git a/chromium/base/debug/trace_event_impl_constants.cc b/chromium/base/trace_event/trace_event_impl_constants.cc index 8e014110b82..ffeacff3831 100644 --- a/chromium/base/debug/trace_event_impl_constants.cc +++ b/chromium/base/trace_event/trace_event_impl_constants.cc @@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/debug/trace_event_impl.h" +#include "base/trace_event/trace_event_impl.h" namespace base { -namespace debug { +namespace trace_event { // Enable everything but debug and test categories by default. const char CategoryFilter::kDefaultCategoryFilterString[] = "-*Debug,-*Test"; @@ -24,5 +24,5 @@ const TraceLog::InternalTraceOptions const TraceLog::InternalTraceOptions TraceLog::kInternalRecordAsMuchAsPossible = 1 << 4; -} // namespace debug +} // namespace trace_event } // namespace base diff --git a/chromium/base/debug/trace_event_memory.cc b/chromium/base/trace_event/trace_event_memory.cc index 28318654750..89595890aec 100644 --- a/chromium/base/debug/trace_event_memory.cc +++ b/chromium/base/trace_event/trace_event_memory.cc @@ -2,20 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/debug/trace_event_memory.h" +#include "base/trace_event/trace_event_memory.h" #include "base/debug/leak_annotations.h" -#include "base/debug/trace_event.h" #include "base/lazy_instance.h" +#include "base/location.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" -#include "base/message_loop/message_loop.h" +#include "base/single_thread_task_runner.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/threading/thread_local_storage.h" +#include "base/trace_event/trace_event.h" namespace base { -namespace debug { +namespace trace_event { namespace { @@ -26,13 +27,13 @@ const size_t kMaxScopeDepth = 16; ///////////////////////////////////////////////////////////////////////////// // Holds a memory dump until the tracing system needs to serialize it. -class MemoryDumpHolder : public base::debug::ConvertableToTraceFormat { +class MemoryDumpHolder : public base::trace_event::ConvertableToTraceFormat { public: // Takes ownership of dump, which must be a JSON string, allocated with // malloc() and NULL terminated. explicit MemoryDumpHolder(char* dump) : dump_(dump) {} - // base::debug::ConvertableToTraceFormat overrides: + // base::trace_event::ConvertableToTraceFormat overrides: void AppendAsTraceFormat(std::string* out) const override { AppendHeapProfileAsTraceFormat(dump_, out); } @@ -70,13 +71,12 @@ void DeleteStackOnThreadCleanup(void* value) { delete stack; } -// Initializes the thread-local TraceMemoryStack pointer. Returns true on -// success or if it is already initialized. -bool InitThreadLocalStorage() { +// Initializes the thread-local TraceMemoryStack pointer. +void InitThreadLocalStorage() { if (tls_trace_memory_stack.initialized()) - return true; - // Initialize the thread-local storage key, returning true on success. - return tls_trace_memory_stack.Initialize(&DeleteStackOnThreadCleanup); + return; + // Initialize the thread-local storage key. + tls_trace_memory_stack.Initialize(&DeleteStackOnThreadCleanup); } // Clean up thread-local-storage in the main thread. @@ -144,11 +144,11 @@ int GetPseudoStack(int skip_count_ignored, void** stack_out) { ////////////////////////////////////////////////////////////////////////////// TraceMemoryController::TraceMemoryController( - scoped_refptr<MessageLoopProxy> message_loop_proxy, + scoped_refptr<SingleThreadTaskRunner> task_runner, HeapProfilerStartFunction heap_profiler_start_function, HeapProfilerStopFunction heap_profiler_stop_function, GetHeapProfileFunction get_heap_profile_function) - : message_loop_proxy_(message_loop_proxy), + : task_runner_(task_runner.Pass()), heap_profiler_start_function_(heap_profiler_start_function), heap_profiler_stop_function_(heap_profiler_stop_function), get_heap_profile_function_(get_heap_profile_function), @@ -165,7 +165,7 @@ TraceMemoryController::~TraceMemoryController() { TraceLog::GetInstance()->RemoveEnabledStateObserver(this); } - // base::debug::TraceLog::EnabledStateChangedObserver overrides: +// base::trace_event::TraceLog::EnabledStateChangedObserver overrides: void TraceMemoryController::OnTraceLogEnabled() { // Check to see if tracing is enabled for the memory category. bool enabled; @@ -174,10 +174,9 @@ void TraceMemoryController::OnTraceLogEnabled() { if (!enabled) return; DVLOG(1) << "OnTraceLogEnabled"; - message_loop_proxy_->PostTask( - FROM_HERE, - base::Bind(&TraceMemoryController::StartProfiling, - weak_factory_.GetWeakPtr())); + task_runner_->PostTask(FROM_HERE, + base::Bind(&TraceMemoryController::StartProfiling, + weak_factory_.GetWeakPtr())); } void TraceMemoryController::OnTraceLogDisabled() { @@ -185,10 +184,9 @@ void TraceMemoryController::OnTraceLogDisabled() { // called, so we cannot tell if it was enabled before. Always try to turn // off profiling. DVLOG(1) << "OnTraceLogDisabled"; - message_loop_proxy_->PostTask( - FROM_HERE, - base::Bind(&TraceMemoryController::StopProfiling, - weak_factory_.GetWeakPtr())); + task_runner_->PostTask(FROM_HERE, + base::Bind(&TraceMemoryController::StopProfiling, + weak_factory_.GetWeakPtr())); } void TraceMemoryController::StartProfiling() { @@ -196,8 +194,7 @@ void TraceMemoryController::StartProfiling() { if (dump_timer_.IsRunning()) return; DVLOG(1) << "Starting trace memory"; - if (!InitThreadLocalStorage()) - return; + InitThreadLocalStorage(); ScopedTraceMemory::set_enabled(true); // Call ::HeapProfilerWithPseudoStackStart(). heap_profiler_start_function_(&GetPseudoStack); @@ -436,5 +433,5 @@ const char* StringFromHexAddress(const std::string& hex_address) { return reinterpret_cast<const char*>(address); } -} // namespace debug +} // namespace trace_event } // namespace base diff --git a/chromium/base/debug/trace_event_memory.h b/chromium/base/trace_event/trace_event_memory.h index 94c3f91cd27..e2b3ae93060 100644 --- a/chromium/base/debug/trace_event_memory.h +++ b/chromium/base/trace_event/trace_event_memory.h @@ -2,15 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_DEBUG_TRACE_EVENT_MEMORY_H_ -#define BASE_DEBUG_TRACE_EVENT_MEMORY_H_ +#ifndef BASE_TRACE_EVENT_TRACE_EVENT_MEMORY_H_ +#define BASE_TRACE_EVENT_TRACE_EVENT_MEMORY_H_ #include "base/base_export.h" -#include "base/debug/trace_event_impl.h" #include "base/gtest_prod_util.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" #include "base/timer/timer.h" +#include "base/trace_event/trace_event_impl.h" // TODO(jamescook): Windows support for memory tracing. #if !defined(NO_TCMALLOC) && !defined(OS_NACL) && \ @@ -20,9 +20,9 @@ namespace base { -class MessageLoopProxy; +class SingleThreadTaskRunner; -namespace debug { +namespace trace_event { // Watches for chrome://tracing to be enabled or disabled. When tracing is // enabled, also enables tcmalloc heap profiling. This class is the preferred @@ -35,18 +35,17 @@ class BASE_EXPORT TraceMemoryController typedef void (*HeapProfilerStopFunction)(); typedef char* (*GetHeapProfileFunction)(); - // |message_loop_proxy| must be a proxy to the primary thread for the client + // |task_runner| must be a task runner for the primary thread for the client // process, e.g. the UI thread in a browser. The function pointers must be // pointers to tcmalloc heap profiling functions; by avoiding direct calls to // these functions we avoid a dependency on third_party/tcmalloc from base. - TraceMemoryController( - scoped_refptr<MessageLoopProxy> message_loop_proxy, - HeapProfilerStartFunction heap_profiler_start_function, - HeapProfilerStopFunction heap_profiler_stop_function, - GetHeapProfileFunction get_heap_profile_function); + TraceMemoryController(scoped_refptr<SingleThreadTaskRunner> task_runner, + HeapProfilerStartFunction heap_profiler_start_function, + HeapProfilerStopFunction heap_profiler_stop_function, + GetHeapProfileFunction get_heap_profile_function); virtual ~TraceMemoryController(); - // base::debug::TraceLog::EnabledStateChangedObserver overrides: + // base::trace_event::TraceLog::EnabledStateChangedObserver overrides: void OnTraceLogEnabled() override; void OnTraceLogDisabled() override; @@ -65,7 +64,7 @@ class BASE_EXPORT TraceMemoryController bool IsTimerRunningForTest() const; // Ensures the observer starts and stops tracing on the primary thread. - scoped_refptr<MessageLoopProxy> message_loop_proxy_; + scoped_refptr<SingleThreadTaskRunner> task_runner_; // Pointers to tcmalloc heap profiling functions. Allows this class to use // tcmalloc functions without introducing a dependency from base to tcmalloc. @@ -146,7 +145,7 @@ BASE_EXPORT bool AppendHeapProfileLineAsTraceFormat(const std::string& line, // and "error" if |address| could not be parsed. Visible for testing. BASE_EXPORT const char* StringFromHexAddress(const std::string& hex_address); -} // namespace debug +} // namespace trace_event } // namespace base // Make local variables with unique names based on the line number. Note that @@ -159,7 +158,7 @@ BASE_EXPORT const char* StringFromHexAddress(const std::string& hex_address); // It generates a unique local variable name using the macros above. #if defined(TCMALLOC_TRACE_MEMORY_SUPPORTED) #define INTERNAL_TRACE_MEMORY(category, name) \ - base::debug::ScopedTraceMemory INTERNAL_TRACE_MEMORY_ID(category, name); + base::trace_event::ScopedTraceMemory INTERNAL_TRACE_MEMORY_ID(category, name); #else #define INTERNAL_TRACE_MEMORY(category, name) #endif // defined(TRACE_MEMORY_SUPPORTED) @@ -169,4 +168,4 @@ BASE_EXPORT const char* StringFromHexAddress(const std::string& hex_address); // visualizer skips them. Must match the value in heap.js. #define TRACE_MEMORY_IGNORE "trace-memory-ignore" -#endif // BASE_DEBUG_TRACE_EVENT_MEMORY_H_ +#endif // BASE_TRACE_EVENT_TRACE_EVENT_MEMORY_H_ diff --git a/chromium/base/debug/trace_event_memory_unittest.cc b/chromium/base/trace_event/trace_event_memory_unittest.cc index 3f5cad3ede2..781a0544c45 100644 --- a/chromium/base/debug/trace_event_memory_unittest.cc +++ b/chromium/base/trace_event/trace_event_memory_unittest.cc @@ -2,13 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/debug/trace_event_memory.h" +#include "base/trace_event/trace_event_memory.h" #include <sstream> #include <string> -#include "base/debug/trace_event_impl.h" -#include "base/message_loop/message_loop.h" +#include "base/trace_event/trace_event_impl.h" #include "testing/gtest/include/gtest/gtest.h" #if defined(TCMALLOC_TRACE_MEMORY_SUPPORTED) @@ -16,14 +15,14 @@ #endif namespace base { -namespace debug { +namespace trace_event { // Tests for the trace event memory tracking system. Exists as a class so it // can be a friend of TraceMemoryController. class TraceMemoryTest : public testing::Test { public: TraceMemoryTest() {} - virtual ~TraceMemoryTest() {} + ~TraceMemoryTest() override {} private: DISALLOW_COPY_AND_ASSIGN(TraceMemoryTest); @@ -40,12 +39,9 @@ TEST_F(TraceMemoryTest, TraceMemoryController) { EXPECT_EQ(0u, TraceLog::GetInstance()->GetObserverCountForTest()); // Creating a controller adds it to the TraceLog observer list. - scoped_ptr<TraceMemoryController> controller( - new TraceMemoryController( - message_loop.message_loop_proxy(), - ::HeapProfilerWithPseudoStackStart, - ::HeapProfilerStop, - ::GetHeapProfile)); + scoped_ptr<TraceMemoryController> controller(new TraceMemoryController( + message_loop.task_runner(), ::HeapProfilerWithPseudoStackStart, + ::HeapProfilerStop, ::GetHeapProfile)); EXPECT_EQ(1u, TraceLog::GetInstance()->GetObserverCountForTest()); EXPECT_TRUE( TraceLog::GetInstance()->HasEnabledStateObserver(controller.get())); @@ -236,5 +232,5 @@ TEST_F(TraceMemoryTest, StringFromHexAddress) { EXPECT_STREQ(kHello, StringFromHexAddress(hex_address.str())); } -} // namespace debug +} // namespace trace_event } // namespace base diff --git a/chromium/base/debug/trace_event_synthetic_delay.cc b/chromium/base/trace_event/trace_event_synthetic_delay.cc index 6abfe183572..bad79ccbc8c 100644 --- a/chromium/base/debug/trace_event_synthetic_delay.cc +++ b/chromium/base/trace_event/trace_event_synthetic_delay.cc @@ -2,15 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/debug/trace_event_synthetic_delay.h" #include "base/memory/singleton.h" +#include "base/third_party/dynamic_annotations/dynamic_annotations.h" +#include "base/trace_event/trace_event_synthetic_delay.h" namespace { const int kMaxSyntheticDelays = 32; } // namespace namespace base { -namespace debug { +namespace trace_event { TraceEventSyntheticDelayClock::TraceEventSyntheticDelayClock() {} TraceEventSyntheticDelayClock::~TraceEventSyntheticDelayClock() {} @@ -184,7 +185,7 @@ TraceEventSyntheticDelay* TraceEventSyntheticDelayRegistry::GetOrCreateDelay( } base::TimeTicks TraceEventSyntheticDelayRegistry::Now() { - return base::TimeTicks::HighResNow(); + return base::TimeTicks::Now(); } void TraceEventSyntheticDelayRegistry::ResetAllDelays() { @@ -200,7 +201,7 @@ void ResetTraceEventSyntheticDelays() { TraceEventSyntheticDelayRegistry::GetInstance()->ResetAllDelays(); } -} // namespace debug +} // namespace trace_event } // namespace base namespace trace_event_internal { @@ -215,15 +216,16 @@ ScopedSyntheticDelay::~ScopedSyntheticDelay() { delay_impl_->EndParallel(end_time_); } -base::debug::TraceEventSyntheticDelay* GetOrCreateDelay( +base::trace_event::TraceEventSyntheticDelay* GetOrCreateDelay( const char* name, base::subtle::AtomicWord* impl_ptr) { - base::debug::TraceEventSyntheticDelay* delay_impl = - reinterpret_cast<base::debug::TraceEventSyntheticDelay*>( + base::trace_event::TraceEventSyntheticDelay* delay_impl = + reinterpret_cast<base::trace_event::TraceEventSyntheticDelay*>( base::subtle::Acquire_Load(impl_ptr)); if (!delay_impl) { - delay_impl = base::debug::TraceEventSyntheticDelayRegistry::GetInstance() - ->GetOrCreateDelay(name); + delay_impl = + base::trace_event::TraceEventSyntheticDelayRegistry::GetInstance() + ->GetOrCreateDelay(name); base::subtle::Release_Store( impl_ptr, reinterpret_cast<base::subtle::AtomicWord>(delay_impl)); } diff --git a/chromium/base/debug/trace_event_synthetic_delay.h b/chromium/base/trace_event/trace_event_synthetic_delay.h index 06d6cdea1f9..0df794b46c2 100644 --- a/chromium/base/debug/trace_event_synthetic_delay.h +++ b/chromium/base/trace_event/trace_event_synthetic_delay.h @@ -29,13 +29,13 @@ // Note that a single delay may begin on one thread and end on another. This // implies that a single delay cannot not be applied in several threads at once. -#ifndef BASE_DEBUG_TRACE_EVENT_SYNTHETIC_DELAY_H_ -#define BASE_DEBUG_TRACE_EVENT_SYNTHETIC_DELAY_H_ +#ifndef BASE_TRACE_EVENT_TRACE_EVENT_SYNTHETIC_DELAY_H_ +#define BASE_TRACE_EVENT_TRACE_EVENT_SYNTHETIC_DELAY_H_ #include "base/atomicops.h" -#include "base/debug/trace_event.h" #include "base/synchronization/lock.h" #include "base/time/time.h" +#include "base/trace_event/trace_event.h" // Apply a named delay in the current scope. #define TRACE_EVENT_SYNTHETIC_DELAY(name) \ @@ -65,7 +65,7 @@ template <typename Type> struct DefaultSingletonTraits; namespace base { -namespace debug { +namespace trace_event { // Time source for computing delay durations. Used for testing. class TRACE_EVENT_API_CLASS_EXPORT TraceEventSyntheticDelayClock { @@ -138,7 +138,7 @@ class TRACE_EVENT_API_CLASS_EXPORT TraceEventSyntheticDelay { // Set the target durations of all registered synthetic delay points to zero. TRACE_EVENT_API_CLASS_EXPORT void ResetTraceEventSyntheticDelays(); -} // namespace debug +} // namespace trace_event } // namespace base namespace trace_event_internal { @@ -151,16 +151,16 @@ class TRACE_EVENT_API_CLASS_EXPORT ScopedSyntheticDelay { ~ScopedSyntheticDelay(); private: - base::debug::TraceEventSyntheticDelay* delay_impl_; + base::trace_event::TraceEventSyntheticDelay* delay_impl_; base::TimeTicks end_time_; DISALLOW_COPY_AND_ASSIGN(ScopedSyntheticDelay); }; // Helper for registering delays. Do not use directly. -TRACE_EVENT_API_CLASS_EXPORT base::debug::TraceEventSyntheticDelay* +TRACE_EVENT_API_CLASS_EXPORT base::trace_event::TraceEventSyntheticDelay* GetOrCreateDelay(const char* name, base::subtle::AtomicWord* impl_ptr); } // namespace trace_event_internal -#endif /* BASE_DEBUG_TRACE_EVENT_SYNTHETIC_DELAY_H_ */ +#endif // BASE_TRACE_EVENT_TRACE_EVENT_SYNTHETIC_DELAY_H_ diff --git a/chromium/base/debug/trace_event_synthetic_delay_unittest.cc b/chromium/base/trace_event/trace_event_synthetic_delay_unittest.cc index 60e4d20368e..1dc0fc26f5c 100644 --- a/chromium/base/debug/trace_event_synthetic_delay_unittest.cc +++ b/chromium/base/trace_event/trace_event_synthetic_delay_unittest.cc @@ -2,12 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/debug/trace_event_synthetic_delay.h" +#include "base/trace_event/trace_event_synthetic_delay.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { -namespace debug { +namespace trace_event { namespace { const int kTargetDurationMs = 100; @@ -21,9 +21,7 @@ class TraceEventSyntheticDelayTest : public testing::Test, public TraceEventSyntheticDelayClock { public: TraceEventSyntheticDelayTest() {} - virtual ~TraceEventSyntheticDelayTest() { - ResetTraceEventSyntheticDelays(); - } + ~TraceEventSyntheticDelayTest() override { ResetTraceEventSyntheticDelays(); } // TraceEventSyntheticDelayClock implementation. base::TimeTicks Now() override { @@ -152,5 +150,5 @@ TEST_F(TraceEventSyntheticDelayTest, BeginParallel) { EXPECT_LT((Now() - start_time).InMilliseconds(), kShortDurationMs); } -} // namespace debug +} // namespace trace_event } // namespace base diff --git a/chromium/base/debug/trace_event_system_stats_monitor.cc b/chromium/base/trace_event/trace_event_system_stats_monitor.cc index 9cbefd8d6cc..98f361a9d9b 100644 --- a/chromium/base/debug/trace_event_system_stats_monitor.cc +++ b/chromium/base/trace_event/trace_event_system_stats_monitor.cc @@ -2,10 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/debug/trace_event_system_stats_monitor.h" +#include "base/trace_event/trace_event_system_stats_monitor.h" #include "base/debug/leak_annotations.h" -#include "base/debug/trace_event.h" #include "base/json/json_writer.h" #include "base/lazy_instance.h" #include "base/logging.h" @@ -14,15 +13,16 @@ #include "base/strings/string_util.h" #include "base/thread_task_runner_handle.h" #include "base/threading/thread_local_storage.h" +#include "base/trace_event/trace_event.h" namespace base { -namespace debug { +namespace trace_event { namespace { ///////////////////////////////////////////////////////////////////////////// // Holds profiled system stats until the tracing system needs to serialize it. -class SystemStatsHolder : public base::debug::ConvertableToTraceFormat { +class SystemStatsHolder : public base::trace_event::ConvertableToTraceFormat { public: SystemStatsHolder() { } @@ -30,7 +30,7 @@ class SystemStatsHolder : public base::debug::ConvertableToTraceFormat { // Uses the previous stats to compute rates if this is not the first profile. void GetSystemProfilingStats(); - // base::debug::ConvertableToTraceFormat overrides: + // base::trace_event::ConvertableToTraceFormat overrides: void AppendAsTraceFormat(std::string* out) const override { AppendSystemProfileAsTraceFormat(system_stats_, out); } @@ -129,5 +129,5 @@ void AppendSystemProfileAsTraceFormat(const SystemMetrics& system_metrics, *output += tmp; } -} // namespace debug +} // namespace trace_event } // namespace base diff --git a/chromium/base/debug/trace_event_system_stats_monitor.h b/chromium/base/trace_event/trace_event_system_stats_monitor.h index 143f1875db8..051669a35f5 100644 --- a/chromium/base/debug/trace_event_system_stats_monitor.h +++ b/chromium/base/trace_event/trace_event_system_stats_monitor.h @@ -2,22 +2,22 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_DEBUG_TRACE_EVENT_SYSTEM_STATS_MONITOR_H_ -#define BASE_DEBUG_TRACE_EVENT_SYSTEM_STATS_MONITOR_H_ +#ifndef BASE_TRACE_EVENT_TRACE_EVENT_SYSTEM_STATS_MONITOR_H_ +#define BASE_TRACE_EVENT_TRACE_EVENT_SYSTEM_STATS_MONITOR_H_ #include "base/base_export.h" -#include "base/debug/trace_event_impl.h" #include "base/gtest_prod_util.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" #include "base/process/process_metrics.h" #include "base/timer/timer.h" +#include "base/trace_event/trace_event_impl.h" namespace base { class SingleThreadTaskRunner; -namespace debug { +namespace trace_event { // Watches for chrome://tracing to be enabled or disabled. When tracing is // enabled, also enables system events profiling. This class is the preferred @@ -35,7 +35,7 @@ class BASE_EXPORT TraceEventSystemStatsMonitor virtual ~TraceEventSystemStatsMonitor(); - // base::debug::TraceLog::EnabledStateChangedObserver overrides: + // base::trace_event::TraceLog::EnabledStateChangedObserver overrides: void OnTraceLogEnabled() override; void OnTraceLogDisabled() override; @@ -69,7 +69,7 @@ BASE_EXPORT void AppendSystemProfileAsTraceFormat(const SystemMetrics& system_stats, std::string* output); -} // namespace debug +} // namespace trace_event } // namespace base -#endif // BASE_DEBUG_TRACE_EVENT_SYSTEM_STATS_MONITOR_H_ +#endif // BASE_TRACE_EVENT_TRACE_EVENT_SYSTEM_STATS_MONITOR_H_ diff --git a/chromium/base/debug/trace_event_system_stats_monitor_unittest.cc b/chromium/base/trace_event/trace_event_system_stats_monitor_unittest.cc index 5cc2a0fd383..03dff591cfa 100644 --- a/chromium/base/debug/trace_event_system_stats_monitor_unittest.cc +++ b/chromium/base/trace_event/trace_event_system_stats_monitor_unittest.cc @@ -2,17 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/debug/trace_event_system_stats_monitor.h" +#include "base/trace_event/trace_event_system_stats_monitor.h" #include <sstream> #include <string> -#include "base/debug/trace_event_impl.h" -#include "base/message_loop/message_loop.h" +#include "base/trace_event/trace_event_impl.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { -namespace debug { +namespace trace_event { #if !defined(OS_IOS) // Tests for the system stats monitor. @@ -20,7 +19,7 @@ namespace debug { class TraceSystemStatsMonitorTest : public testing::Test { public: TraceSystemStatsMonitorTest() {} - virtual ~TraceSystemStatsMonitorTest() {} + ~TraceSystemStatsMonitorTest() override {} private: DISALLOW_COPY_AND_ASSIGN(TraceSystemStatsMonitorTest); @@ -36,8 +35,7 @@ TEST_F(TraceSystemStatsMonitorTest, TraceEventSystemStatsMonitor) { // Creating a system stats monitor adds it to the TraceLog observer list. scoped_ptr<TraceEventSystemStatsMonitor> system_stats_monitor( - new TraceEventSystemStatsMonitor( - message_loop.message_loop_proxy())); + new TraceEventSystemStatsMonitor(message_loop.task_runner())); EXPECT_EQ(1u, TraceLog::GetInstance()->GetObserverCountForTest()); EXPECT_TRUE( TraceLog::GetInstance()->HasEnabledStateObserver( @@ -62,5 +60,5 @@ TEST_F(TraceSystemStatsMonitorTest, TraceEventSystemStatsMonitor) { } #endif // !defined(OS_IOS) -} // namespace debug +} // namespace trace_event } // namespace base diff --git a/chromium/base/debug/trace_event_unittest.cc b/chromium/base/trace_event/trace_event_unittest.cc index 69b574305e2..d03224301cd 100644 --- a/chromium/base/debug/trace_event_unittest.cc +++ b/chromium/base/trace_event/trace_event_unittest.cc @@ -2,32 +2,32 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/debug/trace_event_unittest.h" - #include <math.h> #include <cstdlib> #include "base/bind.h" #include "base/command_line.h" -#include "base/debug/trace_event.h" -#include "base/debug/trace_event_synthetic_delay.h" #include "base/json/json_reader.h" #include "base/json/json_writer.h" +#include "base/location.h" #include "base/memory/ref_counted_memory.h" #include "base/memory/scoped_ptr.h" #include "base/memory/singleton.h" #include "base/process/process_handle.h" +#include "base/single_thread_task_runner.h" #include "base/strings/stringprintf.h" #include "base/synchronization/waitable_event.h" #include "base/threading/platform_thread.h" #include "base/threading/thread.h" #include "base/time/time.h" +#include "base/trace_event/trace_event.h" +#include "base/trace_event/trace_event_synthetic_delay.h" #include "base/values.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { -namespace debug { +namespace trace_event { namespace { @@ -97,10 +97,9 @@ class TraceEventTestFixture : public testing::Test { WaitableEvent flush_complete_event(false, false); Thread flush_thread("flush"); flush_thread.Start(); - flush_thread.message_loop()->PostTask(FROM_HERE, - base::Bind(&TraceEventTestFixture::EndTraceAndFlushAsync, - base::Unretained(this), - &flush_complete_event)); + flush_thread.task_runner()->PostTask( + FROM_HERE, base::Bind(&TraceEventTestFixture::EndTraceAndFlushAsync, + base::Unretained(this), &flush_complete_event)); flush_complete_event.Wait(); } @@ -125,7 +124,7 @@ class TraceEventTestFixture : public testing::Test { base::Unretained(flush_complete_event))); } - virtual void SetUp() override { + void SetUp() override { const char* name = PlatformThread::GetName(); old_thread_name_ = name ? strdup(name) : NULL; @@ -136,7 +135,7 @@ class TraceEventTestFixture : public testing::Test { trace_buffer_.SetOutputCallback(json_output_.GetCallback()); event_watch_notification_ = 0; } - virtual void TearDown() override { + void TearDown() override { if (TraceLog::GetInstance()) EXPECT_FALSE(TraceLog::GetInstance()->IsEnabled()); PlatformThread::SetName(old_thread_name_ ? old_thread_name_ : ""); @@ -871,13 +870,6 @@ void ValidateInstantEventPresentOnEveryThread(const ListValue& trace_parsed, } // namespace -void HighResSleepForTraceTest(base::TimeDelta elapsed) { - base::TimeTicks end_time = base::TimeTicks::HighResNow() + elapsed; - do { - base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(1)); - } while (base::TimeTicks::HighResNow() < end_time); -} - // Simple Test for emitting data and validating it was received. TEST_F(TraceEventTestFixture, DataCaptured) { TraceLog::GetInstance()->SetEnabled( @@ -1214,8 +1206,8 @@ TEST_F(TraceEventTestFixture, Categories) { EndTraceAndFlush(); EXPECT_TRUE(FindMatchingValue("cat", "inc2")); EXPECT_FALSE(FindMatchingValue("cat", "inc")); - EXPECT_FALSE(FindMatchingValue("cat", "inc2,inc")); - EXPECT_FALSE(FindMatchingValue("cat", "inc,inc2")); + EXPECT_TRUE(FindMatchingValue("cat", "inc2,inc")); + EXPECT_TRUE(FindMatchingValue("cat", "inc,inc2")); // Exclude existent wildcard -> all categories not matching wildcard Clear(); @@ -1366,8 +1358,7 @@ TEST_F(TraceEventTestFixture, AsyncBeginEndPointerMangling) { TEST_F(TraceEventTestFixture, StaticStringVsString) { TraceLog* tracer = TraceLog::GetInstance(); // Make sure old events are flushed: - EndTraceAndFlush(); - EXPECT_EQ(0u, tracer->GetEventsSize()); + EXPECT_EQ(0u, tracer->GetStatus().event_count); const unsigned char* category_group_enabled = TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED("cat"); @@ -1384,8 +1375,7 @@ TEST_F(TraceEventTestFixture, StaticStringVsString) { TRACE_EVENT_PHASE_INSTANT, category_group_enabled, "name2", 0, 0, "arg1", TRACE_STR_COPY("argval"), "arg2", TRACE_STR_COPY("argval")); - size_t num_events = tracer->GetEventsSize(); - EXPECT_GT(num_events, 1u); + EXPECT_GT(tracer->GetStatus().event_count, 1u); const TraceEvent* event1 = tracer->GetEventByHandle(handle1); const TraceEvent* event2 = tracer->GetEventByHandle(handle2); ASSERT_TRUE(event1); @@ -1414,8 +1404,7 @@ TEST_F(TraceEventTestFixture, StaticStringVsString) { TRACE_EVENT_PHASE_INSTANT, category_group_enabled, "name2", 0, 0, "arg1", TRACE_STR_COPY(str1), "arg2", TRACE_STR_COPY(str2)); - size_t num_events = tracer->GetEventsSize(); - EXPECT_GT(num_events, 1u); + EXPECT_GT(tracer->GetStatus().event_count, 1u); const TraceEvent* event1 = tracer->GetEventByHandle(handle1); const TraceEvent* event2 = tracer->GetEventByHandle(handle2); ASSERT_TRUE(event1); @@ -1436,7 +1425,7 @@ TEST_F(TraceEventTestFixture, DataCapturedOnThread) { WaitableEvent task_complete_event(false, false); thread.Start(); - thread.message_loop()->PostTask( + thread.task_runner()->PostTask( FROM_HERE, base::Bind(&TraceWithAllMacroVariants, &task_complete_event)); task_complete_event.Wait(); thread.Stop(); @@ -1457,9 +1446,9 @@ TEST_F(TraceEventTestFixture, DataCapturedManyThreads) { threads[i] = new Thread(StringPrintf("Thread %d", i)); task_complete_events[i] = new WaitableEvent(false, false); threads[i]->Start(); - threads[i]->message_loop()->PostTask( - FROM_HERE, base::Bind(&TraceManyInstantEvents, - i, num_events, task_complete_events[i])); + threads[i]->task_runner()->PostTask( + FROM_HERE, base::Bind(&TraceManyInstantEvents, i, num_events, + task_complete_events[i])); } for (int i = 0; i < num_threads; i++) { @@ -1505,9 +1494,9 @@ TEST_F(TraceEventTestFixture, ThreadNames) { task_complete_events[i] = new WaitableEvent(false, false); threads[i]->Start(); thread_ids[i] = threads[i]->thread_id(); - threads[i]->message_loop()->PostTask( - FROM_HERE, base::Bind(&TraceManyInstantEvents, - i, kNumEvents, task_complete_events[i])); + threads[i]->task_runner()->PostTask( + FROM_HERE, base::Bind(&TraceManyInstantEvents, i, kNumEvents, + task_complete_events[i])); } for (int i = 0; i < kNumThreads; i++) { task_complete_events[i]->Wait(); @@ -1538,7 +1527,7 @@ TEST_F(TraceEventTestFixture, ThreadNames) { // See if this thread name is one of the threads we just created for (int j = 0; j < kNumThreads; j++) { - if(static_cast<int>(thread_ids[j]) != tmp_int) + if (static_cast<int>(thread_ids[j]) != tmp_int) continue; std::string expected_name = StringPrintf("Thread %d", j); @@ -1618,6 +1607,22 @@ TEST_F(TraceEventTestFixture, DisabledCategories) { EXPECT_FIND_("disabled-by-default-cc"); EXPECT_FIND_("other_included"); } + + Clear(); + + BeginSpecificTrace("other_included"); + TRACE_EVENT_INSTANT0(TRACE_DISABLED_BY_DEFAULT("cc") ",other_included", + "first", TRACE_EVENT_SCOPE_THREAD); + TRACE_EVENT_INSTANT0("other_included," TRACE_DISABLED_BY_DEFAULT("cc"), + "second", TRACE_EVENT_SCOPE_THREAD); + EndTraceAndFlush(); + + { + const DictionaryValue* item = NULL; + ListValue& trace_parsed = trace_parsed_; + EXPECT_FIND_("disabled-by-default-cc,other_included"); + EXPECT_FIND_("other_included,disabled-by-default-cc"); + } } TEST_F(TraceEventTestFixture, NormallyNoDeepCopy) { @@ -2202,14 +2207,14 @@ TEST_F(TraceEventTestFixture, PrimitiveArgs) { class TraceEventCallbackTest : public TraceEventTestFixture { public: - virtual void SetUp() override { + void SetUp() override { TraceEventTestFixture::SetUp(); ASSERT_EQ(NULL, s_instance); s_instance = this; } - virtual void TearDown() override { + void TearDown() override { TraceLog::GetInstance()->SetDisabled(); - ASSERT_TRUE(!!s_instance); + ASSERT_TRUE(s_instance); s_instance = NULL; TraceEventTestFixture::TearDown(); } @@ -2620,18 +2625,18 @@ TEST_F(TraceEventTestFixture, CategoryFilter) { EXPECT_TRUE(default_cf.IsCategoryGroupEnabled("not-excluded-category")); EXPECT_FALSE( default_cf.IsCategoryGroupEnabled("disabled-by-default-category")); - EXPECT_FALSE(default_cf.IsCategoryGroupEnabled("Category1,CategoryDebug")); - EXPECT_FALSE(default_cf.IsCategoryGroupEnabled("CategoryDebug,Category1")); - EXPECT_FALSE(default_cf.IsCategoryGroupEnabled("CategoryTest,Category2")); + EXPECT_TRUE(default_cf.IsCategoryGroupEnabled("Category1,CategoryDebug")); + EXPECT_TRUE(default_cf.IsCategoryGroupEnabled("CategoryDebug,Category1")); + EXPECT_TRUE(default_cf.IsCategoryGroupEnabled("CategoryTest,Category2")); // Make sure that upon an empty string, we fall back to the default filter. default_cf = CategoryFilter(); category_filter_str = default_cf.ToString(); EXPECT_STREQ("-*Debug,-*Test", category_filter_str.c_str()); EXPECT_TRUE(default_cf.IsCategoryGroupEnabled("not-excluded-category")); - EXPECT_FALSE(default_cf.IsCategoryGroupEnabled("Category1,CategoryDebug")); - EXPECT_FALSE(default_cf.IsCategoryGroupEnabled("CategoryDebug,Category1")); - EXPECT_FALSE(default_cf.IsCategoryGroupEnabled("CategoryTest,Category2")); + EXPECT_TRUE(default_cf.IsCategoryGroupEnabled("Category1,CategoryDebug")); + EXPECT_TRUE(default_cf.IsCategoryGroupEnabled("CategoryDebug,Category1")); + EXPECT_TRUE(default_cf.IsCategoryGroupEnabled("CategoryTest,Category2")); // Using an arbitrary non-empty filter. CategoryFilter cf("included,-excluded,inc_pattern*,-exc_pattern*"); @@ -2721,17 +2726,17 @@ TEST_F(TraceEventTestFixture, SetCurrentThreadBlocksMessageLoopBeforeTracing) { Thread thread("1"); WaitableEvent task_complete_event(false, false); thread.Start(); - thread.message_loop()->PostTask( + thread.task_runner()->PostTask( FROM_HERE, Bind(&TraceLog::SetCurrentThreadBlocksMessageLoop, Unretained(TraceLog::GetInstance()))); - thread.message_loop()->PostTask( + thread.task_runner()->PostTask( FROM_HERE, Bind(&TraceWithAllMacroVariants, &task_complete_event)); task_complete_event.Wait(); WaitableEvent task_start_event(false, false); WaitableEvent task_stop_event(false, false); - thread.message_loop()->PostTask( + thread.task_runner()->PostTask( FROM_HERE, Bind(&BlockUntilStopped, &task_start_event, &task_stop_event)); task_start_event.Wait(); @@ -2792,15 +2797,15 @@ TEST_F(TraceEventTestFixture, SetCurrentThreadBlocksMessageLoopAfterTracing) { WaitableEvent task_complete_event(false, false); thread.Start(); - thread.message_loop()->PostTask( + thread.task_runner()->PostTask( FROM_HERE, Bind(&TraceWithAllMacroVariants, &task_complete_event)); task_complete_event.Wait(); WaitableEvent task_start_event(false, false); WaitableEvent task_stop_event(false, false); - thread.message_loop()->PostTask( - FROM_HERE, Bind(&SetBlockingFlagAndBlockUntilStopped, - &task_start_event, &task_stop_event)); + thread.task_runner()->PostTask( + FROM_HERE, Bind(&SetBlockingFlagAndBlockUntilStopped, &task_start_event, + &task_stop_event)); task_start_event.Wait(); EndTraceAndFlush(); @@ -2817,14 +2822,14 @@ TEST_F(TraceEventTestFixture, ThreadOnceBlocking) { WaitableEvent task_complete_event(false, false); thread.Start(); - thread.message_loop()->PostTask( + thread.task_runner()->PostTask( FROM_HERE, Bind(&TraceWithAllMacroVariants, &task_complete_event)); task_complete_event.Wait(); task_complete_event.Reset(); WaitableEvent task_start_event(false, false); WaitableEvent task_stop_event(false, false); - thread.message_loop()->PostTask( + thread.task_runner()->PostTask( FROM_HERE, Bind(&BlockUntilStopped, &task_start_event, &task_stop_event)); task_start_event.Wait(); @@ -2839,7 +2844,7 @@ TEST_F(TraceEventTestFixture, ThreadOnceBlocking) { // executed in the thread before continuing. task_start_event.Reset(); task_stop_event.Reset(); - thread.message_loop()->PostTask( + thread.task_runner()->PostTask( FROM_HERE, Bind(&BlockUntilStopped, &task_start_event, &task_stop_event)); task_start_event.Wait(); task_stop_event.Signal(); @@ -2848,7 +2853,7 @@ TEST_F(TraceEventTestFixture, ThreadOnceBlocking) { // TraceLog should discover the generation mismatch and recover the thread // local buffer for the thread without any error. BeginTrace(); - thread.message_loop()->PostTask( + thread.task_runner()->PostTask( FROM_HERE, Bind(&TraceWithAllMacroVariants, &task_complete_event)); task_complete_event.Wait(); task_complete_event.Reset(); @@ -3075,6 +3080,5 @@ TEST(TraceOptionsTest, TraceOptionsToString) { } } - -} // namespace debug +} // namespace trace_event } // namespace base diff --git a/chromium/base/debug/trace_event_win.cc b/chromium/base/trace_event/trace_event_win.cc index 99dd242fad6..ebb55c84492 100644 --- a/chromium/base/debug/trace_event_win.cc +++ b/chromium/base/trace_event/trace_event_win.cc @@ -1,14 +1,15 @@ // 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/debug/trace_event_win.h" + +#include "base/trace_event/trace_event_win.h" #include "base/logging.h" #include "base/memory/singleton.h" #include <initguid.h> // NOLINT namespace base { -namespace debug { +namespace trace_event { using base::win::EtwEventType; using base::win::EtwMofEvent; @@ -119,5 +120,5 @@ void TraceEventETWProvider::Resurrect() { StaticMemorySingletonTraits<TraceEventETWProvider>::Resurrect(); } -} // namespace debug +} // namespace trace_event } // namespace base diff --git a/chromium/base/debug/trace_event_win.h b/chromium/base/trace_event/trace_event_win.h index c85ca9217cf..41613615549 100644 --- a/chromium/base/debug/trace_event_win.h +++ b/chromium/base/trace_event/trace_event_win.h @@ -3,13 +3,13 @@ // found in the LICENSE file. // This file contains the Windows-specific declarations for trace_event.h. -#ifndef BASE_DEBUG_TRACE_EVENT_WIN_H_ -#define BASE_DEBUG_TRACE_EVENT_WIN_H_ +#ifndef BASE_TRACE_EVENT_TRACE_EVENT_WIN_H_ +#define BASE_TRACE_EVENT_TRACE_EVENT_WIN_H_ #include <string> #include "base/base_export.h" -#include "base/debug/trace_event.h" +#include "base/trace_event/trace_event.h" #include "base/win/event_trace_provider.h" // Fwd. @@ -17,7 +17,7 @@ template <typename Type> struct StaticMemorySingletonTraits; namespace base { -namespace debug { +namespace trace_event { // This EtwTraceProvider subclass implements ETW logging // for the macros above on Windows. @@ -119,7 +119,7 @@ enum TraceEventETWFlags { // Optionally the stack trace, consisting of a DWORD "depth", followed // by an array of void* (machine bitness) of length "depth". -} // namespace debug +} // namespace trace_event } // namespace base -#endif // BASE_DEBUG_TRACE_EVENT_WIN_H_ +#endif // BASE_TRACE_EVENT_TRACE_EVENT_WIN_H_ diff --git a/chromium/base/debug/trace_event_win_unittest.cc b/chromium/base/trace_event/trace_event_win_unittest.cc index 378264511a2..d4dc8542c30 100644 --- a/chromium/base/debug/trace_event_win_unittest.cc +++ b/chromium/base/trace_event/trace_event_win_unittest.cc @@ -2,15 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/debug/trace_event.h" +#include "base/trace_event/trace_event.h" #include <strstream> #include "base/at_exit.h" #include "base/basictypes.h" -#include "base/debug/trace_event.h" -#include "base/debug/trace_event_win.h" #include "base/files/file_util.h" +#include "base/trace_event/trace_event.h" +#include "base/trace_event/trace_event_win.h" #include "base/win/event_trace_consumer.h" #include "base/win/event_trace_controller.h" #include "base/win/event_trace_provider.h" @@ -20,7 +20,7 @@ #include <initguid.h> // NOLINT - must be last include. namespace base { -namespace debug { +namespace trace_event { namespace { @@ -92,7 +92,7 @@ class TraceEventWinTest: public testing::Test { TraceEventWinTest() { } - void SetUp() { + void SetUp() override { bool is_xp = win::GetVersion() < base::win::VERSION_VISTA; if (is_xp) { @@ -151,7 +151,7 @@ class TraceEventWinTest: public testing::Test { EXPECT_TRUE(tracelog->IsTracing()); } - void TearDown() { + void TearDown() override { EtwTraceProperties prop; if (controller_.session() != 0) EXPECT_HRESULT_SUCCEEDED(controller_.Stop(&prop)); @@ -315,5 +315,5 @@ TEST_F(TraceEventWinTest, Macros) { PlayLog(); } -} // namespace debug +} // namespace trace_event } // namespace base diff --git a/chromium/base/trace_event/winheap_dump_provider_win.cc b/chromium/base/trace_event/winheap_dump_provider_win.cc new file mode 100644 index 00000000000..e1e9bcf4a7c --- /dev/null +++ b/chromium/base/trace_event/winheap_dump_provider_win.cc @@ -0,0 +1,99 @@ +// 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/winheap_dump_provider_win.h" + +#include <windows.h> + +#include "base/trace_event/process_memory_dump.h" + +namespace base { +namespace trace_event { + +namespace { + +// Report a heap dump to a process memory dump. The |heap_info| structure +// contains the information about this heap, and |dump_absolute_name| will be +// used to represent it in the report. +bool ReportHeapDump(ProcessMemoryDump* pmd, + const WinHeapInfo& heap_info, + const std::string& dump_absolute_name) { + MemoryAllocatorDump* dump = pmd->CreateAllocatorDump(dump_absolute_name); + if (!dump) + return false; + dump->AddScalar(MemoryAllocatorDump::kNameOuterSize, + MemoryAllocatorDump::kUnitsBytes, heap_info.committed_size); + dump->AddScalar(MemoryAllocatorDump::kNameInnerSize, + MemoryAllocatorDump::kUnitsBytes, heap_info.allocated_size); + dump->AddScalar(MemoryAllocatorDump::kNameObjectsCount, + MemoryAllocatorDump::kUnitsObjects, heap_info.block_count); + return true; +} + +} // namespace + +WinHeapDumpProvider* WinHeapDumpProvider::GetInstance() { + return Singleton<WinHeapDumpProvider, + LeakySingletonTraits<WinHeapDumpProvider>>::get(); +} + +bool WinHeapDumpProvider::OnMemoryDump(ProcessMemoryDump* pmd) { + // Retrieves the number of heaps in the current process. + DWORD number_of_heaps = ::GetProcessHeaps(0, NULL); + WinHeapInfo all_heap_info = {0}; + + // Try to retrieve a handle to all the heaps owned by this process. Returns + // false if the number of heaps has changed. + // + // This is inherently racy as is, but it's not something that we observe a lot + // in Chrome, the heaps tend to be created at startup only. + scoped_ptr<HANDLE[]> all_heaps(new HANDLE[number_of_heaps]); + if (::GetProcessHeaps(number_of_heaps, all_heaps.get()) != number_of_heaps) + return false; + + // Skip the pointer to the heap array to avoid accounting the memory used by + // this dump provider. + std::set<void*> block_to_skip; + block_to_skip.insert(all_heaps.get()); + + // Retrieves some metrics about each heap. + for (size_t i = 0; i < number_of_heaps; ++i) { + WinHeapInfo heap_info = {0}; + heap_info.heap_id = all_heaps[i]; + GetHeapInformation(&heap_info, block_to_skip); + + all_heap_info.allocated_size += heap_info.allocated_size; + all_heap_info.committed_size += heap_info.committed_size; + all_heap_info.block_count += heap_info.block_count; + } + // Report the heap dump. + if (!ReportHeapDump(pmd, all_heap_info, "winheap")) + return false; + + return true; +} + +bool WinHeapDumpProvider::GetHeapInformation( + WinHeapInfo* heap_info, + const std::set<void*>& block_to_skip) { + CHECK(::HeapLock(heap_info->heap_id) == TRUE); + PROCESS_HEAP_ENTRY heap_entry; + heap_entry.lpData = nullptr; + // Walk over all the entries in this heap. + while (::HeapWalk(heap_info->heap_id, &heap_entry) != FALSE) { + if (block_to_skip.count(heap_entry.lpData) == 1) + continue; + if ((heap_entry.wFlags & PROCESS_HEAP_ENTRY_BUSY) != 0) { + heap_info->allocated_size += heap_entry.cbData; + heap_info->block_count++; + } else if ((heap_entry.wFlags & PROCESS_HEAP_REGION) != 0) { + heap_info->committed_size += heap_entry.Region.dwCommittedSize; + } + } + CHECK(::HeapUnlock(heap_info->heap_id) == TRUE); + return true; +} + +} // namespace trace_event +} // namespace base diff --git a/chromium/base/trace_event/winheap_dump_provider_win.h b/chromium/base/trace_event/winheap_dump_provider_win.h new file mode 100644 index 00000000000..99239a066ef --- /dev/null +++ b/chromium/base/trace_event/winheap_dump_provider_win.h @@ -0,0 +1,52 @@ +// 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_WINHEAP_DUMP_PROVIDER_WIN_H_ +#define BASE_TRACE_EVENT_WINHEAP_DUMP_PROVIDER_WIN_H_ + +#include <set> + +#include "base/memory/singleton.h" +#include "base/trace_event/memory_dump_provider.h" + +namespace base { +namespace trace_event { + +// A structure containing some information about a given heap. +struct WinHeapInfo { + HANDLE heap_id; + size_t committed_size; + size_t allocated_size; + size_t block_count; +}; + +// Dump provider which collects process-wide heap memory stats. This provider +// iterates over all the heaps of the current process to gather some metrics +// about them. +class BASE_EXPORT WinHeapDumpProvider : public MemoryDumpProvider { + public: + static WinHeapDumpProvider* GetInstance(); + + // MemoryDumpProvider implementation. + bool OnMemoryDump(ProcessMemoryDump* pmd) override; + + private: + friend struct DefaultSingletonTraits<WinHeapDumpProvider>; + + // Retrieves the information about given heap. The |heap_info| should contain + // a valid handle to an existing heap. The blocks contained in the + // |block_to_skip| set will be ignored. + bool GetHeapInformation(WinHeapInfo* heap_info, + const std::set<void*>& block_to_skip); + + WinHeapDumpProvider() {} + ~WinHeapDumpProvider() override {} + + DISALLOW_COPY_AND_ASSIGN(WinHeapDumpProvider); +}; + +} // namespace trace_event +} // namespace base + +#endif // BASE_TRACE_EVENT_WINHEAP_DUMP_PROVIDER_WIN_H_ diff --git a/chromium/base/trace_event/winheap_dump_provider_win_unittest.cc b/chromium/base/trace_event/winheap_dump_provider_win_unittest.cc new file mode 100644 index 00000000000..2309802fe79 --- /dev/null +++ b/chromium/base/trace_event/winheap_dump_provider_win_unittest.cc @@ -0,0 +1,27 @@ +// 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/winheap_dump_provider_win.h" + +#include <windows.h> + +#include "base/trace_event/memory_dump_session_state.h" +#include "base/trace_event/process_memory_dump.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { +namespace trace_event { + +TEST(WinHeapDumpProviderTest, OnMemoryDump) { + ProcessMemoryDump pmd(make_scoped_refptr(new MemoryDumpSessionState())); + + WinHeapDumpProvider* winheap_dump_provider = + WinHeapDumpProvider::GetInstance(); + ASSERT_NE(static_cast<WinHeapDumpProvider*>(nullptr), winheap_dump_provider); + + ASSERT_TRUE(winheap_dump_provider->OnMemoryDump(&pmd)); +} + +} // namespace trace_event +} // namespace base diff --git a/chromium/base/tracked_objects.cc b/chromium/base/tracked_objects.cc index c69fada531c..9db05c0d3fd 100644 --- a/chromium/base/tracked_objects.cc +++ b/chromium/base/tracked_objects.cc @@ -28,22 +28,13 @@ class TimeDelta; namespace tracked_objects { namespace { -// Flag to compile out almost all of the task tracking code. -const bool kTrackAllTaskObjects = true; - -// TODO(jar): Evaluate the perf impact of enabling this. If the perf impact is -// negligible, enable by default. -// Flag to compile out parent-child link recording. -const bool kTrackParentChildLinks = false; - // When ThreadData is first initialized, should we start in an ACTIVE state to // record all of the startup-time tasks, or should we start up DEACTIVATED, so // that we only record after parsing the command line flag --enable-tracking. // Note that the flag may force either state, so this really controls only the -// period of time up until that flag is parsed. If there is no flag seen, then +// period of time up until that flag is parsed. If there is no flag seen, then // this state may prevail for much or all of the process lifetime. -const ThreadData::Status kInitialStartupState = - ThreadData::PROFILING_CHILDREN_ACTIVE; +const ThreadData::Status kInitialStartupState = ThreadData::PROFILING_ACTIVE; // Control whether an alternate time source (Now() function) is supported by // the ThreadData class. This compile time flag should be set to true if we @@ -64,9 +55,10 @@ enum { // State of the profiler timing enabledness. base::subtle::Atomic32 g_profiler_timing_enabled = UNDEFINED_TIMING; -// Returns whether profiler timing is enabled. The default is true, but this may -// be overridden by a command-line flag. Some platforms may programmatically set -// this command-line flag to the "off" value if it's not specified. +// Returns whether profiler timing is enabled. The default is true, but this +// may be overridden by a command-line flag. Some platforms may +// programmatically set this command-line flag to the "off" value if it's not +// specified. // This in turn can be overridden by explicitly calling // ThreadData::EnableProfilerTiming, say, based on a field trial. inline bool IsProfilerTimingEnabled() { @@ -76,10 +68,10 @@ inline bool IsProfilerTimingEnabled() { base::subtle::Atomic32 current_timing_enabled = base::subtle::NoBarrier_Load(&g_profiler_timing_enabled); if (current_timing_enabled == UNDEFINED_TIMING) { - if (!CommandLine::InitializedForCurrentProcess()) + if (!base::CommandLine::InitializedForCurrentProcess()) return true; current_timing_enabled = - (CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + (base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( switches::kProfilerTiming) == switches::kProfilerTimingDisabledValue) ? DISABLED_TIMING @@ -95,13 +87,40 @@ inline bool IsProfilerTimingEnabled() { //------------------------------------------------------------------------------ // DeathData tallies durations when a death takes place. -DeathData::DeathData() { - Clear(); -} - -DeathData::DeathData(int count) { - Clear(); - count_ = count; +DeathData::DeathData() + : count_(0), + sample_probability_count_(0), + run_duration_sum_(0), + queue_duration_sum_(0), + run_duration_max_(0), + queue_duration_max_(0), + run_duration_sample_(0), + queue_duration_sample_(0), + last_phase_snapshot_(nullptr) { +} + +DeathData::DeathData(const DeathData& other) + : count_(other.count_), + sample_probability_count_(other.sample_probability_count_), + run_duration_sum_(other.run_duration_sum_), + queue_duration_sum_(other.queue_duration_sum_), + run_duration_max_(other.run_duration_max_), + queue_duration_max_(other.queue_duration_max_), + run_duration_sample_(other.run_duration_sample_), + queue_duration_sample_(other.queue_duration_sample_), + last_phase_snapshot_(nullptr) { + // This constructor will be used by std::map when adding new DeathData values + // to the map. At that point, last_phase_snapshot_ is still NULL, so we don't + // need to worry about ownership transfer. + DCHECK(other.last_phase_snapshot_ == nullptr); +} + +DeathData::~DeathData() { + while (last_phase_snapshot_) { + const DeathDataPhaseSnapshot* snapshot = last_phase_snapshot_; + last_phase_snapshot_ = snapshot->prev; + delete snapshot; + } } // TODO(jar): I need to see if this macro to optimize branching is worth using. @@ -116,10 +135,16 @@ DeathData::DeathData(int count) { void DeathData::RecordDeath(const int32 queue_duration, const int32 run_duration, - int32 random_number) { + const uint32 random_number) { // We'll just clamp at INT_MAX, but we should note this in the UI as such. if (count_ < INT_MAX) ++count_; + + int sample_probability_count = sample_probability_count_; + if (sample_probability_count < INT_MAX) + ++sample_probability_count; + sample_probability_count_ = sample_probability_count; + queue_duration_sum_ += queue_duration; run_duration_sum_ += run_duration; @@ -128,54 +153,54 @@ void DeathData::RecordDeath(const int32 queue_duration, if (run_duration_max_ < run_duration) run_duration_max_ = run_duration; - // Take a uniformly distributed sample over all durations ever supplied. - // The probability that we (instead) use this new sample is 1/count_. This - // results in a completely uniform selection of the sample (at least when we - // don't clamp count_... but that should be inconsequentially likely). - // We ignore the fact that we correlated our selection of a sample to the run - // and queue times (i.e., we used them to generate random_number). - CHECK_GT(count_, 0); - if (0 == (random_number % count_)) { + // Take a uniformly distributed sample over all durations ever supplied during + // the current profiling phase. + // The probability that we (instead) use this new sample is + // 1/sample_probability_count_. This results in a completely uniform selection + // of the sample (at least when we don't clamp sample_probability_count_... + // but that should be inconsequentially likely). We ignore the fact that we + // correlated our selection of a sample to the run and queue times (i.e., we + // used them to generate random_number). + CHECK_GT(sample_probability_count, 0); + if (0 == (random_number % sample_probability_count)) { queue_duration_sample_ = queue_duration; run_duration_sample_ = run_duration; } } -int DeathData::count() const { return count_; } - -int32 DeathData::run_duration_sum() const { return run_duration_sum_; } - -int32 DeathData::run_duration_max() const { return run_duration_max_; } - -int32 DeathData::run_duration_sample() const { - return run_duration_sample_; -} - -int32 DeathData::queue_duration_sum() const { - return queue_duration_sum_; -} - -int32 DeathData::queue_duration_max() const { - return queue_duration_max_; -} - -int32 DeathData::queue_duration_sample() const { - return queue_duration_sample_; -} - -void DeathData::ResetMax() { - run_duration_max_ = 0; - queue_duration_max_ = 0; -} - -void DeathData::Clear() { - count_ = 0; - run_duration_sum_ = 0; +void DeathData::OnProfilingPhaseCompleted(int profiling_phase) { + // Snapshotting and storing current state. + last_phase_snapshot_ = new DeathDataPhaseSnapshot( + profiling_phase, count_, run_duration_sum_, run_duration_max_, + run_duration_sample_, queue_duration_sum_, queue_duration_max_, + queue_duration_sample_, last_phase_snapshot_); + + // Not touching fields for which a delta can be computed by comparing with a + // snapshot from the previous phase. Resetting other fields. Sample values + // will be reset upon next death recording because sample_probability_count_ + // is set to 0. + // We avoid resetting to 0 in favor of deltas whenever possible. The reason + // is that for incrementable fields, resetting to 0 from the snapshot thread + // potentially in parallel with incrementing in the death thread may result in + // significant data corruption that has a potential to grow with time. Not + // resetting incrementable fields and using deltas will cause any + // off-by-little corruptions to be likely fixed at the next snapshot. + // The max values are not incrementable, and cannot be deduced using deltas + // for a given phase. Hence, we have to reset them to 0. But the potential + // damage is limited to getting the previous phase's max to apply for the next + // phase, and the error doesn't have a potential to keep growing with new + // resets. + // sample_probability_count_ is incrementable, but must be reset to 0 at the + // phase end, so that we start a new uniformly randomized sample selection + // after the reset. Corruptions due to race conditions are possible, but the + // damage is limited to selecting a wrong sample, which is not something that + // can cause accumulating or cascading effects. + // If there were no corruptions caused by race conditions, we never send a + // sample for the previous phase in the next phase's snapshot because + // ThreadData::SnapshotExecutedTasks doesn't send deltas with 0 count. + sample_probability_count_ = 0; run_duration_max_ = 0; - run_duration_sample_ = 0; - queue_duration_sum_ = 0; queue_duration_max_ = 0; - queue_duration_sample_ = 0; } //------------------------------------------------------------------------------ @@ -189,20 +214,34 @@ DeathDataSnapshot::DeathDataSnapshot() queue_duration_sample(-1) { } -DeathDataSnapshot::DeathDataSnapshot( - const tracked_objects::DeathData& death_data) - : count(death_data.count()), - run_duration_sum(death_data.run_duration_sum()), - run_duration_max(death_data.run_duration_max()), - run_duration_sample(death_data.run_duration_sample()), - queue_duration_sum(death_data.queue_duration_sum()), - queue_duration_max(death_data.queue_duration_max()), - queue_duration_sample(death_data.queue_duration_sample()) { +DeathDataSnapshot::DeathDataSnapshot(int count, + int32 run_duration_sum, + int32 run_duration_max, + int32 run_duration_sample, + int32 queue_duration_sum, + int32 queue_duration_max, + int32 queue_duration_sample) + : count(count), + run_duration_sum(run_duration_sum), + run_duration_max(run_duration_max), + run_duration_sample(run_duration_sample), + queue_duration_sum(queue_duration_sum), + queue_duration_max(queue_duration_max), + queue_duration_sample(queue_duration_sample) { } DeathDataSnapshot::~DeathDataSnapshot() { } +DeathDataSnapshot DeathDataSnapshot::Delta( + const DeathDataSnapshot& older) const { + return DeathDataSnapshot(count - older.count, + run_duration_sum - older.run_duration_sum, + run_duration_max, run_duration_sample, + queue_duration_sum - older.queue_duration_sum, + queue_duration_max, queue_duration_sample); +} + //------------------------------------------------------------------------------ BirthOnThread::BirthOnThread(const Location& location, const ThreadData& current) @@ -214,8 +253,7 @@ BirthOnThread::BirthOnThread(const Location& location, BirthOnThreadSnapshot::BirthOnThreadSnapshot() { } -BirthOnThreadSnapshot::BirthOnThreadSnapshot( - const tracked_objects::BirthOnThread& birth) +BirthOnThreadSnapshot::BirthOnThreadSnapshot(const BirthOnThread& birth) : location(birth.location()), thread_name(birth.birth_thread()->thread_name()) { } @@ -232,10 +270,6 @@ int Births::birth_count() const { return birth_count_; } void Births::RecordBirth() { ++birth_count_; } -void Births::ForgetBirth() { --birth_count_; } - -void Births::Clear() { birth_count_ = 0; } - //------------------------------------------------------------------------------ // ThreadData maintains the central data for all births and deaths on a single // thread. @@ -250,9 +284,9 @@ NowFunction* ThreadData::now_function_ = NULL; // static bool ThreadData::now_function_is_time_ = false; -// A TLS slot which points to the ThreadData instance for the current thread. We -// do a fake initialization here (zeroing out data), and then the real in-place -// construction happens when we call tls_index_.Initialize(). +// A TLS slot which points to the ThreadData instance for the current thread. +// We do a fake initialization here (zeroing out data), and then the real +// in-place construction happens when we call tls_index_.Initialize(). // static base::ThreadLocalStorage::StaticSlot ThreadData::tls_index_ = TLS_INITIALIZER; @@ -300,14 +334,15 @@ ThreadData::ThreadData(int thread_number) PushToHeadOfList(); // Which sets real incarnation_count_for_pool_. } -ThreadData::~ThreadData() {} +ThreadData::~ThreadData() { +} void ThreadData::PushToHeadOfList() { // Toss in a hint of randomness (atop the uniniitalized value). (void)VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE(&random_number_, sizeof(random_number_)); MSAN_UNPOISON(&random_number_, sizeof(random_number_)); - random_number_ += static_cast<int32>(this - static_cast<ThreadData*>(0)); + random_number_ += static_cast<uint32>(this - static_cast<ThreadData*>(0)); random_number_ ^= (Now() - TrackedTime()).InMilliseconds(); DCHECK(!next_); @@ -327,8 +362,7 @@ ThreadData* ThreadData::next() const { return next_; } // static void ThreadData::InitializeThreadContext(const std::string& suggested_name) { - if (!Initialize()) // Always initialize if needed. - return; + Initialize(); ThreadData* current_thread_data = reinterpret_cast<ThreadData*>(tls_index_.Get()); if (current_thread_data) @@ -373,10 +407,8 @@ ThreadData* ThreadData::Get() { // static void ThreadData::OnThreadTermination(void* thread_data) { DCHECK(thread_data); // TLS should *never* call us with a NULL. - // We must NOT do any allocations during this callback. There is a chance + // We must NOT do any allocations during this callback. There is a chance // that the allocator is no longer active on this thread. - if (!kTrackAllTaskObjects) - return; // Not compiled in. reinterpret_cast<ThreadData*>(thread_data)->OnThreadTerminationCleanup(); } @@ -399,25 +431,56 @@ void ThreadData::OnThreadTerminationCleanup() { } // static -void ThreadData::Snapshot(bool reset_max, ProcessDataSnapshot* process_data) { - // Add births that have run to completion to |collected_data|. - // |birth_counts| tracks the total number of births recorded at each location - // for which we have not seen a death count. +void ThreadData::Snapshot(int current_profiling_phase, + ProcessDataSnapshot* process_data_snapshot) { + // Get an unchanging copy of a ThreadData list. + ThreadData* my_list = ThreadData::first(); + + // Gather data serially. + // This hackish approach *can* get some slightly corrupt tallies, as we are + // grabbing values without the protection of a lock, but it has the advantage + // of working even with threads that don't have message loops. If a user + // sees any strangeness, they can always just run their stats gathering a + // second time. BirthCountMap birth_counts; - ThreadData::SnapshotAllExecutedTasks(reset_max, process_data, &birth_counts); + for (ThreadData* thread_data = my_list; thread_data; + thread_data = thread_data->next()) { + thread_data->SnapshotExecutedTasks(current_profiling_phase, + &process_data_snapshot->phased_snapshots, + &birth_counts); + } // Add births that are still active -- i.e. objects that have tallied a birth, // but have not yet tallied a matching death, and hence must be either // running, queued up, or being held in limbo for future posting. - for (BirthCountMap::const_iterator it = birth_counts.begin(); - it != birth_counts.end(); ++it) { - if (it->second > 0) { - process_data->tasks.push_back( - TaskSnapshot(*it->first, DeathData(it->second), "Still_Alive")); + auto* current_phase_tasks = + &process_data_snapshot->phased_snapshots[current_profiling_phase].tasks; + for (const auto& birth_count : birth_counts) { + if (birth_count.second > 0) { + current_phase_tasks->push_back( + TaskSnapshot(BirthOnThreadSnapshot(*birth_count.first), + DeathDataSnapshot(birth_count.second, 0, 0, 0, 0, 0, 0), + "Still_Alive")); } } } +// static +void ThreadData::OnProfilingPhaseCompleted(int profiling_phase) { + // Get an unchanging copy of a ThreadData list. + ThreadData* my_list = ThreadData::first(); + + // Add snapshots for all instances of death data in all threads serially. + // This hackish approach *can* get some slightly corrupt tallies, as we are + // grabbing values without the protection of a lock, but it has the advantage + // of working even with threads that don't have message loops. Any corruption + // shouldn't cause "cascading damage" to anything else (in later phases). + for (ThreadData* thread_data = my_list; thread_data; + thread_data = thread_data->next()) { + thread_data->OnProfilingPhaseCompletedOnThread(profiling_phase); + } +} + Births* ThreadData::TallyABirth(const Location& location) { BirthMap::iterator it = birth_map_.find(location); Births* child; @@ -432,33 +495,21 @@ Births* ThreadData::TallyABirth(const Location& location) { birth_map_[location] = child; } - if (kTrackParentChildLinks && status_ > PROFILING_ACTIVE && - !parent_stack_.empty()) { - const Births* parent = parent_stack_.top(); - ParentChildPair pair(parent, child); - if (parent_child_set_.find(pair) == parent_child_set_.end()) { - // Lock since the map may get relocated now, and other threads sometimes - // snapshot it (but they lock before copying it). - base::AutoLock lock(map_lock_); - parent_child_set_.insert(pair); - } - } - return child; } -void ThreadData::TallyADeath(const Births& birth, +void ThreadData::TallyADeath(const Births& births, int32 queue_duration, const TaskStopwatch& stopwatch) { int32 run_duration = stopwatch.RunDurationMs(); // Stir in some randomness, plus add constant in case durations are zero. - const int32 kSomePrimeNumber = 2147483647; + const uint32 kSomePrimeNumber = 2147483647; random_number_ += queue_duration + run_duration + kSomePrimeNumber; // An address is going to have some randomness to it as well ;-). - random_number_ ^= static_cast<int32>(&birth - reinterpret_cast<Births*>(0)); + random_number_ ^= static_cast<uint32>(&births - reinterpret_cast<Births*>(0)); - // We don't have queue durations without OS timer. OS timer is automatically + // We don't have queue durations without OS timer. OS timer is automatically // used for task-post-timing, so the use of an alternate timer implies all // queue times are invalid, unless it was explicitly said that we can trust // the alternate timer. @@ -468,29 +519,19 @@ void ThreadData::TallyADeath(const Births& birth, queue_duration = 0; } - DeathMap::iterator it = death_map_.find(&birth); + DeathMap::iterator it = death_map_.find(&births); DeathData* death_data; if (it != death_map_.end()) { death_data = &it->second; } else { base::AutoLock lock(map_lock_); // Lock as the map may get relocated now. - death_data = &death_map_[&birth]; + death_data = &death_map_[&births]; } // Release lock ASAP. death_data->RecordDeath(queue_duration, run_duration, random_number_); - - if (!kTrackParentChildLinks) - return; - if (!parent_stack_.empty()) { // We might get turned off. - DCHECK_EQ(parent_stack_.top(), &birth); - parent_stack_.pop(); - } } // static Births* ThreadData::TallyABirthIfActive(const Location& location) { - if (!kTrackAllTaskObjects) - return NULL; // Not compiled in. - if (!TrackingStatus()) return NULL; ThreadData* current_thread_data = Get(); @@ -503,14 +544,11 @@ Births* ThreadData::TallyABirthIfActive(const Location& location) { void ThreadData::TallyRunOnNamedThreadIfTracking( const base::TrackingInfo& completed_task, const TaskStopwatch& stopwatch) { - if (!kTrackAllTaskObjects) - return; // Not compiled in. - // Even if we have been DEACTIVATED, we will process any pending births so // that our data structures (which counted the outstanding births) remain // consistent. - const Births* birth = completed_task.birth_tally; - if (!birth) + const Births* births = completed_task.birth_tally; + if (!births) return; ThreadData* current_thread_data = stopwatch.GetThreadData(); if (!current_thread_data) @@ -519,7 +557,7 @@ void ThreadData::TallyRunOnNamedThreadIfTracking( // Watch out for a race where status_ is changing, and hence one or both // of start_of_run or end_of_run is zero. In that case, we didn't bother to // get a time value since we "weren't tracking" and we were trying to be - // efficient by not calling for a genuine time value. For simplicity, we'll + // efficient by not calling for a genuine time value. For simplicity, we'll // use a default zero duration when we can't calculate a true value. TrackedTime start_of_run = stopwatch.StartTime(); int32 queue_duration = 0; @@ -527,21 +565,18 @@ void ThreadData::TallyRunOnNamedThreadIfTracking( queue_duration = (start_of_run - completed_task.EffectiveTimePosted()) .InMilliseconds(); } - current_thread_data->TallyADeath(*birth, queue_duration, stopwatch); + current_thread_data->TallyADeath(*births, queue_duration, stopwatch); } // static void ThreadData::TallyRunOnWorkerThreadIfTracking( - const Births* birth, + const Births* births, const TrackedTime& time_posted, const TaskStopwatch& stopwatch) { - if (!kTrackAllTaskObjects) - return; // Not compiled in. - // Even if we have been DEACTIVATED, we will process any pending births so // that our data structures (which counted the outstanding births) remain // consistent. - if (!birth) + if (!births) return; // TODO(jar): Support the option to coalesce all worker-thread activity under @@ -549,7 +584,7 @@ void ThreadData::TallyRunOnWorkerThreadIfTracking( // reduce memory (making it provably bounded), but run incrementally slower // (since we'll use locks on TallyABirth and TallyADeath). The good news is // that the locks on TallyADeath will be *after* the worker thread has run, - // and hence nothing will be waiting for the completion (... besides some + // and hence nothing will be waiting for the completion (... besides some // other thread that might like to run). Also, the worker threads tasks are // generally longer, and hence the cost of the lock may perchance be amortized // over the long task's lifetime. @@ -562,20 +597,17 @@ void ThreadData::TallyRunOnWorkerThreadIfTracking( if (!start_of_run.is_null()) { queue_duration = (start_of_run - time_posted).InMilliseconds(); } - current_thread_data->TallyADeath(*birth, queue_duration, stopwatch); + current_thread_data->TallyADeath(*births, queue_duration, stopwatch); } // static void ThreadData::TallyRunInAScopedRegionIfTracking( - const Births* birth, + const Births* births, const TaskStopwatch& stopwatch) { - if (!kTrackAllTaskObjects) - return; // Not compiled in. - // Even if we have been DEACTIVATED, we will process any pending births so // that our data structures (which counted the outstanding births) remain // consistent. - if (!birth) + if (!births) return; ThreadData* current_thread_data = stopwatch.GetThreadData(); @@ -583,105 +615,74 @@ void ThreadData::TallyRunInAScopedRegionIfTracking( return; int32 queue_duration = 0; - current_thread_data->TallyADeath(*birth, queue_duration, stopwatch); + current_thread_data->TallyADeath(*births, queue_duration, stopwatch); } -// static -void ThreadData::SnapshotAllExecutedTasks(bool reset_max, - ProcessDataSnapshot* process_data, - BirthCountMap* birth_counts) { - if (!kTrackAllTaskObjects) - return; // Not compiled in. - - // Get an unchanging copy of a ThreadData list. - ThreadData* my_list = ThreadData::first(); - - // Gather data serially. - // This hackish approach *can* get some slighly corrupt tallies, as we are - // grabbing values without the protection of a lock, but it has the advantage - // of working even with threads that don't have message loops. If a user - // sees any strangeness, they can always just run their stats gathering a - // second time. - for (ThreadData* thread_data = my_list; - thread_data; - thread_data = thread_data->next()) { - thread_data->SnapshotExecutedTasks(reset_max, process_data, birth_counts); - } -} - -void ThreadData::SnapshotExecutedTasks(bool reset_max, - ProcessDataSnapshot* process_data, - BirthCountMap* birth_counts) { +void ThreadData::SnapshotExecutedTasks( + int current_profiling_phase, + PhasedProcessDataSnapshotMap* phased_snapshots, + BirthCountMap* birth_counts) { // Get copy of data, so that the data will not change during the iterations // and processing. - ThreadData::BirthMap birth_map; - ThreadData::DeathMap death_map; - ThreadData::ParentChildSet parent_child_set; - SnapshotMaps(reset_max, &birth_map, &death_map, &parent_child_set); - - for (ThreadData::DeathMap::const_iterator it = death_map.begin(); - it != death_map.end(); ++it) { - process_data->tasks.push_back( - TaskSnapshot(*it->first, it->second, thread_name())); - (*birth_counts)[it->first] -= it->first->birth_count(); - } + BirthMap birth_map; + DeathsSnapshot deaths; + SnapshotMaps(current_profiling_phase, &birth_map, &deaths); - for (ThreadData::BirthMap::const_iterator it = birth_map.begin(); - it != birth_map.end(); ++it) { - (*birth_counts)[it->second] += it->second->birth_count(); + for (const auto& birth : birth_map) { + (*birth_counts)[birth.second] += birth.second->birth_count(); } - if (!kTrackParentChildLinks) - return; - - for (ThreadData::ParentChildSet::const_iterator it = parent_child_set.begin(); - it != parent_child_set.end(); ++it) { - process_data->descendants.push_back(ParentChildPairSnapshot(*it)); + for (const auto& death : deaths) { + (*birth_counts)[death.first] -= death.first->birth_count(); + + // For the current death data, walk through all its snapshots, starting from + // the current one, then from the previous profiling phase etc., and for + // each snapshot calculate the delta between the snapshot and the previous + // phase, if any. Store the deltas in the result. + for (const DeathDataPhaseSnapshot* phase = &death.second; phase; + phase = phase->prev) { + const DeathDataSnapshot& death_data = + phase->prev ? phase->death_data.Delta(phase->prev->death_data) + : phase->death_data; + + if (death_data.count > 0) { + (*phased_snapshots)[phase->profiling_phase].tasks.push_back( + TaskSnapshot(BirthOnThreadSnapshot(*death.first), death_data, + thread_name())); + } + } } } // This may be called from another thread. -void ThreadData::SnapshotMaps(bool reset_max, +void ThreadData::SnapshotMaps(int profiling_phase, BirthMap* birth_map, - DeathMap* death_map, - ParentChildSet* parent_child_set) { + DeathsSnapshot* deaths) { base::AutoLock lock(map_lock_); - for (BirthMap::const_iterator it = birth_map_.begin(); - it != birth_map_.end(); ++it) - (*birth_map)[it->first] = it->second; - for (DeathMap::iterator it = death_map_.begin(); - it != death_map_.end(); ++it) { - (*death_map)[it->first] = it->second; - if (reset_max) - it->second.ResetMax(); - } - - if (!kTrackParentChildLinks) - return; - - for (ParentChildSet::iterator it = parent_child_set_.begin(); - it != parent_child_set_.end(); ++it) - parent_child_set->insert(*it); -} - -// static -void ThreadData::ResetAllThreadData() { - ThreadData* my_list = first(); - for (ThreadData* thread_data = my_list; - thread_data; - thread_data = thread_data->next()) - thread_data->Reset(); + for (const auto& birth : birth_map_) + (*birth_map)[birth.first] = birth.second; + + for (const auto& death : death_map_) { + deaths->push_back(std::make_pair( + death.first, + DeathDataPhaseSnapshot(profiling_phase, death.second.count(), + death.second.run_duration_sum(), + death.second.run_duration_max(), + death.second.run_duration_sample(), + death.second.queue_duration_sum(), + death.second.queue_duration_max(), + death.second.queue_duration_sample(), + death.second.last_phase_snapshot()))); + } } -void ThreadData::Reset() { +void ThreadData::OnProfilingPhaseCompletedOnThread(int profiling_phase) { base::AutoLock lock(map_lock_); - for (DeathMap::iterator it = death_map_.begin(); - it != death_map_.end(); ++it) - it->second.Clear(); - for (BirthMap::iterator it = birth_map_.begin(); - it != birth_map_.end(); ++it) - it->second->Clear(); + + for (auto& death : death_map_) { + death.second.OnProfilingPhaseCompleted(profiling_phase); + } } static void OptionallyInitializeAlternateTimer() { @@ -690,11 +691,9 @@ static void OptionallyInitializeAlternateTimer() { ThreadData::SetAlternateTimeSource(alternate_time_source); } -bool ThreadData::Initialize() { - if (!kTrackAllTaskObjects) - return false; // Not compiled in. +void ThreadData::Initialize() { if (status_ >= DEACTIVATED) - return true; // Someone else did the initialization. + return; // Someone else did the initialization. // Due to racy lazy initialization in tests, we'll need to recheck status_ // after we acquire the lock. @@ -703,7 +702,7 @@ bool ThreadData::Initialize() { // initialization. base::AutoLock lock(*list_lock_.Pointer()); if (status_ >= DEACTIVATED) - return true; // Someone raced in here and beat us. + return; // Someone raced in here and beat us. // Put an alternate timer in place if the environment calls for it, such as // for tracking TCMalloc allocations. This insertion is idempotent, so we @@ -717,8 +716,7 @@ bool ThreadData::Initialize() { if (!tls_index_.initialized()) { // Testing may have initialized this. DCHECK_EQ(status_, UNINITIALIZED); tls_index_.Initialize(&ThreadData::OnThreadTermination); - if (!tls_index_.initialized()) - return false; + DCHECK(tls_index_.initialized()); } else { // TLS was initialzed for us earlier. DCHECK_EQ(status_, DORMANT_DURING_TESTS); @@ -728,29 +726,23 @@ bool ThreadData::Initialize() { // never again change in this process. ++incarnation_counter_; - // The lock is not critical for setting status_, but it doesn't hurt. It also + // The lock is not critical for setting status_, but it doesn't hurt. It also // ensures that if we have a racy initialization, that we'll bail as soon as // we get the lock earlier in this method. status_ = kInitialStartupState; - if (!kTrackParentChildLinks && - kInitialStartupState == PROFILING_CHILDREN_ACTIVE) - status_ = PROFILING_ACTIVE; DCHECK(status_ != UNINITIALIZED); - return true; } // static -bool ThreadData::InitializeAndSetTrackingStatus(Status status) { +void ThreadData::InitializeAndSetTrackingStatus(Status status) { DCHECK_GE(status, DEACTIVATED); - DCHECK_LE(status, PROFILING_CHILDREN_ACTIVE); + DCHECK_LE(status, PROFILING_ACTIVE); - if (!Initialize()) // No-op if already initialized. - return false; // Not compiled in. + Initialize(); // No-op if already initialized. - if (!kTrackParentChildLinks && status > DEACTIVATED) + if (status > DEACTIVATED) status = PROFILING_ACTIVE; status_ = status; - return true; } // static @@ -764,20 +756,6 @@ bool ThreadData::TrackingStatus() { } // static -bool ThreadData::TrackingParentChildStatus() { - return status_ >= PROFILING_CHILDREN_ACTIVE; -} - -// static -void ThreadData::PrepareForStartOfRun(const Births* parent) { - if (kTrackParentChildLinks && parent && status_ > PROFILING_ACTIVE) { - ThreadData* current_thread_data = Get(); - if (current_thread_data) - current_thread_data->parent_stack_.push(parent); - } -} - -// static void ThreadData::SetAlternateTimeSource(NowFunction* now_function) { DCHECK(now_function); if (kAllowAlternateTimeSourceHandling) @@ -793,7 +771,7 @@ void ThreadData::EnableProfilerTiming() { TrackedTime ThreadData::Now() { if (kAllowAlternateTimeSourceHandling && now_function_) return TrackedTime::FromMilliseconds((*now_function_)()); - if (kTrackAllTaskObjects && IsProfilerTimingEnabled() && TrackingStatus()) + if (IsProfilerTimingEnabled() && TrackingStatus()) return TrackedTime::Now(); return TrackedTime(); // Super fast when disabled, or not compiled. } @@ -818,8 +796,8 @@ void ThreadData::ShutdownSingleThreadedCleanup(bool leak) { // This is only called from test code, where we need to cleanup so that // additional tests can be run. // We must be single threaded... but be careful anyway. - if (!InitializeAndSetTrackingStatus(DEACTIVATED)) - return; + InitializeAndSetTrackingStatus(DEACTIVATED); + ThreadData* thread_data_list; { base::AutoLock lock(*list_lock_.Pointer()); @@ -873,21 +851,21 @@ TaskStopwatch::TaskStopwatch() current_thread_data_(NULL), excluded_duration_ms_(0), parent_(NULL) { -#if DCHECK_IS_ON +#if DCHECK_IS_ON() state_ = CREATED; child_ = NULL; #endif } TaskStopwatch::~TaskStopwatch() { -#if DCHECK_IS_ON +#if DCHECK_IS_ON() DCHECK(state_ != RUNNING); DCHECK(child_ == NULL); #endif } void TaskStopwatch::Start() { -#if DCHECK_IS_ON +#if DCHECK_IS_ON() DCHECK(state_ == CREATED); state_ = RUNNING; #endif @@ -899,7 +877,7 @@ void TaskStopwatch::Start() { return; parent_ = current_thread_data_->current_stopwatch_; -#if DCHECK_IS_ON +#if DCHECK_IS_ON() if (parent_) { DCHECK(parent_->state_ == RUNNING); DCHECK(parent_->child_ == NULL); @@ -911,7 +889,7 @@ void TaskStopwatch::Start() { void TaskStopwatch::Stop() { const TrackedTime end_time = ThreadData::Now(); -#if DCHECK_IS_ON +#if DCHECK_IS_ON() DCHECK(state_ == RUNNING); state_ = STOPPED; DCHECK(child_ == NULL); @@ -929,7 +907,7 @@ void TaskStopwatch::Stop() { if (!parent_) return; -#if DCHECK_IS_ON +#if DCHECK_IS_ON() DCHECK(parent_->state_ == RUNNING); DCHECK(parent_->child_ == this); parent_->child_ = NULL; @@ -939,7 +917,7 @@ void TaskStopwatch::Stop() { } TrackedTime TaskStopwatch::StartTime() const { -#if DCHECK_IS_ON +#if DCHECK_IS_ON() DCHECK(state_ != CREATED); #endif @@ -947,7 +925,7 @@ TrackedTime TaskStopwatch::StartTime() const { } int32 TaskStopwatch::RunDurationMs() const { -#if DCHECK_IS_ON +#if DCHECK_IS_ON() DCHECK(state_ == STOPPED); #endif @@ -955,7 +933,7 @@ int32 TaskStopwatch::RunDurationMs() const { } ThreadData* TaskStopwatch::GetThreadData() const { -#if DCHECK_IS_ON +#if DCHECK_IS_ON() DCHECK(state_ != CREATED); #endif @@ -963,11 +941,37 @@ ThreadData* TaskStopwatch::GetThreadData() const { } //------------------------------------------------------------------------------ +// DeathDataPhaseSnapshot + +DeathDataPhaseSnapshot::DeathDataPhaseSnapshot( + int profiling_phase, + int count, + int32 run_duration_sum, + int32 run_duration_max, + int32 run_duration_sample, + int32 queue_duration_sum, + int32 queue_duration_max, + int32 queue_duration_sample, + const DeathDataPhaseSnapshot* prev) + : profiling_phase(profiling_phase), + death_data(count, + run_duration_sum, + run_duration_max, + run_duration_sample, + queue_duration_sum, + queue_duration_max, + queue_duration_sample), + prev(prev) { +} + +//------------------------------------------------------------------------------ +// TaskSnapshot + TaskSnapshot::TaskSnapshot() { } -TaskSnapshot::TaskSnapshot(const BirthOnThread& birth, - const DeathData& death_data, +TaskSnapshot::TaskSnapshot(const BirthOnThreadSnapshot& birth, + const DeathDataSnapshot& death_data, const std::string& death_thread_name) : birth(birth), death_data(death_data), @@ -978,28 +982,22 @@ TaskSnapshot::~TaskSnapshot() { } //------------------------------------------------------------------------------ -// ParentChildPairSnapshot - -ParentChildPairSnapshot::ParentChildPairSnapshot() { -} +// ProcessDataPhaseSnapshot -ParentChildPairSnapshot::ParentChildPairSnapshot( - const ThreadData::ParentChildPair& parent_child) - : parent(*parent_child.first), - child(*parent_child.second) { +ProcessDataPhaseSnapshot::ProcessDataPhaseSnapshot() { } -ParentChildPairSnapshot::~ParentChildPairSnapshot() { +ProcessDataPhaseSnapshot::~ProcessDataPhaseSnapshot() { } //------------------------------------------------------------------------------ -// ProcessDataSnapshot +// ProcessDataPhaseSnapshot ProcessDataSnapshot::ProcessDataSnapshot() #if !defined(OS_NACL) : process_id(base::GetCurrentProcId()) { #else - : process_id(0) { + : process_id(base::kNullProcessId) { #endif } diff --git a/chromium/base/tracked_objects.h b/chromium/base/tracked_objects.h index 50bea47987f..8f8379409db 100644 --- a/chromium/base/tracked_objects.h +++ b/chromium/base/tracked_objects.h @@ -14,12 +14,15 @@ #include "base/base_export.h" #include "base/basictypes.h" +#include "base/containers/hash_tables.h" #include "base/gtest_prod_util.h" #include "base/lazy_instance.h" #include "base/location.h" +#include "base/process/process_handle.h" #include "base/profiler/alternate_timer.h" #include "base/profiler/tracked_time.h" #include "base/synchronization/lock.h" +#include "base/threading/thread_checker.h" #include "base/threading/thread_local_storage.h" namespace base { @@ -43,7 +46,7 @@ struct TrackingInfo; // computational cost is associated with obtaining start and stop times for // instances as they are created and destroyed. // -// The following describes the lifecycle of tracking an instance. +// The following describes the life cycle of tracking an instance. // // First off, when the instance is created, the FROM_HERE macro is expanded // to specify the birth place (file, line, function) where the instance was @@ -83,7 +86,7 @@ struct TrackingInfo; // threads there are, and how many Locations of construction there are. // Fortunately, we don't use memory that is the product of those two counts, but // rather we only need one Births instance for each thread that constructs an -// instance at a Location. In many cases, instances are only created on one +// instance at a Location. In many cases, instances are only created on one // thread, so the memory utilization is actually fairly restrained. // // Lastly, when an instance is deleted, the final tallies of statistics are @@ -96,22 +99,22 @@ struct TrackingInfo; // lock such DeathData instances. (i.e., these accumulated stats in a DeathData // instance are exclusively updated by the singular owning thread). // -// With the above lifecycle description complete, the major remaining detail is -// explaining how each thread maintains a list of DeathData instances, and of -// Births instances, and is able to avoid additional (redundant/unnecessary) +// With the above life cycle description complete, the major remaining detail +// is explaining how each thread maintains a list of DeathData instances, and +// of Births instances, and is able to avoid additional (redundant/unnecessary) // allocations. // // Each thread maintains a list of data items specific to that thread in a // ThreadData instance (for that specific thread only). The two critical items // are lists of DeathData and Births instances. These lists are maintained in -// STL maps, which are indexed by Location. As noted earlier, we can compare +// STL maps, which are indexed by Location. As noted earlier, we can compare // locations very efficiently as we consider the underlying data (file, // function, line) to be atoms, and hence pointer comparison is used rather than // (slow) string comparisons. // // To provide a mechanism for iterating over all "known threads," which means // threads that have recorded a birth or a death, we create a singly linked list -// of ThreadData instances. Each such instance maintains a pointer to the next +// of ThreadData instances. Each such instance maintains a pointer to the next // one. A static member of ThreadData provides a pointer to the first item on // this global list, and access via that all_thread_data_list_head_ item // requires the use of the list_lock_. @@ -119,7 +122,7 @@ struct TrackingInfo; // which ensures that any prior acquisition of the list is valid (i.e., the // holder can iterate over it without fear of it changing, or the necessity of // using an additional lock. Iterations are actually pretty rare (used -// primarilly for cleanup, or snapshotting data for display), so this lock has +// primarily for cleanup, or snapshotting data for display), so this lock has // very little global performance impact. // // The above description tries to define the high performance (run time) @@ -146,17 +149,20 @@ struct TrackingInfo; // TaskSnapshot instances, so that such instances can be sorted and // aggregated (and remain frozen during our processing). // -// The ProcessDataSnapshot struct is a serialized representation of the list -// of ThreadData objects for a process. It holds a set of TaskSnapshots -// and tracks parent/child relationships for the executed tasks. The statistics -// in a snapshot are gathered asynhcronously relative to their ongoing updates. +// Profiling consists of phases. The concrete phase in the sequence of phases +// is identified by its 0-based index. +// +// The ProcessDataPhaseSnapshot struct is a serialized representation of the +// list of ThreadData objects for a process for a concrete profiling phase. It +// holds a set of TaskSnapshots. The statistics in a snapshot are gathered +// asynhcronously relative to their ongoing updates. // It is possible, though highly unlikely, that stats could be incorrectly // recorded by this process (all data is held in 32 bit ints, but we are not // atomically collecting all data, so we could have count that does not, for // example, match with the number of durations we accumulated). The advantage // to having fast (non-atomic) updates of the data outweighs the minimal risk of // a singular corrupt statistic snapshot (only the snapshot could be corrupt, -// not the underlying and ongoing statistic). In constrast, pointer data that +// not the underlying and ongoing statistic). In contrast, pointer data that // is accessed during snapshotting is completely invariant, and hence is // perfectly acquired (i.e., no potential corruption, and no risk of a bad // memory reference). @@ -167,21 +173,14 @@ struct TrackingInfo; // them will continue to be asynchronous). We had an implementation of this in // the past, but the difficulty is dealing with message loops being terminated. // We can *try* to spam the available threads via some message loop proxy to -// achieve this feat, and it *might* be valuable when we are colecting data for -// upload via UMA (where correctness of data may be more significant than for a -// single screen of about:profiler). -// -// TODO(jar): We should support (optionally) the recording of parent-child -// relationships for tasks. This should be done by detecting what tasks are -// Born during the running of a parent task. The resulting data can be used by -// a smarter profiler to aggregate the cost of a series of child tasks into -// the ancestor task. It can also be used to illuminate what child or parent is -// related to each task. +// achieve this feat, and it *might* be valuable when we are collecting data +// for upload via UMA (where correctness of data may be more significant than +// for a single screen of about:profiler). // // TODO(jar): We need to store DataCollections, and provide facilities for // taking the difference between two gathered DataCollections. For now, we're // just adding a hack that Reset()s to zero all counts and stats. This is also -// done in a slighly thread-unsafe fashion, as the resetting is done +// done in a slightly thread-unsafe fashion, as the resetting is done // asynchronously relative to ongoing updates (but all data is 32 bit in size). // For basic profiling, this will work "most of the time," and should be // sufficient... but storing away DataCollections is the "right way" to do this. @@ -240,13 +239,6 @@ class BASE_EXPORT Births: public BirthOnThread { // When we have a birth we update the count for this birthplace. void RecordBirth(); - // When a birthplace is changed (updated), we need to decrement the counter - // for the old instance. - void ForgetBirth(); - - // Hack to quickly reset all counts to zero. - void Clear(); - private: // The number of births on this thread for our location_. int birth_count_; @@ -255,74 +247,139 @@ class BASE_EXPORT Births: public BirthOnThread { }; //------------------------------------------------------------------------------ -// Basic info summarizing multiple destructions of a tracked object with a -// single birthplace (fixed Location). Used both on specific threads, and also -// in snapshots when integrating assembled data. +// A "snapshotted" representation of the DeathData class. + +struct BASE_EXPORT DeathDataSnapshot { + DeathDataSnapshot(); + + // Constructs the snapshot from individual values. + // The alternative would be taking a DeathData parameter, but this would + // create a loop since DeathData indirectly refers DeathDataSnapshot. Passing + // a wrapper structure as a param or using an empty constructor for + // snapshotting DeathData would be less efficient. + DeathDataSnapshot(int count, + int32 run_duration_sum, + int32 run_duration_max, + int32 run_duration_sample, + int32 queue_duration_sum, + int32 queue_duration_max, + int32 queue_duration_sample); + ~DeathDataSnapshot(); + + // Calculates and returns the delta between this snapshot and an earlier + // snapshot of the same task |older|. + DeathDataSnapshot Delta(const DeathDataSnapshot& older) const; + + int count; + int32 run_duration_sum; + int32 run_duration_max; + int32 run_duration_sample; + int32 queue_duration_sum; + int32 queue_duration_max; + int32 queue_duration_sample; +}; + +//------------------------------------------------------------------------------ +// A "snapshotted" representation of the DeathData for a particular profiling +// phase. Used as an element of the list of phase snapshots owned by DeathData. + +struct DeathDataPhaseSnapshot { + DeathDataPhaseSnapshot(int profiling_phase, + int count, + int32 run_duration_sum, + int32 run_duration_max, + int32 run_duration_sample, + int32 queue_duration_sum, + int32 queue_duration_max, + int32 queue_duration_sample, + const DeathDataPhaseSnapshot* prev); + + // Profiling phase at which completion this snapshot was taken. + int profiling_phase; + + // Death data snapshot. + DeathDataSnapshot death_data; + + // Pointer to a snapshot from the previous phase. + const DeathDataPhaseSnapshot* prev; +}; + +//------------------------------------------------------------------------------ +// Information about deaths of a task on a given thread, called "death thread". +// Access to members of this class is never protected by a lock. The fields +// are accessed in such a way that corruptions resulting from race conditions +// are not significant, and don't accumulate as a result of multiple accesses. +// All invocations of DeathData::OnProfilingPhaseCompleted and +// ThreadData::SnapshotMaps (which takes DeathData snapshot) in a given process +// must be called from the same thread. It doesn't matter what thread it is, but +// it's important the same thread is used as a snapshot thread during the whole +// process lifetime. All fields except sample_probability_count_ can be +// snapshotted. class BASE_EXPORT DeathData { public: - // Default initializer. DeathData(); - - // When deaths have not yet taken place, and we gather data from all the - // threads, we create DeathData stats that tally the number of births without - // a corresponding death. - explicit DeathData(int count); + DeathData(const DeathData& other); + ~DeathData(); // Update stats for a task destruction (death) that had a Run() time of // |duration|, and has had a queueing delay of |queue_duration|. void RecordDeath(const int32 queue_duration, const int32 run_duration, - int random_number); - - // Metrics accessors, used only for serialization and in tests. - int count() const; - int32 run_duration_sum() const; - int32 run_duration_max() const; - int32 run_duration_sample() const; - int32 queue_duration_sum() const; - int32 queue_duration_max() const; - int32 queue_duration_sample() const; - - // Reset the max values to zero. - void ResetMax(); - - // Reset all tallies to zero. This is used as a hack on realtime data. - void Clear(); + const uint32 random_number); + + // Metrics and past snapshots accessors, used only for serialization and in + // tests. + int count() const { return count_; } + int32 run_duration_sum() const { return run_duration_sum_; } + int32 run_duration_max() const { return run_duration_max_; } + int32 run_duration_sample() const { return run_duration_sample_; } + int32 queue_duration_sum() const { return queue_duration_sum_; } + int32 queue_duration_max() const { return queue_duration_max_; } + int32 queue_duration_sample() const { return queue_duration_sample_; } + const DeathDataPhaseSnapshot* last_phase_snapshot() const { + return last_phase_snapshot_; + } + + // Called when the current profiling phase, identified by |profiling_phase|, + // ends. + // Must be called only on the snapshot thread. + void OnProfilingPhaseCompleted(int profiling_phase); private: // Members are ordered from most regularly read and updated, to least // frequently used. This might help a bit with cache lines. // Number of runs seen (divisor for calculating averages). + // Can be incremented only on the death thread. int count_; - // Basic tallies, used to compute averages. + + // Count used in determining probability of selecting exec/queue times from a + // recorded death as samples. + // Gets incremented only on the death thread, but can be set to 0 by + // OnProfilingPhaseCompleted() on the snapshot thread. + int sample_probability_count_; + + // Basic tallies, used to compute averages. Can be incremented only on the + // death thread. int32 run_duration_sum_; int32 queue_duration_sum_; // Max values, used by local visualization routines. These are often read, - // but rarely updated. + // but rarely updated. The max values get assigned only on the death thread, + // but these fields can be set to 0 by OnProfilingPhaseCompleted() on the + // snapshot thread. int32 run_duration_max_; int32 queue_duration_max_; // Samples, used by crowd sourcing gatherers. These are almost never read, - // and rarely updated. + // and rarely updated. They can be modified only on the death thread. int32 run_duration_sample_; int32 queue_duration_sample_; -}; -//------------------------------------------------------------------------------ -// A "snapshotted" representation of the DeathData class. + // Snapshot of this death data made at the last profiling phase completion, if + // any. DeathData owns the whole list starting with this pointer. + // Can be accessed only on the snapshot thread. + const DeathDataPhaseSnapshot* last_phase_snapshot_; -struct BASE_EXPORT DeathDataSnapshot { - DeathDataSnapshot(); - explicit DeathDataSnapshot(const DeathData& death_data); - ~DeathDataSnapshot(); - - int count; - int32 run_duration_sum; - int32 run_duration_max; - int32 run_duration_sample; - int32 queue_duration_sum; - int32 queue_duration_max; - int32 queue_duration_sample; + DISALLOW_ASSIGN(DeathData); }; //------------------------------------------------------------------------------ @@ -334,12 +391,14 @@ struct BASE_EXPORT DeathDataSnapshot { struct BASE_EXPORT TaskSnapshot { TaskSnapshot(); - TaskSnapshot(const BirthOnThread& birth, - const DeathData& death_data, + TaskSnapshot(const BirthOnThreadSnapshot& birth, + const DeathDataSnapshot& death_data, const std::string& death_thread_name); ~TaskSnapshot(); BirthOnThreadSnapshot birth; + // Delta between death data for a thread for a certain profiling phase and the + // snapshot for the pervious phase, if any. Otherwise, just a snapshot. DeathDataSnapshot death_data; std::string death_thread_name; }; @@ -351,27 +410,29 @@ struct BASE_EXPORT TaskSnapshot { // We also have a linked list of ThreadData instances, and that list is used to // harvest data from all existing instances. +struct ProcessDataPhaseSnapshot; struct ProcessDataSnapshot; class BASE_EXPORT TaskStopwatch; +// Map from profiling phase number to the process-wide snapshotted +// representation of the list of ThreadData objects that died during the given +// phase. +typedef std::map<int, ProcessDataPhaseSnapshot> PhasedProcessDataSnapshotMap; + class BASE_EXPORT ThreadData { public: // Current allowable states of the tracking system. The states can vary // between ACTIVE and DEACTIVATED, but can never go back to UNINITIALIZED. enum Status { - UNINITIALIZED, // PRistine, link-time state before running. - DORMANT_DURING_TESTS, // Only used during testing. - DEACTIVATED, // No longer recording profling. - PROFILING_ACTIVE, // Recording profiles (no parent-child links). - PROFILING_CHILDREN_ACTIVE, // Fully active, recording parent-child links. - STATUS_LAST = PROFILING_CHILDREN_ACTIVE + UNINITIALIZED, // Pristine, link-time state before running. + DORMANT_DURING_TESTS, // Only used during testing. + DEACTIVATED, // No longer recording profiling. + PROFILING_ACTIVE, // Recording profiles. + STATUS_LAST = PROFILING_ACTIVE }; - typedef std::map<Location, Births*> BirthMap; + typedef base::hash_map<Location, Births*, Location::Hash> BirthMap; typedef std::map<const Births*, DeathData> DeathMap; - typedef std::pair<const Births*, const Births*> ParentChildPair; - typedef std::set<ParentChildPair> ParentChildSet; - typedef std::stack<const Births*> ParentStack; // Initialize the current thread context with a new instance of ThreadData. // This is used by all threads that have names, and should be explicitly @@ -385,10 +446,22 @@ class BASE_EXPORT ThreadData { // This may return NULL if the system is disabled for any reason. static ThreadData* Get(); - // Fills |process_data| with all the recursive results in our process. - // During the scavenging, if |reset_max| is true, then the DeathData instances - // max-values are reset to zero during this scan. - static void Snapshot(bool reset_max, ProcessDataSnapshot* process_data); + // Fills |process_data_snapshot| with phased snapshots of all profiling + // phases, including the current one, identified by |current_profiling_phase|. + // |current_profiling_phase| is necessary because a child process can start + // after several phase-changing events, so it needs to receive the current + // phase number from the browser process to fill the correct entry for the + // current phase in the |process_data_snapshot| map. + static void Snapshot(int current_profiling_phase, + ProcessDataSnapshot* process_data_snapshot); + + // Called when the current profiling phase, identified by |profiling_phase|, + // ends. + // |profiling_phase| is necessary because a child process can start after + // several phase-changing events, so it needs to receive the phase number from + // the browser process to fill the correct entry in the + // completed_phases_snapshots_ map. + static void OnProfilingPhaseCompleted(int profiling_phase); // Finds (or creates) a place to count births from the given location in this // thread, and increment that tally. @@ -402,7 +475,7 @@ class BASE_EXPORT ThreadData { // delayed tasks, and it indicates when the task should have run (i.e., when // it should have posted out of the timer queue, and into the work queue. // The |end_of_run| was just obtained by a call to Now() (just after the task - // finished). It is provided as an argument to help with testing. + // finished). It is provided as an argument to help with testing. static void TallyRunOnNamedThreadIfTracking( const base::TrackingInfo& completed_task, const TaskStopwatch& stopwatch); @@ -414,40 +487,25 @@ class BASE_EXPORT ThreadData { // the task. // The |end_of_run| was just obtained by a call to Now() (just after the task // finished). - static void TallyRunOnWorkerThreadIfTracking( - const Births* birth, - const TrackedTime& time_posted, - const TaskStopwatch& stopwatch); + static void TallyRunOnWorkerThreadIfTracking(const Births* births, + const TrackedTime& time_posted, + const TaskStopwatch& stopwatch); // Record the end of execution in region, generally corresponding to a scope // being exited. - static void TallyRunInAScopedRegionIfTracking( - const Births* birth, - const TaskStopwatch& stopwatch); + static void TallyRunInAScopedRegionIfTracking(const Births* births, + const TaskStopwatch& stopwatch); const std::string& thread_name() const { return thread_name_; } - // Hack: asynchronously clear all birth counts and death tallies data values - // in all ThreadData instances. The numerical (zeroing) part is done without - // use of a locks or atomics exchanges, and may (for int64 values) produce - // bogus counts VERY rarely. - static void ResetAllThreadData(); - // Initializes all statics if needed (this initialization call should be made - // while we are single threaded). Returns false if unable to initialize. - static bool Initialize(); + // while we are single threaded). + static void Initialize(); // Sets internal status_. // If |status| is false, then status_ is set to DEACTIVATED. - // If |status| is true, then status_ is set to, PROFILING_ACTIVE, or - // PROFILING_CHILDREN_ACTIVE. - // If tracking is not compiled in, this function will return false. - // If parent-child tracking is not compiled in, then an attempt to set the - // status to PROFILING_CHILDREN_ACTIVE will only result in a status of - // PROFILING_ACTIVE (i.e., it can't be set to a higher level than what is - // compiled into the binary, and parent-child tracking at the - // PROFILING_CHILDREN_ACTIVE level might not be compiled in). - static bool InitializeAndSetTrackingStatus(Status status); + // If |status| is true, then status_ is set to PROFILING_ACTIVE. + static void InitializeAndSetTrackingStatus(Status status); static Status status(); @@ -455,17 +513,6 @@ class BASE_EXPORT ThreadData { // DEACTIVATED). static bool TrackingStatus(); - // For testing only, indicate if the status of parent-child tracking is turned - // on. This is currently a compiled option, atop TrackingStatus(). - static bool TrackingParentChildStatus(); - - // Marks a start of a tracked run. It's super fast when tracking is disabled, - // and has some internal side effects when we are tracking, so that we can - // deduce the amount of time accumulated outside of execution of tracked runs. - // The task that will be tracked is passed in as |parent| so that parent-child - // relationships can be (optionally) calculated. - static void PrepareForStartOfRun(const Births* parent); - // Enables profiler timing. static void EnableProfilerTiming(); @@ -496,10 +543,12 @@ class BASE_EXPORT ThreadData { friend class TrackedObjectsTest; FRIEND_TEST_ALL_PREFIXES(TrackedObjectsTest, MinimalStartupShutdown); FRIEND_TEST_ALL_PREFIXES(TrackedObjectsTest, TinyStartupShutdown); - FRIEND_TEST_ALL_PREFIXES(TrackedObjectsTest, ParentChildTest); typedef std::map<const BirthOnThread*, int> BirthCountMap; + typedef std::vector<std::pair<const Births*, DeathDataPhaseSnapshot>> + DeathsSnapshot; + // Worker thread construction creates a name since there is none. explicit ThreadData(int thread_number); @@ -524,43 +573,31 @@ class BASE_EXPORT ThreadData { Births* TallyABirth(const Location& location); // Find a place to record a death on this thread. - void TallyADeath(const Births& birth, + void TallyADeath(const Births& births, int32 queue_duration, const TaskStopwatch& stopwatch); - // Snapshot (under a lock) the profiled data for the tasks in each ThreadData - // instance. Also updates the |birth_counts| tally for each task to keep - // track of the number of living instances of the task. If |reset_max| is - // true, then the max values in each DeathData instance are reset during the - // scan. - static void SnapshotAllExecutedTasks(bool reset_max, - ProcessDataSnapshot* process_data, - BirthCountMap* birth_counts); - // Snapshots (under a lock) the profiled data for the tasks for this thread - // and writes all of the executed tasks' data -- i.e. the data for the tasks - // with with entries in the death_map_ -- into |process_data|. Also updates - // the |birth_counts| tally for each task to keep track of the number of - // living instances of the task -- that is, each task maps to the number of - // births for the task that have not yet been balanced by a death. If - // |reset_max| is true, then the max values in each DeathData instance are - // reset during the scan. - void SnapshotExecutedTasks(bool reset_max, - ProcessDataSnapshot* process_data, + // and writes all of the executed tasks' data -- i.e. the data for all + // profiling phases (including the current one: |current_profiling_phase|) for + // the tasks with with entries in the death_map_ -- into |phased_snapshots|. + // Also updates the |birth_counts| tally for each task to keep track of the + // number of living instances of the task -- that is, each task maps to the + // number of births for the task that have not yet been balanced by a death. + void SnapshotExecutedTasks(int current_profiling_phase, + PhasedProcessDataSnapshotMap* phased_snapshots, BirthCountMap* birth_counts); // Using our lock, make a copy of the specified maps. This call may be made // on non-local threads, which necessitate the use of the lock to prevent - // the map(s) from being reallocaed while they are copied. If |reset_max| is - // true, then, just after we copy the DeathMap, we will set the max values to - // zero in the active DeathMap (not the snapshot). - void SnapshotMaps(bool reset_max, + // the map(s) from being reallocated while they are copied. + void SnapshotMaps(int profiling_phase, BirthMap* birth_map, - DeathMap* death_map, - ParentChildSet* parent_child_set); + DeathsSnapshot* deaths); - // Using our lock to protect the iteration, Clear all birth and death data. - void Reset(); + // Called for this thread when the current profiling phase, identified by + // |profiling_phase|, ends. + void OnProfilingPhaseCompletedOnThread(int profiling_phase); // This method is called by the TLS system when a thread terminates. // The argument may be NULL if this thread has never tracked a birth or death. @@ -592,8 +629,8 @@ class BASE_EXPORT ThreadData { // We use thread local store to identify which ThreadData to interact with. static base::ThreadLocalStorage::StaticSlot tls_index_; - // List of ThreadData instances for use with worker threads. When a worker - // thread is done (terminated), we push it onto this llist. When a new worker + // List of ThreadData instances for use with worker threads. When a worker + // thread is done (terminated), we push it onto this list. When a new worker // thread is created, we first try to re-use a ThreadData instance from the // list, and if none are available, construct a new one. // This is only accessed while list_lock_ is held. @@ -609,7 +646,7 @@ class BASE_EXPORT ThreadData { static int worker_thread_data_creation_count_; // The number of times TLS has called us back to cleanup a ThreadData - // instance. This is only accessed while list_lock_ is held. + // instance. This is only accessed while list_lock_ is held. static int cleanup_count_; // Incarnation sequence number, indicating how many times (during unittests) @@ -626,7 +663,7 @@ class BASE_EXPORT ThreadData { // We set status_ to SHUTDOWN when we shut down the tracking service. static Status status_; - // Link to next instance (null terminated list). Used to globally track all + // Link to next instance (null terminated list). Used to globally track all // registered instances (corresponds to all registered threads where we keep // data). ThreadData* next_; @@ -658,11 +695,6 @@ class BASE_EXPORT ThreadData { // locking before reading it. DeathMap death_map_; - // A set of parents that created children tasks on this thread. Each pair - // corresponds to potentially non-local Births (location and thread), and a - // local Births (that took place on this thread). - ParentChildSet parent_child_set_; - // Lock to protect *some* access to BirthMap and DeathMap. The maps are // regularly read and written on this thread, but may only be read from other // threads. To support this, we acquire this lock if we are writing from this @@ -671,21 +703,11 @@ class BASE_EXPORT ThreadData { // writing is only done from this thread. mutable base::Lock map_lock_; - // The stack of parents that are currently being profiled. This includes only - // tasks that have started a timer recently via PrepareForStartOfRun(), but - // not yet concluded with a NowForEndOfRun(). Usually this stack is one deep, - // but if a scoped region is profiled, or <sigh> a task runs a nested-message - // loop, then the stack can grow larger. Note that we don't try to deduct - // time in nested porfiles, as our current timer is based on wall-clock time, - // and not CPU time (and we're hopeful that nested timing won't be a - // significant additional cost). - ParentStack parent_stack_; - // A random number that we used to select decide which sample to keep as a // representative sample in each DeathData instance. We can't start off with // much randomness (because we can't call RandInt() on all our threads), so // we stir in more and more as we go. - int32 random_number_; + uint32 random_number_; // Record of what the incarnation_counter_ was when this instance was created. // If the incarnation_counter_ has changed, then we avoid pushing into the @@ -702,7 +724,7 @@ class BASE_EXPORT ThreadData { //------------------------------------------------------------------------------ // Stopwatch to measure task run time or simply create a time interval that will -// be subtracted from the current most nested task's run time. Stopwatches +// be subtracted from the current most nested task's run time. Stopwatches // coordinate with the stopwatches in which they are nested to avoid // double-counting nested tasks run times. @@ -749,43 +771,40 @@ class BASE_EXPORT TaskStopwatch { // duration of this stopwatch. TaskStopwatch* parent_; -#if DCHECK_IS_ON - // State of the stopwatch. Stopwatch is first constructed in a created state +#if DCHECK_IS_ON() + // State of the stopwatch. Stopwatch is first constructed in a created state // state, then is optionally started/stopped, then destructed. enum { CREATED, RUNNING, STOPPED } state_; // Currently running stopwatch that is directly nested in this one, if such - // stopwatch exists. NULL otherwise. + // stopwatch exists. NULL otherwise. TaskStopwatch* child_; #endif }; //------------------------------------------------------------------------------ -// A snapshotted representation of a (parent, child) task pair, for tracking -// hierarchical profiles. +// A snapshotted representation of the list of ThreadData objects for a process, +// for a single profiling phase. -struct BASE_EXPORT ParentChildPairSnapshot { +struct BASE_EXPORT ProcessDataPhaseSnapshot { public: - ParentChildPairSnapshot(); - explicit ParentChildPairSnapshot( - const ThreadData::ParentChildPair& parent_child); - ~ParentChildPairSnapshot(); + ProcessDataPhaseSnapshot(); + ~ProcessDataPhaseSnapshot(); - BirthOnThreadSnapshot parent; - BirthOnThreadSnapshot child; + std::vector<TaskSnapshot> tasks; }; //------------------------------------------------------------------------------ -// A snapshotted representation of the list of ThreadData objects for a process. +// A snapshotted representation of the list of ThreadData objects for a process, +// for all profiling phases, including the current one. struct BASE_EXPORT ProcessDataSnapshot { public: ProcessDataSnapshot(); ~ProcessDataSnapshot(); - std::vector<TaskSnapshot> tasks; - std::vector<ParentChildPairSnapshot> descendants; - int process_id; + PhasedProcessDataSnapshotMap phased_snapshots; + base::ProcessId process_id; }; } // namespace tracked_objects diff --git a/chromium/base/tracked_objects_unittest.cc b/chromium/base/tracked_objects_unittest.cc index f19ba7b2c63..cdbf9ac7a6c 100644 --- a/chromium/base/tracked_objects_unittest.cc +++ b/chromium/base/tracked_objects_unittest.cc @@ -34,7 +34,7 @@ class TrackedObjectsTest : public testing::Test { ThreadData::now_function_is_time_ = true; } - virtual ~TrackedObjectsTest() { + ~TrackedObjectsTest() override { // We should not need to leak any structures we create, since we are // single threaded, and carefully accounting for items. ThreadData::ShutdownSingleThreadedCleanup(false); @@ -71,28 +71,35 @@ class TrackedObjectsTest : public testing::Test { int count, int run_ms, int queue_ms) { - ASSERT_EQ(1u, process_data.tasks.size()); + ASSERT_EQ(1u, process_data.phased_snapshots.size()); + auto it = process_data.phased_snapshots.find(0); + ASSERT_TRUE(it != process_data.phased_snapshots.end()); + const ProcessDataPhaseSnapshot& process_data_phase = it->second; - EXPECT_EQ(kFile, process_data.tasks[0].birth.location.file_name); + ASSERT_EQ(1u, process_data_phase.tasks.size()); + + EXPECT_EQ(kFile, process_data_phase.tasks[0].birth.location.file_name); EXPECT_EQ(function_name, - process_data.tasks[0].birth.location.function_name); - EXPECT_EQ(kLineNumber, process_data.tasks[0].birth.location.line_number); + process_data_phase.tasks[0].birth.location.function_name); + EXPECT_EQ(kLineNumber, + process_data_phase.tasks[0].birth.location.line_number); - EXPECT_EQ(birth_thread, process_data.tasks[0].birth.thread_name); + EXPECT_EQ(birth_thread, process_data_phase.tasks[0].birth.thread_name); - EXPECT_EQ(count, process_data.tasks[0].death_data.count); + EXPECT_EQ(count, process_data_phase.tasks[0].death_data.count); EXPECT_EQ(count * run_ms, - process_data.tasks[0].death_data.run_duration_sum); - EXPECT_EQ(run_ms, process_data.tasks[0].death_data.run_duration_max); - EXPECT_EQ(run_ms, process_data.tasks[0].death_data.run_duration_sample); + process_data_phase.tasks[0].death_data.run_duration_sum); + EXPECT_EQ(run_ms, process_data_phase.tasks[0].death_data.run_duration_max); + EXPECT_EQ(run_ms, + process_data_phase.tasks[0].death_data.run_duration_sample); EXPECT_EQ(count * queue_ms, - process_data.tasks[0].death_data.queue_duration_sum); - EXPECT_EQ(queue_ms, process_data.tasks[0].death_data.queue_duration_max); - EXPECT_EQ(queue_ms, process_data.tasks[0].death_data.queue_duration_sample); + process_data_phase.tasks[0].death_data.queue_duration_sum); + EXPECT_EQ(queue_ms, + process_data_phase.tasks[0].death_data.queue_duration_max); + EXPECT_EQ(queue_ms, + process_data_phase.tasks[0].death_data.queue_duration_sample); - EXPECT_EQ(death_thread, process_data.tasks[0].death_thread_name); - - EXPECT_EQ(0u, process_data.descendants.size()); + EXPECT_EQ(death_thread, process_data_phase.tasks[0].death_thread_name); EXPECT_EQ(base::GetCurrentProcId(), process_data.process_id); } @@ -112,10 +119,7 @@ class TrackedObjectsTest : public testing::Test { unsigned int TrackedObjectsTest::test_time_; TEST_F(TrackedObjectsTest, TaskStopwatchNoStartStop) { - if (!ThreadData::InitializeAndSetTrackingStatus( - ThreadData::PROFILING_CHILDREN_ACTIVE)) { - return; - } + ThreadData::InitializeAndSetTrackingStatus(ThreadData::PROFILING_ACTIVE); // Check that creating and destroying a stopwatch without starting it doesn't // crash. @@ -124,10 +128,7 @@ TEST_F(TrackedObjectsTest, TaskStopwatchNoStartStop) { TEST_F(TrackedObjectsTest, MinimalStartupShutdown) { // Minimal test doesn't even create any tasks. - if (!ThreadData::InitializeAndSetTrackingStatus( - ThreadData::PROFILING_CHILDREN_ACTIVE)) { - return; - } + ThreadData::InitializeAndSetTrackingStatus(ThreadData::PROFILING_ACTIVE); EXPECT_FALSE(ThreadData::first()); // No activity even on this thread. ThreadData* data = ThreadData::Get(); @@ -136,19 +137,16 @@ TEST_F(TrackedObjectsTest, MinimalStartupShutdown) { EXPECT_FALSE(data->next()); EXPECT_EQ(data, ThreadData::Get()); ThreadData::BirthMap birth_map; - ThreadData::DeathMap death_map; - ThreadData::ParentChildSet parent_child_set; - data->SnapshotMaps(false, &birth_map, &death_map, &parent_child_set); + ThreadData::DeathsSnapshot deaths; + data->SnapshotMaps(0, &birth_map, &deaths); EXPECT_EQ(0u, birth_map.size()); - EXPECT_EQ(0u, death_map.size()); - EXPECT_EQ(0u, parent_child_set.size()); + EXPECT_EQ(0u, deaths.size()); // Clean up with no leaking. Reset(); // Do it again, just to be sure we reset state completely. - EXPECT_TRUE(ThreadData::InitializeAndSetTrackingStatus( - ThreadData::PROFILING_CHILDREN_ACTIVE)); + ThreadData::InitializeAndSetTrackingStatus(ThreadData::PROFILING_ACTIVE); EXPECT_FALSE(ThreadData::first()); // No activity even on this thread. data = ThreadData::Get(); EXPECT_TRUE(ThreadData::first()); // Now class was constructed. @@ -156,42 +154,34 @@ TEST_F(TrackedObjectsTest, MinimalStartupShutdown) { EXPECT_FALSE(data->next()); EXPECT_EQ(data, ThreadData::Get()); birth_map.clear(); - death_map.clear(); - parent_child_set.clear(); - data->SnapshotMaps(false, &birth_map, &death_map, &parent_child_set); + deaths.clear(); + data->SnapshotMaps(0, &birth_map, &deaths); EXPECT_EQ(0u, birth_map.size()); - EXPECT_EQ(0u, death_map.size()); - EXPECT_EQ(0u, parent_child_set.size()); + EXPECT_EQ(0u, deaths.size()); } TEST_F(TrackedObjectsTest, TinyStartupShutdown) { - if (!ThreadData::InitializeAndSetTrackingStatus( - ThreadData::PROFILING_CHILDREN_ACTIVE)) { - return; - } + ThreadData::InitializeAndSetTrackingStatus(ThreadData::PROFILING_ACTIVE); // Instigate tracking on a single tracked object, on our thread. const char kFunction[] = "TinyStartupShutdown"; Location location(kFunction, kFile, kLineNumber, NULL); - Births* first_birth = ThreadData::TallyABirthIfActive(location); + ThreadData::TallyABirthIfActive(location); ThreadData* data = ThreadData::first(); ASSERT_TRUE(data); EXPECT_FALSE(data->next()); EXPECT_EQ(data, ThreadData::Get()); ThreadData::BirthMap birth_map; - ThreadData::DeathMap death_map; - ThreadData::ParentChildSet parent_child_set; - data->SnapshotMaps(false, &birth_map, &death_map, &parent_child_set); + ThreadData::DeathsSnapshot deaths; + data->SnapshotMaps(0, &birth_map, &deaths); EXPECT_EQ(1u, birth_map.size()); // 1 birth location. EXPECT_EQ(1, birth_map.begin()->second->birth_count()); // 1 birth. - EXPECT_EQ(0u, death_map.size()); // No deaths. - EXPECT_EQ(0u, parent_child_set.size()); // No children. + EXPECT_EQ(0u, deaths.size()); // No deaths. // Now instigate another birth, while we are timing the run of the first // execution. - ThreadData::PrepareForStartOfRun(first_birth); // Create a child (using the same birth location). // TrackingInfo will call TallyABirth() during construction. const int32 start_time = 1; @@ -209,74 +199,56 @@ TEST_F(TrackedObjectsTest, TinyStartupShutdown) { ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, stopwatch); birth_map.clear(); - death_map.clear(); - parent_child_set.clear(); - data->SnapshotMaps(false, &birth_map, &death_map, &parent_child_set); + deaths.clear(); + data->SnapshotMaps(0, &birth_map, &deaths); EXPECT_EQ(1u, birth_map.size()); // 1 birth location. EXPECT_EQ(2, birth_map.begin()->second->birth_count()); // 2 births. - EXPECT_EQ(1u, death_map.size()); // 1 location. - EXPECT_EQ(1, death_map.begin()->second.count()); // 1 death. - if (ThreadData::TrackingParentChildStatus()) { - EXPECT_EQ(1u, parent_child_set.size()); // 1 child. - EXPECT_EQ(parent_child_set.begin()->first, - parent_child_set.begin()->second); - } else { - EXPECT_EQ(0u, parent_child_set.size()); // no stats. - } + EXPECT_EQ(1u, deaths.size()); // 1 location. + EXPECT_EQ(1, deaths.begin()->second.death_data.count); // 1 death. // The births were at the same location as the one known death. - EXPECT_EQ(birth_map.begin()->second, death_map.begin()->first); + EXPECT_EQ(birth_map.begin()->second, deaths.begin()->first); ProcessDataSnapshot process_data; - ThreadData::Snapshot(false, &process_data); - - ASSERT_EQ(1u, process_data.tasks.size()); - EXPECT_EQ(kFile, process_data.tasks[0].birth.location.file_name); - EXPECT_EQ(kFunction, process_data.tasks[0].birth.location.function_name); - EXPECT_EQ(kLineNumber, process_data.tasks[0].birth.location.line_number); - EXPECT_EQ(kWorkerThreadName, process_data.tasks[0].birth.thread_name); - EXPECT_EQ(1, process_data.tasks[0].death_data.count); - EXPECT_EQ(time_elapsed, process_data.tasks[0].death_data.run_duration_sum); - EXPECT_EQ(time_elapsed, process_data.tasks[0].death_data.run_duration_max); - EXPECT_EQ(time_elapsed, process_data.tasks[0].death_data.run_duration_sample); - EXPECT_EQ(0, process_data.tasks[0].death_data.queue_duration_sum); - EXPECT_EQ(0, process_data.tasks[0].death_data.queue_duration_max); - EXPECT_EQ(0, process_data.tasks[0].death_data.queue_duration_sample); - EXPECT_EQ(kWorkerThreadName, process_data.tasks[0].death_thread_name); - - if (ThreadData::TrackingParentChildStatus()) { - ASSERT_EQ(1u, process_data.descendants.size()); - EXPECT_EQ(kFile, process_data.descendants[0].parent.location.file_name); - EXPECT_EQ(kFunction, - process_data.descendants[0].parent.location.function_name); - EXPECT_EQ(kLineNumber, - process_data.descendants[0].parent.location.line_number); - EXPECT_EQ(kWorkerThreadName, - process_data.descendants[0].parent.thread_name); - EXPECT_EQ(kFile, process_data.descendants[0].child.location.file_name); - EXPECT_EQ(kFunction, - process_data.descendants[0].child.location.function_name); - EXPECT_EQ(kLineNumber, - process_data.descendants[0].child.location.line_number); - EXPECT_EQ(kWorkerThreadName, process_data.descendants[0].child.thread_name); - } else { - EXPECT_EQ(0u, process_data.descendants.size()); - } + ThreadData::Snapshot(0, &process_data); + + ASSERT_EQ(1u, process_data.phased_snapshots.size()); + auto it = process_data.phased_snapshots.find(0); + ASSERT_TRUE(it != process_data.phased_snapshots.end()); + const ProcessDataPhaseSnapshot& process_data_phase = it->second; + ASSERT_EQ(1u, process_data_phase.tasks.size()); + EXPECT_EQ(kFile, process_data_phase.tasks[0].birth.location.file_name); + EXPECT_EQ(kFunction, + process_data_phase.tasks[0].birth.location.function_name); + EXPECT_EQ(kLineNumber, + process_data_phase.tasks[0].birth.location.line_number); + EXPECT_EQ(kWorkerThreadName, process_data_phase.tasks[0].birth.thread_name); + EXPECT_EQ(1, process_data_phase.tasks[0].death_data.count); + EXPECT_EQ(time_elapsed, + process_data_phase.tasks[0].death_data.run_duration_sum); + EXPECT_EQ(time_elapsed, + process_data_phase.tasks[0].death_data.run_duration_max); + EXPECT_EQ(time_elapsed, + process_data_phase.tasks[0].death_data.run_duration_sample); + EXPECT_EQ(0, process_data_phase.tasks[0].death_data.queue_duration_sum); + EXPECT_EQ(0, process_data_phase.tasks[0].death_data.queue_duration_max); + EXPECT_EQ(0, process_data_phase.tasks[0].death_data.queue_duration_sample); + EXPECT_EQ(kWorkerThreadName, process_data_phase.tasks[0].death_thread_name); } -TEST_F(TrackedObjectsTest, DeathDataTest) { - if (!ThreadData::InitializeAndSetTrackingStatus( - ThreadData::PROFILING_CHILDREN_ACTIVE)) { - return; - } +TEST_F(TrackedObjectsTest, DeathDataTestRecordDeath) { + ThreadData::InitializeAndSetTrackingStatus(ThreadData::PROFILING_ACTIVE); scoped_ptr<DeathData> data(new DeathData()); ASSERT_NE(data, reinterpret_cast<DeathData*>(NULL)); EXPECT_EQ(data->run_duration_sum(), 0); + EXPECT_EQ(data->run_duration_max(), 0); EXPECT_EQ(data->run_duration_sample(), 0); EXPECT_EQ(data->queue_duration_sum(), 0); + EXPECT_EQ(data->queue_duration_max(), 0); EXPECT_EQ(data->queue_duration_sample(), 0); EXPECT_EQ(data->count(), 0); + EXPECT_EQ(nullptr, data->last_phase_snapshot()); int32 run_ms = 42; int32 queue_ms = 8; @@ -284,106 +256,199 @@ TEST_F(TrackedObjectsTest, DeathDataTest) { const int kUnrandomInt = 0; // Fake random int that ensure we sample data. data->RecordDeath(queue_ms, run_ms, kUnrandomInt); EXPECT_EQ(data->run_duration_sum(), run_ms); + EXPECT_EQ(data->run_duration_max(), run_ms); EXPECT_EQ(data->run_duration_sample(), run_ms); EXPECT_EQ(data->queue_duration_sum(), queue_ms); + EXPECT_EQ(data->queue_duration_max(), queue_ms); EXPECT_EQ(data->queue_duration_sample(), queue_ms); EXPECT_EQ(data->count(), 1); + EXPECT_EQ(nullptr, data->last_phase_snapshot()); + + data->RecordDeath(queue_ms, run_ms, kUnrandomInt); + EXPECT_EQ(data->run_duration_sum(), run_ms + run_ms); + EXPECT_EQ(data->run_duration_max(), run_ms); + EXPECT_EQ(data->run_duration_sample(), run_ms); + EXPECT_EQ(data->queue_duration_sum(), queue_ms + queue_ms); + EXPECT_EQ(data->queue_duration_max(), queue_ms); + EXPECT_EQ(data->queue_duration_sample(), queue_ms); + EXPECT_EQ(data->count(), 2); + EXPECT_EQ(nullptr, data->last_phase_snapshot()); +} + +TEST_F(TrackedObjectsTest, DeathDataTest2Phases) { + ThreadData::InitializeAndSetTrackingStatus(ThreadData::PROFILING_ACTIVE); + + scoped_ptr<DeathData> data(new DeathData()); + ASSERT_NE(data, reinterpret_cast<DeathData*>(NULL)); + + int32 run_ms = 42; + int32 queue_ms = 8; + const int kUnrandomInt = 0; // Fake random int that ensure we sample data. + data->RecordDeath(queue_ms, run_ms, kUnrandomInt); data->RecordDeath(queue_ms, run_ms, kUnrandomInt); + + data->OnProfilingPhaseCompleted(123); EXPECT_EQ(data->run_duration_sum(), run_ms + run_ms); + EXPECT_EQ(data->run_duration_max(), 0); EXPECT_EQ(data->run_duration_sample(), run_ms); EXPECT_EQ(data->queue_duration_sum(), queue_ms + queue_ms); + EXPECT_EQ(data->queue_duration_max(), 0); EXPECT_EQ(data->queue_duration_sample(), queue_ms); EXPECT_EQ(data->count(), 2); + ASSERT_NE(nullptr, data->last_phase_snapshot()); + EXPECT_EQ(123, data->last_phase_snapshot()->profiling_phase); + EXPECT_EQ(2, data->last_phase_snapshot()->death_data.count); + EXPECT_EQ(2 * run_ms, + data->last_phase_snapshot()->death_data.run_duration_sum); + EXPECT_EQ(run_ms, data->last_phase_snapshot()->death_data.run_duration_max); + EXPECT_EQ(run_ms, + data->last_phase_snapshot()->death_data.run_duration_sample); + EXPECT_EQ(2 * queue_ms, + data->last_phase_snapshot()->death_data.queue_duration_sum); + EXPECT_EQ(queue_ms, + data->last_phase_snapshot()->death_data.queue_duration_max); + EXPECT_EQ(queue_ms, + data->last_phase_snapshot()->death_data.queue_duration_sample); + EXPECT_EQ(nullptr, data->last_phase_snapshot()->prev); + + int32 run_ms1 = 21; + int32 queue_ms1 = 4; + + data->RecordDeath(queue_ms1, run_ms1, kUnrandomInt); + EXPECT_EQ(data->run_duration_sum(), run_ms + run_ms + run_ms1); + EXPECT_EQ(data->run_duration_max(), run_ms1); + EXPECT_EQ(data->run_duration_sample(), run_ms1); + EXPECT_EQ(data->queue_duration_sum(), queue_ms + queue_ms + queue_ms1); + EXPECT_EQ(data->queue_duration_max(), queue_ms1); + EXPECT_EQ(data->queue_duration_sample(), queue_ms1); + EXPECT_EQ(data->count(), 3); + ASSERT_NE(nullptr, data->last_phase_snapshot()); + EXPECT_EQ(123, data->last_phase_snapshot()->profiling_phase); + EXPECT_EQ(2, data->last_phase_snapshot()->death_data.count); + EXPECT_EQ(2 * run_ms, + data->last_phase_snapshot()->death_data.run_duration_sum); + EXPECT_EQ(run_ms, data->last_phase_snapshot()->death_data.run_duration_max); + EXPECT_EQ(run_ms, + data->last_phase_snapshot()->death_data.run_duration_sample); + EXPECT_EQ(2 * queue_ms, + data->last_phase_snapshot()->death_data.queue_duration_sum); + EXPECT_EQ(queue_ms, + data->last_phase_snapshot()->death_data.queue_duration_max); + EXPECT_EQ(queue_ms, + data->last_phase_snapshot()->death_data.queue_duration_sample); + EXPECT_EQ(nullptr, data->last_phase_snapshot()->prev); +} - DeathDataSnapshot snapshot(*data); - EXPECT_EQ(2, snapshot.count); - EXPECT_EQ(2 * run_ms, snapshot.run_duration_sum); - EXPECT_EQ(run_ms, snapshot.run_duration_max); - EXPECT_EQ(run_ms, snapshot.run_duration_sample); - EXPECT_EQ(2 * queue_ms, snapshot.queue_duration_sum); - EXPECT_EQ(queue_ms, snapshot.queue_duration_max); - EXPECT_EQ(queue_ms, snapshot.queue_duration_sample); +TEST_F(TrackedObjectsTest, Delta) { + ThreadData::InitializeAndSetTrackingStatus(ThreadData::PROFILING_ACTIVE); + + DeathDataSnapshot snapshot; + snapshot.count = 10; + snapshot.run_duration_sum = 100; + snapshot.run_duration_max = 50; + snapshot.run_duration_sample = 25; + snapshot.queue_duration_sum = 200; + snapshot.queue_duration_max = 101; + snapshot.queue_duration_sample = 26; + + DeathDataSnapshot older_snapshot; + older_snapshot.count = 2; + older_snapshot.run_duration_sum = 95; + older_snapshot.run_duration_max = 48; + older_snapshot.run_duration_sample = 22; + older_snapshot.queue_duration_sum = 190; + older_snapshot.queue_duration_max = 99; + older_snapshot.queue_duration_sample = 21; + + const DeathDataSnapshot& delta = snapshot.Delta(older_snapshot); + EXPECT_EQ(8, delta.count); + EXPECT_EQ(5, delta.run_duration_sum); + EXPECT_EQ(50, delta.run_duration_max); + EXPECT_EQ(25, delta.run_duration_sample); + EXPECT_EQ(10, delta.queue_duration_sum); + EXPECT_EQ(101, delta.queue_duration_max); + EXPECT_EQ(26, delta.queue_duration_sample); } TEST_F(TrackedObjectsTest, DeactivatedBirthOnlyToSnapshotWorkerThread) { // Start in the deactivated state. - if (!ThreadData::InitializeAndSetTrackingStatus(ThreadData::DEACTIVATED)) { - return; - } + ThreadData::InitializeAndSetTrackingStatus(ThreadData::DEACTIVATED); const char kFunction[] = "DeactivatedBirthOnlyToSnapshotWorkerThread"; Location location(kFunction, kFile, kLineNumber, NULL); TallyABirth(location, std::string()); ProcessDataSnapshot process_data; - ThreadData::Snapshot(false, &process_data); - EXPECT_EQ(0u, process_data.tasks.size()); - EXPECT_EQ(0u, process_data.descendants.size()); + ThreadData::Snapshot(0, &process_data); + + ASSERT_EQ(1u, process_data.phased_snapshots.size()); + + auto it = process_data.phased_snapshots.find(0); + ASSERT_TRUE(it != process_data.phased_snapshots.end()); + const ProcessDataPhaseSnapshot& process_data_phase = it->second; + + ASSERT_EQ(0u, process_data_phase.tasks.size()); + EXPECT_EQ(base::GetCurrentProcId(), process_data.process_id); } TEST_F(TrackedObjectsTest, DeactivatedBirthOnlyToSnapshotMainThread) { // Start in the deactivated state. - if (!ThreadData::InitializeAndSetTrackingStatus(ThreadData::DEACTIVATED)) { - return; - } + ThreadData::InitializeAndSetTrackingStatus(ThreadData::DEACTIVATED); const char kFunction[] = "DeactivatedBirthOnlyToSnapshotMainThread"; Location location(kFunction, kFile, kLineNumber, NULL); TallyABirth(location, kMainThreadName); ProcessDataSnapshot process_data; - ThreadData::Snapshot(false, &process_data); - EXPECT_EQ(0u, process_data.tasks.size()); - EXPECT_EQ(0u, process_data.descendants.size()); + ThreadData::Snapshot(0, &process_data); + + ASSERT_EQ(1u, process_data.phased_snapshots.size()); + + auto it = process_data.phased_snapshots.find(0); + ASSERT_TRUE(it != process_data.phased_snapshots.end()); + const ProcessDataPhaseSnapshot& process_data_phase = it->second; + + ASSERT_EQ(0u, process_data_phase.tasks.size()); + EXPECT_EQ(base::GetCurrentProcId(), process_data.process_id); } TEST_F(TrackedObjectsTest, BirthOnlyToSnapshotWorkerThread) { - if (!ThreadData::InitializeAndSetTrackingStatus( - ThreadData::PROFILING_CHILDREN_ACTIVE)) { - return; - } + ThreadData::InitializeAndSetTrackingStatus(ThreadData::PROFILING_ACTIVE); const char kFunction[] = "BirthOnlyToSnapshotWorkerThread"; Location location(kFunction, kFile, kLineNumber, NULL); TallyABirth(location, std::string()); ProcessDataSnapshot process_data; - ThreadData::Snapshot(false, &process_data); + ThreadData::Snapshot(0, &process_data); ExpectSimpleProcessData(process_data, kFunction, kWorkerThreadName, kStillAlive, 1, 0, 0); } TEST_F(TrackedObjectsTest, BirthOnlyToSnapshotMainThread) { - if (!ThreadData::InitializeAndSetTrackingStatus( - ThreadData::PROFILING_CHILDREN_ACTIVE)) { - return; - } + ThreadData::InitializeAndSetTrackingStatus(ThreadData::PROFILING_ACTIVE); const char kFunction[] = "BirthOnlyToSnapshotMainThread"; Location location(kFunction, kFile, kLineNumber, NULL); TallyABirth(location, kMainThreadName); ProcessDataSnapshot process_data; - ThreadData::Snapshot(false, &process_data); + ThreadData::Snapshot(0, &process_data); ExpectSimpleProcessData(process_data, kFunction, kMainThreadName, kStillAlive, 1, 0, 0); } TEST_F(TrackedObjectsTest, LifeCycleToSnapshotMainThread) { - if (!ThreadData::InitializeAndSetTrackingStatus( - ThreadData::PROFILING_CHILDREN_ACTIVE)) { - return; - } + ThreadData::InitializeAndSetTrackingStatus(ThreadData::PROFILING_ACTIVE); const char kFunction[] = "LifeCycleToSnapshotMainThread"; Location location(kFunction, kFile, kLineNumber, NULL); TallyABirth(location, kMainThreadName); - const base::TimeTicks kTimePosted = base::TimeTicks() + - base::TimeDelta::FromMilliseconds(1); + const TrackedTime kTimePosted = TrackedTime::FromMilliseconds(1); const base::TimeTicks kDelayedStartTime = base::TimeTicks(); // TrackingInfo will call TallyABirth() during construction. base::TrackingInfo pending_task(location, kDelayedStartTime); @@ -400,36 +465,24 @@ TEST_F(TrackedObjectsTest, LifeCycleToSnapshotMainThread) { ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, stopwatch); ProcessDataSnapshot process_data; - ThreadData::Snapshot(false, &process_data); + ThreadData::Snapshot(0, &process_data); ExpectSimpleProcessData(process_data, kFunction, kMainThreadName, kMainThreadName, 1, 2, 4); } -// We will deactivate tracking after the birth, and before the death, and -// demonstrate that the lifecycle is completely tallied. This ensures that -// our tallied births are matched by tallied deaths (except for when the -// task is still running, or is queued). -TEST_F(TrackedObjectsTest, LifeCycleMidDeactivatedToSnapshotMainThread) { - if (!ThreadData::InitializeAndSetTrackingStatus( - ThreadData::PROFILING_CHILDREN_ACTIVE)) { - return; - } +TEST_F(TrackedObjectsTest, TwoPhases) { + ThreadData::InitializeAndSetTrackingStatus(ThreadData::PROFILING_ACTIVE); - const char kFunction[] = "LifeCycleMidDeactivatedToSnapshotMainThread"; + const char kFunction[] = "TwoPhases"; Location location(kFunction, kFile, kLineNumber, NULL); TallyABirth(location, kMainThreadName); - const base::TimeTicks kTimePosted = base::TimeTicks() + - base::TimeDelta::FromMilliseconds(1); + const TrackedTime kTimePosted = TrackedTime::FromMilliseconds(1); const base::TimeTicks kDelayedStartTime = base::TimeTicks(); // TrackingInfo will call TallyABirth() during construction. base::TrackingInfo pending_task(location, kDelayedStartTime); pending_task.time_posted = kTimePosted; // Overwrite implied Now(). - // Turn off tracking now that we have births. - EXPECT_TRUE(ThreadData::InitializeAndSetTrackingStatus( - ThreadData::DEACTIVATED)); - const unsigned int kStartOfRun = 5; const unsigned int kEndOfRun = 7; SetTestTime(kStartOfRun); @@ -440,26 +493,231 @@ TEST_F(TrackedObjectsTest, LifeCycleMidDeactivatedToSnapshotMainThread) { ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, stopwatch); + ThreadData::OnProfilingPhaseCompleted(0); + + TallyABirth(location, kMainThreadName); + + const TrackedTime kTimePosted1 = TrackedTime::FromMilliseconds(9); + const base::TimeTicks kDelayedStartTime1 = base::TimeTicks(); + // TrackingInfo will call TallyABirth() during construction. + base::TrackingInfo pending_task1(location, kDelayedStartTime1); + pending_task1.time_posted = kTimePosted1; // Overwrite implied Now(). + + const unsigned int kStartOfRun1 = 11; + const unsigned int kEndOfRun1 = 21; + SetTestTime(kStartOfRun1); + TaskStopwatch stopwatch1; + stopwatch1.Start(); + SetTestTime(kEndOfRun1); + stopwatch1.Stop(); + + ThreadData::TallyRunOnNamedThreadIfTracking(pending_task1, stopwatch1); + ProcessDataSnapshot process_data; - ThreadData::Snapshot(false, &process_data); - ExpectSimpleProcessData(process_data, kFunction, kMainThreadName, - kMainThreadName, 1, 2, 4); + ThreadData::Snapshot(1, &process_data); + + ASSERT_EQ(2u, process_data.phased_snapshots.size()); + + auto it0 = process_data.phased_snapshots.find(0); + ASSERT_TRUE(it0 != process_data.phased_snapshots.end()); + const ProcessDataPhaseSnapshot& process_data_phase0 = it0->second; + + ASSERT_EQ(1u, process_data_phase0.tasks.size()); + + EXPECT_EQ(kFile, process_data_phase0.tasks[0].birth.location.file_name); + EXPECT_EQ(kFunction, + process_data_phase0.tasks[0].birth.location.function_name); + EXPECT_EQ(kLineNumber, + process_data_phase0.tasks[0].birth.location.line_number); + + EXPECT_EQ(kMainThreadName, process_data_phase0.tasks[0].birth.thread_name); + + EXPECT_EQ(1, process_data_phase0.tasks[0].death_data.count); + EXPECT_EQ(2, process_data_phase0.tasks[0].death_data.run_duration_sum); + EXPECT_EQ(2, process_data_phase0.tasks[0].death_data.run_duration_max); + EXPECT_EQ(2, process_data_phase0.tasks[0].death_data.run_duration_sample); + EXPECT_EQ(4, process_data_phase0.tasks[0].death_data.queue_duration_sum); + EXPECT_EQ(4, process_data_phase0.tasks[0].death_data.queue_duration_max); + EXPECT_EQ(4, process_data_phase0.tasks[0].death_data.queue_duration_sample); + + EXPECT_EQ(kMainThreadName, process_data_phase0.tasks[0].death_thread_name); + + auto it1 = process_data.phased_snapshots.find(1); + ASSERT_TRUE(it1 != process_data.phased_snapshots.end()); + const ProcessDataPhaseSnapshot& process_data_phase1 = it1->second; + + ASSERT_EQ(1u, process_data_phase1.tasks.size()); + + EXPECT_EQ(kFile, process_data_phase1.tasks[0].birth.location.file_name); + EXPECT_EQ(kFunction, + process_data_phase1.tasks[0].birth.location.function_name); + EXPECT_EQ(kLineNumber, + process_data_phase1.tasks[0].birth.location.line_number); + + EXPECT_EQ(kMainThreadName, process_data_phase1.tasks[0].birth.thread_name); + + EXPECT_EQ(1, process_data_phase1.tasks[0].death_data.count); + EXPECT_EQ(10, process_data_phase1.tasks[0].death_data.run_duration_sum); + EXPECT_EQ(10, process_data_phase1.tasks[0].death_data.run_duration_max); + EXPECT_EQ(10, process_data_phase1.tasks[0].death_data.run_duration_sample); + EXPECT_EQ(2, process_data_phase1.tasks[0].death_data.queue_duration_sum); + EXPECT_EQ(2, process_data_phase1.tasks[0].death_data.queue_duration_max); + EXPECT_EQ(2, process_data_phase1.tasks[0].death_data.queue_duration_sample); + + EXPECT_EQ(kMainThreadName, process_data_phase1.tasks[0].death_thread_name); + + EXPECT_EQ(base::GetCurrentProcId(), process_data.process_id); } -// We will deactivate tracking before starting a life cycle, and neither -// the birth nor the death will be recorded. -TEST_F(TrackedObjectsTest, LifeCyclePreDeactivatedToSnapshotMainThread) { - // Start in the deactivated state. - if (!ThreadData::InitializeAndSetTrackingStatus(ThreadData::DEACTIVATED)) { - return; +TEST_F(TrackedObjectsTest, ThreePhases) { + ThreadData::InitializeAndSetTrackingStatus(ThreadData::PROFILING_ACTIVE); + + const char kFunction[] = "ThreePhases"; + Location location(kFunction, kFile, kLineNumber, NULL); + + // Phase 0 + { + TallyABirth(location, kMainThreadName); + + // TrackingInfo will call TallyABirth() during construction. + SetTestTime(10); + base::TrackingInfo pending_task(location, base::TimeTicks()); + + SetTestTime(17); + TaskStopwatch stopwatch; + stopwatch.Start(); + SetTestTime(23); + stopwatch.Stop(); + + ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, stopwatch); } - const char kFunction[] = "LifeCyclePreDeactivatedToSnapshotMainThread"; + ThreadData::OnProfilingPhaseCompleted(0); + + // Phase 1 + { + TallyABirth(location, kMainThreadName); + + SetTestTime(30); + base::TrackingInfo pending_task(location, base::TimeTicks()); + + SetTestTime(35); + TaskStopwatch stopwatch; + stopwatch.Start(); + SetTestTime(39); + stopwatch.Stop(); + + ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, stopwatch); + } + + ThreadData::OnProfilingPhaseCompleted(1); + + // Phase 2 + { + TallyABirth(location, kMainThreadName); + + // TrackingInfo will call TallyABirth() during construction. + SetTestTime(40); + base::TrackingInfo pending_task(location, base::TimeTicks()); + + SetTestTime(43); + TaskStopwatch stopwatch; + stopwatch.Start(); + SetTestTime(45); + stopwatch.Stop(); + + ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, stopwatch); + } + + // Snapshot and check results. + ProcessDataSnapshot process_data; + ThreadData::Snapshot(2, &process_data); + + ASSERT_EQ(3u, process_data.phased_snapshots.size()); + + auto it0 = process_data.phased_snapshots.find(0); + ASSERT_TRUE(it0 != process_data.phased_snapshots.end()); + const ProcessDataPhaseSnapshot& process_data_phase0 = it0->second; + + ASSERT_EQ(1u, process_data_phase0.tasks.size()); + + EXPECT_EQ(kFile, process_data_phase0.tasks[0].birth.location.file_name); + EXPECT_EQ(kFunction, + process_data_phase0.tasks[0].birth.location.function_name); + EXPECT_EQ(kLineNumber, + process_data_phase0.tasks[0].birth.location.line_number); + + EXPECT_EQ(kMainThreadName, process_data_phase0.tasks[0].birth.thread_name); + + EXPECT_EQ(1, process_data_phase0.tasks[0].death_data.count); + EXPECT_EQ(6, process_data_phase0.tasks[0].death_data.run_duration_sum); + EXPECT_EQ(6, process_data_phase0.tasks[0].death_data.run_duration_max); + EXPECT_EQ(6, process_data_phase0.tasks[0].death_data.run_duration_sample); + EXPECT_EQ(7, process_data_phase0.tasks[0].death_data.queue_duration_sum); + EXPECT_EQ(7, process_data_phase0.tasks[0].death_data.queue_duration_max); + EXPECT_EQ(7, process_data_phase0.tasks[0].death_data.queue_duration_sample); + + EXPECT_EQ(kMainThreadName, process_data_phase0.tasks[0].death_thread_name); + + auto it1 = process_data.phased_snapshots.find(1); + ASSERT_TRUE(it1 != process_data.phased_snapshots.end()); + const ProcessDataPhaseSnapshot& process_data_phase1 = it1->second; + + ASSERT_EQ(1u, process_data_phase1.tasks.size()); + + EXPECT_EQ(kFile, process_data_phase1.tasks[0].birth.location.file_name); + EXPECT_EQ(kFunction, + process_data_phase1.tasks[0].birth.location.function_name); + EXPECT_EQ(kLineNumber, + process_data_phase1.tasks[0].birth.location.line_number); + + EXPECT_EQ(kMainThreadName, process_data_phase1.tasks[0].birth.thread_name); + + EXPECT_EQ(1, process_data_phase1.tasks[0].death_data.count); + EXPECT_EQ(4, process_data_phase1.tasks[0].death_data.run_duration_sum); + EXPECT_EQ(4, process_data_phase1.tasks[0].death_data.run_duration_max); + EXPECT_EQ(4, process_data_phase1.tasks[0].death_data.run_duration_sample); + EXPECT_EQ(5, process_data_phase1.tasks[0].death_data.queue_duration_sum); + EXPECT_EQ(5, process_data_phase1.tasks[0].death_data.queue_duration_max); + EXPECT_EQ(5, process_data_phase1.tasks[0].death_data.queue_duration_sample); + + EXPECT_EQ(kMainThreadName, process_data_phase1.tasks[0].death_thread_name); + + auto it2 = process_data.phased_snapshots.find(2); + ASSERT_TRUE(it2 != process_data.phased_snapshots.end()); + const ProcessDataPhaseSnapshot& process_data_phase2 = it2->second; + + ASSERT_EQ(1u, process_data_phase2.tasks.size()); + + EXPECT_EQ(kFile, process_data_phase2.tasks[0].birth.location.file_name); + EXPECT_EQ(kFunction, + process_data_phase2.tasks[0].birth.location.function_name); + EXPECT_EQ(kLineNumber, + process_data_phase2.tasks[0].birth.location.line_number); + + EXPECT_EQ(kMainThreadName, process_data_phase2.tasks[0].birth.thread_name); + + EXPECT_EQ(1, process_data_phase2.tasks[0].death_data.count); + EXPECT_EQ(2, process_data_phase2.tasks[0].death_data.run_duration_sum); + EXPECT_EQ(2, process_data_phase2.tasks[0].death_data.run_duration_max); + EXPECT_EQ(2, process_data_phase2.tasks[0].death_data.run_duration_sample); + EXPECT_EQ(3, process_data_phase2.tasks[0].death_data.queue_duration_sum); + EXPECT_EQ(3, process_data_phase2.tasks[0].death_data.queue_duration_max); + EXPECT_EQ(3, process_data_phase2.tasks[0].death_data.queue_duration_sample); + + EXPECT_EQ(kMainThreadName, process_data_phase2.tasks[0].death_thread_name); + + EXPECT_EQ(base::GetCurrentProcId(), process_data.process_id); +} + +TEST_F(TrackedObjectsTest, TwoPhasesSecondEmpty) { + ThreadData::InitializeAndSetTrackingStatus(ThreadData::PROFILING_ACTIVE); + + const char kFunction[] = "TwoPhasesSecondEmpty"; Location location(kFunction, kFile, kLineNumber, NULL); - TallyABirth(location, kMainThreadName); + ThreadData::InitializeThreadContext(kMainThreadName); - const base::TimeTicks kTimePosted = base::TimeTicks() + - base::TimeDelta::FromMilliseconds(1); + const TrackedTime kTimePosted = TrackedTime::FromMilliseconds(1); const base::TimeTicks kDelayedStartTime = base::TimeTicks(); // TrackingInfo will call TallyABirth() during construction. base::TrackingInfo pending_task(location, kDelayedStartTime); @@ -475,26 +733,61 @@ TEST_F(TrackedObjectsTest, LifeCyclePreDeactivatedToSnapshotMainThread) { ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, stopwatch); + ThreadData::OnProfilingPhaseCompleted(0); + ProcessDataSnapshot process_data; - ThreadData::Snapshot(false, &process_data); - EXPECT_EQ(0u, process_data.tasks.size()); - EXPECT_EQ(0u, process_data.descendants.size()); + ThreadData::Snapshot(1, &process_data); + + ASSERT_EQ(2u, process_data.phased_snapshots.size()); + + auto it0 = process_data.phased_snapshots.find(0); + ASSERT_TRUE(it0 != process_data.phased_snapshots.end()); + const ProcessDataPhaseSnapshot& process_data_phase0 = it0->second; + + ASSERT_EQ(1u, process_data_phase0.tasks.size()); + + EXPECT_EQ(kFile, process_data_phase0.tasks[0].birth.location.file_name); + EXPECT_EQ(kFunction, + process_data_phase0.tasks[0].birth.location.function_name); + EXPECT_EQ(kLineNumber, + process_data_phase0.tasks[0].birth.location.line_number); + + EXPECT_EQ(kMainThreadName, process_data_phase0.tasks[0].birth.thread_name); + + EXPECT_EQ(1, process_data_phase0.tasks[0].death_data.count); + EXPECT_EQ(2, process_data_phase0.tasks[0].death_data.run_duration_sum); + EXPECT_EQ(2, process_data_phase0.tasks[0].death_data.run_duration_max); + EXPECT_EQ(2, process_data_phase0.tasks[0].death_data.run_duration_sample); + EXPECT_EQ(4, process_data_phase0.tasks[0].death_data.queue_duration_sum); + EXPECT_EQ(4, process_data_phase0.tasks[0].death_data.queue_duration_max); + EXPECT_EQ(4, process_data_phase0.tasks[0].death_data.queue_duration_sample); + + EXPECT_EQ(kMainThreadName, process_data_phase0.tasks[0].death_thread_name); + + auto it1 = process_data.phased_snapshots.find(1); + ASSERT_TRUE(it1 != process_data.phased_snapshots.end()); + const ProcessDataPhaseSnapshot& process_data_phase1 = it1->second; + + ASSERT_EQ(0u, process_data_phase1.tasks.size()); + EXPECT_EQ(base::GetCurrentProcId(), process_data.process_id); } -TEST_F(TrackedObjectsTest, LifeCycleToSnapshotWorkerThread) { - if (!ThreadData::InitializeAndSetTrackingStatus( - ThreadData::PROFILING_CHILDREN_ACTIVE)) { - return; - } +TEST_F(TrackedObjectsTest, TwoPhasesFirstEmpty) { + ThreadData::InitializeAndSetTrackingStatus(ThreadData::PROFILING_ACTIVE); + + ThreadData::OnProfilingPhaseCompleted(0); - const char kFunction[] = "LifeCycleToSnapshotWorkerThread"; + const char kFunction[] = "TwoPhasesSecondEmpty"; Location location(kFunction, kFile, kLineNumber, NULL); - // Do not delete |birth|. We don't own it. - Births* birth = ThreadData::TallyABirthIfActive(location); - EXPECT_NE(reinterpret_cast<Births*>(NULL), birth); + ThreadData::InitializeThreadContext(kMainThreadName); + + const TrackedTime kTimePosted = TrackedTime::FromMilliseconds(1); + const base::TimeTicks kDelayedStartTime = base::TimeTicks(); + // TrackingInfo will call TallyABirth() during construction. + base::TrackingInfo pending_task(location, kDelayedStartTime); + pending_task.time_posted = kTimePosted; // Overwrite implied Now(). - const unsigned int kTimePosted = 1; const unsigned int kStartOfRun = 5; const unsigned int kEndOfRun = 7; SetTestTime(kStartOfRun); @@ -503,61 +796,124 @@ TEST_F(TrackedObjectsTest, LifeCycleToSnapshotWorkerThread) { SetTestTime(kEndOfRun); stopwatch.Stop(); - ThreadData::TallyRunOnWorkerThreadIfTracking( - birth, TrackedTime() + Duration::FromMilliseconds(kTimePosted), stopwatch); + ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, stopwatch); - // Call for the ToSnapshot, but tell it to not reset the maxes after scanning. ProcessDataSnapshot process_data; - ThreadData::Snapshot(false, &process_data); - ExpectSimpleProcessData(process_data, kFunction, kWorkerThreadName, - kWorkerThreadName, 1, 2, 4); + ThreadData::Snapshot(1, &process_data); - // Call for the ToSnapshot, but tell it to reset the maxes after scanning. - // We'll still get the same values, but the data will be reset (which we'll - // see in a moment). - ProcessDataSnapshot process_data_pre_reset; - ThreadData::Snapshot(true, &process_data_pre_reset); - ExpectSimpleProcessData(process_data, kFunction, kWorkerThreadName, - kWorkerThreadName, 1, 2, 4); - - // Call for the ToSnapshot, and now we'll see the result of the last - // translation, as the max will have been pushed back to zero. - ProcessDataSnapshot process_data_post_reset; - ThreadData::Snapshot(true, &process_data_post_reset); - ASSERT_EQ(1u, process_data_post_reset.tasks.size()); - EXPECT_EQ(kFile, process_data_post_reset.tasks[0].birth.location.file_name); + ASSERT_EQ(1u, process_data.phased_snapshots.size()); + + auto it1 = process_data.phased_snapshots.find(1); + ASSERT_TRUE(it1 != process_data.phased_snapshots.end()); + const ProcessDataPhaseSnapshot& process_data_phase1 = it1->second; + + ASSERT_EQ(1u, process_data_phase1.tasks.size()); + + EXPECT_EQ(kFile, process_data_phase1.tasks[0].birth.location.file_name); EXPECT_EQ(kFunction, - process_data_post_reset.tasks[0].birth.location.function_name); + process_data_phase1.tasks[0].birth.location.function_name); EXPECT_EQ(kLineNumber, - process_data_post_reset.tasks[0].birth.location.line_number); - EXPECT_EQ(kWorkerThreadName, - process_data_post_reset.tasks[0].birth.thread_name); - EXPECT_EQ(1, process_data_post_reset.tasks[0].death_data.count); - EXPECT_EQ(2, process_data_post_reset.tasks[0].death_data.run_duration_sum); - EXPECT_EQ(0, process_data_post_reset.tasks[0].death_data.run_duration_max); - EXPECT_EQ(2, process_data_post_reset.tasks[0].death_data.run_duration_sample); - EXPECT_EQ(4, process_data_post_reset.tasks[0].death_data.queue_duration_sum); - EXPECT_EQ(0, process_data_post_reset.tasks[0].death_data.queue_duration_max); - EXPECT_EQ(4, - process_data_post_reset.tasks[0].death_data.queue_duration_sample); - EXPECT_EQ(kWorkerThreadName, - process_data_post_reset.tasks[0].death_thread_name); - EXPECT_EQ(0u, process_data_post_reset.descendants.size()); - EXPECT_EQ(base::GetCurrentProcId(), process_data_post_reset.process_id); + process_data_phase1.tasks[0].birth.location.line_number); + + EXPECT_EQ(kMainThreadName, process_data_phase1.tasks[0].birth.thread_name); + + EXPECT_EQ(1, process_data_phase1.tasks[0].death_data.count); + EXPECT_EQ(2, process_data_phase1.tasks[0].death_data.run_duration_sum); + EXPECT_EQ(2, process_data_phase1.tasks[0].death_data.run_duration_max); + EXPECT_EQ(2, process_data_phase1.tasks[0].death_data.run_duration_sample); + EXPECT_EQ(4, process_data_phase1.tasks[0].death_data.queue_duration_sum); + EXPECT_EQ(4, process_data_phase1.tasks[0].death_data.queue_duration_max); + EXPECT_EQ(4, process_data_phase1.tasks[0].death_data.queue_duration_sample); + + EXPECT_EQ(kMainThreadName, process_data_phase1.tasks[0].death_thread_name); + + EXPECT_EQ(base::GetCurrentProcId(), process_data.process_id); +} + +// We will deactivate tracking after the birth, and before the death, and +// demonstrate that the lifecycle is completely tallied. This ensures that +// our tallied births are matched by tallied deaths (except for when the +// task is still running, or is queued). +TEST_F(TrackedObjectsTest, LifeCycleMidDeactivatedToSnapshotMainThread) { + ThreadData::InitializeAndSetTrackingStatus(ThreadData::PROFILING_ACTIVE); + + const char kFunction[] = "LifeCycleMidDeactivatedToSnapshotMainThread"; + Location location(kFunction, kFile, kLineNumber, NULL); + TallyABirth(location, kMainThreadName); + + const TrackedTime kTimePosted = TrackedTime::FromMilliseconds(1); + const base::TimeTicks kDelayedStartTime = base::TimeTicks(); + // TrackingInfo will call TallyABirth() during construction. + base::TrackingInfo pending_task(location, kDelayedStartTime); + pending_task.time_posted = kTimePosted; // Overwrite implied Now(). + + // Turn off tracking now that we have births. + ThreadData::InitializeAndSetTrackingStatus(ThreadData::DEACTIVATED); + + const unsigned int kStartOfRun = 5; + const unsigned int kEndOfRun = 7; + SetTestTime(kStartOfRun); + TaskStopwatch stopwatch; + stopwatch.Start(); + SetTestTime(kEndOfRun); + stopwatch.Stop(); + + ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, stopwatch); + + ProcessDataSnapshot process_data; + ThreadData::Snapshot(0, &process_data); + ExpectSimpleProcessData(process_data, kFunction, kMainThreadName, + kMainThreadName, 1, 2, 4); +} + +// We will deactivate tracking before starting a life cycle, and neither +// the birth nor the death will be recorded. +TEST_F(TrackedObjectsTest, LifeCyclePreDeactivatedToSnapshotMainThread) { + // Start in the deactivated state. + ThreadData::InitializeAndSetTrackingStatus(ThreadData::DEACTIVATED); + + const char kFunction[] = "LifeCyclePreDeactivatedToSnapshotMainThread"; + Location location(kFunction, kFile, kLineNumber, NULL); + TallyABirth(location, kMainThreadName); + + const TrackedTime kTimePosted = TrackedTime::FromMilliseconds(1); + const base::TimeTicks kDelayedStartTime = base::TimeTicks(); + // TrackingInfo will call TallyABirth() during construction. + base::TrackingInfo pending_task(location, kDelayedStartTime); + pending_task.time_posted = kTimePosted; // Overwrite implied Now(). + + const unsigned int kStartOfRun = 5; + const unsigned int kEndOfRun = 7; + SetTestTime(kStartOfRun); + TaskStopwatch stopwatch; + stopwatch.Start(); + SetTestTime(kEndOfRun); + stopwatch.Stop(); + + ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, stopwatch); + + ProcessDataSnapshot process_data; + ThreadData::Snapshot(0, &process_data); + + ASSERT_EQ(1u, process_data.phased_snapshots.size()); + + auto it = process_data.phased_snapshots.find(0); + ASSERT_TRUE(it != process_data.phased_snapshots.end()); + const ProcessDataPhaseSnapshot& process_data_phase = it->second; + + ASSERT_EQ(0u, process_data_phase.tasks.size()); + + EXPECT_EQ(base::GetCurrentProcId(), process_data.process_id); } TEST_F(TrackedObjectsTest, TwoLives) { - if (!ThreadData::InitializeAndSetTrackingStatus( - ThreadData::PROFILING_CHILDREN_ACTIVE)) { - return; - } + ThreadData::InitializeAndSetTrackingStatus(ThreadData::PROFILING_ACTIVE); const char kFunction[] = "TwoLives"; Location location(kFunction, kFile, kLineNumber, NULL); TallyABirth(location, kMainThreadName); - const base::TimeTicks kTimePosted = base::TimeTicks() + - base::TimeDelta::FromMilliseconds(1); + const TrackedTime kTimePosted = TrackedTime::FromMilliseconds(1); const base::TimeTicks kDelayedStartTime = base::TimeTicks(); // TrackingInfo will call TallyABirth() during construction. base::TrackingInfo pending_task(location, kDelayedStartTime); @@ -585,24 +941,20 @@ TEST_F(TrackedObjectsTest, TwoLives) { ThreadData::TallyRunOnNamedThreadIfTracking(pending_task2, stopwatch2); ProcessDataSnapshot process_data; - ThreadData::Snapshot(false, &process_data); + ThreadData::Snapshot(0, &process_data); ExpectSimpleProcessData(process_data, kFunction, kMainThreadName, kMainThreadName, 2, 2, 4); } TEST_F(TrackedObjectsTest, DifferentLives) { - if (!ThreadData::InitializeAndSetTrackingStatus( - ThreadData::PROFILING_CHILDREN_ACTIVE)) { - return; - } + ThreadData::InitializeAndSetTrackingStatus(ThreadData::PROFILING_ACTIVE); // Use a well named thread. ThreadData::InitializeThreadContext(kMainThreadName); const char kFunction[] = "DifferentLives"; Location location(kFunction, kFile, kLineNumber, NULL); - const base::TimeTicks kTimePosted = base::TimeTicks() + - base::TimeDelta::FromMilliseconds(1); + const TrackedTime kTimePosted = TrackedTime::FromMilliseconds(1); const base::TimeTicks kDelayedStartTime = base::TimeTicks(); // TrackingInfo will call TallyABirth() during construction. base::TrackingInfo pending_task(location, kDelayedStartTime); @@ -626,50 +978,54 @@ TEST_F(TrackedObjectsTest, DifferentLives) { pending_task2.time_posted = kTimePosted; // Overwrite implied Now(). ProcessDataSnapshot process_data; - ThreadData::Snapshot(false, &process_data); - ASSERT_EQ(2u, process_data.tasks.size()); - - EXPECT_EQ(kFile, process_data.tasks[0].birth.location.file_name); - EXPECT_EQ(kFunction, process_data.tasks[0].birth.location.function_name); - EXPECT_EQ(kLineNumber, process_data.tasks[0].birth.location.line_number); - EXPECT_EQ(kMainThreadName, process_data.tasks[0].birth.thread_name); - EXPECT_EQ(1, process_data.tasks[0].death_data.count); - EXPECT_EQ(2, process_data.tasks[0].death_data.run_duration_sum); - EXPECT_EQ(2, process_data.tasks[0].death_data.run_duration_max); - EXPECT_EQ(2, process_data.tasks[0].death_data.run_duration_sample); - EXPECT_EQ(4, process_data.tasks[0].death_data.queue_duration_sum); - EXPECT_EQ(4, process_data.tasks[0].death_data.queue_duration_max); - EXPECT_EQ(4, process_data.tasks[0].death_data.queue_duration_sample); - EXPECT_EQ(kMainThreadName, process_data.tasks[0].death_thread_name); - EXPECT_EQ(kFile, process_data.tasks[1].birth.location.file_name); - EXPECT_EQ(kFunction, process_data.tasks[1].birth.location.function_name); + ThreadData::Snapshot(0, &process_data); + + ASSERT_EQ(1u, process_data.phased_snapshots.size()); + auto it = process_data.phased_snapshots.find(0); + ASSERT_TRUE(it != process_data.phased_snapshots.end()); + const ProcessDataPhaseSnapshot& process_data_phase = it->second; + + ASSERT_EQ(2u, process_data_phase.tasks.size()); + + EXPECT_EQ(kFile, process_data_phase.tasks[0].birth.location.file_name); + EXPECT_EQ(kFunction, + process_data_phase.tasks[0].birth.location.function_name); + EXPECT_EQ(kLineNumber, + process_data_phase.tasks[0].birth.location.line_number); + EXPECT_EQ(kMainThreadName, process_data_phase.tasks[0].birth.thread_name); + EXPECT_EQ(1, process_data_phase.tasks[0].death_data.count); + EXPECT_EQ(2, process_data_phase.tasks[0].death_data.run_duration_sum); + EXPECT_EQ(2, process_data_phase.tasks[0].death_data.run_duration_max); + EXPECT_EQ(2, process_data_phase.tasks[0].death_data.run_duration_sample); + EXPECT_EQ(4, process_data_phase.tasks[0].death_data.queue_duration_sum); + EXPECT_EQ(4, process_data_phase.tasks[0].death_data.queue_duration_max); + EXPECT_EQ(4, process_data_phase.tasks[0].death_data.queue_duration_sample); + EXPECT_EQ(kMainThreadName, process_data_phase.tasks[0].death_thread_name); + EXPECT_EQ(kFile, process_data_phase.tasks[1].birth.location.file_name); + EXPECT_EQ(kFunction, + process_data_phase.tasks[1].birth.location.function_name); EXPECT_EQ(kSecondFakeLineNumber, - process_data.tasks[1].birth.location.line_number); - EXPECT_EQ(kMainThreadName, process_data.tasks[1].birth.thread_name); - EXPECT_EQ(1, process_data.tasks[1].death_data.count); - EXPECT_EQ(0, process_data.tasks[1].death_data.run_duration_sum); - EXPECT_EQ(0, process_data.tasks[1].death_data.run_duration_max); - EXPECT_EQ(0, process_data.tasks[1].death_data.run_duration_sample); - EXPECT_EQ(0, process_data.tasks[1].death_data.queue_duration_sum); - EXPECT_EQ(0, process_data.tasks[1].death_data.queue_duration_max); - EXPECT_EQ(0, process_data.tasks[1].death_data.queue_duration_sample); - EXPECT_EQ(kStillAlive, process_data.tasks[1].death_thread_name); - EXPECT_EQ(0u, process_data.descendants.size()); + process_data_phase.tasks[1].birth.location.line_number); + EXPECT_EQ(kMainThreadName, process_data_phase.tasks[1].birth.thread_name); + EXPECT_EQ(1, process_data_phase.tasks[1].death_data.count); + EXPECT_EQ(0, process_data_phase.tasks[1].death_data.run_duration_sum); + EXPECT_EQ(0, process_data_phase.tasks[1].death_data.run_duration_max); + EXPECT_EQ(0, process_data_phase.tasks[1].death_data.run_duration_sample); + EXPECT_EQ(0, process_data_phase.tasks[1].death_data.queue_duration_sum); + EXPECT_EQ(0, process_data_phase.tasks[1].death_data.queue_duration_max); + EXPECT_EQ(0, process_data_phase.tasks[1].death_data.queue_duration_sample); + EXPECT_EQ(kStillAlive, process_data_phase.tasks[1].death_thread_name); EXPECT_EQ(base::GetCurrentProcId(), process_data.process_id); } TEST_F(TrackedObjectsTest, TaskWithNestedExclusion) { - if (!ThreadData::InitializeAndSetTrackingStatus( - ThreadData::PROFILING_CHILDREN_ACTIVE)) { - return; - } + ThreadData::InitializeAndSetTrackingStatus(ThreadData::PROFILING_ACTIVE); const char kFunction[] = "TaskWithNestedExclusion"; Location location(kFunction, kFile, kLineNumber, NULL); TallyABirth(location, kMainThreadName); - const base::TimeTicks kTimePosted = base::TimeTicks() + - base::TimeDelta::FromMilliseconds(1); + const TrackedTime kTimePosted = TrackedTime::FromMilliseconds(1); const base::TimeTicks kDelayedStartTime = base::TimeTicks(); // TrackingInfo will call TallyABirth() during construction. base::TrackingInfo pending_task(location, kDelayedStartTime); @@ -691,23 +1047,19 @@ TEST_F(TrackedObjectsTest, TaskWithNestedExclusion) { ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, task_stopwatch); ProcessDataSnapshot process_data; - ThreadData::Snapshot(false, &process_data); + ThreadData::Snapshot(0, &process_data); ExpectSimpleProcessData(process_data, kFunction, kMainThreadName, kMainThreadName, 1, 6, 4); } TEST_F(TrackedObjectsTest, TaskWith2NestedExclusions) { - if (!ThreadData::InitializeAndSetTrackingStatus( - ThreadData::PROFILING_CHILDREN_ACTIVE)) { - return; - } + ThreadData::InitializeAndSetTrackingStatus(ThreadData::PROFILING_ACTIVE); const char kFunction[] = "TaskWith2NestedExclusions"; Location location(kFunction, kFile, kLineNumber, NULL); TallyABirth(location, kMainThreadName); - const base::TimeTicks kTimePosted = base::TimeTicks() + - base::TimeDelta::FromMilliseconds(1); + const TrackedTime kTimePosted = TrackedTime::FromMilliseconds(1); const base::TimeTicks kDelayedStartTime = base::TimeTicks(); // TrackingInfo will call TallyABirth() during construction. base::TrackingInfo pending_task(location, kDelayedStartTime); @@ -735,16 +1087,13 @@ TEST_F(TrackedObjectsTest, TaskWith2NestedExclusions) { ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, task_stopwatch); ProcessDataSnapshot process_data; - ThreadData::Snapshot(false, &process_data); + ThreadData::Snapshot(0, &process_data); ExpectSimpleProcessData(process_data, kFunction, kMainThreadName, kMainThreadName, 1, 13, 4); } TEST_F(TrackedObjectsTest, TaskWithNestedExclusionWithNestedTask) { - if (!ThreadData::InitializeAndSetTrackingStatus( - ThreadData::PROFILING_CHILDREN_ACTIVE)) { - return; - } + ThreadData::InitializeAndSetTrackingStatus(ThreadData::PROFILING_ACTIVE); const char kFunction[] = "TaskWithNestedExclusionWithNestedTask"; Location location(kFunction, kFile, kLineNumber, NULL); @@ -753,8 +1102,7 @@ TEST_F(TrackedObjectsTest, TaskWithNestedExclusionWithNestedTask) { TallyABirth(location, kMainThreadName); - const base::TimeTicks kTimePosted = base::TimeTicks() + - base::TimeDelta::FromMilliseconds(1); + const TrackedTime kTimePosted = TrackedTime::FromMilliseconds(1); const base::TimeTicks kDelayedStartTime = base::TimeTicks(); // TrackingInfo will call TallyABirth() during construction. base::TrackingInfo pending_task(location, kDelayedStartTime); @@ -771,8 +1119,7 @@ TEST_F(TrackedObjectsTest, TaskWithNestedExclusionWithNestedTask) { Location second_location(kFunction, kFile, kSecondFakeLineNumber, NULL); base::TrackingInfo nested_task(second_location, kDelayedStartTime); // Overwrite implied Now(). - nested_task.time_posted = - base::TimeTicks() + base::TimeDelta::FromMilliseconds(8); + nested_task.time_posted = TrackedTime::FromMilliseconds(8); SetTestTime(9); TaskStopwatch nested_task_stopwatch; nested_task_stopwatch.Start(); @@ -790,40 +1137,49 @@ TEST_F(TrackedObjectsTest, TaskWithNestedExclusionWithNestedTask) { ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, task_stopwatch); ProcessDataSnapshot process_data; - ThreadData::Snapshot(false, &process_data); + ThreadData::Snapshot(0, &process_data); + + ASSERT_EQ(1u, process_data.phased_snapshots.size()); + auto it = process_data.phased_snapshots.find(0); + ASSERT_TRUE(it != process_data.phased_snapshots.end()); + const ProcessDataPhaseSnapshot& process_data_phase = it->second; // The order in which the two task follow is platform-dependent. - int t0 = (process_data.tasks[0].birth.location.line_number == kLineNumber) ? - 0 : 1; + int t0 = + (process_data_phase.tasks[0].birth.location.line_number == kLineNumber) + ? 0 + : 1; int t1 = 1 - t0; - ASSERT_EQ(2u, process_data.tasks.size()); - EXPECT_EQ(kFile, process_data.tasks[t0].birth.location.file_name); - EXPECT_EQ(kFunction, process_data.tasks[t0].birth.location.function_name); - EXPECT_EQ(kLineNumber, process_data.tasks[t0].birth.location.line_number); - EXPECT_EQ(kMainThreadName, process_data.tasks[t0].birth.thread_name); - EXPECT_EQ(1, process_data.tasks[t0].death_data.count); - EXPECT_EQ(6, process_data.tasks[t0].death_data.run_duration_sum); - EXPECT_EQ(6, process_data.tasks[t0].death_data.run_duration_max); - EXPECT_EQ(6, process_data.tasks[t0].death_data.run_duration_sample); - EXPECT_EQ(4, process_data.tasks[t0].death_data.queue_duration_sum); - EXPECT_EQ(4, process_data.tasks[t0].death_data.queue_duration_max); - EXPECT_EQ(4, process_data.tasks[t0].death_data.queue_duration_sample); - EXPECT_EQ(kMainThreadName, process_data.tasks[t0].death_thread_name); - EXPECT_EQ(kFile, process_data.tasks[t1].birth.location.file_name); - EXPECT_EQ(kFunction, process_data.tasks[t1].birth.location.function_name); + ASSERT_EQ(2u, process_data_phase.tasks.size()); + EXPECT_EQ(kFile, process_data_phase.tasks[t0].birth.location.file_name); + EXPECT_EQ(kFunction, + process_data_phase.tasks[t0].birth.location.function_name); + EXPECT_EQ(kLineNumber, + process_data_phase.tasks[t0].birth.location.line_number); + EXPECT_EQ(kMainThreadName, process_data_phase.tasks[t0].birth.thread_name); + EXPECT_EQ(1, process_data_phase.tasks[t0].death_data.count); + EXPECT_EQ(6, process_data_phase.tasks[t0].death_data.run_duration_sum); + EXPECT_EQ(6, process_data_phase.tasks[t0].death_data.run_duration_max); + EXPECT_EQ(6, process_data_phase.tasks[t0].death_data.run_duration_sample); + EXPECT_EQ(4, process_data_phase.tasks[t0].death_data.queue_duration_sum); + EXPECT_EQ(4, process_data_phase.tasks[t0].death_data.queue_duration_max); + EXPECT_EQ(4, process_data_phase.tasks[t0].death_data.queue_duration_sample); + EXPECT_EQ(kMainThreadName, process_data_phase.tasks[t0].death_thread_name); + EXPECT_EQ(kFile, process_data_phase.tasks[t1].birth.location.file_name); + EXPECT_EQ(kFunction, + process_data_phase.tasks[t1].birth.location.function_name); EXPECT_EQ(kSecondFakeLineNumber, - process_data.tasks[t1].birth.location.line_number); - EXPECT_EQ(kMainThreadName, process_data.tasks[t1].birth.thread_name); - EXPECT_EQ(1, process_data.tasks[t1].death_data.count); - EXPECT_EQ(2, process_data.tasks[t1].death_data.run_duration_sum); - EXPECT_EQ(2, process_data.tasks[t1].death_data.run_duration_max); - EXPECT_EQ(2, process_data.tasks[t1].death_data.run_duration_sample); - EXPECT_EQ(1, process_data.tasks[t1].death_data.queue_duration_sum); - EXPECT_EQ(1, process_data.tasks[t1].death_data.queue_duration_max); - EXPECT_EQ(1, process_data.tasks[t1].death_data.queue_duration_sample); - EXPECT_EQ(kMainThreadName, process_data.tasks[t1].death_thread_name); - EXPECT_EQ(0u, process_data.descendants.size()); + process_data_phase.tasks[t1].birth.location.line_number); + EXPECT_EQ(kMainThreadName, process_data_phase.tasks[t1].birth.thread_name); + EXPECT_EQ(1, process_data_phase.tasks[t1].death_data.count); + EXPECT_EQ(2, process_data_phase.tasks[t1].death_data.run_duration_sum); + EXPECT_EQ(2, process_data_phase.tasks[t1].death_data.run_duration_max); + EXPECT_EQ(2, process_data_phase.tasks[t1].death_data.run_duration_sample); + EXPECT_EQ(1, process_data_phase.tasks[t1].death_data.queue_duration_sum); + EXPECT_EQ(1, process_data_phase.tasks[t1].death_data.queue_duration_max); + EXPECT_EQ(1, process_data_phase.tasks[t1].death_data.queue_duration_sample); + EXPECT_EQ(kMainThreadName, process_data_phase.tasks[t1].death_thread_name); EXPECT_EQ(base::GetCurrentProcId(), process_data.process_id); } diff --git a/chromium/base/tracking_info.cc b/chromium/base/tracking_info.cc index 0b091f8b68b..c02b2f47585 100644 --- a/chromium/base/tracking_info.cc +++ b/chromium/base/tracking_info.cc @@ -18,7 +18,7 @@ TrackingInfo::TrackingInfo( base::TimeTicks delayed_run_time) : birth_tally( tracked_objects::ThreadData::TallyABirthIfActive(posted_from)), - time_posted(TimeTicks::Now()), + time_posted(tracked_objects::ThreadData::Now()), delayed_run_time(delayed_run_time) { } diff --git a/chromium/base/tracking_info.h b/chromium/base/tracking_info.h index a1c6392d2ea..6c3bcd1a514 100644 --- a/chromium/base/tracking_info.h +++ b/chromium/base/tracking_info.h @@ -36,15 +36,18 @@ struct BASE_EXPORT TrackingInfo { // unserviced, after they *could* be serviced. This is the same stat as we // have for non-delayed tasks, and we consistently call it queuing delay. tracked_objects::TrackedTime EffectiveTimePosted() const { - return tracked_objects::TrackedTime( - delayed_run_time.is_null() ? time_posted : delayed_run_time); + return delayed_run_time.is_null() + ? time_posted + : tracked_objects::TrackedTime(delayed_run_time); } // Record of location and thread that the task came from. tracked_objects::Births* birth_tally; - // Time when the related task was posted. - base::TimeTicks time_posted; + // Time when the related task was posted. Note that this value may be empty + // if task profiling is disabled, and should only be used in conjunction with + // profiling-related reporting. + tracked_objects::TrackedTime time_posted; // The time when the task should be run. base::TimeTicks delayed_run_time; diff --git a/chromium/base/tuple.h b/chromium/base/tuple.h index 12f7f84ea50..4628aa9417c 100644 --- a/chromium/base/tuple.h +++ b/chromium/base/tuple.h @@ -2,20 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// A Tuple is a generic templatized container, similar in concept to std::pair. -// There are classes Tuple0 to Tuple6, cooresponding to the number of elements -// it contains. The convenient MakeTuple() function takes 0 to 6 arguments, -// and will construct and return the appropriate Tuple object. The functions -// DispatchToMethod and DispatchToFunction take a function pointer or instance -// and method pointer, and unpack a tuple into arguments to the call. +// A Tuple is a generic templatized container, similar in concept to std::pair +// and std::tuple. The convenient MakeTuple() function takes any number of +// arguments and will construct and return the appropriate Tuple object. The +// functions DispatchToMethod and DispatchToFunction take a function pointer or +// instance and method pointer, and unpack a tuple into arguments to the call. // // Tuple elements are copied by value, and stored in the tuple. See the unit // tests for more details of how/when the values are copied. // // Example usage: // // These two methods of creating a Tuple are identical. -// Tuple2<int, const char*> tuple_a(1, "wee"); -// Tuple2<int, const char*> tuple_b = MakeTuple(1, "wee"); +// Tuple<int, const char*> tuple_a(1, "wee"); +// Tuple<int, const char*> tuple_b = MakeTuple(1, "wee"); // // void SomeFunc(int a, const char* b) { } // DispatchToFunction(&SomeFunc, tuple_a); // SomeFunc(1, "wee") @@ -26,11 +25,85 @@ // DispatchToMethod(&foo, &Foo::SomeMeth, MakeTuple(1, 2, 3)); // // foo->SomeMeth(1, 2, 3); -#ifndef BASE_TUPLE_H__ -#define BASE_TUPLE_H__ +#ifndef BASE_TUPLE_H_ +#define BASE_TUPLE_H_ #include "base/bind_helpers.h" +// Index sequences +// +// Minimal clone of the similarly-named C++14 functionality. + +template <size_t...> +struct IndexSequence {}; + +template <size_t... Ns> +struct MakeIndexSequenceImpl; + +#if defined(_PREFAST_) && defined(OS_WIN) + +// Work around VC++ 2013 /analyze internal compiler error: +// https://connect.microsoft.com/VisualStudio/feedback/details/1053626 + +template <> struct MakeIndexSequenceImpl<0> { + using Type = IndexSequence<>; +}; +template <> struct MakeIndexSequenceImpl<1> { + using Type = IndexSequence<0>; +}; +template <> struct MakeIndexSequenceImpl<2> { + using Type = IndexSequence<0,1>; +}; +template <> struct MakeIndexSequenceImpl<3> { + using Type = IndexSequence<0,1,2>; +}; +template <> struct MakeIndexSequenceImpl<4> { + using Type = IndexSequence<0,1,2,3>; +}; +template <> struct MakeIndexSequenceImpl<5> { + using Type = IndexSequence<0,1,2,3,4>; +}; +template <> struct MakeIndexSequenceImpl<6> { + using Type = IndexSequence<0,1,2,3,4,5>; +}; +template <> struct MakeIndexSequenceImpl<7> { + using Type = IndexSequence<0,1,2,3,4,5,6>; +}; +template <> struct MakeIndexSequenceImpl<8> { + using Type = IndexSequence<0,1,2,3,4,5,6,7>; +}; +template <> struct MakeIndexSequenceImpl<9> { + using Type = IndexSequence<0,1,2,3,4,5,6,7,8>; +}; +template <> struct MakeIndexSequenceImpl<10> { + using Type = IndexSequence<0,1,2,3,4,5,6,7,8,9>; +}; +template <> struct MakeIndexSequenceImpl<11> { + using Type = IndexSequence<0,1,2,3,4,5,6,7,8,9,10>; +}; +template <> struct MakeIndexSequenceImpl<12> { + using Type = IndexSequence<0,1,2,3,4,5,6,7,8,9,10,11>; +}; +template <> struct MakeIndexSequenceImpl<13> { + using Type = IndexSequence<0,1,2,3,4,5,6,7,8,9,10,11,12>; +}; + +#else // defined(WIN) && defined(_PREFAST_) + +template <size_t... Ns> +struct MakeIndexSequenceImpl<0, Ns...> { + using Type = IndexSequence<Ns...>; +}; + +template <size_t N, size_t... Ns> +struct MakeIndexSequenceImpl<N, Ns...> + : MakeIndexSequenceImpl<N - 1, N - 1, Ns...> {}; + +#endif // defined(WIN) && defined(_PREFAST_) + +template <size_t N> +using MakeIndexSequence = typename MakeIndexSequenceImpl<N>::Type; + // Traits ---------------------------------------------------------------------- // // A simple traits class for tuple arguments. @@ -53,9 +126,6 @@ struct TupleTraits<P&> { typedef P& ParamType; }; -template <class P> -struct TupleTypes { }; - // Tuple ----------------------------------------------------------------------- // // This set of classes is useful for bundling 0 or more heterogeneous data types @@ -63,357 +133,81 @@ struct TupleTypes { }; // function objects that need to take an arbitrary number of parameters; see // RunnableMethod and IPC::MessageWithTuple. // -// Tuple0 is supplied to act as a 'void' type. It can be used, for example, +// Tuple<> is supplied to act as a 'void' type. It can be used, for example, // when dispatching to a function that accepts no arguments (see the // Dispatchers below). -// Tuple1<A> is rarely useful. One such use is when A is non-const ref that you +// Tuple<A> is rarely useful. One such use is when A is non-const ref that you // want filled by the dispatchee, and the tuple is merely a container for that // output (a "tier"). See MakeRefTuple and its usages. -struct Tuple0 { - typedef Tuple0 ValueTuple; - typedef Tuple0 RefTuple; - typedef Tuple0 ParamTuple; +template <typename IxSeq, typename... Ts> +struct TupleBaseImpl; +template <typename... Ts> +using TupleBase = TupleBaseImpl<MakeIndexSequence<sizeof...(Ts)>, Ts...>; +template <size_t N, typename T> +struct TupleLeaf; + +template <typename... Ts> +struct Tuple : TupleBase<Ts...> { + Tuple() : TupleBase<Ts...>() {} + explicit Tuple(typename TupleTraits<Ts>::ParamType... args) + : TupleBase<Ts...>(args...) {} }; -template <class A> -struct Tuple1 { - public: - typedef A TypeA; - - Tuple1() {} - explicit Tuple1(typename TupleTraits<A>::ParamType a) : a(a) {} - - A a; -}; - -template <class A, class B> -struct Tuple2 { - public: - typedef A TypeA; - typedef B TypeB; - - Tuple2() {} - Tuple2(typename TupleTraits<A>::ParamType a, - typename TupleTraits<B>::ParamType b) - : a(a), b(b) { - } - - A a; - B b; -}; - -template <class A, class B, class C> -struct Tuple3 { - public: - typedef A TypeA; - typedef B TypeB; - typedef C TypeC; - - Tuple3() {} - Tuple3(typename TupleTraits<A>::ParamType a, - typename TupleTraits<B>::ParamType b, - typename TupleTraits<C>::ParamType c) - : a(a), b(b), c(c){ - } - - A a; - B b; - C c; -}; - -template <class A, class B, class C, class D> -struct Tuple4 { - public: - typedef A TypeA; - typedef B TypeB; - typedef C TypeC; - typedef D TypeD; - - Tuple4() {} - Tuple4(typename TupleTraits<A>::ParamType a, - typename TupleTraits<B>::ParamType b, - typename TupleTraits<C>::ParamType c, - typename TupleTraits<D>::ParamType d) - : a(a), b(b), c(c), d(d) { - } - - A a; - B b; - C c; - D d; -}; - -template <class A, class B, class C, class D, class E> -struct Tuple5 { - public: - typedef A TypeA; - typedef B TypeB; - typedef C TypeC; - typedef D TypeD; - typedef E TypeE; - - Tuple5() {} - Tuple5(typename TupleTraits<A>::ParamType a, - typename TupleTraits<B>::ParamType b, - typename TupleTraits<C>::ParamType c, - typename TupleTraits<D>::ParamType d, - typename TupleTraits<E>::ParamType e) - : a(a), b(b), c(c), d(d), e(e) { - } - - A a; - B b; - C c; - D d; - E e; -}; - -template <class A, class B, class C, class D, class E, class F> -struct Tuple6 { - public: - typedef A TypeA; - typedef B TypeB; - typedef C TypeC; - typedef D TypeD; - typedef E TypeE; - typedef F TypeF; - - Tuple6() {} - Tuple6(typename TupleTraits<A>::ParamType a, - typename TupleTraits<B>::ParamType b, - typename TupleTraits<C>::ParamType c, - typename TupleTraits<D>::ParamType d, - typename TupleTraits<E>::ParamType e, - typename TupleTraits<F>::ParamType f) - : a(a), b(b), c(c), d(d), e(e), f(f) { - } +// Avoids ambiguity between Tuple's two constructors. +template <> +struct Tuple<> {}; - A a; - B b; - C c; - D d; - E e; - F f; +template <size_t... Ns, typename... Ts> +struct TupleBaseImpl<IndexSequence<Ns...>, Ts...> : TupleLeaf<Ns, Ts>... { + TupleBaseImpl() : TupleLeaf<Ns, Ts>()... {} + explicit TupleBaseImpl(typename TupleTraits<Ts>::ParamType... args) + : TupleLeaf<Ns, Ts>(args)... {} }; -template <class A, class B, class C, class D, class E, class F, class G> -struct Tuple7 { - public: - typedef A TypeA; - typedef B TypeB; - typedef C TypeC; - typedef D TypeD; - typedef E TypeE; - typedef F TypeF; - typedef G TypeG; +template <size_t N, typename T> +struct TupleLeaf { + TupleLeaf() {} + explicit TupleLeaf(typename TupleTraits<T>::ParamType x) : x(x) {} - Tuple7() {} - Tuple7(typename TupleTraits<A>::ParamType a, - typename TupleTraits<B>::ParamType b, - typename TupleTraits<C>::ParamType c, - typename TupleTraits<D>::ParamType d, - typename TupleTraits<E>::ParamType e, - typename TupleTraits<F>::ParamType f, - typename TupleTraits<G>::ParamType g) - : a(a), b(b), c(c), d(d), e(e), f(f), g(g) { - } + T& get() { return x; } + const T& get() const { return x; } - A a; - B b; - C c; - D d; - E e; - F f; - G g; + T x; }; -template <class A, class B, class C, class D, class E, class F, class G, - class H> -struct Tuple8 { - public: - typedef A TypeA; - typedef B TypeB; - typedef C TypeC; - typedef D TypeD; - typedef E TypeE; - typedef F TypeF; - typedef G TypeG; - typedef H TypeH; +// Tuple getters -------------------------------------------------------------- +// +// Allows accessing an arbitrary tuple element by index. +// +// Example usage: +// Tuple<int, double> t2; +// get<0>(t2) = 42; +// get<1>(t2) = 3.14; - Tuple8() {} - Tuple8(typename TupleTraits<A>::ParamType a, - typename TupleTraits<B>::ParamType b, - typename TupleTraits<C>::ParamType c, - typename TupleTraits<D>::ParamType d, - typename TupleTraits<E>::ParamType e, - typename TupleTraits<F>::ParamType f, - typename TupleTraits<G>::ParamType g, - typename TupleTraits<H>::ParamType h) - : a(a), b(b), c(c), d(d), e(e), f(f), g(g), h(h) { - } +template <size_t I, typename T> +T& get(TupleLeaf<I, T>& leaf) { + return leaf.get(); +} - A a; - B b; - C c; - D d; - E e; - F f; - G g; - H h; -}; +template <size_t I, typename T> +const T& get(const TupleLeaf<I, T>& leaf) { + return leaf.get(); +} // Tuple types ---------------------------------------------------------------- // // Allows for selection of ValueTuple/RefTuple/ParamTuple without needing the // definitions of class types the tuple takes as parameters. -template <> -struct TupleTypes< Tuple0 > { - typedef Tuple0 ValueTuple; - typedef Tuple0 RefTuple; - typedef Tuple0 ParamTuple; -}; - -template <class A> -struct TupleTypes< Tuple1<A> > { - typedef Tuple1<typename TupleTraits<A>::ValueType> ValueTuple; - typedef Tuple1<typename TupleTraits<A>::RefType> RefTuple; - typedef Tuple1<typename TupleTraits<A>::ParamType> ParamTuple; -}; - -template <class A, class B> -struct TupleTypes< Tuple2<A, B> > { - typedef Tuple2<typename TupleTraits<A>::ValueType, - typename TupleTraits<B>::ValueType> ValueTuple; -typedef Tuple2<typename TupleTraits<A>::RefType, - typename TupleTraits<B>::RefType> RefTuple; - typedef Tuple2<typename TupleTraits<A>::ParamType, - typename TupleTraits<B>::ParamType> ParamTuple; -}; - -template <class A, class B, class C> -struct TupleTypes< Tuple3<A, B, C> > { - typedef Tuple3<typename TupleTraits<A>::ValueType, - typename TupleTraits<B>::ValueType, - typename TupleTraits<C>::ValueType> ValueTuple; -typedef Tuple3<typename TupleTraits<A>::RefType, - typename TupleTraits<B>::RefType, - typename TupleTraits<C>::RefType> RefTuple; - typedef Tuple3<typename TupleTraits<A>::ParamType, - typename TupleTraits<B>::ParamType, - typename TupleTraits<C>::ParamType> ParamTuple; -}; - -template <class A, class B, class C, class D> -struct TupleTypes< Tuple4<A, B, C, D> > { - typedef Tuple4<typename TupleTraits<A>::ValueType, - typename TupleTraits<B>::ValueType, - typename TupleTraits<C>::ValueType, - typename TupleTraits<D>::ValueType> ValueTuple; -typedef Tuple4<typename TupleTraits<A>::RefType, - typename TupleTraits<B>::RefType, - typename TupleTraits<C>::RefType, - typename TupleTraits<D>::RefType> RefTuple; - typedef Tuple4<typename TupleTraits<A>::ParamType, - typename TupleTraits<B>::ParamType, - typename TupleTraits<C>::ParamType, - typename TupleTraits<D>::ParamType> ParamTuple; -}; - -template <class A, class B, class C, class D, class E> -struct TupleTypes< Tuple5<A, B, C, D, E> > { - typedef Tuple5<typename TupleTraits<A>::ValueType, - typename TupleTraits<B>::ValueType, - typename TupleTraits<C>::ValueType, - typename TupleTraits<D>::ValueType, - typename TupleTraits<E>::ValueType> ValueTuple; -typedef Tuple5<typename TupleTraits<A>::RefType, - typename TupleTraits<B>::RefType, - typename TupleTraits<C>::RefType, - typename TupleTraits<D>::RefType, - typename TupleTraits<E>::RefType> RefTuple; - typedef Tuple5<typename TupleTraits<A>::ParamType, - typename TupleTraits<B>::ParamType, - typename TupleTraits<C>::ParamType, - typename TupleTraits<D>::ParamType, - typename TupleTraits<E>::ParamType> ParamTuple; -}; - -template <class A, class B, class C, class D, class E, class F> -struct TupleTypes< Tuple6<A, B, C, D, E, F> > { - typedef Tuple6<typename TupleTraits<A>::ValueType, - typename TupleTraits<B>::ValueType, - typename TupleTraits<C>::ValueType, - typename TupleTraits<D>::ValueType, - typename TupleTraits<E>::ValueType, - typename TupleTraits<F>::ValueType> ValueTuple; -typedef Tuple6<typename TupleTraits<A>::RefType, - typename TupleTraits<B>::RefType, - typename TupleTraits<C>::RefType, - typename TupleTraits<D>::RefType, - typename TupleTraits<E>::RefType, - typename TupleTraits<F>::RefType> RefTuple; - typedef Tuple6<typename TupleTraits<A>::ParamType, - typename TupleTraits<B>::ParamType, - typename TupleTraits<C>::ParamType, - typename TupleTraits<D>::ParamType, - typename TupleTraits<E>::ParamType, - typename TupleTraits<F>::ParamType> ParamTuple; -}; - -template <class A, class B, class C, class D, class E, class F, class G> -struct TupleTypes< Tuple7<A, B, C, D, E, F, G> > { - typedef Tuple7<typename TupleTraits<A>::ValueType, - typename TupleTraits<B>::ValueType, - typename TupleTraits<C>::ValueType, - typename TupleTraits<D>::ValueType, - typename TupleTraits<E>::ValueType, - typename TupleTraits<F>::ValueType, - typename TupleTraits<G>::ValueType> ValueTuple; -typedef Tuple7<typename TupleTraits<A>::RefType, - typename TupleTraits<B>::RefType, - typename TupleTraits<C>::RefType, - typename TupleTraits<D>::RefType, - typename TupleTraits<E>::RefType, - typename TupleTraits<F>::RefType, - typename TupleTraits<G>::RefType> RefTuple; - typedef Tuple7<typename TupleTraits<A>::ParamType, - typename TupleTraits<B>::ParamType, - typename TupleTraits<C>::ParamType, - typename TupleTraits<D>::ParamType, - typename TupleTraits<E>::ParamType, - typename TupleTraits<F>::ParamType, - typename TupleTraits<G>::ParamType> ParamTuple; -}; +template <typename T> +struct TupleTypes; -template <class A, class B, class C, class D, class E, class F, class G, - class H> -struct TupleTypes< Tuple8<A, B, C, D, E, F, G, H> > { - typedef Tuple8<typename TupleTraits<A>::ValueType, - typename TupleTraits<B>::ValueType, - typename TupleTraits<C>::ValueType, - typename TupleTraits<D>::ValueType, - typename TupleTraits<E>::ValueType, - typename TupleTraits<F>::ValueType, - typename TupleTraits<G>::ValueType, - typename TupleTraits<H>::ValueType> ValueTuple; -typedef Tuple8<typename TupleTraits<A>::RefType, - typename TupleTraits<B>::RefType, - typename TupleTraits<C>::RefType, - typename TupleTraits<D>::RefType, - typename TupleTraits<E>::RefType, - typename TupleTraits<F>::RefType, - typename TupleTraits<G>::RefType, - typename TupleTraits<H>::RefType> RefTuple; - typedef Tuple8<typename TupleTraits<A>::ParamType, - typename TupleTraits<B>::ParamType, - typename TupleTraits<C>::ParamType, - typename TupleTraits<D>::ParamType, - typename TupleTraits<E>::ParamType, - typename TupleTraits<F>::ParamType, - typename TupleTraits<G>::ParamType, - typename TupleTraits<H>::ParamType> ParamTuple; +template <typename... Ts> +struct TupleTypes<Tuple<Ts...>> { + using ValueTuple = Tuple<typename TupleTraits<Ts>::ValueType...>; + using RefTuple = Tuple<typename TupleTraits<Ts>::RefType...>; + using ParamTuple = Tuple<typename TupleTraits<Ts>::ParamType...>; }; // Tuple creators ------------------------------------------------------------- @@ -421,105 +215,17 @@ typedef Tuple8<typename TupleTraits<A>::RefType, // Helper functions for constructing tuples while inferring the template // argument types. -inline Tuple0 MakeTuple() { - return Tuple0(); -} - -template <class A> -inline Tuple1<A> MakeTuple(const A& a) { - return Tuple1<A>(a); -} - -template <class A, class B> -inline Tuple2<A, B> MakeTuple(const A& a, const B& b) { - return Tuple2<A, B>(a, b); -} - -template <class A, class B, class C> -inline Tuple3<A, B, C> MakeTuple(const A& a, const B& b, const C& c) { - return Tuple3<A, B, C>(a, b, c); -} - -template <class A, class B, class C, class D> -inline Tuple4<A, B, C, D> MakeTuple(const A& a, const B& b, const C& c, - const D& d) { - return Tuple4<A, B, C, D>(a, b, c, d); -} - -template <class A, class B, class C, class D, class E> -inline Tuple5<A, B, C, D, E> MakeTuple(const A& a, const B& b, const C& c, - const D& d, const E& e) { - return Tuple5<A, B, C, D, E>(a, b, c, d, e); -} - -template <class A, class B, class C, class D, class E, class F> -inline Tuple6<A, B, C, D, E, F> MakeTuple(const A& a, const B& b, const C& c, - const D& d, const E& e, const F& f) { - return Tuple6<A, B, C, D, E, F>(a, b, c, d, e, f); -} - -template <class A, class B, class C, class D, class E, class F, class G> -inline Tuple7<A, B, C, D, E, F, G> MakeTuple(const A& a, const B& b, const C& c, - const D& d, const E& e, const F& f, - const G& g) { - return Tuple7<A, B, C, D, E, F, G>(a, b, c, d, e, f, g); -} - -template <class A, class B, class C, class D, class E, class F, class G, - class H> -inline Tuple8<A, B, C, D, E, F, G, H> MakeTuple(const A& a, const B& b, - const C& c, const D& d, - const E& e, const F& f, - const G& g, const H& h) { - return Tuple8<A, B, C, D, E, F, G, H>(a, b, c, d, e, f, g, h); +template <typename... Ts> +inline Tuple<Ts...> MakeTuple(const Ts&... arg) { + return Tuple<Ts...>(arg...); } // The following set of helpers make what Boost refers to as "Tiers" - a tuple // of references. -template <class A> -inline Tuple1<A&> MakeRefTuple(A& a) { - return Tuple1<A&>(a); -} - -template <class A, class B> -inline Tuple2<A&, B&> MakeRefTuple(A& a, B& b) { - return Tuple2<A&, B&>(a, b); -} - -template <class A, class B, class C> -inline Tuple3<A&, B&, C&> MakeRefTuple(A& a, B& b, C& c) { - return Tuple3<A&, B&, C&>(a, b, c); -} - -template <class A, class B, class C, class D> -inline Tuple4<A&, B&, C&, D&> MakeRefTuple(A& a, B& b, C& c, D& d) { - return Tuple4<A&, B&, C&, D&>(a, b, c, d); -} - -template <class A, class B, class C, class D, class E> -inline Tuple5<A&, B&, C&, D&, E&> MakeRefTuple(A& a, B& b, C& c, D& d, E& e) { - return Tuple5<A&, B&, C&, D&, E&>(a, b, c, d, e); -} - -template <class A, class B, class C, class D, class E, class F> -inline Tuple6<A&, B&, C&, D&, E&, F&> MakeRefTuple(A& a, B& b, C& c, D& d, E& e, - F& f) { - return Tuple6<A&, B&, C&, D&, E&, F&>(a, b, c, d, e, f); -} - -template <class A, class B, class C, class D, class E, class F, class G> -inline Tuple7<A&, B&, C&, D&, E&, F&, G&> MakeRefTuple(A& a, B& b, C& c, D& d, - E& e, F& f, G& g) { - return Tuple7<A&, B&, C&, D&, E&, F&, G&>(a, b, c, d, e, f, g); -} - -template <class A, class B, class C, class D, class E, class F, class G, - class H> -inline Tuple8<A&, B&, C&, D&, E&, F&, G&, H&> MakeRefTuple(A& a, B& b, C& c, - D& d, E& e, F& f, - G& g, H& h) { - return Tuple8<A&, B&, C&, D&, E&, F&, G&, H&>(a, b, c, d, e, f, g, h); +template <typename... Ts> +inline Tuple<Ts&...> MakeRefTuple(Ts&... arg) { + return Tuple<Ts&...>(arg...); } // Dispatchers ---------------------------------------------------------------- @@ -533,759 +239,94 @@ inline Tuple8<A&, B&, C&, D&, E&, F&, G&, H&> MakeRefTuple(A& a, B& b, C& c, // Non-Static Dispatchers with no out params. -template <class ObjT, class Method> -inline void DispatchToMethod(ObjT* obj, Method method, const Tuple0& arg) { - (obj->*method)(); -} - -template <class ObjT, class Method, class A> +template <typename ObjT, typename Method, typename A> inline void DispatchToMethod(ObjT* obj, Method method, const A& arg) { (obj->*method)(base::internal::UnwrapTraits<A>::Unwrap(arg)); } -template <class ObjT, class Method, class A> -inline void DispatchToMethod(ObjT* obj, Method method, const Tuple1<A>& arg) { - (obj->*method)(base::internal::UnwrapTraits<A>::Unwrap(arg.a)); +template <typename ObjT, typename Method, typename... Ts, size_t... Ns> +inline void DispatchToMethodImpl(ObjT* obj, + Method method, + const Tuple<Ts...>& arg, + IndexSequence<Ns...>) { + (obj->*method)(base::internal::UnwrapTraits<Ts>::Unwrap(get<Ns>(arg))...); } -template<class ObjT, class Method, class A, class B> +template <typename ObjT, typename Method, typename... Ts> inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple2<A, B>& arg) { - (obj->*method)(base::internal::UnwrapTraits<A>::Unwrap(arg.a), - base::internal::UnwrapTraits<B>::Unwrap(arg.b)); -} - -template<class ObjT, class Method, class A, class B, class C> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple3<A, B, C>& arg) { - (obj->*method)(base::internal::UnwrapTraits<A>::Unwrap(arg.a), - base::internal::UnwrapTraits<B>::Unwrap(arg.b), - base::internal::UnwrapTraits<C>::Unwrap(arg.c)); -} - -template<class ObjT, class Method, class A, class B, class C, class D> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple4<A, B, C, D>& arg) { - (obj->*method)(base::internal::UnwrapTraits<A>::Unwrap(arg.a), - base::internal::UnwrapTraits<B>::Unwrap(arg.b), - base::internal::UnwrapTraits<C>::Unwrap(arg.c), - base::internal::UnwrapTraits<D>::Unwrap(arg.d)); -} - -template<class ObjT, class Method, class A, class B, class C, class D, class E> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple5<A, B, C, D, E>& arg) { - (obj->*method)(base::internal::UnwrapTraits<A>::Unwrap(arg.a), - base::internal::UnwrapTraits<B>::Unwrap(arg.b), - base::internal::UnwrapTraits<C>::Unwrap(arg.c), - base::internal::UnwrapTraits<D>::Unwrap(arg.d), - base::internal::UnwrapTraits<E>::Unwrap(arg.e)); -} - -template<class ObjT, class Method, class A, class B, class C, class D, class E, - class F> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple6<A, B, C, D, E, F>& arg) { - (obj->*method)(base::internal::UnwrapTraits<A>::Unwrap(arg.a), - base::internal::UnwrapTraits<B>::Unwrap(arg.b), - base::internal::UnwrapTraits<C>::Unwrap(arg.c), - base::internal::UnwrapTraits<D>::Unwrap(arg.d), - base::internal::UnwrapTraits<E>::Unwrap(arg.e), - base::internal::UnwrapTraits<F>::Unwrap(arg.f)); -} - -template<class ObjT, class Method, class A, class B, class C, class D, class E, - class F, class G> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple7<A, B, C, D, E, F, G>& arg) { - (obj->*method)(base::internal::UnwrapTraits<A>::Unwrap(arg.a), - base::internal::UnwrapTraits<B>::Unwrap(arg.b), - base::internal::UnwrapTraits<C>::Unwrap(arg.c), - base::internal::UnwrapTraits<D>::Unwrap(arg.d), - base::internal::UnwrapTraits<E>::Unwrap(arg.e), - base::internal::UnwrapTraits<F>::Unwrap(arg.f), - base::internal::UnwrapTraits<G>::Unwrap(arg.g)); -} - -template<class ObjT, class Method, class A, class B, class C, class D, class E, - class F, class G, class H> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple8<A, B, C, D, E, F, G, H>& arg) { - (obj->*method)(base::internal::UnwrapTraits<A>::Unwrap(arg.a), - base::internal::UnwrapTraits<B>::Unwrap(arg.b), - base::internal::UnwrapTraits<C>::Unwrap(arg.c), - base::internal::UnwrapTraits<D>::Unwrap(arg.d), - base::internal::UnwrapTraits<E>::Unwrap(arg.e), - base::internal::UnwrapTraits<F>::Unwrap(arg.f), - base::internal::UnwrapTraits<G>::Unwrap(arg.g), - base::internal::UnwrapTraits<H>::Unwrap(arg.h)); + const Tuple<Ts...>& arg) { + DispatchToMethodImpl(obj, method, arg, MakeIndexSequence<sizeof...(Ts)>()); } // Static Dispatchers with no out params. -template <class Function> -inline void DispatchToFunction(Function function, const Tuple0& arg) { - (*function)(); -} - -template <class Function, class A> -inline void DispatchToFunction(Function function, const A& arg) { - (*function)(arg); -} - -template <class Function, class A> -inline void DispatchToFunction(Function function, const Tuple1<A>& arg) { - (*function)(base::internal::UnwrapTraits<A>::Unwrap(arg.a)); -} - -template<class Function, class A, class B> -inline void DispatchToFunction(Function function, const Tuple2<A, B>& arg) { - (*function)(base::internal::UnwrapTraits<A>::Unwrap(arg.a), - base::internal::UnwrapTraits<B>::Unwrap(arg.b)); +template <typename Function, typename A> +inline void DispatchToMethod(Function function, const A& arg) { + (*function)(base::internal::UnwrapTraits<A>::Unwrap(arg)); } -template<class Function, class A, class B, class C> -inline void DispatchToFunction(Function function, const Tuple3<A, B, C>& arg) { - (*function)(base::internal::UnwrapTraits<A>::Unwrap(arg.a), - base::internal::UnwrapTraits<B>::Unwrap(arg.b), - base::internal::UnwrapTraits<C>::Unwrap(arg.c)); +template <typename Function, typename... Ts, size_t... Ns> +inline void DispatchToFunctionImpl(Function function, + const Tuple<Ts...>& arg, + IndexSequence<Ns...>) { + (*function)(base::internal::UnwrapTraits<Ts>::Unwrap(get<Ns>(arg))...); } -template<class Function, class A, class B, class C, class D> -inline void DispatchToFunction(Function function, - const Tuple4<A, B, C, D>& arg) { - (*function)(base::internal::UnwrapTraits<A>::Unwrap(arg.a), - base::internal::UnwrapTraits<B>::Unwrap(arg.b), - base::internal::UnwrapTraits<C>::Unwrap(arg.c), - base::internal::UnwrapTraits<D>::Unwrap(arg.d)); +template <typename Function, typename... Ts> +inline void DispatchToFunction(Function function, const Tuple<Ts...>& arg) { + DispatchToFunctionImpl(function, arg, MakeIndexSequence<sizeof...(Ts)>()); } -template<class Function, class A, class B, class C, class D, class E> -inline void DispatchToFunction(Function function, - const Tuple5<A, B, C, D, E>& arg) { - (*function)(base::internal::UnwrapTraits<A>::Unwrap(arg.a), - base::internal::UnwrapTraits<B>::Unwrap(arg.b), - base::internal::UnwrapTraits<C>::Unwrap(arg.c), - base::internal::UnwrapTraits<D>::Unwrap(arg.d), - base::internal::UnwrapTraits<E>::Unwrap(arg.e)); -} +// Dispatchers with out parameters. -template<class Function, class A, class B, class C, class D, class E, class F> -inline void DispatchToFunction(Function function, - const Tuple6<A, B, C, D, E, F>& arg) { - (*function)(base::internal::UnwrapTraits<A>::Unwrap(arg.a), - base::internal::UnwrapTraits<B>::Unwrap(arg.b), - base::internal::UnwrapTraits<C>::Unwrap(arg.c), - base::internal::UnwrapTraits<D>::Unwrap(arg.d), - base::internal::UnwrapTraits<E>::Unwrap(arg.e), - base::internal::UnwrapTraits<F>::Unwrap(arg.f)); +template <typename ObjT, + typename Method, + typename In, + typename... OutTs, + size_t... OutNs> +inline void DispatchToMethodImpl(ObjT* obj, + Method method, + const In& in, + Tuple<OutTs...>* out, + IndexSequence<OutNs...>) { + (obj->*method)(base::internal::UnwrapTraits<In>::Unwrap(in), + &get<OutNs>(*out)...); } -template<class Function, class A, class B, class C, class D, class E, class F, - class G> -inline void DispatchToFunction(Function function, - const Tuple7<A, B, C, D, E, F, G>& arg) { - (*function)(base::internal::UnwrapTraits<A>::Unwrap(arg.a), - base::internal::UnwrapTraits<B>::Unwrap(arg.b), - base::internal::UnwrapTraits<C>::Unwrap(arg.c), - base::internal::UnwrapTraits<D>::Unwrap(arg.d), - base::internal::UnwrapTraits<E>::Unwrap(arg.e), - base::internal::UnwrapTraits<F>::Unwrap(arg.f), - base::internal::UnwrapTraits<G>::Unwrap(arg.g)); -} - -template<class Function, class A, class B, class C, class D, class E, class F, - class G, class H> -inline void DispatchToFunction(Function function, - const Tuple8<A, B, C, D, E, F, G, H>& arg) { - (*function)(base::internal::UnwrapTraits<A>::Unwrap(arg.a), - base::internal::UnwrapTraits<B>::Unwrap(arg.b), - base::internal::UnwrapTraits<C>::Unwrap(arg.c), - base::internal::UnwrapTraits<D>::Unwrap(arg.d), - base::internal::UnwrapTraits<E>::Unwrap(arg.e), - base::internal::UnwrapTraits<F>::Unwrap(arg.f), - base::internal::UnwrapTraits<G>::Unwrap(arg.g), - base::internal::UnwrapTraits<H>::Unwrap(arg.h)); -} - -// Dispatchers with 0 out param (as a Tuple0). - -template <class ObjT, class Method> +template <typename ObjT, typename Method, typename In, typename... OutTs> inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple0& arg, Tuple0*) { - (obj->*method)(); -} - -template <class ObjT, class Method, class A> -inline void DispatchToMethod(ObjT* obj, Method method, const A& arg, Tuple0*) { - (obj->*method)(base::internal::UnwrapTraits<A>::Unwrap(arg)); -} - -template <class ObjT, class Method, class A> -inline void DispatchToMethod(ObjT* obj, - Method method, - const Tuple1<A>& arg, Tuple0*) { - (obj->*method)(base::internal::UnwrapTraits<A>::Unwrap(arg.a)); -} - -template<class ObjT, class Method, class A, class B> + const In& in, + Tuple<OutTs...>* out) { + DispatchToMethodImpl(obj, method, in, out, + MakeIndexSequence<sizeof...(OutTs)>()); +} + +template <typename ObjT, + typename Method, + typename... InTs, + typename... OutTs, + size_t... InNs, + size_t... OutNs> +inline void DispatchToMethodImpl(ObjT* obj, + Method method, + const Tuple<InTs...>& in, + Tuple<OutTs...>* out, + IndexSequence<InNs...>, + IndexSequence<OutNs...>) { + (obj->*method)(base::internal::UnwrapTraits<InTs>::Unwrap(get<InNs>(in))..., + &get<OutNs>(*out)...); +} + +template <typename ObjT, typename Method, typename... InTs, typename... OutTs> inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple2<A, B>& arg, Tuple0*) { - (obj->*method)(base::internal::UnwrapTraits<A>::Unwrap(arg.a), - base::internal::UnwrapTraits<B>::Unwrap(arg.b)); -} - -template<class ObjT, class Method, class A, class B, class C> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple3<A, B, C>& arg, Tuple0*) { - (obj->*method)(base::internal::UnwrapTraits<A>::Unwrap(arg.a), - base::internal::UnwrapTraits<B>::Unwrap(arg.b), - base::internal::UnwrapTraits<C>::Unwrap(arg.c)); -} - -template<class ObjT, class Method, class A, class B, class C, class D> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple4<A, B, C, D>& arg, Tuple0*) { - (obj->*method)(base::internal::UnwrapTraits<A>::Unwrap(arg.a), - base::internal::UnwrapTraits<B>::Unwrap(arg.b), - base::internal::UnwrapTraits<C>::Unwrap(arg.c), - base::internal::UnwrapTraits<D>::Unwrap(arg.d)); -} - -template<class ObjT, class Method, class A, class B, class C, class D, class E> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple5<A, B, C, D, E>& arg, Tuple0*) { - (obj->*method)(base::internal::UnwrapTraits<A>::Unwrap(arg.a), - base::internal::UnwrapTraits<B>::Unwrap(arg.b), - base::internal::UnwrapTraits<C>::Unwrap(arg.c), - base::internal::UnwrapTraits<D>::Unwrap(arg.d), - base::internal::UnwrapTraits<E>::Unwrap(arg.e)); -} - -template<class ObjT, class Method, class A, class B, class C, class D, class E, - class F> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple6<A, B, C, D, E, F>& arg, Tuple0*) { - (obj->*method)(base::internal::UnwrapTraits<A>::Unwrap(arg.a), - base::internal::UnwrapTraits<B>::Unwrap(arg.b), - base::internal::UnwrapTraits<C>::Unwrap(arg.c), - base::internal::UnwrapTraits<D>::Unwrap(arg.d), - base::internal::UnwrapTraits<E>::Unwrap(arg.e), - base::internal::UnwrapTraits<F>::Unwrap(arg.f)); -} - -// Dispatchers with 1 out param. - -template<class ObjT, class Method, - class OutA> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple0& in, - Tuple1<OutA>* out) { - (obj->*method)(&out->a); -} - -template<class ObjT, class Method, class InA, - class OutA> -inline void DispatchToMethod(ObjT* obj, Method method, - const InA& in, - Tuple1<OutA>* out) { - (obj->*method)(in, &out->a); -} - -template<class ObjT, class Method, class InA, - class OutA> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple1<InA>& in, - Tuple1<OutA>* out) { - (obj->*method)(base::internal::UnwrapTraits<InA>::Unwrap(in.a), &out->a); -} - -template<class ObjT, class Method, class InA, class InB, - class OutA> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple2<InA, InB>& in, - Tuple1<OutA>* out) { - (obj->*method)(base::internal::UnwrapTraits<InA>::Unwrap(in.a), - base::internal::UnwrapTraits<InB>::Unwrap(in.b), - &out->a); -} - -template<class ObjT, class Method, class InA, class InB, class InC, - class OutA> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple3<InA, InB, InC>& in, - Tuple1<OutA>* out) { - (obj->*method)(base::internal::UnwrapTraits<InA>::Unwrap(in.a), - base::internal::UnwrapTraits<InB>::Unwrap(in.b), - base::internal::UnwrapTraits<InC>::Unwrap(in.c), - &out->a); -} - -template<class ObjT, class Method, class InA, class InB, class InC, class InD, - class OutA> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple4<InA, InB, InC, InD>& in, - Tuple1<OutA>* out) { - (obj->*method)(base::internal::UnwrapTraits<InA>::Unwrap(in.a), - base::internal::UnwrapTraits<InB>::Unwrap(in.b), - base::internal::UnwrapTraits<InC>::Unwrap(in.c), - base::internal::UnwrapTraits<InD>::Unwrap(in.d), - &out->a); -} - -template<class ObjT, class Method, class InA, class InB, class InC, class InD, - class InE, class OutA> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple5<InA, InB, InC, InD, InE>& in, - Tuple1<OutA>* out) { - (obj->*method)(base::internal::UnwrapTraits<InA>::Unwrap(in.a), - base::internal::UnwrapTraits<InB>::Unwrap(in.b), - base::internal::UnwrapTraits<InC>::Unwrap(in.c), - base::internal::UnwrapTraits<InD>::Unwrap(in.d), - base::internal::UnwrapTraits<InE>::Unwrap(in.e), - &out->a); -} - -template<class ObjT, class Method, - class InA, class InB, class InC, class InD, class InE, class InF, - class OutA> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple6<InA, InB, InC, InD, InE, InF>& in, - Tuple1<OutA>* out) { - (obj->*method)(base::internal::UnwrapTraits<InA>::Unwrap(in.a), - base::internal::UnwrapTraits<InB>::Unwrap(in.b), - base::internal::UnwrapTraits<InC>::Unwrap(in.c), - base::internal::UnwrapTraits<InD>::Unwrap(in.d), - base::internal::UnwrapTraits<InE>::Unwrap(in.e), - base::internal::UnwrapTraits<InF>::Unwrap(in.f), - &out->a); -} - -// Dispatchers with 2 out params. - -template<class ObjT, class Method, - class OutA, class OutB> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple0& in, - Tuple2<OutA, OutB>* out) { - (obj->*method)(&out->a, &out->b); -} - -template<class ObjT, class Method, class InA, - class OutA, class OutB> -inline void DispatchToMethod(ObjT* obj, Method method, - const InA& in, - Tuple2<OutA, OutB>* out) { - (obj->*method)(in, &out->a, &out->b); -} - -template<class ObjT, class Method, class InA, - class OutA, class OutB> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple1<InA>& in, - Tuple2<OutA, OutB>* out) { - (obj->*method)( - base::internal::UnwrapTraits<InA>::Unwrap(in.a), &out->a, &out->b); -} - -template<class ObjT, class Method, class InA, class InB, - class OutA, class OutB> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple2<InA, InB>& in, - Tuple2<OutA, OutB>* out) { - (obj->*method)(base::internal::UnwrapTraits<InA>::Unwrap(in.a), - base::internal::UnwrapTraits<InB>::Unwrap(in.b), - &out->a, - &out->b); -} - -template<class ObjT, class Method, class InA, class InB, class InC, - class OutA, class OutB> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple3<InA, InB, InC>& in, - Tuple2<OutA, OutB>* out) { - (obj->*method)(base::internal::UnwrapTraits<InA>::Unwrap(in.a), - base::internal::UnwrapTraits<InB>::Unwrap(in.b), - base::internal::UnwrapTraits<InC>::Unwrap(in.c), - &out->a, - &out->b); -} - -template<class ObjT, class Method, class InA, class InB, class InC, class InD, - class OutA, class OutB> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple4<InA, InB, InC, InD>& in, - Tuple2<OutA, OutB>* out) { - (obj->*method)(base::internal::UnwrapTraits<InA>::Unwrap(in.a), - base::internal::UnwrapTraits<InB>::Unwrap(in.b), - base::internal::UnwrapTraits<InC>::Unwrap(in.c), - base::internal::UnwrapTraits<InD>::Unwrap(in.d), - &out->a, - &out->b); -} - -template<class ObjT, class Method, - class InA, class InB, class InC, class InD, class InE, - class OutA, class OutB> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple5<InA, InB, InC, InD, InE>& in, - Tuple2<OutA, OutB>* out) { - (obj->*method)(base::internal::UnwrapTraits<InA>::Unwrap(in.a), - base::internal::UnwrapTraits<InB>::Unwrap(in.b), - base::internal::UnwrapTraits<InC>::Unwrap(in.c), - base::internal::UnwrapTraits<InD>::Unwrap(in.d), - base::internal::UnwrapTraits<InE>::Unwrap(in.e), - &out->a, - &out->b); -} - -template<class ObjT, class Method, - class InA, class InB, class InC, class InD, class InE, class InF, - class OutA, class OutB> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple6<InA, InB, InC, InD, InE, InF>& in, - Tuple2<OutA, OutB>* out) { - (obj->*method)(base::internal::UnwrapTraits<InA>::Unwrap(in.a), - base::internal::UnwrapTraits<InB>::Unwrap(in.b), - base::internal::UnwrapTraits<InC>::Unwrap(in.c), - base::internal::UnwrapTraits<InD>::Unwrap(in.d), - base::internal::UnwrapTraits<InE>::Unwrap(in.e), - base::internal::UnwrapTraits<InF>::Unwrap(in.f), - &out->a, - &out->b); -} - -// Dispatchers with 3 out params. - -template<class ObjT, class Method, - class OutA, class OutB, class OutC> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple0& in, - Tuple3<OutA, OutB, OutC>* out) { - (obj->*method)(&out->a, &out->b, &out->c); -} - -template<class ObjT, class Method, class InA, - class OutA, class OutB, class OutC> -inline void DispatchToMethod(ObjT* obj, Method method, - const InA& in, - Tuple3<OutA, OutB, OutC>* out) { - (obj->*method)(in, &out->a, &out->b, &out->c); -} - -template<class ObjT, class Method, class InA, - class OutA, class OutB, class OutC> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple1<InA>& in, - Tuple3<OutA, OutB, OutC>* out) { - (obj->*method)(base::internal::UnwrapTraits<InA>::Unwrap(in.a), - &out->a, - &out->b, - &out->c); -} - -template<class ObjT, class Method, class InA, class InB, - class OutA, class OutB, class OutC> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple2<InA, InB>& in, - Tuple3<OutA, OutB, OutC>* out) { - (obj->*method)(base::internal::UnwrapTraits<InA>::Unwrap(in.a), - base::internal::UnwrapTraits<InB>::Unwrap(in.b), - &out->a, - &out->b, - &out->c); -} - -template<class ObjT, class Method, class InA, class InB, class InC, - class OutA, class OutB, class OutC> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple3<InA, InB, InC>& in, - Tuple3<OutA, OutB, OutC>* out) { - (obj->*method)(base::internal::UnwrapTraits<InA>::Unwrap(in.a), - base::internal::UnwrapTraits<InB>::Unwrap(in.b), - base::internal::UnwrapTraits<InC>::Unwrap(in.c), - &out->a, - &out->b, - &out->c); -} - -template<class ObjT, class Method, class InA, class InB, class InC, class InD, - class OutA, class OutB, class OutC> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple4<InA, InB, InC, InD>& in, - Tuple3<OutA, OutB, OutC>* out) { - (obj->*method)(base::internal::UnwrapTraits<InA>::Unwrap(in.a), - base::internal::UnwrapTraits<InB>::Unwrap(in.b), - base::internal::UnwrapTraits<InC>::Unwrap(in.c), - base::internal::UnwrapTraits<InD>::Unwrap(in.d), - &out->a, - &out->b, - &out->c); -} - -template<class ObjT, class Method, - class InA, class InB, class InC, class InD, class InE, - class OutA, class OutB, class OutC> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple5<InA, InB, InC, InD, InE>& in, - Tuple3<OutA, OutB, OutC>* out) { - (obj->*method)(base::internal::UnwrapTraits<InA>::Unwrap(in.a), - base::internal::UnwrapTraits<InB>::Unwrap(in.b), - base::internal::UnwrapTraits<InC>::Unwrap(in.c), - base::internal::UnwrapTraits<InD>::Unwrap(in.d), - base::internal::UnwrapTraits<InE>::Unwrap(in.e), - &out->a, - &out->b, - &out->c); -} - -template<class ObjT, class Method, - class InA, class InB, class InC, class InD, class InE, class InF, - class OutA, class OutB, class OutC> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple6<InA, InB, InC, InD, InE, InF>& in, - Tuple3<OutA, OutB, OutC>* out) { - (obj->*method)(base::internal::UnwrapTraits<InA>::Unwrap(in.a), - base::internal::UnwrapTraits<InB>::Unwrap(in.b), - base::internal::UnwrapTraits<InC>::Unwrap(in.c), - base::internal::UnwrapTraits<InD>::Unwrap(in.d), - base::internal::UnwrapTraits<InE>::Unwrap(in.e), - base::internal::UnwrapTraits<InF>::Unwrap(in.f), - &out->a, - &out->b, - &out->c); -} - -// Dispatchers with 4 out params. - -template<class ObjT, class Method, - class OutA, class OutB, class OutC, class OutD> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple0& in, - Tuple4<OutA, OutB, OutC, OutD>* out) { - (obj->*method)(&out->a, &out->b, &out->c, &out->d); -} - -template<class ObjT, class Method, class InA, - class OutA, class OutB, class OutC, class OutD> -inline void DispatchToMethod(ObjT* obj, Method method, - const InA& in, - Tuple4<OutA, OutB, OutC, OutD>* out) { - (obj->*method)(base::internal::UnwrapTraits<InA>::Unwrap(in), - &out->a, - &out->b, - &out->c, - &out->d); -} - -template<class ObjT, class Method, class InA, - class OutA, class OutB, class OutC, class OutD> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple1<InA>& in, - Tuple4<OutA, OutB, OutC, OutD>* out) { - (obj->*method)(base::internal::UnwrapTraits<InA>::Unwrap(in.a), - &out->a, - &out->b, - &out->c, - &out->d); -} - -template<class ObjT, class Method, class InA, class InB, - class OutA, class OutB, class OutC, class OutD> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple2<InA, InB>& in, - Tuple4<OutA, OutB, OutC, OutD>* out) { - (obj->*method)(base::internal::UnwrapTraits<InA>::Unwrap(in.a), - base::internal::UnwrapTraits<InB>::Unwrap(in.b), - &out->a, - &out->b, - &out->c, - &out->d); -} - -template<class ObjT, class Method, class InA, class InB, class InC, - class OutA, class OutB, class OutC, class OutD> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple3<InA, InB, InC>& in, - Tuple4<OutA, OutB, OutC, OutD>* out) { - (obj->*method)(base::internal::UnwrapTraits<InA>::Unwrap(in.a), - base::internal::UnwrapTraits<InB>::Unwrap(in.b), - base::internal::UnwrapTraits<InC>::Unwrap(in.c), - &out->a, - &out->b, - &out->c, - &out->d); -} - -template<class ObjT, class Method, class InA, class InB, class InC, class InD, - class OutA, class OutB, class OutC, class OutD> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple4<InA, InB, InC, InD>& in, - Tuple4<OutA, OutB, OutC, OutD>* out) { - (obj->*method)(base::internal::UnwrapTraits<InA>::Unwrap(in.a), - base::internal::UnwrapTraits<InB>::Unwrap(in.b), - base::internal::UnwrapTraits<InC>::Unwrap(in.c), - base::internal::UnwrapTraits<InD>::Unwrap(in.d), - &out->a, - &out->b, - &out->c, - &out->d); -} - -template<class ObjT, class Method, - class InA, class InB, class InC, class InD, class InE, - class OutA, class OutB, class OutC, class OutD> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple5<InA, InB, InC, InD, InE>& in, - Tuple4<OutA, OutB, OutC, OutD>* out) { - (obj->*method)(base::internal::UnwrapTraits<InA>::Unwrap(in.a), - base::internal::UnwrapTraits<InB>::Unwrap(in.b), - base::internal::UnwrapTraits<InC>::Unwrap(in.c), - base::internal::UnwrapTraits<InD>::Unwrap(in.d), - base::internal::UnwrapTraits<InE>::Unwrap(in.e), - &out->a, - &out->b, - &out->c, - &out->d); -} - -template<class ObjT, class Method, - class InA, class InB, class InC, class InD, class InE, class InF, - class OutA, class OutB, class OutC, class OutD> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple6<InA, InB, InC, InD, InE, InF>& in, - Tuple4<OutA, OutB, OutC, OutD>* out) { - (obj->*method)(base::internal::UnwrapTraits<InA>::Unwrap(in.a), - base::internal::UnwrapTraits<InB>::Unwrap(in.b), - base::internal::UnwrapTraits<InC>::Unwrap(in.c), - base::internal::UnwrapTraits<InD>::Unwrap(in.d), - base::internal::UnwrapTraits<InE>::Unwrap(in.e), - base::internal::UnwrapTraits<InF>::Unwrap(in.f), - &out->a, - &out->b, - &out->c, - &out->d); -} - -// Dispatchers with 5 out params. - -template<class ObjT, class Method, - class OutA, class OutB, class OutC, class OutD, class OutE> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple0& in, - Tuple5<OutA, OutB, OutC, OutD, OutE>* out) { - (obj->*method)(&out->a, &out->b, &out->c, &out->d, &out->e); -} - -template<class ObjT, class Method, class InA, - class OutA, class OutB, class OutC, class OutD, class OutE> -inline void DispatchToMethod(ObjT* obj, Method method, - const InA& in, - Tuple5<OutA, OutB, OutC, OutD, OutE>* out) { - (obj->*method)(base::internal::UnwrapTraits<InA>::Unwrap(in), - &out->a, - &out->b, - &out->c, - &out->d, - &out->e); -} - -template<class ObjT, class Method, class InA, - class OutA, class OutB, class OutC, class OutD, class OutE> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple1<InA>& in, - Tuple5<OutA, OutB, OutC, OutD, OutE>* out) { - (obj->*method)(base::internal::UnwrapTraits<InA>::Unwrap(in.a), - &out->a, - &out->b, - &out->c, - &out->d, - &out->e); -} - -template<class ObjT, class Method, class InA, class InB, - class OutA, class OutB, class OutC, class OutD, class OutE> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple2<InA, InB>& in, - Tuple5<OutA, OutB, OutC, OutD, OutE>* out) { - (obj->*method)(base::internal::UnwrapTraits<InA>::Unwrap(in.a), - base::internal::UnwrapTraits<InB>::Unwrap(in.b), - &out->a, - &out->b, - &out->c, - &out->d, - &out->e); -} - -template<class ObjT, class Method, class InA, class InB, class InC, - class OutA, class OutB, class OutC, class OutD, class OutE> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple3<InA, InB, InC>& in, - Tuple5<OutA, OutB, OutC, OutD, OutE>* out) { - (obj->*method)(base::internal::UnwrapTraits<InA>::Unwrap(in.a), - base::internal::UnwrapTraits<InB>::Unwrap(in.b), - base::internal::UnwrapTraits<InC>::Unwrap(in.c), - &out->a, - &out->b, - &out->c, - &out->d, - &out->e); -} - -template<class ObjT, class Method, class InA, class InB, class InC, class InD, - class OutA, class OutB, class OutC, class OutD, class OutE> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple4<InA, InB, InC, InD>& in, - Tuple5<OutA, OutB, OutC, OutD, OutE>* out) { - (obj->*method)(base::internal::UnwrapTraits<InA>::Unwrap(in.a), - base::internal::UnwrapTraits<InB>::Unwrap(in.b), - base::internal::UnwrapTraits<InC>::Unwrap(in.c), - base::internal::UnwrapTraits<InD>::Unwrap(in.d), - &out->a, - &out->b, - &out->c, - &out->d, - &out->e); -} - -template<class ObjT, class Method, - class InA, class InB, class InC, class InD, class InE, - class OutA, class OutB, class OutC, class OutD, class OutE> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple5<InA, InB, InC, InD, InE>& in, - Tuple5<OutA, OutB, OutC, OutD, OutE>* out) { - (obj->*method)(base::internal::UnwrapTraits<InA>::Unwrap(in.a), - base::internal::UnwrapTraits<InB>::Unwrap(in.b), - base::internal::UnwrapTraits<InC>::Unwrap(in.c), - base::internal::UnwrapTraits<InD>::Unwrap(in.d), - base::internal::UnwrapTraits<InE>::Unwrap(in.e), - &out->a, - &out->b, - &out->c, - &out->d, - &out->e); -} - -template<class ObjT, class Method, - class InA, class InB, class InC, class InD, class InE, class InF, - class OutA, class OutB, class OutC, class OutD, class OutE> -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple6<InA, InB, InC, InD, InE, InF>& in, - Tuple5<OutA, OutB, OutC, OutD, OutE>* out) { - (obj->*method)(base::internal::UnwrapTraits<InA>::Unwrap(in.a), - base::internal::UnwrapTraits<InB>::Unwrap(in.b), - base::internal::UnwrapTraits<InC>::Unwrap(in.c), - base::internal::UnwrapTraits<InD>::Unwrap(in.d), - base::internal::UnwrapTraits<InE>::Unwrap(in.e), - base::internal::UnwrapTraits<InF>::Unwrap(in.f), - &out->a, - &out->b, - &out->c, - &out->d, - &out->e); + const Tuple<InTs...>& in, + Tuple<OutTs...>* out) { + DispatchToMethodImpl(obj, method, in, out, + MakeIndexSequence<sizeof...(InTs)>(), + MakeIndexSequence<sizeof...(OutTs)>()); } -#endif // BASE_TUPLE_H__ +#endif // BASE_TUPLE_H_ diff --git a/chromium/base/tuple_unittest.cc b/chromium/base/tuple_unittest.cc index 8d620deb639..5b43affc600 100644 --- a/chromium/base/tuple_unittest.cc +++ b/chromium/base/tuple_unittest.cc @@ -30,50 +30,50 @@ struct Addz { } // namespace TEST(TupleTest, Basic) { - Tuple0 t0 = MakeTuple(); + Tuple<> t0 = MakeTuple(); ALLOW_UNUSED_LOCAL(t0); - Tuple1<int> t1(1); - Tuple2<int, const char*> t2 = MakeTuple(1, static_cast<const char*>("wee")); - Tuple3<int, int, int> t3(1, 2, 3); - Tuple4<int, int, int, int*> t4(1, 2, 3, &t1.a); - Tuple5<int, int, int, int, int*> t5(1, 2, 3, 4, &t4.a); - Tuple6<int, int, int, int, int, int*> t6(1, 2, 3, 4, 5, &t4.a); - - EXPECT_EQ(1, t1.a); - EXPECT_EQ(1, t2.a); - EXPECT_EQ(1, t3.a); - EXPECT_EQ(2, t3.b); - EXPECT_EQ(3, t3.c); - EXPECT_EQ(1, t4.a); - EXPECT_EQ(2, t4.b); - EXPECT_EQ(3, t4.c); - EXPECT_EQ(1, t5.a); - EXPECT_EQ(2, t5.b); - EXPECT_EQ(3, t5.c); - EXPECT_EQ(4, t5.d); - EXPECT_EQ(1, t6.a); - EXPECT_EQ(2, t6.b); - EXPECT_EQ(3, t6.c); - EXPECT_EQ(4, t6.d); - EXPECT_EQ(5, t6.e); - - EXPECT_EQ(1, t1.a); + Tuple<int> t1(1); + Tuple<int, const char*> t2 = MakeTuple(1, static_cast<const char*>("wee")); + Tuple<int, int, int> t3(1, 2, 3); + Tuple<int, int, int, int*> t4(1, 2, 3, &get<0>(t1)); + Tuple<int, int, int, int, int*> t5(1, 2, 3, 4, &get<0>(t4)); + Tuple<int, int, int, int, int, int*> t6(1, 2, 3, 4, 5, &get<0>(t4)); + + EXPECT_EQ(1, get<0>(t1)); + EXPECT_EQ(1, get<0>(t2)); + EXPECT_EQ(1, get<0>(t3)); + EXPECT_EQ(2, get<1>(t3)); + EXPECT_EQ(3, get<2>(t3)); + EXPECT_EQ(1, get<0>(t4)); + EXPECT_EQ(2, get<1>(t4)); + EXPECT_EQ(3, get<2>(t4)); + EXPECT_EQ(1, get<0>(t5)); + EXPECT_EQ(2, get<1>(t5)); + EXPECT_EQ(3, get<2>(t5)); + EXPECT_EQ(4, get<3>(t5)); + EXPECT_EQ(1, get<0>(t6)); + EXPECT_EQ(2, get<1>(t6)); + EXPECT_EQ(3, get<2>(t6)); + EXPECT_EQ(4, get<3>(t6)); + EXPECT_EQ(5, get<4>(t6)); + + EXPECT_EQ(1, get<0>(t1)); DispatchToFunction(&DoAdd, t4); - EXPECT_EQ(6, t1.a); + EXPECT_EQ(6, get<0>(t1)); int res = 0; DispatchToFunction(&DoAdd, MakeTuple(9, 8, 7, &res)); EXPECT_EQ(24, res); Addy addy; - EXPECT_EQ(1, t4.a); + EXPECT_EQ(1, get<0>(t4)); DispatchToMethod(&addy, &Addy::DoAdd, t5); - EXPECT_EQ(10, t4.a); + EXPECT_EQ(10, get<0>(t4)); Addz addz; - EXPECT_EQ(10, t4.a); + EXPECT_EQ(10, get<0>(t4)); DispatchToMethod(&addz, &Addz::DoAdd, t6); - EXPECT_EQ(15, t4.a); + EXPECT_EQ(15, get<0>(t4)); } namespace { @@ -108,8 +108,8 @@ TEST(TupleTest, Copying) { bool res = false; // Creating the tuple should copy the class to store internally in the tuple. - Tuple3<CopyLogger, CopyLogger*, bool*> tuple(logger, &logger, &res); - tuple.b = &tuple.a; + Tuple<CopyLogger, CopyLogger*, bool*> tuple(logger, &logger, &res); + get<1>(tuple) = &get<0>(tuple); EXPECT_EQ(2, CopyLogger::TimesConstructed); EXPECT_EQ(1, CopyLogger::TimesCopied); diff --git a/chromium/base/value_conversions.h b/chromium/base/value_conversions.h index fde9a269299..452c587cb84 100644 --- a/chromium/base/value_conversions.h +++ b/chromium/base/value_conversions.h @@ -24,6 +24,6 @@ BASE_EXPORT bool GetValueAsFilePath(const Value& value, FilePath* file_path); BASE_EXPORT StringValue* CreateTimeDeltaValue(const TimeDelta& time); BASE_EXPORT bool GetValueAsTimeDelta(const Value& value, TimeDelta* time); -} // namespace +} // namespace base #endif // BASE_VALUE_CONVERSIONS_H_ diff --git a/chromium/base/values.cc b/chromium/base/values.cc index 5d45ec36c55..4093eba67aa 100644 --- a/chromium/base/values.cc +++ b/chromium/base/values.cc @@ -7,9 +7,9 @@ #include <string.h> #include <algorithm> +#include <cmath> #include <ostream> -#include "base/float_util.h" #include "base/json/json_writer.h" #include "base/logging.h" #include "base/move.h" @@ -85,8 +85,12 @@ Value::~Value() { } // static -Value* Value::CreateNullValue() { - return new Value(TYPE_NULL); +scoped_ptr<Value> Value::CreateNullValue() { + return make_scoped_ptr(new Value(TYPE_NULL)); +} + +bool Value::GetAsBinary(const BinaryValue** out_value) const { + return false; } bool Value::GetAsBoolean(bool* out_value) const { @@ -133,7 +137,11 @@ Value* Value::DeepCopy() const { // This method should only be getting called for null Values--all subclasses // need to provide their own implementation;. DCHECK(IsType(TYPE_NULL)); - return CreateNullValue(); + return CreateNullValue().release(); +} + +scoped_ptr<Value> Value::CreateDeepCopy() const { + return make_scoped_ptr(DeepCopy()); } bool Value::Equals(const Value* other) const { @@ -171,7 +179,7 @@ FundamentalValue::FundamentalValue(int in_value) FundamentalValue::FundamentalValue(double in_value) : Value(TYPE_DOUBLE), double_value_(in_value) { - if (!IsFinite(double_value_)) { + if (!std::isfinite(double_value_)) { NOTREACHED() << "Non-finite (i.e. NaN or positive/negative infinity) " << "values cannot be represented in JSON"; double_value_ = 0.0; @@ -319,6 +327,12 @@ BinaryValue* BinaryValue::CreateWithCopiedBuffer(const char* buffer, return new BinaryValue(scoped_buffer_copy.Pass(), size); } +bool BinaryValue::GetAsBinary(const BinaryValue** out_value) const { + if (out_value) + *out_value = this; + return true; +} + BinaryValue* BinaryValue::DeepCopy() const { return CreateWithCopiedBuffer(buffer_.get(), size_); } @@ -371,7 +385,7 @@ void DictionaryValue::Clear() { dictionary_.clear(); } -void DictionaryValue::Set(const std::string& path, Value* in_value) { +void DictionaryValue::Set(const std::string& path, scoped_ptr<Value> in_value) { DCHECK(IsStringUTF8(path)); DCHECK(in_value); @@ -392,7 +406,11 @@ void DictionaryValue::Set(const std::string& path, Value* in_value) { current_path.erase(0, delimiter_position + 1); } - current_dictionary->SetWithoutPathExpansion(current_path, in_value); + current_dictionary->SetWithoutPathExpansion(current_path, in_value.Pass()); +} + +void DictionaryValue::Set(const std::string& path, Value* in_value) { + Set(path, make_scoped_ptr(in_value)); } void DictionaryValue::SetBoolean(const std::string& path, bool in_value) { @@ -418,18 +436,24 @@ void DictionaryValue::SetString(const std::string& path, } void DictionaryValue::SetWithoutPathExpansion(const std::string& key, - Value* in_value) { + scoped_ptr<Value> in_value) { + Value* bare_ptr = in_value.release(); // If there's an existing value here, we need to delete it, because // we own all our children. std::pair<ValueMap::iterator, bool> ins_res = - dictionary_.insert(std::make_pair(key, in_value)); + dictionary_.insert(std::make_pair(key, bare_ptr)); if (!ins_res.second) { - DCHECK_NE(ins_res.first->second, in_value); // This would be bogus + DCHECK_NE(ins_res.first->second, bare_ptr); // This would be bogus delete ins_res.first->second; - ins_res.first->second = in_value; + ins_res.first->second = bare_ptr; } } +void DictionaryValue::SetWithoutPathExpansion(const std::string& key, + Value* in_value) { + SetWithoutPathExpansion(key, make_scoped_ptr(in_value)); +} + void DictionaryValue::SetBooleanWithoutPathExpansion( const std::string& path, bool in_value) { SetWithoutPathExpansion(path, new FundamentalValue(in_value)); @@ -809,6 +833,10 @@ DictionaryValue* DictionaryValue::DeepCopy() const { return result; } +scoped_ptr<DictionaryValue> DictionaryValue::CreateDeepCopy() const { + return make_scoped_ptr(DeepCopy()); +} + bool DictionaryValue::Equals(const Value* other) const { if (other->GetType() != GetType()) return false; @@ -863,6 +891,10 @@ bool ListValue::Set(size_t index, Value* in_value) { return true; } +bool ListValue::Set(size_t index, scoped_ptr<Value> in_value) { + return Set(index, in_value.release()); +} + bool ListValue::Get(size_t index, const Value** out_value) const { if (index >= list_.size()) return false; @@ -1012,6 +1044,10 @@ ListValue::iterator ListValue::Erase(iterator iter, return list_.erase(iter); } +void ListValue::Append(scoped_ptr<Value> in_value) { + Append(in_value.release()); +} + void ListValue::Append(Value* in_value) { DCHECK(in_value); list_.push_back(in_value); @@ -1101,6 +1137,10 @@ ListValue* ListValue::DeepCopy() const { return result; } +scoped_ptr<ListValue> ListValue::CreateDeepCopy() const { + return make_scoped_ptr(DeepCopy()); +} + bool ListValue::Equals(const Value* other) const { if (other->GetType() != GetType()) return false; @@ -1123,6 +1163,9 @@ bool ListValue::Equals(const Value* other) const { ValueSerializer::~ValueSerializer() { } +ValueDeserializer::~ValueDeserializer() { +} + std::ostream& operator<<(std::ostream& out, const Value& value) { std::string json; JSONWriter::WriteWithOptions(&value, diff --git a/chromium/base/values.h b/chromium/base/values.h index 04b2d26eac6..e32edecf2ec 100644 --- a/chromium/base/values.h +++ b/chromium/base/values.h @@ -33,6 +33,7 @@ namespace base { +class BinaryValue; class DictionaryValue; class FundamentalValue; class ListValue; @@ -63,7 +64,7 @@ class BASE_EXPORT Value { virtual ~Value(); - static Value* CreateNullValue(); + static scoped_ptr<Value> CreateNullValue(); // Returns the type of the value stored by the current Value object. // Each type will be implemented by only one subclass of Value, so it's @@ -85,6 +86,7 @@ class BASE_EXPORT Value { virtual bool GetAsString(std::string* out_value) const; virtual bool GetAsString(string16* out_value) const; virtual bool GetAsString(const StringValue** out_value) const; + virtual bool GetAsBinary(const BinaryValue** out_value) const; virtual bool GetAsList(ListValue** out_value); virtual bool GetAsList(const ListValue** out_value) const; virtual bool GetAsDictionary(DictionaryValue** out_value); @@ -97,6 +99,8 @@ class BASE_EXPORT Value { // Subclasses return their own type directly in their overrides; // this works because C++ supports covariant return types. virtual Value* DeepCopy() const; + // Preferred version of DeepCopy. TODO(estade): remove the above. + scoped_ptr<Value> CreateDeepCopy() const; // Compares if two Value objects have equal contents. virtual bool Equals(const Value* other) const; @@ -188,6 +192,7 @@ class BASE_EXPORT BinaryValue: public Value { const char* GetBuffer() const { return buffer_.get(); } // Overridden from Value: + bool GetAsBinary(const BinaryValue** out_value) const override; BinaryValue* DeepCopy() const override; bool Equals(const Value* other) const override; @@ -228,9 +233,9 @@ class BASE_EXPORT DictionaryValue : public Value { // within a key, but there are no other restrictions on keys. // If the key at any step of the way doesn't exist, or exists but isn't // a DictionaryValue, a new DictionaryValue will be created and attached - // to the path in that location. - // Note that the dictionary takes ownership of the value referenced by - // |in_value|, and therefore |in_value| must be non-NULL. + // to the path in that location. |in_value| must be non-null. + void Set(const std::string& path, scoped_ptr<Value> in_value); + // Deprecated version of the above. TODO(estade): remove. void Set(const std::string& path, Value* in_value); // Convenience forms of Set(). These methods will replace any existing @@ -243,6 +248,9 @@ class BASE_EXPORT DictionaryValue : public Value { // Like Set(), but without special treatment of '.'. This allows e.g. URLs to // be used as paths. + void SetWithoutPathExpansion(const std::string& key, + scoped_ptr<Value> in_value); + // Deprecated version of the above. TODO(estade): remove. void SetWithoutPathExpansion(const std::string& key, Value* in_value); // Convenience forms of SetWithoutPathExpansion(). @@ -362,6 +370,8 @@ class BASE_EXPORT DictionaryValue : public Value { // Overridden from Value: DictionaryValue* DeepCopy() const override; + // Preferred version of DeepCopy. TODO(estade): remove the above. + scoped_ptr<DictionaryValue> CreateDeepCopy() const; bool Equals(const Value* other) const override; private: @@ -394,6 +404,8 @@ class BASE_EXPORT ListValue : public Value { // Returns true if successful, or false if the index was negative or // the value is a null pointer. bool Set(size_t index, Value* in_value); + // Preferred version of the above. TODO(estade): remove the above. + bool Set(size_t index, scoped_ptr<Value> in_value); // Gets the Value at the given index. Modifies |out_value| (and returns true) // only if the index falls within the current list range. @@ -439,6 +451,8 @@ class BASE_EXPORT ListValue : public Value { iterator Erase(iterator iter, scoped_ptr<Value>* out_value); // Appends a Value to the end of the list. + void Append(scoped_ptr<Value> in_value); + // Deprecated version of the above. TODO(estade): remove. void Append(Value* in_value); // Convenience forms of Append. @@ -480,19 +494,29 @@ class BASE_EXPORT ListValue : public Value { ListValue* DeepCopy() const override; bool Equals(const Value* other) const override; + // Preferred version of DeepCopy. TODO(estade): remove DeepCopy. + scoped_ptr<ListValue> CreateDeepCopy() const; + private: ValueVector list_; DISALLOW_COPY_AND_ASSIGN(ListValue); }; -// This interface is implemented by classes that know how to serialize and -// deserialize Value objects. +// This interface is implemented by classes that know how to serialize +// Value objects. class BASE_EXPORT ValueSerializer { public: virtual ~ValueSerializer(); virtual bool Serialize(const Value& root) = 0; +}; + +// This interface is implemented by classes that know how to deserialize Value +// objects. +class BASE_EXPORT ValueDeserializer { + public: + virtual ~ValueDeserializer(); // This method deserializes the subclass-specific format into a Value object. // If the return value is non-NULL, the caller takes ownership of returned diff --git a/chromium/base/values_unittest.cc b/chromium/base/values_unittest.cc index cbb07f3199d..6466a962554 100644 --- a/chromium/base/values_unittest.cc +++ b/chromium/base/values_unittest.cc @@ -20,7 +20,7 @@ TEST(ValuesTest, Basic) { ASSERT_EQ(std::string("http://google.com"), homepage); ASSERT_FALSE(settings.Get("global", NULL)); - settings.Set("global", new FundamentalValue(true)); + settings.SetBoolean("global", true); ASSERT_TRUE(settings.Get("global", NULL)); settings.SetString("global.homepage", "http://scurvy.com"); ASSERT_TRUE(settings.Get("global", NULL)); @@ -33,14 +33,14 @@ TEST(ValuesTest, Basic) { ASSERT_FALSE( settings.GetList("global.toolbar.bookmarks", &toolbar_bookmarks)); - toolbar_bookmarks = new ListValue; - settings.Set("global.toolbar.bookmarks", toolbar_bookmarks); + scoped_ptr<ListValue> new_toolbar_bookmarks(new ListValue); + settings.Set("global.toolbar.bookmarks", new_toolbar_bookmarks.Pass()); ASSERT_TRUE(settings.GetList("global.toolbar.bookmarks", &toolbar_bookmarks)); - DictionaryValue* new_bookmark = new DictionaryValue; + scoped_ptr<DictionaryValue> new_bookmark(new DictionaryValue); new_bookmark->SetString("name", "Froogle"); new_bookmark->SetString("url", "http://froogle.com"); - toolbar_bookmarks->Append(new_bookmark); + toolbar_bookmarks->Append(new_bookmark.Pass()); ListValue* bookmark_list; ASSERT_TRUE(settings.GetList("global.toolbar.bookmarks", &bookmark_list)); @@ -57,10 +57,10 @@ TEST(ValuesTest, Basic) { TEST(ValuesTest, List) { scoped_ptr<ListValue> mixed_list(new ListValue()); - mixed_list->Set(0, new FundamentalValue(true)); - mixed_list->Set(1, new FundamentalValue(42)); - mixed_list->Set(2, new FundamentalValue(88.8)); - mixed_list->Set(3, new StringValue("foo")); + mixed_list->Set(0, make_scoped_ptr(new FundamentalValue(true))); + mixed_list->Set(1, make_scoped_ptr(new FundamentalValue(42))); + mixed_list->Set(2, make_scoped_ptr(new FundamentalValue(88.8))); + mixed_list->Set(3, make_scoped_ptr(new StringValue("foo"))); ASSERT_EQ(4u, mixed_list->GetSize()); Value *value = NULL; @@ -112,11 +112,12 @@ TEST(ValuesTest, BinaryValue) { ASSERT_EQ(0U, binary->GetSize()); // Test the common case of a non-empty buffer - char* buffer = new char[15]; - binary.reset(new BinaryValue(scoped_ptr<char[]>(buffer), 15)); + scoped_ptr<char[]> buffer(new char[15]); + char* original_buffer = buffer.get(); + binary.reset(new BinaryValue(buffer.Pass(), 15)); ASSERT_TRUE(binary.get()); ASSERT_TRUE(binary->GetBuffer()); - ASSERT_EQ(buffer, binary->GetBuffer()); + ASSERT_EQ(original_buffer, binary->GetBuffer()); ASSERT_EQ(15U, binary->GetSize()); char stack_buffer[42]; @@ -127,6 +128,12 @@ TEST(ValuesTest, BinaryValue) { ASSERT_NE(stack_buffer, binary->GetBuffer()); ASSERT_EQ(42U, binary->GetSize()); ASSERT_EQ(0, memcmp(stack_buffer, binary->GetBuffer(), binary->GetSize())); + + // Test overloaded GetAsBinary. + Value* narrow_value = binary.get(); + const BinaryValue* narrow_binary = NULL; + ASSERT_TRUE(narrow_value->GetAsBinary(&narrow_binary)); + EXPECT_EQ(binary.get(), narrow_binary); } TEST(ValuesTest, StringValue) { @@ -188,14 +195,14 @@ TEST(ValuesTest, ListDeletion) { { ListValue list; - list.Append(new DeletionTestValue(&deletion_flag)); + list.Append(make_scoped_ptr(new DeletionTestValue(&deletion_flag))); EXPECT_FALSE(deletion_flag); } EXPECT_TRUE(deletion_flag); { ListValue list; - list.Append(new DeletionTestValue(&deletion_flag)); + list.Append(make_scoped_ptr(new DeletionTestValue(&deletion_flag))); EXPECT_FALSE(deletion_flag); list.Clear(); EXPECT_TRUE(deletion_flag); @@ -203,7 +210,7 @@ TEST(ValuesTest, ListDeletion) { { ListValue list; - list.Append(new DeletionTestValue(&deletion_flag)); + list.Append(make_scoped_ptr(new DeletionTestValue(&deletion_flag))); EXPECT_FALSE(deletion_flag); EXPECT_TRUE(list.Set(0, Value::CreateNullValue())); EXPECT_TRUE(deletion_flag); @@ -216,7 +223,7 @@ TEST(ValuesTest, ListRemoval) { { ListValue list; - list.Append(new DeletionTestValue(&deletion_flag)); + list.Append(make_scoped_ptr(new DeletionTestValue(&deletion_flag))); EXPECT_FALSE(deletion_flag); EXPECT_EQ(1U, list.GetSize()); EXPECT_FALSE(list.Remove(std::numeric_limits<size_t>::max(), @@ -232,7 +239,7 @@ TEST(ValuesTest, ListRemoval) { { ListValue list; - list.Append(new DeletionTestValue(&deletion_flag)); + list.Append(make_scoped_ptr(new DeletionTestValue(&deletion_flag))); EXPECT_FALSE(deletion_flag); EXPECT_TRUE(list.Remove(0, NULL)); EXPECT_TRUE(deletion_flag); @@ -241,11 +248,12 @@ TEST(ValuesTest, ListRemoval) { { ListValue list; - DeletionTestValue* value = new DeletionTestValue(&deletion_flag); - list.Append(value); + scoped_ptr<DeletionTestValue> value(new DeletionTestValue(&deletion_flag)); + DeletionTestValue* original_value = value.get(); + list.Append(value.Pass()); EXPECT_FALSE(deletion_flag); size_t index = 0; - list.Remove(*value, &index); + list.Remove(*original_value, &index); EXPECT_EQ(0U, index); EXPECT_TRUE(deletion_flag); EXPECT_EQ(0U, list.GetSize()); @@ -258,14 +266,14 @@ TEST(ValuesTest, DictionaryDeletion) { { DictionaryValue dict; - dict.Set(key, new DeletionTestValue(&deletion_flag)); + dict.Set(key, make_scoped_ptr(new DeletionTestValue(&deletion_flag))); EXPECT_FALSE(deletion_flag); } EXPECT_TRUE(deletion_flag); { DictionaryValue dict; - dict.Set(key, new DeletionTestValue(&deletion_flag)); + dict.Set(key, make_scoped_ptr(new DeletionTestValue(&deletion_flag))); EXPECT_FALSE(deletion_flag); dict.Clear(); EXPECT_TRUE(deletion_flag); @@ -273,7 +281,7 @@ TEST(ValuesTest, DictionaryDeletion) { { DictionaryValue dict; - dict.Set(key, new DeletionTestValue(&deletion_flag)); + dict.Set(key, make_scoped_ptr(new DeletionTestValue(&deletion_flag))); EXPECT_FALSE(deletion_flag); dict.Set(key, Value::CreateNullValue()); EXPECT_TRUE(deletion_flag); @@ -287,7 +295,7 @@ TEST(ValuesTest, DictionaryRemoval) { { DictionaryValue dict; - dict.Set(key, new DeletionTestValue(&deletion_flag)); + dict.Set(key, make_scoped_ptr(new DeletionTestValue(&deletion_flag))); EXPECT_FALSE(deletion_flag); EXPECT_TRUE(dict.HasKey(key)); EXPECT_FALSE(dict.Remove("absent key", &removed_item)); @@ -301,7 +309,7 @@ TEST(ValuesTest, DictionaryRemoval) { { DictionaryValue dict; - dict.Set(key, new DeletionTestValue(&deletion_flag)); + dict.Set(key, make_scoped_ptr(new DeletionTestValue(&deletion_flag))); EXPECT_FALSE(deletion_flag); EXPECT_TRUE(dict.HasKey(key)); EXPECT_TRUE(dict.Remove(key, NULL)); @@ -332,10 +340,34 @@ TEST(ValuesTest, DictionaryWithoutPathExpansion) { EXPECT_EQ(Value::TYPE_NULL, value4->GetType()); } +// Tests the deprecated version of SetWithoutPathExpansion. +// TODO(estade): remove. +TEST(ValuesTest, DictionaryWithoutPathExpansionDeprecated) { + DictionaryValue dict; + dict.Set("this.is.expanded", Value::CreateNullValue()); + dict.SetWithoutPathExpansion("this.isnt.expanded", Value::CreateNullValue()); + + EXPECT_FALSE(dict.HasKey("this.is.expanded")); + EXPECT_TRUE(dict.HasKey("this")); + Value* value1; + EXPECT_TRUE(dict.Get("this", &value1)); + DictionaryValue* value2; + ASSERT_TRUE(dict.GetDictionaryWithoutPathExpansion("this", &value2)); + EXPECT_EQ(value1, value2); + EXPECT_EQ(1U, value2->size()); + + EXPECT_TRUE(dict.HasKey("this.isnt.expanded")); + Value* value3; + EXPECT_FALSE(dict.Get("this.isnt.expanded", &value3)); + Value* value4; + ASSERT_TRUE(dict.GetWithoutPathExpansion("this.isnt.expanded", &value4)); + EXPECT_EQ(Value::TYPE_NULL, value4->GetType()); +} + TEST(ValuesTest, DictionaryRemovePath) { DictionaryValue dict; - dict.Set("a.long.way.down", new FundamentalValue(1)); - dict.Set("a.long.key.path", new FundamentalValue(true)); + dict.SetInteger("a.long.way.down", 1); + dict.SetBoolean("a.long.key.path", true); scoped_ptr<Value> removed_item; EXPECT_TRUE(dict.RemovePath("a.long.way.down", &removed_item)); @@ -359,36 +391,49 @@ TEST(ValuesTest, DictionaryRemovePath) { TEST(ValuesTest, DeepCopy) { DictionaryValue original_dict; - Value* original_null = Value::CreateNullValue(); - original_dict.Set("null", original_null); - FundamentalValue* original_bool = new FundamentalValue(true); - original_dict.Set("bool", original_bool); - FundamentalValue* original_int = new FundamentalValue(42); - original_dict.Set("int", original_int); - FundamentalValue* original_double = new FundamentalValue(3.14); - original_dict.Set("double", original_double); - StringValue* original_string = new StringValue("hello"); - original_dict.Set("string", original_string); - StringValue* original_string16 = new StringValue(ASCIIToUTF16("hello16")); - original_dict.Set("string16", original_string16); + scoped_ptr<Value> scoped_null = Value::CreateNullValue(); + Value* original_null = scoped_null.get(); + original_dict.Set("null", scoped_null.Pass()); + scoped_ptr<FundamentalValue> scoped_bool(new FundamentalValue(true)); + FundamentalValue* original_bool = scoped_bool.get(); + original_dict.Set("bool", scoped_bool.Pass()); + scoped_ptr<FundamentalValue> scoped_int(new FundamentalValue(42)); + FundamentalValue* original_int = scoped_int.get(); + original_dict.Set("int", scoped_int.Pass()); + scoped_ptr<FundamentalValue> scoped_double(new FundamentalValue(3.14)); + FundamentalValue* original_double = scoped_double.get(); + original_dict.Set("double", scoped_double.Pass()); + scoped_ptr<StringValue> scoped_string(new StringValue("hello")); + StringValue* original_string = scoped_string.get(); + original_dict.Set("string", scoped_string.Pass()); + scoped_ptr<StringValue> scoped_string16( + new StringValue(ASCIIToUTF16("hello16"))); + StringValue* original_string16 = scoped_string16.get(); + original_dict.Set("string16", scoped_string16.Pass()); scoped_ptr<char[]> original_buffer(new char[42]); memset(original_buffer.get(), '!', 42); - BinaryValue* original_binary = new BinaryValue(original_buffer.Pass(), 42); - original_dict.Set("binary", original_binary); - - ListValue* original_list = new ListValue(); - FundamentalValue* original_list_element_0 = new FundamentalValue(0); - original_list->Append(original_list_element_0); - FundamentalValue* original_list_element_1 = new FundamentalValue(1); - original_list->Append(original_list_element_1); - original_dict.Set("list", original_list); - - DictionaryValue* original_nested_dictionary = new DictionaryValue(); - original_nested_dictionary->Set("key", new StringValue("value")); - original_dict.Set("dictionary", original_nested_dictionary); - - scoped_ptr<DictionaryValue> copy_dict(original_dict.DeepCopy()); + scoped_ptr<BinaryValue> scoped_binary( + new BinaryValue(original_buffer.Pass(), 42)); + BinaryValue* original_binary = scoped_binary.get(); + original_dict.Set("binary", scoped_binary.Pass()); + + scoped_ptr<ListValue> scoped_list(new ListValue()); + Value* original_list = scoped_list.get(); + scoped_ptr<FundamentalValue> scoped_list_element_0(new FundamentalValue(0)); + Value* original_list_element_0 = scoped_list_element_0.get(); + scoped_list->Append(scoped_list_element_0.Pass()); + scoped_ptr<FundamentalValue> scoped_list_element_1(new FundamentalValue(1)); + Value* original_list_element_1 = scoped_list_element_1.get(); + scoped_list->Append(scoped_list_element_1.Pass()); + original_dict.Set("list", scoped_list.Pass()); + + scoped_ptr<DictionaryValue> scoped_nested_dictionary(new DictionaryValue()); + Value* original_nested_dictionary = scoped_nested_dictionary.get(); + scoped_nested_dictionary->SetString("key", "value"); + original_dict.Set("dictionary", scoped_nested_dictionary.Pass()); + + scoped_ptr<DictionaryValue> copy_dict = original_dict.CreateDeepCopy(); ASSERT_TRUE(copy_dict.get()); ASSERT_NE(copy_dict.get(), &original_dict); @@ -498,16 +543,13 @@ TEST(ValuesTest, DeepCopy) { } TEST(ValuesTest, Equals) { - Value* null1 = Value::CreateNullValue(); - Value* null2 = Value::CreateNullValue(); - EXPECT_NE(null1, null2); - EXPECT_TRUE(null1->Equals(null2)); + scoped_ptr<Value> null1(Value::CreateNullValue()); + scoped_ptr<Value> null2(Value::CreateNullValue()); + EXPECT_NE(null1.get(), null2.get()); + EXPECT_TRUE(null1->Equals(null2.get())); - Value* boolean = new FundamentalValue(false); - EXPECT_FALSE(null1->Equals(boolean)); - delete null1; - delete null2; - delete boolean; + FundamentalValue boolean(false); + EXPECT_FALSE(null1->Equals(&boolean)); DictionaryValue dv; dv.SetBoolean("a", false); @@ -517,24 +559,25 @@ TEST(ValuesTest, Equals) { dv.SetString("d2", ASCIIToUTF16("http://google.com")); dv.Set("e", Value::CreateNullValue()); - scoped_ptr<DictionaryValue> copy; - copy.reset(dv.DeepCopy()); + scoped_ptr<DictionaryValue> copy = dv.CreateDeepCopy(); EXPECT_TRUE(dv.Equals(copy.get())); - ListValue* list = new ListValue; + scoped_ptr<ListValue> list(new ListValue); + ListValue* original_list = list.get(); list->Append(Value::CreateNullValue()); - list->Append(new DictionaryValue); - dv.Set("f", list); + list->Append(make_scoped_ptr(new DictionaryValue)); + scoped_ptr<Value> list_copy(list->CreateDeepCopy()); + dv.Set("f", list.Pass()); EXPECT_FALSE(dv.Equals(copy.get())); - copy->Set("f", list->DeepCopy()); + copy->Set("f", list_copy.Pass()); EXPECT_TRUE(dv.Equals(copy.get())); - list->Append(new FundamentalValue(true)); + original_list->Append(make_scoped_ptr(new FundamentalValue(true))); EXPECT_FALSE(dv.Equals(copy.get())); // Check if Equals detects differences in only the keys. - copy.reset(dv.DeepCopy()); + copy = dv.CreateDeepCopy(); EXPECT_TRUE(dv.Equals(copy.get())); copy->Remove("a", NULL); copy->SetBoolean("aa", false); @@ -566,71 +609,75 @@ TEST(ValuesTest, StaticEquals) { TEST(ValuesTest, DeepCopyCovariantReturnTypes) { DictionaryValue original_dict; - Value* original_null = Value::CreateNullValue(); - original_dict.Set("null", original_null); - FundamentalValue* original_bool = new FundamentalValue(true); - original_dict.Set("bool", original_bool); - FundamentalValue* original_int = new FundamentalValue(42); - original_dict.Set("int", original_int); - FundamentalValue* original_double = new FundamentalValue(3.14); - original_dict.Set("double", original_double); - StringValue* original_string = new StringValue("hello"); - original_dict.Set("string", original_string); - StringValue* original_string16 = new StringValue(ASCIIToUTF16("hello16")); - original_dict.Set("string16", original_string16); + scoped_ptr<Value> scoped_null(Value::CreateNullValue()); + Value* original_null = scoped_null.get(); + original_dict.Set("null", scoped_null.Pass()); + scoped_ptr<FundamentalValue> scoped_bool(new FundamentalValue(true)); + Value* original_bool = scoped_bool.get(); + original_dict.Set("bool", scoped_bool.Pass()); + scoped_ptr<FundamentalValue> scoped_int(new FundamentalValue(42)); + Value* original_int = scoped_int.get(); + original_dict.Set("int", scoped_int.Pass()); + scoped_ptr<FundamentalValue> scoped_double(new FundamentalValue(3.14)); + Value* original_double = scoped_double.get(); + original_dict.Set("double", scoped_double.Pass()); + scoped_ptr<StringValue> scoped_string(new StringValue("hello")); + Value* original_string = scoped_string.get(); + original_dict.Set("string", scoped_string.Pass()); + scoped_ptr<StringValue> scoped_string16( + new StringValue(ASCIIToUTF16("hello16"))); + Value* original_string16 = scoped_string16.get(); + original_dict.Set("string16", scoped_string16.Pass()); scoped_ptr<char[]> original_buffer(new char[42]); memset(original_buffer.get(), '!', 42); - BinaryValue* original_binary = new BinaryValue(original_buffer.Pass(), 42); - original_dict.Set("binary", original_binary); - - ListValue* original_list = new ListValue(); - FundamentalValue* original_list_element_0 = new FundamentalValue(0); - original_list->Append(original_list_element_0); - FundamentalValue* original_list_element_1 = new FundamentalValue(1); - original_list->Append(original_list_element_1); - original_dict.Set("list", original_list); - - Value* original_dict_value = &original_dict; - Value* original_bool_value = original_bool; - Value* original_int_value = original_int; - Value* original_double_value = original_double; - Value* original_string_value = original_string; - Value* original_string16_value = original_string16; - Value* original_binary_value = original_binary; - Value* original_list_value = original_list; - - scoped_ptr<Value> copy_dict_value(original_dict_value->DeepCopy()); - scoped_ptr<Value> copy_bool_value(original_bool_value->DeepCopy()); - scoped_ptr<Value> copy_int_value(original_int_value->DeepCopy()); - scoped_ptr<Value> copy_double_value(original_double_value->DeepCopy()); - scoped_ptr<Value> copy_string_value(original_string_value->DeepCopy()); - scoped_ptr<Value> copy_string16_value(original_string16_value->DeepCopy()); - scoped_ptr<Value> copy_binary_value(original_binary_value->DeepCopy()); - scoped_ptr<Value> copy_list_value(original_list_value->DeepCopy()); - - EXPECT_TRUE(original_dict_value->Equals(copy_dict_value.get())); - EXPECT_TRUE(original_bool_value->Equals(copy_bool_value.get())); - EXPECT_TRUE(original_int_value->Equals(copy_int_value.get())); - EXPECT_TRUE(original_double_value->Equals(copy_double_value.get())); - EXPECT_TRUE(original_string_value->Equals(copy_string_value.get())); - EXPECT_TRUE(original_string16_value->Equals(copy_string16_value.get())); - EXPECT_TRUE(original_binary_value->Equals(copy_binary_value.get())); - EXPECT_TRUE(original_list_value->Equals(copy_list_value.get())); + scoped_ptr<BinaryValue> scoped_binary( + new BinaryValue(original_buffer.Pass(), 42)); + Value* original_binary = scoped_binary.get(); + original_dict.Set("binary", scoped_binary.Pass()); + + scoped_ptr<ListValue> scoped_list(new ListValue()); + Value* original_list = scoped_list.get(); + scoped_ptr<FundamentalValue> scoped_list_element_0(new FundamentalValue(0)); + scoped_list->Append(scoped_list_element_0.Pass()); + scoped_ptr<FundamentalValue> scoped_list_element_1(new FundamentalValue(1)); + scoped_list->Append(scoped_list_element_1.Pass()); + original_dict.Set("list", scoped_list.Pass()); + + scoped_ptr<Value> copy_dict = original_dict.CreateDeepCopy(); + scoped_ptr<Value> copy_null = original_null->CreateDeepCopy(); + scoped_ptr<Value> copy_bool = original_bool->CreateDeepCopy(); + scoped_ptr<Value> copy_int = original_int->CreateDeepCopy(); + scoped_ptr<Value> copy_double = original_double->CreateDeepCopy(); + scoped_ptr<Value> copy_string = original_string->CreateDeepCopy(); + scoped_ptr<Value> copy_string16 = original_string16->CreateDeepCopy(); + scoped_ptr<Value> copy_binary = original_binary->CreateDeepCopy(); + scoped_ptr<Value> copy_list = original_list->CreateDeepCopy(); + + EXPECT_TRUE(original_dict.Equals(copy_dict.get())); + EXPECT_TRUE(original_null->Equals(copy_null.get())); + EXPECT_TRUE(original_bool->Equals(copy_bool.get())); + EXPECT_TRUE(original_int->Equals(copy_int.get())); + EXPECT_TRUE(original_double->Equals(copy_double.get())); + EXPECT_TRUE(original_string->Equals(copy_string.get())); + EXPECT_TRUE(original_string16->Equals(copy_string16.get())); + EXPECT_TRUE(original_binary->Equals(copy_binary.get())); + EXPECT_TRUE(original_list->Equals(copy_list.get())); } TEST(ValuesTest, RemoveEmptyChildren) { scoped_ptr<DictionaryValue> root(new DictionaryValue); // Remove empty lists and dictionaries. - root->Set("empty_dict", new DictionaryValue); - root->Set("empty_list", new ListValue); - root->SetWithoutPathExpansion("a.b.c.d.e", new DictionaryValue); + root->Set("empty_dict", make_scoped_ptr(new DictionaryValue)); + root->Set("empty_list", make_scoped_ptr(new ListValue)); + root->SetWithoutPathExpansion("a.b.c.d.e", + make_scoped_ptr(new DictionaryValue)); root.reset(root->DeepCopyWithoutEmptyChildren()); EXPECT_TRUE(root->empty()); // Make sure we don't prune too much. root->SetBoolean("bool", true); - root->Set("empty_dict", new DictionaryValue); + root->Set("empty_dict", make_scoped_ptr(new DictionaryValue)); root->SetString("empty_string", std::string()); root.reset(root->DeepCopyWithoutEmptyChildren()); EXPECT_EQ(2U, root->size()); @@ -642,55 +689,57 @@ TEST(ValuesTest, RemoveEmptyChildren) { // Nested test cases. These should all reduce back to the bool and string // set above. { - root->Set("a.b.c.d.e", new DictionaryValue); + root->Set("a.b.c.d.e", make_scoped_ptr(new DictionaryValue)); root.reset(root->DeepCopyWithoutEmptyChildren()); EXPECT_EQ(2U, root->size()); } { - DictionaryValue* inner = new DictionaryValue; - root->Set("dict_with_emtpy_children", inner); - inner->Set("empty_dict", new DictionaryValue); - inner->Set("empty_list", new ListValue); + scoped_ptr<DictionaryValue> inner(new DictionaryValue); + inner->Set("empty_dict", make_scoped_ptr(new DictionaryValue)); + inner->Set("empty_list", make_scoped_ptr(new ListValue)); + root->Set("dict_with_empty_children", inner.Pass()); root.reset(root->DeepCopyWithoutEmptyChildren()); EXPECT_EQ(2U, root->size()); } { - ListValue* inner = new ListValue; - root->Set("list_with_empty_children", inner); - inner->Append(new DictionaryValue); - inner->Append(new ListValue); + scoped_ptr<ListValue> inner(new ListValue); + inner->Append(make_scoped_ptr(new DictionaryValue)); + inner->Append(make_scoped_ptr(new ListValue)); + root->Set("list_with_empty_children", inner.Pass()); root.reset(root->DeepCopyWithoutEmptyChildren()); EXPECT_EQ(2U, root->size()); } // Nested with siblings. { - ListValue* inner = new ListValue; - root->Set("list_with_empty_children", inner); - inner->Append(new DictionaryValue); - inner->Append(new ListValue); - DictionaryValue* inner2 = new DictionaryValue; - root->Set("dict_with_empty_children", inner2); - inner2->Set("empty_dict", new DictionaryValue); - inner2->Set("empty_list", new ListValue); + scoped_ptr<ListValue> inner(new ListValue()); + inner->Append(make_scoped_ptr(new DictionaryValue)); + inner->Append(make_scoped_ptr(new ListValue)); + root->Set("list_with_empty_children", inner.Pass()); + scoped_ptr<DictionaryValue> inner2(new DictionaryValue); + inner2->Set("empty_dict", make_scoped_ptr(new DictionaryValue)); + inner2->Set("empty_list", make_scoped_ptr(new ListValue)); + root->Set("dict_with_empty_children", inner2.Pass()); root.reset(root->DeepCopyWithoutEmptyChildren()); EXPECT_EQ(2U, root->size()); } // Make sure nested values don't get pruned. { - ListValue* inner = new ListValue; - root->Set("list_with_empty_children", inner); - ListValue* inner2 = new ListValue; - inner->Append(new DictionaryValue); - inner->Append(inner2); - inner2->Append(new StringValue("hello")); + scoped_ptr<ListValue> inner(new ListValue); + scoped_ptr<ListValue> inner2(new ListValue); + inner2->Append(make_scoped_ptr(new StringValue("hello"))); + inner->Append(make_scoped_ptr(new DictionaryValue)); + inner->Append(inner2.Pass()); + root->Set("list_with_empty_children", inner.Pass()); root.reset(root->DeepCopyWithoutEmptyChildren()); EXPECT_EQ(3U, root->size()); - EXPECT_TRUE(root->GetList("list_with_empty_children", &inner)); - EXPECT_EQ(1U, inner->GetSize()); // Dictionary was pruned. - EXPECT_TRUE(inner->GetList(0, &inner2)); - EXPECT_EQ(1U, inner2->GetSize()); + + ListValue* inner_value, *inner_value2; + EXPECT_TRUE(root->GetList("list_with_empty_children", &inner_value)); + EXPECT_EQ(1U, inner_value->GetSize()); // Dictionary was pruned. + EXPECT_TRUE(inner_value->GetList(0, &inner_value2)); + EXPECT_EQ(1U, inner_value2->GetSize()); } } @@ -698,18 +747,18 @@ TEST(ValuesTest, MergeDictionary) { scoped_ptr<DictionaryValue> base(new DictionaryValue); base->SetString("base_key", "base_key_value_base"); base->SetString("collide_key", "collide_key_value_base"); - DictionaryValue* base_sub_dict = new DictionaryValue; + scoped_ptr<DictionaryValue> base_sub_dict(new DictionaryValue); base_sub_dict->SetString("sub_base_key", "sub_base_key_value_base"); base_sub_dict->SetString("sub_collide_key", "sub_collide_key_value_base"); - base->Set("sub_dict_key", base_sub_dict); + base->Set("sub_dict_key", base_sub_dict.Pass()); scoped_ptr<DictionaryValue> merge(new DictionaryValue); merge->SetString("merge_key", "merge_key_value_merge"); merge->SetString("collide_key", "collide_key_value_merge"); - DictionaryValue* merge_sub_dict = new DictionaryValue; + scoped_ptr<DictionaryValue> merge_sub_dict(new DictionaryValue); merge_sub_dict->SetString("sub_merge_key", "sub_merge_key_value_merge"); merge_sub_dict->SetString("sub_collide_key", "sub_collide_key_value_merge"); - merge->Set("sub_dict_key", merge_sub_dict); + merge->Set("sub_dict_key", merge_sub_dict.Pass()); base->MergeDictionary(merge.get()); @@ -740,7 +789,8 @@ TEST(ValuesTest, MergeDictionary) { } TEST(ValuesTest, MergeDictionaryDeepCopy) { - DictionaryValue* child = new DictionaryValue; + scoped_ptr<DictionaryValue> child(new DictionaryValue); + DictionaryValue* original_child = child.get(); child->SetString("test", "value"); EXPECT_EQ(1U, child->size()); @@ -749,22 +799,22 @@ TEST(ValuesTest, MergeDictionaryDeepCopy) { EXPECT_EQ("value", value); scoped_ptr<DictionaryValue> base(new DictionaryValue); - base->Set("dict", child); + base->Set("dict", child.Pass()); EXPECT_EQ(1U, base->size()); DictionaryValue* ptr; EXPECT_TRUE(base->GetDictionary("dict", &ptr)); - EXPECT_EQ(child, ptr); + EXPECT_EQ(original_child, ptr); scoped_ptr<DictionaryValue> merged(new DictionaryValue); merged->MergeDictionary(base.get()); EXPECT_EQ(1U, merged->size()); EXPECT_TRUE(merged->GetDictionary("dict", &ptr)); - EXPECT_NE(child, ptr); + EXPECT_NE(original_child, ptr); EXPECT_TRUE(ptr->GetString("test", &value)); EXPECT_EQ("value", value); - child->SetString("test", "overwrite"); + original_child->SetString("test", "overwrite"); base.reset(); EXPECT_TRUE(ptr->GetString("test", &value)); EXPECT_EQ("value", value); @@ -777,7 +827,7 @@ TEST(ValuesTest, DictionaryIterator) { } StringValue value1("value1"); - dict.Set("key1", value1.DeepCopy()); + dict.Set("key1", value1.CreateDeepCopy()); bool seen1 = false; for (DictionaryValue::Iterator it(dict); !it.IsAtEnd(); it.Advance()) { EXPECT_FALSE(seen1); @@ -788,7 +838,7 @@ TEST(ValuesTest, DictionaryIterator) { EXPECT_TRUE(seen1); StringValue value2("value2"); - dict.Set("key2", value2.DeepCopy()); + dict.Set("key2", value2.CreateDeepCopy()); bool seen2 = seen1 = false; for (DictionaryValue::Iterator it(dict); !it.IsAtEnd(); it.Advance()) { if (it.key() == "key1") { @@ -821,21 +871,21 @@ TEST(ValuesTest, GetWithNullOutValue) { DictionaryValue dict_value; ListValue list_value; - main_dict.Set("bool", bool_value.DeepCopy()); - main_dict.Set("int", int_value.DeepCopy()); - main_dict.Set("double", double_value.DeepCopy()); - main_dict.Set("string", string_value.DeepCopy()); - main_dict.Set("binary", binary_value.DeepCopy()); - main_dict.Set("dict", dict_value.DeepCopy()); - main_dict.Set("list", list_value.DeepCopy()); - - main_list.Append(bool_value.DeepCopy()); - main_list.Append(int_value.DeepCopy()); - main_list.Append(double_value.DeepCopy()); - main_list.Append(string_value.DeepCopy()); - main_list.Append(binary_value.DeepCopy()); - main_list.Append(dict_value.DeepCopy()); - main_list.Append(list_value.DeepCopy()); + main_dict.Set("bool", bool_value.CreateDeepCopy()); + main_dict.Set("int", int_value.CreateDeepCopy()); + main_dict.Set("double", double_value.CreateDeepCopy()); + main_dict.Set("string", string_value.CreateDeepCopy()); + main_dict.Set("binary", binary_value.CreateDeepCopy()); + main_dict.Set("dict", dict_value.CreateDeepCopy()); + main_dict.Set("list", list_value.CreateDeepCopy()); + + main_list.Append(bool_value.CreateDeepCopy()); + main_list.Append(int_value.CreateDeepCopy()); + main_list.Append(double_value.CreateDeepCopy()); + main_list.Append(string_value.CreateDeepCopy()); + main_list.Append(binary_value.CreateDeepCopy()); + main_list.Append(dict_value.CreateDeepCopy()); + main_list.Append(list_value.CreateDeepCopy()); EXPECT_TRUE(main_dict.Get("bool", NULL)); EXPECT_TRUE(main_dict.Get("int", NULL)); diff --git a/chromium/base/version.cc b/chromium/base/version.cc index 6318b350edc..ede8a4586eb 100644 --- a/chromium/base/version.cc +++ b/chromium/base/version.cc @@ -23,7 +23,7 @@ namespace { // is the resulting integer vector. Function returns true if all numbers were // parsed successfully, false otherwise. bool ParseVersionNumbers(const std::string& version_str, - std::vector<uint16>* parsed) { + std::vector<uint32_t>* parsed) { std::vector<std::string> numbers; SplitString(version_str, '.', &numbers); if (numbers.empty()) @@ -31,22 +31,20 @@ bool ParseVersionNumbers(const std::string& version_str, for (std::vector<std::string>::const_iterator it = numbers.begin(); it != numbers.end(); ++it) { - int num; - if (!StringToInt(*it, &num)) + if (StartsWithASCII(*it, "+", false)) return false; - - if (num < 0) - return false; - - const uint16 max = 0xFFFF; - if (num > max) + unsigned int num; + if (!StringToUint(*it, &num)) return false; - // This throws out things like +3, or 032. - if (IntToString(num) != *it) + // This throws out leading zeros for the first item only. + if (it == numbers.begin() && UintToString(num) != *it) return false; - parsed->push_back(static_cast<uint16>(num)); + // StringToUint returns unsigned int but Version fields are uint32_t. + static_assert(sizeof (uint32_t) == sizeof (unsigned int), + "uint32_t must be same as unsigned int"); + parsed->push_back(num); } return true; } @@ -54,8 +52,8 @@ bool ParseVersionNumbers(const std::string& version_str, // Compares version components in |components1| with components in // |components2|. Returns -1, 0 or 1 if |components1| is less than, equal to, // or greater than |components2|, respectively. -int CompareVersionComponents(const std::vector<uint16>& components1, - const std::vector<uint16>& components2) { +int CompareVersionComponents(const std::vector<uint32_t>& components1, + const std::vector<uint32_t>& components2) { const size_t count = std::min(components1.size(), components2.size()); for (size_t i = 0; i < count; ++i) { if (components1[i] > components2[i]) @@ -86,7 +84,7 @@ Version::~Version() { } Version::Version(const std::string& version_str) { - std::vector<uint16> parsed; + std::vector<uint32_t> parsed; if (!ParseVersionNumbers(version_str, &parsed)) return; @@ -125,7 +123,7 @@ int Version::CompareToWildcardString(const std::string& wildcard_string) const { return CompareTo(version); } - std::vector<uint16> parsed; + std::vector<uint32_t> parsed; const bool success = ParseVersionNumbers( wildcard_string.substr(0, wildcard_string.length() - 2), &parsed); DCHECK(success); diff --git a/chromium/base/version.h b/chromium/base/version.h index b3012eb921b..814acaa2b4e 100644 --- a/chromium/base/version.h +++ b/chromium/base/version.h @@ -5,6 +5,7 @@ #ifndef BASE_VERSION_H_ #define BASE_VERSION_H_ +#include <stdint.h> #include <string> #include <vector> @@ -57,10 +58,10 @@ class BASE_EXPORT Version { // Return the string representation of this version. const std::string GetString() const; - const std::vector<uint16>& components() const { return components_; } + const std::vector<uint32_t>& components() const { return components_; } private: - std::vector<uint16> components_; + std::vector<uint32_t> components_; }; } // namespace base diff --git a/chromium/base/version_unittest.cc b/chromium/base/version_unittest.cc index 3119c3972a7..f40ed27d881 100644 --- a/chromium/base/version_unittest.cc +++ b/chromium/base/version_unittest.cc @@ -31,33 +31,44 @@ TEST(VersionTest, GetVersionFromString) { static const struct version_string { const char* input; size_t parts; + uint32_t firstpart; bool success; } cases[] = { - {"", 0, false}, - {" ", 0, false}, - {"\t", 0, false}, - {"\n", 0, false}, - {" ", 0, false}, - {".", 0, false}, - {" . ", 0, false}, - {"0", 1, true}, - {"0.0", 2, true}, - {"65537.0", 0, false}, - {"-1.0", 0, false}, - {"1.-1.0", 0, false}, - {"+1.0", 0, false}, - {"1.+1.0", 0, false}, - {"1.0a", 0, false}, - {"1.2.3.4.5.6.7.8.9.0", 10, true}, - {"02.1", 0, false}, - {"f.1", 0, false}, + {"", 0, 0, false}, + {" ", 0, 0, false}, + {"\t", 0, 0, false}, + {"\n", 0, 0, false}, + {" ", 0, 0, false}, + {".", 0, 0, false}, + {" . ", 0, 0, false}, + {"0", 1, 0, true}, + {"0.", 0, 0, false}, + {"0.0", 2, 0, true}, + {"4294967295.0", 2, 4294967295, true}, + {"4294967296.0", 0, 0, false}, + {"-1.0", 0, 0, false}, + {"1.-1.0", 0, 0, false}, + {"1,--1.0", 0, 0, false}, + {"+1.0", 0, 0, false}, + {"1.+1.0", 0, 0, false}, + {"1+1.0", 0, 0, false}, + {"++1.0", 0, 0, false}, + {"1.0a", 0, 0, false}, + {"1.2.3.4.5.6.7.8.9.0", 10, 1, true}, + {"02.1", 0, 0, false}, + {"0.01", 2, 0, true}, + {"f.1", 0, 0, false}, + {"15.007.20011", 3, 15, true}, + {"15.5.28.130162", 4, 15, true}, }; for (size_t i = 0; i < arraysize(cases); ++i) { Version version(cases[i].input); EXPECT_EQ(cases[i].success, version.IsValid()); - if (cases[i].success) + if (cases[i].success) { EXPECT_EQ(cases[i].parts, version.components().size()); + EXPECT_EQ(cases[i].firstpart, version.components()[0]); + } } } @@ -77,6 +88,8 @@ TEST(VersionTest, Compare) { {"1.1", "1.0.1", 1}, {"1.0.0", "1.0", 0}, {"1.0.3", "1.0.20", -1}, + {"11.0.10", "15.007.20011", -1}, + {"11.0.10", "15.5.28.130162", -1}, }; for (size_t i = 0; i < arraysize(cases); ++i) { Version lhs(cases[i].lhs); diff --git a/chromium/base/vlog.cc b/chromium/base/vlog.cc index 434a70e0917..519ceff10c0 100644 --- a/chromium/base/vlog.cc +++ b/chromium/base/vlog.cc @@ -50,7 +50,6 @@ VlogInfo::VlogInfo(const std::string& v_switch, : min_log_level_(min_log_level) { DCHECK(min_log_level != NULL); - typedef std::pair<std::string, std::string> KVPair; int vlog_level = 0; if (!v_switch.empty()) { if (base::StringToInt(v_switch, &vlog_level)) { @@ -60,13 +59,13 @@ VlogInfo::VlogInfo(const std::string& v_switch, } } - std::vector<KVPair> kv_pairs; + base::StringPairs kv_pairs; if (!base::SplitStringIntoKeyValuePairs( vmodule_switch, '=', ',', &kv_pairs)) { DLOG(WARNING) << "Could not fully parse vmodule switch \"" << vmodule_switch << "\""; } - for (std::vector<KVPair>::const_iterator it = kv_pairs.begin(); + for (base::StringPairs::const_iterator it = kv_pairs.begin(); it != kv_pairs.end(); ++it) { VmodulePattern pattern(it->first); if (!base::StringToInt(it->second, &pattern.vlog_level)) { @@ -178,4 +177,4 @@ bool MatchVlogPattern(const base::StringPiece& string, return false; } -} // namespace +} // namespace logging diff --git a/chromium/base/win/enum_variant.h b/chromium/base/win/enum_variant.h index d978eaa7dda..2caaccded9f 100644 --- a/chromium/base/win/enum_variant.h +++ b/chromium/base/win/enum_variant.h @@ -39,7 +39,7 @@ class BASE_EXPORT EnumVariant STDMETHODIMP Clone(IEnumVARIANT** out_cloned_object) override; private: - ~EnumVariant(); + ~EnumVariant() override; scoped_ptr<VARIANT[]> items_; unsigned long count_; diff --git a/chromium/base/win/event_trace_consumer.h b/chromium/base/win/event_trace_consumer.h index 9322e1e9b4f..fd44894993f 100644 --- a/chromium/base/win/event_trace_consumer.h +++ b/chromium/base/win/event_trace_consumer.h @@ -119,7 +119,7 @@ HRESULT EtwTraceConsumerBase<ImplClass>::OpenFileSession( template <class ImplClass> inline HRESULT EtwTraceConsumerBase<ImplClass>::Consume() { ULONG err = ::ProcessTrace(&trace_handles_[0], - trace_handles_.size(), + static_cast<ULONG>(trace_handles_.size()), NULL, NULL); return HRESULT_FROM_WIN32(err); diff --git a/chromium/base/win/event_trace_consumer_unittest.cc b/chromium/base/win/event_trace_consumer_unittest.cc index 3043152af0c..ecbf238bea4 100644 --- a/chromium/base/win/event_trace_consumer_unittest.cc +++ b/chromium/base/win/event_trace_consumer_unittest.cc @@ -82,7 +82,7 @@ class EtwTraceConsumerBaseTest: public testing::Test { : session_name_(StringPrintf(L"TestSession-%d", GetCurrentProcId())) { } - virtual void SetUp() { + void SetUp() override { // Cleanup any potentially dangling sessions. EtwTraceProperties ignore; EtwTraceController::Stop(session_name_.c_str(), &ignore); @@ -91,7 +91,7 @@ class EtwTraceConsumerBaseTest: public testing::Test { ASSERT_HRESULT_SUCCEEDED(::CoCreateGuid(&test_provider_)); } - virtual void TearDown() { + void TearDown() override { // Cleanup any potentially dangling sessions. EtwTraceProperties ignore; EtwTraceController::Stop(session_name_.c_str(), &ignore); @@ -125,13 +125,13 @@ namespace { class EtwTraceConsumerRealtimeTest: public EtwTraceConsumerBaseTest { public: - virtual void SetUp() { + void SetUp() override { EtwTraceConsumerBaseTest::SetUp(); ASSERT_HRESULT_SUCCEEDED( consumer_.OpenRealtimeSession(session_name_.c_str())); } - virtual void TearDown() { + void TearDown() override { consumer_.Close(); EtwTraceConsumerBaseTest::TearDown(); } @@ -261,7 +261,7 @@ class EtwTraceConsumerDataTest: public EtwTraceConsumerBaseTest { EtwTraceConsumerDataTest() { } - virtual void SetUp() { + void SetUp() override { EtwTraceConsumerBaseTest::SetUp(); EtwTraceProperties prop; @@ -273,7 +273,7 @@ class EtwTraceConsumerDataTest: public EtwTraceConsumerBaseTest { temp_file_ = temp_dir_.path().Append(L"test.etl"); } - virtual void TearDown() { + void TearDown() override { EXPECT_TRUE(base::DeleteFile(temp_file_, false)); EtwTraceConsumerBaseTest::TearDown(); diff --git a/chromium/base/win/event_trace_controller_unittest.cc b/chromium/base/win/event_trace_controller_unittest.cc index be11128d009..a2cd81cf709 100644 --- a/chromium/base/win/event_trace_controller_unittest.cc +++ b/chromium/base/win/event_trace_controller_unittest.cc @@ -42,12 +42,8 @@ class TestingProvider: public EtwTraceProvider { } private: - virtual void OnEventsEnabled() { - ::SetEvent(callback_event_.Get()); - } - virtual void PostEventsDisabled() { - ::SetEvent(callback_event_.Get()); - } + void OnEventsEnabled() override { ::SetEvent(callback_event_.Get()); } + void PostEventsDisabled() override { ::SetEvent(callback_event_.Get()); } ScopedHandle callback_event_; @@ -113,7 +109,7 @@ class EtwTraceControllerTest : public testing::Test { : session_name_(StringPrintf(L"TestSession-%d", GetCurrentProcId())) { } - virtual void SetUp() { + void SetUp() override { EtwTraceProperties ignore; EtwTraceController::Stop(session_name_.c_str(), &ignore); @@ -121,7 +117,7 @@ class EtwTraceControllerTest : public testing::Test { ASSERT_HRESULT_SUCCEEDED(::CoCreateGuid(&test_provider_)); } - virtual void TearDown() { + void TearDown() override { EtwTraceProperties prop; EtwTraceController::Stop(session_name_.c_str(), &prop); } diff --git a/chromium/base/win/iunknown_impl.h b/chromium/base/win/iunknown_impl.h index 4283d020c43..b7de205ac6c 100644 --- a/chromium/base/win/iunknown_impl.h +++ b/chromium/base/win/iunknown_impl.h @@ -19,11 +19,11 @@ class BASE_EXPORT IUnknownImpl : public IUnknown { public: IUnknownImpl(); - virtual ULONG STDMETHODCALLTYPE AddRef() override; - virtual ULONG STDMETHODCALLTYPE Release() override; + ULONG STDMETHODCALLTYPE AddRef() override; + ULONG STDMETHODCALLTYPE Release() override; // Subclasses should extend this to return any interfaces they provide. - virtual STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override; + STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override; protected: virtual ~IUnknownImpl(); diff --git a/chromium/base/win/iunknown_impl_unittest.cc b/chromium/base/win/iunknown_impl_unittest.cc index db86214daf5..874a43a39a9 100644 --- a/chromium/base/win/iunknown_impl_unittest.cc +++ b/chromium/base/win/iunknown_impl_unittest.cc @@ -15,9 +15,7 @@ class TestIUnknownImplSubclass : public IUnknownImpl { TestIUnknownImplSubclass() { ++instance_count; } - virtual ~TestIUnknownImplSubclass() { - --instance_count; - } + ~TestIUnknownImplSubclass() override { --instance_count; } static int instance_count; }; diff --git a/chromium/base/win/memory_pressure_monitor.cc b/chromium/base/win/memory_pressure_monitor.cc new file mode 100644 index 00000000000..ed49a40a816 --- /dev/null +++ b/chromium/base/win/memory_pressure_monitor.cc @@ -0,0 +1,254 @@ +// 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/win/memory_pressure_monitor.h" + +#include <windows.h> + +#include "base/metrics/histogram_macros.h" +#include "base/single_thread_task_runner.h" +#include "base/thread_task_runner_handle.h" +#include "base/time/time.h" + +namespace base { +namespace win { + +namespace { + +static const DWORDLONG kMBBytes = 1024 * 1024; + +// Enumeration of UMA memory pressure levels. This needs to be kept in sync with +// histograms.xml and the memory pressure levels defined in +// MemoryPressureListener. +enum MemoryPressureLevelUMA { + UMA_MEMORY_PRESSURE_LEVEL_NONE = 0, + UMA_MEMORY_PRESSURE_LEVEL_MODERATE = 1, + UMA_MEMORY_PRESSURE_LEVEL_CRITICAL = 2, + // This must be the last value in the enum. + UMA_MEMORY_PRESSURE_LEVEL_COUNT, +}; + +// Converts a memory pressure level to an UMA enumeration value. +MemoryPressureLevelUMA MemoryPressureLevelToUmaEnumValue( + MemoryPressureListener::MemoryPressureLevel level) { + switch (level) { + case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE: + return UMA_MEMORY_PRESSURE_LEVEL_NONE; + case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE: + return UMA_MEMORY_PRESSURE_LEVEL_MODERATE; + case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL: + return UMA_MEMORY_PRESSURE_LEVEL_CRITICAL; + } + NOTREACHED(); + return UMA_MEMORY_PRESSURE_LEVEL_NONE; +} + +} // namespace + +// The following constants have been lifted from similar values in the ChromeOS +// memory pressure monitor. The values were determined experimentally to ensure +// sufficient responsiveness of the memory pressure subsystem, and minimal +// overhead. +const int MemoryPressureMonitor::kPollingIntervalMs = 5000; +const int MemoryPressureMonitor::kModeratePressureCooldownMs = 10000; +const int MemoryPressureMonitor::kModeratePressureCooldownCycles = + kModeratePressureCooldownMs / kPollingIntervalMs; + +// TODO(chrisha): Explore the following constants further with an experiment. + +// A system is considered 'high memory' if it has more than 1.5GB of system +// memory available for use by the memory manager (not reserved for hardware +// and drivers). This is a fuzzy version of the ~2GB discussed below. +const int MemoryPressureMonitor::kLargeMemoryThresholdMb = 1536; + +// These are the default thresholds used for systems with < ~2GB of physical +// memory. Such systems have been observed to always maintain ~100MB of +// available memory, paging until that is the case. To try to avoid paging a +// threshold slightly above this is chosen. The moderate threshold is slightly +// less grounded in reality and chosen as 2.5x critical. +const int MemoryPressureMonitor::kSmallMemoryDefaultModerateThresholdMb = 500; +const int MemoryPressureMonitor::kSmallMemoryDefaultCriticalThresholdMb = 200; + +// These are the default thresholds used for systems with >= ~2GB of physical +// memory. Such systems have been observed to always maintain ~300MB of +// available memory, paging until that is the case. +const int MemoryPressureMonitor::kLargeMemoryDefaultModerateThresholdMb = 1000; +const int MemoryPressureMonitor::kLargeMemoryDefaultCriticalThresholdMb = 400; + +MemoryPressureMonitor::MemoryPressureMonitor() + : moderate_threshold_mb_(0), + critical_threshold_mb_(0), + current_memory_pressure_level_( + MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE), + moderate_pressure_repeat_count_(0), + weak_ptr_factory_(this) { + InferThresholds(); + StartObserving(); +} + +MemoryPressureMonitor::MemoryPressureMonitor(int moderate_threshold_mb, + int critical_threshold_mb) + : moderate_threshold_mb_(moderate_threshold_mb), + critical_threshold_mb_(critical_threshold_mb), + current_memory_pressure_level_( + MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE), + moderate_pressure_repeat_count_(0), + weak_ptr_factory_(this) { + DCHECK_GE(moderate_threshold_mb_, critical_threshold_mb_); + DCHECK_LE(0, critical_threshold_mb_); + StartObserving(); +} + +MemoryPressureMonitor::~MemoryPressureMonitor() { + StopObserving(); +} + +void MemoryPressureMonitor::CheckMemoryPressureSoon() { + DCHECK(thread_checker_.CalledOnValidThread()); + + ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, Bind(&MemoryPressureMonitor::CheckMemoryPressure, + weak_ptr_factory_.GetWeakPtr())); +} + +MemoryPressureListener::MemoryPressureLevel +MemoryPressureMonitor::GetCurrentPressureLevel() const { + return current_memory_pressure_level_; +} + +void MemoryPressureMonitor::InferThresholds() { + // Default to a 'high' memory situation, which uses more conservative + // thresholds. + bool high_memory = true; + MEMORYSTATUSEX mem_status = {}; + if (GetSystemMemoryStatus(&mem_status)) { + static const DWORDLONG kLargeMemoryThresholdBytes = + static_cast<DWORDLONG>(kLargeMemoryThresholdMb) * kMBBytes; + high_memory = mem_status.ullTotalPhys >= kLargeMemoryThresholdBytes; + } + + if (high_memory) { + moderate_threshold_mb_ = kLargeMemoryDefaultModerateThresholdMb; + critical_threshold_mb_ = kLargeMemoryDefaultCriticalThresholdMb; + } else { + moderate_threshold_mb_ = kSmallMemoryDefaultModerateThresholdMb; + critical_threshold_mb_ = kSmallMemoryDefaultCriticalThresholdMb; + } +} + +void MemoryPressureMonitor::StartObserving() { + DCHECK(thread_checker_.CalledOnValidThread()); + + timer_.Start(FROM_HERE, + TimeDelta::FromMilliseconds(kPollingIntervalMs), + Bind(&MemoryPressureMonitor:: + CheckMemoryPressureAndRecordStatistics, + weak_ptr_factory_.GetWeakPtr())); +} + +void MemoryPressureMonitor::StopObserving() { + DCHECK(thread_checker_.CalledOnValidThread()); + + // If StartObserving failed, StopObserving will still get called. + timer_.Stop(); + weak_ptr_factory_.InvalidateWeakPtrs(); +} + +void MemoryPressureMonitor::CheckMemoryPressure() { + DCHECK(thread_checker_.CalledOnValidThread()); + + // Get the previous pressure level and update the current one. + MemoryPressureLevel old_pressure = current_memory_pressure_level_; + current_memory_pressure_level_ = CalculateCurrentPressureLevel(); + + // |notify| will be set to true if MemoryPressureListeners need to be + // notified of a memory pressure level state change. + bool notify = false; + switch (current_memory_pressure_level_) { + case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE: + break; + + case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE: + if (old_pressure != current_memory_pressure_level_) { + // This is a new transition to moderate pressure so notify. + moderate_pressure_repeat_count_ = 0; + notify = true; + } else { + // Already in moderate pressure, only notify if sustained over the + // cooldown period. + if (++moderate_pressure_repeat_count_ == + kModeratePressureCooldownCycles) { + moderate_pressure_repeat_count_ = 0; + notify = true; + } + } + break; + + case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL: + // Always notify of critical pressure levels. + notify = true; + break; + } + + if (!notify) + return; + + // Emit a notification of the current memory pressure level. This can only + // happen for moderate and critical pressure levels. + DCHECK_NE(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, + current_memory_pressure_level_); + MemoryPressureListener::NotifyMemoryPressure(current_memory_pressure_level_); +} + +void MemoryPressureMonitor::CheckMemoryPressureAndRecordStatistics() { + DCHECK(thread_checker_.CalledOnValidThread()); + + CheckMemoryPressure(); + + UMA_HISTOGRAM_ENUMERATION( + "Memory.PressureLevel", + MemoryPressureLevelToUmaEnumValue(current_memory_pressure_level_), + UMA_MEMORY_PRESSURE_LEVEL_COUNT); +} + +MemoryPressureListener::MemoryPressureLevel +MemoryPressureMonitor::CalculateCurrentPressureLevel() { + MEMORYSTATUSEX mem_status = {}; + if (!GetSystemMemoryStatus(&mem_status)) + return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE; + + // How much system memory is actively available for use right now, in MBs. + int phys_free = static_cast<int>(mem_status.ullAvailPhys / kMBBytes); + + // TODO(chrisha): This should eventually care about address space pressure, + // but the browser process (where this is running) effectively never runs out + // of address space. Renderers occasionally do, but it does them no good to + // have the browser process monitor address space pressure. Long term, + // renderers should run their own address space pressure monitors and act + // accordingly, with the browser making cross-process decisions based on + // system memory pressure. + + // Determine if the physical memory is under critical memory pressure. + if (phys_free <= critical_threshold_mb_) + return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL; + + // Determine if the physical memory is under moderate memory pressure. + if (phys_free <= moderate_threshold_mb_) + return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE; + + // No memory pressure was detected. + return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE; +} + +bool MemoryPressureMonitor::GetSystemMemoryStatus( + MEMORYSTATUSEX* mem_status) { + DCHECK(mem_status != nullptr); + mem_status->dwLength = sizeof(*mem_status); + if (!::GlobalMemoryStatusEx(mem_status)) + return false; + return true; +} + +} // namespace win +} // namespace base diff --git a/chromium/base/win/memory_pressure_monitor.h b/chromium/base/win/memory_pressure_monitor.h new file mode 100644 index 00000000000..933d91290f1 --- /dev/null +++ b/chromium/base/win/memory_pressure_monitor.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_WIN_MEMORY_PRESSURE_MONITOR_H_ +#define BASE_WIN_MEMORY_PRESSURE_MONITOR_H_ + +#include "base/base_export.h" +#include "base/memory/memory_pressure_listener.h" +#include "base/memory/memory_pressure_monitor.h" +#include "base/memory/weak_ptr.h" +#include "base/threading/thread_checker.h" +#include "base/timer/timer.h" + +// To not pull in windows.h. +typedef struct _MEMORYSTATUSEX MEMORYSTATUSEX; + +namespace base { +namespace win { + +// Windows memory pressure monitor. Because there is no OS provided signal this +// polls at a low frequency (once per second), and applies internal hysteresis. +class BASE_EXPORT MemoryPressureMonitor : public base::MemoryPressureMonitor { + public: + // Constants governing the polling and hysteresis behaviour of the observer. + + // The polling interval, in milliseconds. While under critical pressure, this + // is also the timer to repeat cleanup attempts. + static const int kPollingIntervalMs; + // The time which should pass between 2 successive moderate memory pressure + // signals, in milliseconds. + static const int kModeratePressureCooldownMs; + // The number of cycles that should pass between 2 successive moderate memory + // pressure signals. + static const int kModeratePressureCooldownCycles; + + // Constants governing the memory pressure level detection. + + // The amount of total system memory beyond which a system is considered to be + // a large-memory system. + static const int kLargeMemoryThresholdMb; + // Default minimum free memory thresholds for small-memory systems, in MB. + static const int kSmallMemoryDefaultModerateThresholdMb; + static const int kSmallMemoryDefaultCriticalThresholdMb; + // Default minimum free memory thresholds for large-memory systems, in MB. + static const int kLargeMemoryDefaultModerateThresholdMb; + static const int kLargeMemoryDefaultCriticalThresholdMb; + + // Default constructor. Will choose thresholds automatically basd on the + // actual amount of system memory. + MemoryPressureMonitor(); + + // Constructor with explicit memory thresholds. These represent the amount of + // free memory below which the applicable memory pressure state engages. + MemoryPressureMonitor(int moderate_threshold_mb, int critical_threshold_mb); + + ~MemoryPressureMonitor() override; + + // Schedules a memory pressure check to run soon. This must be called on the + // same thread where the monitor was instantiated. + void CheckMemoryPressureSoon(); + + // Get the current memory pressure level. This can be called from any thread. + MemoryPressureLevel GetCurrentPressureLevel() const override; + + // Returns the moderate pressure level free memory threshold, in MB. + int moderate_threshold_mb() const { return moderate_threshold_mb_; } + + // Returns the critical pressure level free memory threshold, in MB. + int critical_threshold_mb() const { return critical_threshold_mb_; } + + protected: + // Internals are exposed for unittests. + + // Automatically infers threshold values based on system memory. This invokes + // GetMemoryStatus so it can be mocked in unittests. + void InferThresholds(); + + // Starts observing the memory fill level. Calls to StartObserving should + // always be matched with calls to StopObserving. + void StartObserving(); + + // Stop observing the memory fill level. May be safely called if + // StartObserving has not been called. Must be called from the same thread on + // which the monitor was instantiated. + void StopObserving(); + + // Checks memory pressure, storing the current level, applying any hysteresis + // and emitting memory pressure level change signals as necessary. This + // function is called periodically while the monitor is observing memory + // pressure. This is split out from CheckMemoryPressureAndRecordStatistics so + // that it may be called by CheckMemoryPressureSoon and not invoke UMA + // logging. Must be called from the same thread on which the monitor was + // instantiated. + void CheckMemoryPressure(); + + // Wrapper to CheckMemoryPressure that also records the observed memory + // pressure level via an UMA enumeration. This is the function that is called + // periodically by the timer. Must be called from the same thread on which the + // monitor was instantiated. + void CheckMemoryPressureAndRecordStatistics(); + + // Calculates the current instantaneous memory pressure level. This does not + // use any hysteresis and simply returns the result at the current moment. Can + // be called on any thread. + MemoryPressureLevel CalculateCurrentPressureLevel(); + + // Gets system memory status. This is virtual as a unittesting hook. Returns + // true if the system call succeeds, false otherwise. Can be called on any + // thread. + virtual bool GetSystemMemoryStatus(MEMORYSTATUSEX* mem_status); + + private: + // Threshold amounts of available memory that trigger pressure levels. See + // memory_pressure_monitor.cc for a discussion of reasonable values for these. + int moderate_threshold_mb_; + int critical_threshold_mb_; + + // A periodic timer to check for memory pressure changes. + base::RepeatingTimer<MemoryPressureMonitor> timer_; + + // The current memory pressure. + MemoryPressureLevel current_memory_pressure_level_; + + // To slow down the amount of moderate pressure event calls, this gets used to + // count the number of events since the last event occured. This is used by + // |CheckMemoryPressure| to apply hysteresis on the raw results of + // |CalculateCurrentPressureLevel|. + int moderate_pressure_repeat_count_; + + // Ensures that this object is used from a single thread. + base::ThreadChecker thread_checker_; + + // Weak pointer factory to ourself used for scheduling calls to + // CheckMemoryPressure/CheckMemoryPressureAndRecordStatistics via |timer_|. + base::WeakPtrFactory<MemoryPressureMonitor> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(MemoryPressureMonitor); +}; + +} // namespace win +} // namespace base + +#endif // BASE_WIN_MEMORY_PRESSURE_MONITOR_H_ diff --git a/chromium/base/win/memory_pressure_monitor_unittest.cc b/chromium/base/win/memory_pressure_monitor_unittest.cc new file mode 100644 index 00000000000..40a25a79dd8 --- /dev/null +++ b/chromium/base/win/memory_pressure_monitor_unittest.cc @@ -0,0 +1,298 @@ +// 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/win/memory_pressure_monitor.h" + +#include "base/basictypes.h" +#include "base/memory/memory_pressure_listener.h" +#include "base/message_loop/message_loop.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { +namespace win { + +namespace { + +struct PressureSettings { + int phys_left_mb; + MemoryPressureListener::MemoryPressureLevel level; +}; + +} // namespace + +// This is outside of the anonymous namespace so that it can be seen as a friend +// to the monitor class. +class TestMemoryPressureMonitor : public MemoryPressureMonitor { + public: + using MemoryPressureMonitor::CalculateCurrentPressureLevel; + using MemoryPressureMonitor::CheckMemoryPressure; + + static const DWORDLONG kMBBytes = 1024 * 1024; + + explicit TestMemoryPressureMonitor(bool large_memory) + : mem_status_() { + // Generate a plausible amount of memory. + mem_status_.ullTotalPhys = + static_cast<DWORDLONG>(GenerateTotalMemoryMb(large_memory)) * kMBBytes; + + // Rerun InferThresholds using the test fixture's GetSystemMemoryStatus. + InferThresholds(); + // Stop the timer. + StopObserving(); + } + + TestMemoryPressureMonitor(int system_memory_mb, + int moderate_threshold_mb, + int critical_threshold_mb) + : MemoryPressureMonitor(moderate_threshold_mb, critical_threshold_mb), + mem_status_() { + // Set the amount of system memory. + mem_status_.ullTotalPhys = static_cast<DWORDLONG>( + system_memory_mb * kMBBytes); + + // Stop the timer. + StopObserving(); + } + + virtual ~TestMemoryPressureMonitor() {} + + MOCK_METHOD1(OnMemoryPressure, + void(MemoryPressureListener::MemoryPressureLevel level)); + + // Generates an amount of total memory that is consistent with the requested + // memory model. + int GenerateTotalMemoryMb(bool large_memory) { + int total_mb = 64; + while (total_mb < MemoryPressureMonitor::kLargeMemoryThresholdMb) + total_mb *= 2; + if (large_memory) + return total_mb * 2; + return total_mb / 2; + } + + // Sets up the memory status to reflect the provided absolute memory left. + void SetMemoryFree(int phys_left_mb) { + // ullTotalPhys is set in the constructor and not modified. + + // Set the amount of available memory. + mem_status_.ullAvailPhys = + static_cast<DWORDLONG>(phys_left_mb) * kMBBytes; + DCHECK_LT(mem_status_.ullAvailPhys, mem_status_.ullTotalPhys); + + // These fields are unused. + mem_status_.dwMemoryLoad = 0; + mem_status_.ullTotalPageFile = 0; + mem_status_.ullAvailPageFile = 0; + mem_status_.ullTotalVirtual = 0; + mem_status_.ullAvailVirtual = 0; + } + + void SetNone() { + SetMemoryFree(moderate_threshold_mb() + 1); + } + + void SetModerate() { + SetMemoryFree(moderate_threshold_mb() - 1); + } + + void SetCritical() { + SetMemoryFree(critical_threshold_mb() - 1); + } + + private: + bool GetSystemMemoryStatus(MEMORYSTATUSEX* mem_status) override { + // Simply copy the memory status set by the test fixture. + *mem_status = mem_status_; + return true; + } + + MEMORYSTATUSEX mem_status_; + + DISALLOW_COPY_AND_ASSIGN(TestMemoryPressureMonitor); +}; + +class WinMemoryPressureMonitorTest : public testing::Test { + protected: + void CalculateCurrentMemoryPressureLevelTest( + TestMemoryPressureMonitor* monitor) { + + int mod = monitor->moderate_threshold_mb(); + monitor->SetMemoryFree(mod + 1); + EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, + monitor->CalculateCurrentPressureLevel()); + + monitor->SetMemoryFree(mod); + EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, + monitor->CalculateCurrentPressureLevel()); + + monitor->SetMemoryFree(mod - 1); + EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, + monitor->CalculateCurrentPressureLevel()); + + int crit = monitor->critical_threshold_mb(); + monitor->SetMemoryFree(crit + 1); + EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, + monitor->CalculateCurrentPressureLevel()); + + monitor->SetMemoryFree(crit); + EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL, + monitor->CalculateCurrentPressureLevel()); + + monitor->SetMemoryFree(crit - 1); + EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL, + monitor->CalculateCurrentPressureLevel()); + } + + base::MessageLoopForUI message_loop_; +}; + +// Tests the fundamental direct calculation of memory pressure with automatic +// small-memory thresholds. +TEST_F(WinMemoryPressureMonitorTest, CalculateCurrentMemoryPressureLevelSmall) { + static const int kModerateMb = + MemoryPressureMonitor::kSmallMemoryDefaultModerateThresholdMb; + static const int kCriticalMb = + MemoryPressureMonitor::kSmallMemoryDefaultCriticalThresholdMb; + + TestMemoryPressureMonitor monitor(false); // Small-memory model. + + EXPECT_EQ(kModerateMb, monitor.moderate_threshold_mb()); + EXPECT_EQ(kCriticalMb, monitor.critical_threshold_mb()); + + ASSERT_NO_FATAL_FAILURE(CalculateCurrentMemoryPressureLevelTest(&monitor)); +} + +// Tests the fundamental direct calculation of memory pressure with automatic +// large-memory thresholds. +TEST_F(WinMemoryPressureMonitorTest, CalculateCurrentMemoryPressureLevelLarge) { + static const int kModerateMb = + MemoryPressureMonitor::kLargeMemoryDefaultModerateThresholdMb; + static const int kCriticalMb = + MemoryPressureMonitor::kLargeMemoryDefaultCriticalThresholdMb; + + TestMemoryPressureMonitor monitor(true); // Large-memory model. + + EXPECT_EQ(kModerateMb, monitor.moderate_threshold_mb()); + EXPECT_EQ(kCriticalMb, monitor.critical_threshold_mb()); + + ASSERT_NO_FATAL_FAILURE(CalculateCurrentMemoryPressureLevelTest(&monitor)); +} + +// Tests the fundamental direct calculation of memory pressure with manually +// specified threshold levels. +TEST_F(WinMemoryPressureMonitorTest, + CalculateCurrentMemoryPressureLevelCustom) { + static const int kSystemMb = 512; + static const int kModerateMb = 256; + static const int kCriticalMb = 128; + + TestMemoryPressureMonitor monitor(kSystemMb, kModerateMb, kCriticalMb); + + EXPECT_EQ(kModerateMb, monitor.moderate_threshold_mb()); + EXPECT_EQ(kCriticalMb, monitor.critical_threshold_mb()); + + ASSERT_NO_FATAL_FAILURE(CalculateCurrentMemoryPressureLevelTest(&monitor)); +} + +// This test tests the various transition states from memory pressure, looking +// for the correct behavior on event reposting as well as state updates. +TEST_F(WinMemoryPressureMonitorTest, CheckMemoryPressure) { + // Large-memory. + testing::StrictMock<TestMemoryPressureMonitor> monitor(true); + MemoryPressureListener listener( + base::Bind(&TestMemoryPressureMonitor::OnMemoryPressure, + base::Unretained(&monitor))); + + // Checking the memory pressure at 0% load should not produce any + // events. + monitor.SetNone(); + monitor.CheckMemoryPressure(); + message_loop_.RunUntilIdle(); + EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, + monitor.GetCurrentPressureLevel()); + + // Setting the memory level to 80% should produce a moderate pressure level. + EXPECT_CALL(monitor, + OnMemoryPressure(MemoryPressureListener:: + MEMORY_PRESSURE_LEVEL_MODERATE)); + monitor.SetModerate(); + monitor.CheckMemoryPressure(); + message_loop_.RunUntilIdle(); + EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, + monitor.GetCurrentPressureLevel()); + testing::Mock::VerifyAndClearExpectations(&monitor); + + // Check that the event gets reposted after a while. + for (int i = 0; i < monitor.kModeratePressureCooldownCycles; ++i) { + if (i + 1 == monitor.kModeratePressureCooldownCycles) { + EXPECT_CALL(monitor, + OnMemoryPressure(MemoryPressureListener:: + MEMORY_PRESSURE_LEVEL_MODERATE)); + } + monitor.CheckMemoryPressure(); + message_loop_.RunUntilIdle(); + EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, + monitor.GetCurrentPressureLevel()); + testing::Mock::VerifyAndClearExpectations(&monitor); + } + + // Setting the memory usage to 99% should produce critical levels. + EXPECT_CALL(monitor, + OnMemoryPressure(MemoryPressureListener:: + MEMORY_PRESSURE_LEVEL_CRITICAL)); + monitor.SetCritical(); + monitor.CheckMemoryPressure(); + message_loop_.RunUntilIdle(); + EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL, + monitor.GetCurrentPressureLevel()); + testing::Mock::VerifyAndClearExpectations(&monitor); + + // Calling it again should immediately produce a second call. + EXPECT_CALL(monitor, + OnMemoryPressure(MemoryPressureListener:: + MEMORY_PRESSURE_LEVEL_CRITICAL)); + monitor.CheckMemoryPressure(); + message_loop_.RunUntilIdle(); + EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL, + monitor.GetCurrentPressureLevel()); + testing::Mock::VerifyAndClearExpectations(&monitor); + + // When lowering the pressure again there should be a notification and the + // pressure should go back to moderate. + EXPECT_CALL(monitor, + OnMemoryPressure(MemoryPressureListener:: + MEMORY_PRESSURE_LEVEL_MODERATE)); + monitor.SetModerate(); + monitor.CheckMemoryPressure(); + message_loop_.RunUntilIdle(); + EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, + monitor.GetCurrentPressureLevel()); + testing::Mock::VerifyAndClearExpectations(&monitor); + + // Check that the event gets reposted after a while. + for (int i = 0; i < monitor.kModeratePressureCooldownCycles; ++i) { + if (i + 1 == monitor.kModeratePressureCooldownCycles) { + EXPECT_CALL(monitor, + OnMemoryPressure(MemoryPressureListener:: + MEMORY_PRESSURE_LEVEL_MODERATE)); + } + monitor.CheckMemoryPressure(); + message_loop_.RunUntilIdle(); + EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, + monitor.GetCurrentPressureLevel()); + testing::Mock::VerifyAndClearExpectations(&monitor); + } + + // Going down to no pressure should not produce an notification. + monitor.SetNone(); + monitor.CheckMemoryPressure(); + message_loop_.RunUntilIdle(); + EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, + monitor.GetCurrentPressureLevel()); + testing::Mock::VerifyAndClearExpectations(&monitor); +} + +} // namespace win +} // namespace base diff --git a/chromium/base/win/message_window.cc b/chromium/base/win/message_window.cc index 57fe64c7981..0b4b29f5aa6 100644 --- a/chromium/base/win/message_window.cc +++ b/chromium/base/win/message_window.cc @@ -7,6 +7,7 @@ #include "base/lazy_instance.h" #include "base/logging.h" #include "base/process/memory.h" +#include "base/profiler/scoped_tracker.h" #include "base/win/wrapped_window_proc.h" const wchar_t kMessageWindowClassName[] = L"Chrome_MessageWindow"; @@ -120,6 +121,10 @@ LRESULT CALLBACK MessageWindow::WindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { + // TODO(vadimt): Remove ScopedTracker below once crbug.com/440919 is fixed. + tracked_objects::ScopedTracker tracking_profile( + FROM_HERE_WITH_EXPLICIT_FUNCTION("440919 MessageWindow::WindowProc")); + MessageWindow* self = reinterpret_cast<MessageWindow*>( GetWindowLongPtr(hwnd, GWLP_USERDATA)); diff --git a/chromium/base/win/metro.cc b/chromium/base/win/metro.cc index 6e443ba66b6..794669803ca 100644 --- a/chromium/base/win/metro.cc +++ b/chromium/base/win/metro.cc @@ -4,10 +4,7 @@ #include "base/win/metro.h" -#include "base/message_loop/message_loop.h" #include "base/strings/string_util.h" -#include "base/win/scoped_comptr.h" -#include "base/win/windows_version.h" namespace base { namespace win { @@ -73,38 +70,6 @@ wchar_t* LocalAllocAndCopyString(const string16& src) { return dest; } -bool IsParentalControlActivityLoggingOn() { - // Query this info on Windows 7 and above. - if (base::win::GetVersion() < base::win::VERSION_WIN7) - return false; - - static bool parental_control_logging_required = false; - static bool parental_control_status_determined = false; - - if (parental_control_status_determined) - return parental_control_logging_required; - - parental_control_status_determined = true; - - ScopedComPtr<IWindowsParentalControlsCore> parent_controls; - HRESULT hr = parent_controls.CreateInstance( - __uuidof(WindowsParentalControls)); - if (FAILED(hr)) - return false; - - ScopedComPtr<IWPCSettings> settings; - hr = parent_controls->GetUserSettings(NULL, settings.Receive()); - if (FAILED(hr)) - return false; - - unsigned long restrictions = 0; - settings->GetRestrictions(&restrictions); - - parental_control_logging_required = - (restrictions & WPCFLAG_LOGGING_REQUIRED) == WPCFLAG_LOGGING_REQUIRED; - return parental_control_logging_required; -} - // Metro driver exports for getting the launch type, initial url, initial // search term, etc. extern "C" { diff --git a/chromium/base/win/metro.h b/chromium/base/win/metro.h index 5894ef06dae..06960067519 100644 --- a/chromium/base/win/metro.h +++ b/chromium/base/win/metro.h @@ -6,7 +6,6 @@ #define BASE_WIN_METRO_H_ #include <windows.h> -#include <wpcapi.h> #include "base/base_export.h" #include "base/callback.h" @@ -80,11 +79,6 @@ BASE_EXPORT bool IsProcessImmersive(HANDLE process); // copying the src to it. BASE_EXPORT wchar_t* LocalAllocAndCopyString(const string16& src); -// Returns true if Windows Parental control activity logging is enabled. This -// feature is available on Windows Vista and beyond. -// This function should ideally be called on the UI thread. -BASE_EXPORT bool IsParentalControlActivityLoggingOn(); - // Returns the type of launch and the activation params. For example if the // the launch is for METRO_PROTOCOL then the params is a url. BASE_EXPORT MetroLaunchType GetMetroLaunchParams(string16* params); diff --git a/chromium/base/win/object_watcher.cc b/chromium/base/win/object_watcher.cc index fe209f5e338..5ebe1856d31 100644 --- a/chromium/base/win/object_watcher.cc +++ b/chromium/base/win/object_watcher.cc @@ -77,7 +77,11 @@ bool ObjectWatcher::StopWatching() { return true; } -HANDLE ObjectWatcher::GetWatchedObject() { +bool ObjectWatcher::IsWatching() const { + return object_ != NULL; +} + +HANDLE ObjectWatcher::GetWatchedObject() const { return object_; } @@ -88,7 +92,7 @@ void CALLBACK ObjectWatcher::DoneWaiting(void* param, BOOLEAN timed_out) { // The destructor blocks on any callbacks that are in flight, so we know that // that is always a pointer to a valid ObjectWater. ObjectWatcher* that = static_cast<ObjectWatcher*>(param); - that->origin_loop_->PostTask(FROM_HERE, that->callback_); + that->origin_loop_->task_runner()->PostTask(FROM_HERE, that->callback_); that->callback_.Reset(); } diff --git a/chromium/base/win/object_watcher.h b/chromium/base/win/object_watcher.h index bf7f8b3e92c..d68d9350b38 100644 --- a/chromium/base/win/object_watcher.h +++ b/chromium/base/win/object_watcher.h @@ -56,7 +56,7 @@ class BASE_EXPORT ObjectWatcher : public MessageLoop::DestructionObserver { }; ObjectWatcher(); - ~ObjectWatcher(); + ~ObjectWatcher() override; // When the object is signaled, the given delegate is notified on the thread // where StartWatching is called. The ObjectWatcher is not responsible for @@ -74,9 +74,11 @@ class BASE_EXPORT ObjectWatcher : public MessageLoop::DestructionObserver { // bool StopWatching(); - // Returns the handle of the object being watched, or NULL if the object - // watcher is stopped. - HANDLE GetWatchedObject(); + // Returns true if currently watching an object. + bool IsWatching() const; + + // Returns the handle of the object being watched. + HANDLE GetWatchedObject() const; private: // Called on a background thread when done waiting. @@ -85,7 +87,7 @@ class BASE_EXPORT ObjectWatcher : public MessageLoop::DestructionObserver { void Signal(Delegate* delegate); // MessageLoop::DestructionObserver implementation: - virtual void WillDestroyCurrentMessageLoop(); + void WillDestroyCurrentMessageLoop() override; // Internal state. Closure callback_; @@ -101,4 +103,4 @@ class BASE_EXPORT ObjectWatcher : public MessageLoop::DestructionObserver { } // namespace win } // namespace base -#endif // BASE_OBJECT_WATCHER_H_ +#endif // BASE_WIN_OBJECT_WATCHER_H_ diff --git a/chromium/base/win/object_watcher_unittest.cc b/chromium/base/win/object_watcher_unittest.cc index 46b98de524d..b30ca41a4fe 100644 --- a/chromium/base/win/object_watcher_unittest.cc +++ b/chromium/base/win/object_watcher_unittest.cc @@ -17,7 +17,7 @@ namespace { class QuitDelegate : public ObjectWatcher::Delegate { public: - virtual void OnObjectSignaled(HANDLE object) { + void OnObjectSignaled(HANDLE object) override { MessageLoop::current()->QuitWhenIdle(); } }; @@ -26,9 +26,8 @@ class DecrementCountDelegate : public ObjectWatcher::Delegate { public: explicit DecrementCountDelegate(int* counter) : counter_(counter) { } - virtual void OnObjectSignaled(HANDLE object) { - --(*counter_); - } + void OnObjectSignaled(HANDLE object) override { --(*counter_); } + private: int* counter_; }; @@ -37,7 +36,7 @@ void RunTest_BasicSignal(MessageLoop::Type message_loop_type) { MessageLoop message_loop(message_loop_type); ObjectWatcher watcher; - EXPECT_EQ(NULL, watcher.GetWatchedObject()); + EXPECT_FALSE(watcher.IsWatching()); // A manual-reset event that is not yet signaled. HANDLE event = CreateEvent(NULL, TRUE, FALSE, NULL); @@ -45,13 +44,14 @@ void RunTest_BasicSignal(MessageLoop::Type message_loop_type) { QuitDelegate delegate; bool ok = watcher.StartWatching(event, &delegate); EXPECT_TRUE(ok); + EXPECT_TRUE(watcher.IsWatching()); EXPECT_EQ(event, watcher.GetWatchedObject()); SetEvent(event); MessageLoop::current()->Run(); - EXPECT_EQ(NULL, watcher.GetWatchedObject()); + EXPECT_FALSE(watcher.IsWatching()); CloseHandle(event); } @@ -115,7 +115,7 @@ void RunTest_SignalBeforeWatch(MessageLoop::Type message_loop_type) { MessageLoop::current()->Run(); - EXPECT_EQ(NULL, watcher.GetWatchedObject()); + EXPECT_FALSE(watcher.IsWatching()); CloseHandle(event); } diff --git a/chromium/base/win/pe_image.cc b/chromium/base/win/pe_image.cc index db28699b734..692b7b66507 100644 --- a/chromium/base/win/pe_image.cc +++ b/chromium/base/win/pe_image.cc @@ -10,14 +10,7 @@ namespace base { namespace win { -#if defined(_WIN64) && !defined(NACL_WIN64) -// TODO(jschuh): crbug.com/167707 Make sure this is ok. -#pragma message ("Warning: \ - This code is not tested on x64. Please make sure all the base unit tests\ - pass before doing any real work. The current unit tests don't test the\ - differences between 32- and 64-bits implementations. Bugs may slip through.\ - You need to improve the coverage before continuing.") -#endif +// TODO(jschuh): crbug.com/167707 Make sure this code works on 64-bit. // Structure to perform imports enumerations. struct EnumAllImportsStorage { @@ -27,6 +20,9 @@ struct EnumAllImportsStorage { namespace { + // PdbInfo Signature + const DWORD kPdbInfoSignature = 'SDSR'; + // Compare two strings byte by byte on an unsigned basis. // if s1 == s2, return 0 // if s1 < s2, return negative @@ -42,6 +38,12 @@ namespace { *reinterpret_cast<const unsigned char*>(s2)); } + struct PdbInfo { + DWORD Signature; + GUID Guid; + DWORD Age; + char PdbFileName[1]; + }; } // namespace // Callback used to enumerate imports. See EnumImportChunksFunction. @@ -149,6 +151,36 @@ PIMAGE_SECTION_HEADER PEImage::GetImageSectionHeaderByName( return ret; } +bool PEImage::GetDebugId(LPGUID guid, LPDWORD age) const { + if (NULL == guid || NULL == age) { + return false; + } + + DWORD debug_directory_size = + GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_DEBUG); + PIMAGE_DEBUG_DIRECTORY debug_directory = + reinterpret_cast<PIMAGE_DEBUG_DIRECTORY>( + GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_DEBUG)); + + size_t directory_count = + debug_directory_size / sizeof(IMAGE_DEBUG_DIRECTORY); + + for (size_t index = 0; index < directory_count; ++index) { + if (debug_directory[index].Type == IMAGE_DEBUG_TYPE_CODEVIEW) { + PdbInfo* pdb_info = reinterpret_cast<PdbInfo*>( + RVAToAddr(debug_directory[index].AddressOfRawData)); + if (pdb_info->Signature != kPdbInfoSignature) { + // Unsupported PdbInfo signature + return false; + } + *guid = pdb_info->Guid; + *age = pdb_info->Age; + return true; + } + } + return false; +} + PDWORD PEImage::GetExportEntry(LPCSTR name) const { PIMAGE_EXPORT_DIRECTORY exports = GetExportDirectory(); @@ -536,14 +568,11 @@ bool PEImage::ImageAddrToOnDiskOffset(LPVOID address, if (NULL == section_header) return false; -#pragma warning(push) -#pragma warning(disable: 4311) - // These casts generate warnings because they are 32 bit specific. // Don't follow the virtual RVAToAddr, use the one on the base. - DWORD offset_within_section = reinterpret_cast<DWORD>(address) - - reinterpret_cast<DWORD>(PEImage::RVAToAddr( - section_header->VirtualAddress)); -#pragma warning(pop) + DWORD offset_within_section = + static_cast<DWORD>(reinterpret_cast<uintptr_t>(address)) - + static_cast<DWORD>(reinterpret_cast<uintptr_t>( + PEImage::RVAToAddr(section_header->VirtualAddress))); *on_disk_offset = section_header->PointerToRawData + offset_within_section; return true; diff --git a/chromium/base/win/pe_image.h b/chromium/base/win/pe_image.h index d93e99dc143..343d2866436 100644 --- a/chromium/base/win/pe_image.h +++ b/chromium/base/win/pe_image.h @@ -132,6 +132,9 @@ class PEImage { // Returns the exports directory. PIMAGE_EXPORT_DIRECTORY GetExportDirectory() const; + // Returns the debug id (guid+age). + bool GetDebugId(LPGUID guid, LPDWORD age) const; + // Returns a given export entry. // Use: e = image.GetExportEntry(f); // Pre: 'f' is either a zero terminated string or ordinal @@ -235,19 +238,15 @@ class PEImageAsData : public PEImage { public: explicit PEImageAsData(HMODULE hModule) : PEImage(hModule) {} - virtual PVOID RVAToAddr(DWORD rva) const; + PVOID RVAToAddr(DWORD rva) const override; }; inline bool PEImage::IsOrdinal(LPCSTR name) { -#pragma warning(push) -#pragma warning(disable: 4311) - // This cast generates a warning because it is 32 bit specific. - return reinterpret_cast<DWORD>(name) <= 0xFFFF; -#pragma warning(pop) + return reinterpret_cast<uintptr_t>(name) <= 0xFFFF; } inline WORD PEImage::ToOrdinal(LPCSTR name) { - return reinterpret_cast<WORD>(name); + return static_cast<WORD>(reinterpret_cast<intptr_t>(name)); } inline HMODULE PEImage::module() const { diff --git a/chromium/base/win/pe_image_test.cc b/chromium/base/win/pe_image_test.cc new file mode 100644 index 00000000000..e374598ada7 --- /dev/null +++ b/chromium/base/win/pe_image_test.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 <windows.h> + +#include <cfgmgr32.h> +#include <shellapi.h> + +extern "C" { + +__declspec(dllexport) void ExportFunc1() { + // Call into user32.dll. + HWND dummy = GetDesktopWindow(); + SetWindowTextA(dummy, "dummy"); +} + +__declspec(dllexport) void ExportFunc2() { + // Call into cfgmgr32.dll. + CM_MapCrToWin32Err(CR_SUCCESS, ERROR_SUCCESS); + + // Call into shell32.dll. + SHFILEOPSTRUCT file_operation = {0}; + SHFileOperation(&file_operation); + + // Call into kernel32.dll. + HANDLE h = CreateEvent(NULL, FALSE, FALSE, NULL); + CloseHandle(h); +} + +} // extern "C" diff --git a/chromium/base/win/pe_image_unittest.cc b/chromium/base/win/pe_image_unittest.cc index 238c924f62c..28b65a4e0ba 100644 --- a/chromium/base/win/pe_image_unittest.cc +++ b/chromium/base/win/pe_image_unittest.cc @@ -3,29 +3,21 @@ // found in the LICENSE file. // This file contains unit tests for PEImage. +#include <algorithm> +#include <vector> -#include "testing/gtest/include/gtest/gtest.h" +#include "base/files/file_path.h" +#include "base/path_service.h" #include "base/win/pe_image.h" -#include "base/win/windows_version.h" +#include "testing/gtest/include/gtest/gtest.h" namespace base { namespace win { -// Just counts the number of invocations. -bool ExportsCallback(const PEImage &image, - DWORD ordinal, - DWORD hint, - LPCSTR name, - PVOID function, - LPCSTR forward, - PVOID cookie) { - int* count = reinterpret_cast<int*>(cookie); - (*count)++; - return true; -} +namespace { // Just counts the number of invocations. -bool ImportsCallback(const PEImage &image, +bool ImportsCallback(const PEImage& image, LPCSTR module, DWORD ordinal, LPCSTR name, @@ -38,18 +30,18 @@ bool ImportsCallback(const PEImage &image, } // Just counts the number of invocations. -bool SectionsCallback(const PEImage &image, - PIMAGE_SECTION_HEADER header, - PVOID section_start, - DWORD section_size, - PVOID cookie) { +bool SectionsCallback(const PEImage& image, + PIMAGE_SECTION_HEADER header, + PVOID section_start, + DWORD section_size, + PVOID cookie) { int* count = reinterpret_cast<int*>(cookie); (*count)++; return true; } // Just counts the number of invocations. -bool RelocsCallback(const PEImage &image, +bool RelocsCallback(const PEImage& image, WORD type, PVOID address, PVOID cookie) { @@ -59,7 +51,7 @@ bool RelocsCallback(const PEImage &image, } // Just counts the number of invocations. -bool ImportChunksCallback(const PEImage &image, +bool ImportChunksCallback(const PEImage& image, LPCSTR module, PIMAGE_THUNK_DATA name_table, PIMAGE_THUNK_DATA iat, @@ -70,7 +62,7 @@ bool ImportChunksCallback(const PEImage &image, } // Just counts the number of invocations. -bool DelayImportChunksCallback(const PEImage &image, +bool DelayImportChunksCallback(const PEImage& image, PImgDelayDescr delay_descriptor, LPCSTR module, PIMAGE_THUNK_DATA name_table, @@ -83,167 +75,83 @@ bool DelayImportChunksCallback(const PEImage &image, return true; } -// Identifiers for the set of supported expectations. -enum ExpectationSet { - WIN_2K_SET, - WIN_XP_SET, - WIN_VISTA_SET, - WIN_7_SET, - WIN_8_SET, - UNSUPPORTED_SET, -}; - -// We'll be using some known values for the tests. -enum Value { - sections = 0, - imports_dlls, - delay_dlls, - exports, - imports, - delay_imports, - relocs -}; - -ExpectationSet GetExpectationSet(DWORD os) { - if (os == 50) - return WIN_2K_SET; - if (os == 51) - return WIN_XP_SET; - if (os == 60) - return WIN_VISTA_SET; - if (os == 61) - return WIN_7_SET; - if (os >= 62) - return WIN_8_SET; - return UNSUPPORTED_SET; -} - -// Retrieves the expected value from advapi32.dll based on the OS. -int GetExpectedValue(Value value, DWORD os) { - const int xp_delay_dlls = 2; - const int xp_exports = 675; - const int xp_imports = 422; - const int xp_delay_imports = 8; - const int xp_relocs = 9180; - const int vista_delay_dlls = 4; - const int vista_exports = 799; - const int vista_imports = 476; - const int vista_delay_imports = 24; - const int vista_relocs = 10188; - const int w2k_delay_dlls = 0; - const int w2k_exports = 566; - const int w2k_imports = 357; - const int w2k_delay_imports = 0; - const int w2k_relocs = 7388; - const int win7_delay_dlls = 7; - const int win7_exports = 806; - const int win7_imports = 568; - const int win7_delay_imports = 71; - int win7_relocs = 7812; - int win7_sections = 4; - const int win8_delay_dlls = 9; - const int win8_exports = 806; - const int win8_imports = 568; - const int win8_delay_imports = 113; - const int win8_relocs = 9478; - int win8_sections = 4; - int win8_import_dlls = 17; - - base::win::OSInfo* os_info = base::win::OSInfo::GetInstance(); - // 32-bit process on a 32-bit system. - if (os_info->architecture() == base::win::OSInfo::X86_ARCHITECTURE) { - win8_sections = 5; - win8_import_dlls = 19; - - // 64-bit process on a 64-bit system. - } else if (os_info->wow64_status() == base::win::OSInfo::WOW64_DISABLED) { - win7_sections = 6; - win7_relocs = 2712; - } - - // Contains the expected value, for each enumerated property (Value), and the - // OS version: [Value][os_version] - const int expected[][5] = { - {4, 4, 4, win7_sections, win8_sections}, - {3, 3, 3, 13, win8_import_dlls}, - {w2k_delay_dlls, xp_delay_dlls, vista_delay_dlls, win7_delay_dlls, - win8_delay_dlls}, - {w2k_exports, xp_exports, vista_exports, win7_exports, win8_exports}, - {w2k_imports, xp_imports, vista_imports, win7_imports, win8_imports}, - {w2k_delay_imports, xp_delay_imports, - vista_delay_imports, win7_delay_imports, win8_delay_imports}, - {w2k_relocs, xp_relocs, vista_relocs, win7_relocs, win8_relocs} - }; - COMPILE_ASSERT(arraysize(expected[0]) == UNSUPPORTED_SET, - expected_value_set_mismatch); - - if (value > relocs) - return 0; - ExpectationSet expected_set = GetExpectationSet(os); - if (expected_set >= arraysize(expected)) { - // This should never happen. Log a failure if it does. - EXPECT_NE(UNSUPPORTED_SET, expected_set); - expected_set = WIN_2K_SET; - } - - return expected[value][expected_set]; +// Just counts the number of invocations. +bool ExportsCallback(const PEImage& image, + DWORD ordinal, + DWORD hint, + LPCSTR name, + PVOID function, + LPCSTR forward, + PVOID cookie) { + int* count = reinterpret_cast<int*>(cookie); + (*count)++; + return true; } +} // namespace -// TODO(jschuh): crbug.com/167707 Need to fix test on Win64 bots -#if defined(OS_WIN) && defined(ARCH_CPU_X86_64) -#define MAYBE_EnumeratesPE DISABLED_EnumeratesPE +// Tests that we are able to enumerate stuff from a PE file, and that +// the actual number of items found matches an expected value. +TEST(PEImageTest, EnumeratesPE) { + base::FilePath pe_image_test_path; + ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &pe_image_test_path)); + pe_image_test_path = pe_image_test_path.Append(FILE_PATH_LITERAL("pe_image")); + +#if defined(ARCH_CPU_64_BITS) + pe_image_test_path = + pe_image_test_path.Append(FILE_PATH_LITERAL("pe_image_test_64.dll")); + const int sections = 6; + const int imports_dlls = 2; + const int delay_dlls = 2; + const int exports = 2; + const int imports = 69; + const int delay_imports = 2; + const int relocs = 632; #else -#define MAYBE_EnumeratesPE EnumeratesPE + pe_image_test_path = + pe_image_test_path.Append(FILE_PATH_LITERAL("pe_image_test_32.dll")); + const int sections = 5; + const int imports_dlls = 2; + const int delay_dlls = 2; + const int exports = 2; + const int imports = 66; + const int delay_imports = 2; + const int relocs = 1586; #endif -// Tests that we are able to enumerate stuff from a PE file, and that -// the actual number of items found is within the expected range. -TEST(PEImageTest, MAYBE_EnumeratesPE) { - HMODULE module = LoadLibrary(L"advapi32.dll"); + HMODULE module = LoadLibrary(pe_image_test_path.value().c_str()); ASSERT_TRUE(NULL != module); PEImage pe(module); int count = 0; EXPECT_TRUE(pe.VerifyMagic()); - DWORD os = pe.GetNTHeaders()->OptionalHeader.MajorOperatingSystemVersion; - os = os * 10 + pe.GetNTHeaders()->OptionalHeader.MinorOperatingSystemVersion; - - // Skip this test for unsupported OS versions. - if (GetExpectationSet(os) == UNSUPPORTED_SET) - return; - pe.EnumSections(SectionsCallback, &count); - EXPECT_EQ(GetExpectedValue(sections, os), count); + EXPECT_EQ(sections, count); count = 0; pe.EnumImportChunks(ImportChunksCallback, &count); - EXPECT_EQ(GetExpectedValue(imports_dlls, os), count); + EXPECT_EQ(imports_dlls, count); count = 0; pe.EnumDelayImportChunks(DelayImportChunksCallback, &count); - EXPECT_EQ(GetExpectedValue(delay_dlls, os), count); + EXPECT_EQ(delay_dlls, count); count = 0; pe.EnumExports(ExportsCallback, &count); - EXPECT_GT(count, GetExpectedValue(exports, os) - 20); - EXPECT_LT(count, GetExpectedValue(exports, os) + 100); + EXPECT_EQ(exports, count); count = 0; pe.EnumAllImports(ImportsCallback, &count); - EXPECT_GT(count, GetExpectedValue(imports, os) - 20); - EXPECT_LT(count, GetExpectedValue(imports, os) + 100); + EXPECT_EQ(imports, count); count = 0; pe.EnumAllDelayImports(ImportsCallback, &count); - EXPECT_GT(count, GetExpectedValue(delay_imports, os) - 2); - EXPECT_LT(count, GetExpectedValue(delay_imports, os) + 8); + EXPECT_EQ(delay_imports, count); count = 0; pe.EnumRelocs(RelocsCallback, &count); - EXPECT_GT(count, GetExpectedValue(relocs, os) - 150); - EXPECT_LT(count, GetExpectedValue(relocs, os) + 1500); + EXPECT_EQ(relocs, count); FreeLibrary(module); } @@ -267,5 +175,21 @@ TEST(PEImageTest, RetrievesExports) { FreeLibrary(module); } +// Test that we can get debug id out of a module. +TEST(PEImageTest, GetDebugId) { + HMODULE module = LoadLibrary(L"advapi32.dll"); + ASSERT_TRUE(NULL != module); + + PEImage pe(module); + GUID guid = {0}; + DWORD age = 0; + EXPECT_TRUE(pe.GetDebugId(&guid, &age)); + + GUID empty_guid = {0}; + EXPECT_TRUE(!IsEqualGUID(empty_guid, guid)); + EXPECT_NE(0U, age); + FreeLibrary(module); +} + } // namespace win } // namespace base diff --git a/chromium/base/win/registry.cc b/chromium/base/win/registry.cc index 23ad12cad64..47afcbfb77b 100644 --- a/chromium/base/win/registry.cc +++ b/chromium/base/win/registry.cc @@ -38,7 +38,7 @@ const REGSAM kWow64AccessMask = KEY_WOW64_32KEY | KEY_WOW64_64KEY; class RegKey::Watcher : public ObjectWatcher::Delegate { public: explicit Watcher(RegKey* owner) : owner_(owner) {} - ~Watcher() {} + ~Watcher() override {} bool StartWatching(HKEY key, const ChangeCallback& callback); @@ -210,7 +210,7 @@ void RegKey::Set(HKEY key) { } HKEY RegKey::Take() { - DCHECK(wow64access_ == 0); + DCHECK_EQ(wow64access_, 0u); HKEY key = key_; key_ = NULL; return key; @@ -662,8 +662,8 @@ void RegistryKeyIterator::Initialize(HKEY root_key, key_ = NULL; } else { DWORD count = 0; - LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL, - NULL, NULL, NULL, NULL, NULL); + result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL, NULL, + NULL, NULL, NULL, NULL); if (result != ERROR_SUCCESS) { ::RegCloseKey(key_); diff --git a/chromium/base/win/registry_unittest.cc b/chromium/base/win/registry_unittest.cc index 6548474c4fa..22576634f9a 100644 --- a/chromium/base/win/registry_unittest.cc +++ b/chromium/base/win/registry_unittest.cc @@ -31,7 +31,7 @@ class RegistryTest : public testing::Test { #endif // _WIN64 RegistryTest() {} - virtual void SetUp() override { + void SetUp() override { // Create a temporary key. RegKey key(HKEY_CURRENT_USER, L"", KEY_ALL_ACCESS); key.DeleteKey(kRootKey); @@ -42,7 +42,7 @@ class RegistryTest : public testing::Test { foo_software_key_ += L"\\Foo"; } - virtual void TearDown() override { + void TearDown() override { // Clean up the temporary key. RegKey key(HKEY_CURRENT_USER, L"", KEY_SET_VALUE); ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(kRootKey)); diff --git a/chromium/base/win/resource_util.h b/chromium/base/win/resource_util.h index f3444ae79f5..8a8baa0f532 100644 --- a/chromium/base/win/resource_util.h +++ b/chromium/base/win/resource_util.h @@ -5,8 +5,8 @@ // This file contains utility functions for accessing resources in external // files (DLLs) or embedded in the executable itself. -#ifndef BASE_WIN_RESOURCE_UTIL_H__ -#define BASE_WIN_RESOURCE_UTIL_H__ +#ifndef BASE_WIN_RESOURCE_UTIL_H_ +#define BASE_WIN_RESOURCE_UTIL_H_ #include <windows.h> @@ -36,4 +36,4 @@ bool BASE_EXPORT GetDataResourceFromModule(HMODULE module, } // namespace win } // namespace base -#endif // BASE_WIN_RESOURCE_UTIL_H__ +#endif // BASE_WIN_RESOURCE_UTIL_H_ diff --git a/chromium/base/win/scoped_bstr.h b/chromium/base/win/scoped_bstr.h index d703f62dac8..7c9f5df0868 100644 --- a/chromium/base/win/scoped_bstr.h +++ b/chromium/base/win/scoped_bstr.h @@ -94,4 +94,4 @@ class BASE_EXPORT ScopedBstr { } // namespace win } // namespace base -#endif // BASE_SCOPED_BSTR_H_ +#endif // BASE_WIN_SCOPED_BSTR_H_ diff --git a/chromium/base/win/scoped_comptr.h b/chromium/base/win/scoped_comptr.h index 98cea0ff4c8..373c0c3ba50 100644 --- a/chromium/base/win/scoped_comptr.h +++ b/chromium/base/win/scoped_comptr.h @@ -133,8 +133,7 @@ class ScopedComPtr : public scoped_refptr<Interface> { ScopedComPtr<IUnknown> other_identity; other->QueryInterface(other_identity.Receive()); - return static_cast<IUnknown*>(my_identity) == - static_cast<IUnknown*>(other_identity); + return my_identity == other_identity; } // Provides direct access to the interface. diff --git a/chromium/base/win/scoped_comptr_unittest.cc b/chromium/base/win/scoped_comptr_unittest.cc index d8d12be87df..d38752dfb9f 100644 --- a/chromium/base/win/scoped_comptr_unittest.cc +++ b/chromium/base/win/scoped_comptr_unittest.cc @@ -31,8 +31,8 @@ const IID dummy_iid = { 0x12345678u, 0x1234u, 0x5678u, 01, 23, 45, 67, 89, } // namespace TEST(ScopedComPtrTest, ScopedComPtr) { - EXPECT_TRUE(memcmp(&ScopedComPtr<IUnknown>::iid(), &IID_IUnknown, - sizeof(IID)) == 0); + EXPECT_EQ(memcmp(&ScopedComPtr<IUnknown>::iid(), &IID_IUnknown, sizeof(IID)), + 0); base::win::ScopedCOMInitializer com_initializer; EXPECT_TRUE(com_initializer.succeeded()); @@ -41,8 +41,8 @@ TEST(ScopedComPtrTest, ScopedComPtr) { EXPECT_TRUE(SUCCEEDED(unk.CreateInstance(CLSID_ShellLink))); ScopedComPtr<IUnknown> unk2; unk2.Attach(unk.Detach()); - EXPECT_TRUE(unk == NULL); - EXPECT_TRUE(unk2 != NULL); + EXPECT_TRUE(unk.get() == NULL); + EXPECT_TRUE(unk2.get() != NULL); ScopedComPtr<IMalloc> mem_alloc; EXPECT_TRUE(SUCCEEDED(CoGetMalloc(1, mem_alloc.Receive()))); @@ -55,26 +55,26 @@ TEST(ScopedComPtrTest, ScopedComPtr) { // test ScopedComPtr& constructor ScopedComPtr<IMalloc> copy1(mem_alloc); - EXPECT_TRUE(copy1.IsSameObject(mem_alloc)); - EXPECT_FALSE(copy1.IsSameObject(unk2)); // unk2 is valid but different - EXPECT_FALSE(copy1.IsSameObject(unk)); // unk is NULL + EXPECT_TRUE(copy1.IsSameObject(mem_alloc.get())); + EXPECT_FALSE(copy1.IsSameObject(unk2.get())); // unk2 is valid but different + EXPECT_FALSE(copy1.IsSameObject(unk.get())); // unk is NULL IMalloc* naked_copy = copy1.Detach(); copy1 = naked_copy; // Test the =(T*) operator. naked_copy->Release(); copy1.Release(); - EXPECT_FALSE(copy1.IsSameObject(unk2)); // unk2 is valid, copy1 is not + EXPECT_FALSE(copy1.IsSameObject(unk2.get())); // unk2 is valid, copy1 is not // test Interface* constructor - ScopedComPtr<IMalloc> copy2(static_cast<IMalloc*>(mem_alloc)); - EXPECT_TRUE(copy2.IsSameObject(mem_alloc)); + ScopedComPtr<IMalloc> copy2(static_cast<IMalloc*>(mem_alloc.get())); + EXPECT_TRUE(copy2.IsSameObject(mem_alloc.get())); - EXPECT_TRUE(SUCCEEDED(unk.QueryFrom(mem_alloc))); - EXPECT_TRUE(unk != NULL); + EXPECT_TRUE(SUCCEEDED(unk.QueryFrom(mem_alloc.get()))); + EXPECT_TRUE(unk.get() != NULL); unk.Release(); - EXPECT_TRUE(unk == NULL); - EXPECT_TRUE(unk.IsSameObject(copy1)); // both are NULL + EXPECT_TRUE(unk.get() == NULL); + EXPECT_TRUE(unk.IsSameObject(copy1.get())); // both are NULL } TEST(ScopedComPtrTest, ScopedComPtrVector) { @@ -98,7 +98,7 @@ TEST(ScopedComPtrTest, ScopedComPtrVector) { bleh.push_back(p2); EXPECT_EQ(p->adds, 4); EXPECT_EQ(p->releases, 1); - EXPECT_EQ(bleh[0], p.get()); + EXPECT_EQ(bleh[0].get(), p.get()); bleh.pop_back(); EXPECT_EQ(p->adds, 4); EXPECT_EQ(p->releases, 2); diff --git a/chromium/base/win/scoped_handle.cc b/chromium/base/win/scoped_handle.cc index 28030216920..ce944e43b9f 100644 --- a/chromium/base/win/scoped_handle.cc +++ b/chromium/base/win/scoped_handle.cc @@ -12,6 +12,11 @@ #include "base/logging.h" #include "base/synchronization/lock_impl.h" +extern "C" { +__declspec(dllexport) void* GetHandleVerifier(); +typedef void* (*GetHandleVerifierFn)(); +} + namespace { struct HandleHash { @@ -30,20 +35,9 @@ struct Info { }; typedef std::unordered_map<HANDLE, Info, HandleHash> HandleMap; -// g_lock protects g_handle_map and g_closing. +// g_lock protects the handle map and setting g_active_verifier. typedef base::internal::LockImpl NativeLock; base::LazyInstance<NativeLock>::Leaky g_lock = LAZY_INSTANCE_INITIALIZER; -base::LazyInstance<HandleMap>::Leaky g_handle_map = LAZY_INSTANCE_INITIALIZER; -bool g_closing = false; - -// g_verifier_enabled is not protected by g_lock because that would require -// using the lock (hence, synchornizing multiple threads) even when the -// verifier is not in use. Note that this variable is initialized to track all -// handles, and it should only move to the disabled state, and never back to -// enabled, because that would crash when seeing handles created while the -// verifier was disabled. This also implies that it is OK if the value change is -// not propagated immediately to all CPUs (as would happen with a lock). -bool g_verifier_enabled = true; bool CloseHandleWrapper(HANDLE handle) { if (!::CloseHandle(handle)) @@ -68,82 +62,196 @@ class AutoNativeLock { DISALLOW_COPY_AND_ASSIGN(AutoNativeLock); }; -} // namespace +// Implements the actual object that is verifying handles for this process. +// The active instance is shared across the module boundary but there is no +// way to delete this object from the wrong side of it (or any side, actually). +class ActiveVerifier { + public: + explicit ActiveVerifier(bool enabled) + : enabled_(enabled), closing_(false), lock_(g_lock.Pointer()) { + } -namespace base { -namespace win { + // Retrieves the current verifier. + static ActiveVerifier* Get(); -// Static. -bool HandleTraits::CloseHandle(HANDLE handle) { - if (!g_verifier_enabled) - return CloseHandleWrapper(handle); + // The methods required by HandleTraits. They are virtual because we need to + // forward the call execution to another module, instead of letting the + // compiler call the version that is linked in the current module. + virtual bool CloseHandle(HANDLE handle); + virtual void StartTracking(HANDLE handle, const void* owner, + const void* pc1, const void* pc2); + virtual void StopTracking(HANDLE handle, const void* owner, + const void* pc1, const void* pc2); + virtual void Disable(); + virtual void OnHandleBeingClosed(HANDLE handle); + + private: + ~ActiveVerifier(); // Not implemented. + + static void InstallVerifier(); + + bool enabled_; + bool closing_; + NativeLock* lock_; + HandleMap map_; + DISALLOW_COPY_AND_ASSIGN(ActiveVerifier); +}; +ActiveVerifier* g_active_verifier = NULL; + +// static +ActiveVerifier* ActiveVerifier::Get() { + if (!g_active_verifier) + ActiveVerifier::InstallVerifier(); + + return g_active_verifier; +} +// static +void ActiveVerifier::InstallVerifier() { +#if defined(COMPONENT_BUILD) AutoNativeLock lock(g_lock.Get()); - g_closing = true; + g_active_verifier = new ActiveVerifier(true); +#else + // If you are reading this, wondering why your process seems deadlocked, take + // a look at your DllMain code and remove things that should not be done + // there, like doing whatever gave you that nice windows handle you are trying + // to store in a ScopedHandle. + HMODULE main_module = ::GetModuleHandle(NULL); + GetHandleVerifierFn get_handle_verifier = + reinterpret_cast<GetHandleVerifierFn>(::GetProcAddress( + main_module, "GetHandleVerifier")); + + if (!get_handle_verifier) { + g_active_verifier = new ActiveVerifier(false); + return; + } + + ActiveVerifier* verifier = + reinterpret_cast<ActiveVerifier*>(get_handle_verifier()); + + // This lock only protects against races in this module, which is fine. + AutoNativeLock lock(g_lock.Get()); + g_active_verifier = verifier ? verifier : new ActiveVerifier(true); +#endif +} + +bool ActiveVerifier::CloseHandle(HANDLE handle) { + if (!enabled_) + return CloseHandleWrapper(handle); + + AutoNativeLock lock(*lock_); + closing_ = true; CloseHandleWrapper(handle); - g_closing = false; + closing_ = false; return true; } -// Static. -void VerifierTraits::StartTracking(HANDLE handle, const void* owner, +void ActiveVerifier::StartTracking(HANDLE handle, const void* owner, const void* pc1, const void* pc2) { - if (!g_verifier_enabled) + if (!enabled_) return; + // Idea here is to make our handles non-closable until we close it ourselves. + // Handles provided could be totally fabricated especially through our + // unittest, we are ignoring that for now by not checking return value. + ::SetHandleInformation(handle, HANDLE_FLAG_PROTECT_FROM_CLOSE, + HANDLE_FLAG_PROTECT_FROM_CLOSE); + // Grab the thread id before the lock. DWORD thread_id = GetCurrentThreadId(); - AutoNativeLock lock(g_lock.Get()); + AutoNativeLock lock(*lock_); Info handle_info = { owner, pc1, pc2, thread_id }; std::pair<HANDLE, Info> item(handle, handle_info); - std::pair<HandleMap::iterator, bool> result = g_handle_map.Get().insert(item); + std::pair<HandleMap::iterator, bool> result = map_.insert(item); if (!result.second) { Info other = result.first->second; - debug::Alias(&other); + base::debug::Alias(&other); CHECK(false); } } -// Static. -void VerifierTraits::StopTracking(HANDLE handle, const void* owner, +void ActiveVerifier::StopTracking(HANDLE handle, const void* owner, const void* pc1, const void* pc2) { - if (!g_verifier_enabled) + if (!enabled_) return; - AutoNativeLock lock(g_lock.Get()); - HandleMap::iterator i = g_handle_map.Get().find(handle); - if (i == g_handle_map.Get().end()) + // We expect handle to be protected till this point. + DWORD flags = 0; + if (::GetHandleInformation(handle, &flags)) { + CHECK_NE(0U, (flags & HANDLE_FLAG_PROTECT_FROM_CLOSE)); + + // Unprotect handle so that it could be closed. + ::SetHandleInformation(handle, HANDLE_FLAG_PROTECT_FROM_CLOSE, 0); + } + + AutoNativeLock lock(*lock_); + HandleMap::iterator i = map_.find(handle); + if (i == map_.end()) CHECK(false); Info other = i->second; if (other.owner != owner) { - debug::Alias(&other); + base::debug::Alias(&other); CHECK(false); } - g_handle_map.Get().erase(i); + map_.erase(i); } -void DisableHandleVerifier() { - g_verifier_enabled = false; +void ActiveVerifier::Disable() { + enabled_ = false; } -void OnHandleBeingClosed(HANDLE handle) { - AutoNativeLock lock(g_lock.Get()); - if (g_closing) +void ActiveVerifier::OnHandleBeingClosed(HANDLE handle) { + AutoNativeLock lock(*lock_); + if (closing_) return; - HandleMap::iterator i = g_handle_map.Get().find(handle); - if (i == g_handle_map.Get().end()) + HandleMap::iterator i = map_.find(handle); + if (i == map_.end()) return; Info other = i->second; - debug::Alias(&other); + base::debug::Alias(&other); CHECK(false); } +} // namespace + +void* GetHandleVerifier() { + return g_active_verifier; +} + +namespace base { +namespace win { + +// Static. +bool HandleTraits::CloseHandle(HANDLE handle) { + return ActiveVerifier::Get()->CloseHandle(handle); +} + +// Static. +void VerifierTraits::StartTracking(HANDLE handle, const void* owner, + const void* pc1, const void* pc2) { + return ActiveVerifier::Get()->StartTracking(handle, owner, pc1, pc2); +} + +// Static. +void VerifierTraits::StopTracking(HANDLE handle, const void* owner, + const void* pc1, const void* pc2) { + return ActiveVerifier::Get()->StopTracking(handle, owner, pc1, pc2); +} + +void DisableHandleVerifier() { + return ActiveVerifier::Get()->Disable(); +} + +void OnHandleBeingClosed(HANDLE handle) { + return ActiveVerifier::Get()->OnHandleBeingClosed(handle); +} + } // namespace win } // namespace base diff --git a/chromium/base/win/scoped_handle.h b/chromium/base/win/scoped_handle.h index db24f4bb362..97fd7a5c79a 100644 --- a/chromium/base/win/scoped_handle.h +++ b/chromium/base/win/scoped_handle.h @@ -174,4 +174,4 @@ void BASE_EXPORT OnHandleBeingClosed(HANDLE handle); } // namespace win } // namespace base -#endif // BASE_SCOPED_HANDLE_WIN_H_ +#endif // BASE_WIN_SCOPED_HANDLE_H_ diff --git a/chromium/base/win/scoped_hdc.h b/chromium/base/win/scoped_hdc.h index 9aead967929..2452067dfb3 100644 --- a/chromium/base/win/scoped_hdc.h +++ b/chromium/base/win/scoped_hdc.h @@ -68,7 +68,7 @@ class CreateDCTraits { DISALLOW_IMPLICIT_CONSTRUCTORS(CreateDCTraits); }; -typedef GenericScopedHandle<CreateDCTraits, VerifierTraits> ScopedCreateDC; +typedef GenericScopedHandle<CreateDCTraits, DummyVerifierTraits> ScopedCreateDC; } // namespace win } // namespace base diff --git a/chromium/base/win/scoped_process_information.cc b/chromium/base/win/scoped_process_information.cc index bb2463774a8..634a538eece 100644 --- a/chromium/base/win/scoped_process_information.cc +++ b/chromium/base/win/scoped_process_information.cc @@ -6,6 +6,7 @@ #include "base/logging.h" #include "base/win/scoped_handle.h" +#include "base/win/windows_version.h" namespace base { namespace win { @@ -20,11 +21,49 @@ bool CheckAndDuplicateHandle(HANDLE source, ScopedHandle* target) { return true; HANDLE temp = NULL; - if (!::DuplicateHandle(::GetCurrentProcess(), source, - ::GetCurrentProcess(), &temp, 0, FALSE, - DUPLICATE_SAME_ACCESS)) { - DPLOG(ERROR) << "Failed to duplicate a handle."; - return false; + + // TODO(shrikant): Remove following code as soon as we gather some + // information regarding AppContainer related DuplicateHandle failures that + // only seem to happen on certain machine and only random launches (normally + // renderer launches seem to succeed even on those machines.) + if (base::win::GetVersion() == base::win::VERSION_WIN8 || + base::win::GetVersion() == base::win::VERSION_WIN8_1) { + typedef LONG (WINAPI *NtDuplicateObject)( + IN HANDLE SourceProcess, + IN HANDLE SourceHandle, + IN HANDLE TargetProcess, + OUT PHANDLE TargetHandle, + IN ACCESS_MASK DesiredAccess, + IN ULONG Attributes, + IN ULONG Options); + + typedef ULONG (WINAPI *RtlNtStatusToDosError)(IN LONG Status); + + NtDuplicateObject nt_duplicate_object = + reinterpret_cast<NtDuplicateObject>(::GetProcAddress( + GetModuleHandle(L"ntdll.dll"), "NtDuplicateObject")); + if (nt_duplicate_object != NULL) { + LONG status = nt_duplicate_object(::GetCurrentProcess(), source, + ::GetCurrentProcess(), &temp, + 0, FALSE, DUPLICATE_SAME_ACCESS); + if (status < 0) { + DPLOG(ERROR) << "Failed to duplicate a handle."; + RtlNtStatusToDosError ntstatus_to_doserror = + reinterpret_cast<RtlNtStatusToDosError>(::GetProcAddress( + GetModuleHandle(L"ntdll.dll"), "RtlNtStatusToDosError")); + if (ntstatus_to_doserror != NULL) { + ::SetLastError(ntstatus_to_doserror(status)); + } + return false; + } + } + } else { + if (!::DuplicateHandle(::GetCurrentProcess(), source, + ::GetCurrentProcess(), &temp, 0, FALSE, + DUPLICATE_SAME_ACCESS)) { + DPLOG(ERROR) << "Failed to duplicate a handle."; + return false; + } } target->Set(temp); return true; diff --git a/chromium/base/win/scoped_process_information_unittest.cc b/chromium/base/win/scoped_process_information_unittest.cc index ccfa7290a97..614504d414e 100644 --- a/chromium/base/win/scoped_process_information_unittest.cc +++ b/chromium/base/win/scoped_process_information_unittest.cc @@ -8,6 +8,7 @@ #include "base/command_line.h" #include "base/process/kill.h" +#include "base/process/process.h" #include "base/test/multiprocess_test.h" #include "base/win/scoped_process_information.h" #include "testing/multiprocess_func_list.h" @@ -135,13 +136,13 @@ TEST_F(ScopedProcessInformationTest, Duplicate) { // Validate that we have separate handles that are good. int exit_code = 0; - ASSERT_TRUE(base::WaitForExitCode(process_info.TakeProcessHandle(), - &exit_code)); + base::Process process(process_info.TakeProcessHandle()); + ASSERT_TRUE(process.WaitForExit(&exit_code)); ASSERT_EQ(7, exit_code); exit_code = 0; - ASSERT_TRUE(base::WaitForExitCode(duplicate.TakeProcessHandle(), - &exit_code)); + base::Process dup_process(duplicate.TakeProcessHandle()); + ASSERT_TRUE(dup_process.WaitForExit(&exit_code)); ASSERT_EQ(7, exit_code); ASSERT_TRUE(::CloseHandle(process_info.TakeThreadHandle())); diff --git a/chromium/base/win/scoped_propvariant.h b/chromium/base/win/scoped_propvariant.h index 711d51adb40..62cc6a6d152 100644 --- a/chromium/base/win/scoped_propvariant.h +++ b/chromium/base/win/scoped_propvariant.h @@ -41,8 +41,7 @@ class ScopedPropVariant { } const PROPVARIANT& get() const { return pv_; } - - const PROPVARIANT* operator&() const { return &pv_; } + const PROPVARIANT* ptr() const { return &pv_; } private: PROPVARIANT pv_; diff --git a/chromium/base/win/scoped_variant.h b/chromium/base/win/scoped_variant.h index b6e65798fbd..322fcf71e55 100644 --- a/chromium/base/win/scoped_variant.h +++ b/chromium/base/win/scoped_variant.h @@ -122,9 +122,7 @@ class BASE_EXPORT ScopedVariant { // This support is necessary for the V_XYZ (e.g. V_BSTR) set of macros to // work properly but still doesn't allow modifications since we want control // over that. - const VARIANT* operator&() const { - return &var_; - } + const VARIANT* ptr() const { return &var_; } // Like other scoped classes (e.g scoped_refptr, ScopedComPtr, ScopedBstr) // we support the assignment operator for the type we wrap. @@ -161,6 +159,6 @@ class BASE_EXPORT ScopedVariant { }; } // namespace win -} // namesoace base +} // namespace base #endif // BASE_WIN_SCOPED_VARIANT_H_ diff --git a/chromium/base/win/scoped_variant_unittest.cc b/chromium/base/win/scoped_variant_unittest.cc index 1f017cf94ab..d530d5bcd91 100644 --- a/chromium/base/win/scoped_variant_unittest.cc +++ b/chromium/base/win/scoped_variant_unittest.cc @@ -27,34 +27,34 @@ class FakeComObject : public IDispatch { FakeComObject() : ref_(0) { } - STDMETHOD_(DWORD, AddRef)() { + STDMETHOD_(DWORD, AddRef)() override { ref_++; return ref_; } - STDMETHOD_(DWORD, Release)() { + STDMETHOD_(DWORD, Release)() override { ref_--; return ref_; } - STDMETHOD(QueryInterface)(REFIID, void**) { - return E_NOTIMPL; - } + STDMETHOD(QueryInterface)(REFIID, void**) override { return E_NOTIMPL; } - STDMETHOD(GetTypeInfoCount)(UINT*) { - return E_NOTIMPL; - } + STDMETHOD(GetTypeInfoCount)(UINT*) override { return E_NOTIMPL; } - STDMETHOD(GetTypeInfo)(UINT, LCID, ITypeInfo**) { - return E_NOTIMPL; - } + STDMETHOD(GetTypeInfo)(UINT, LCID, ITypeInfo**) override { return E_NOTIMPL; } - STDMETHOD(GetIDsOfNames)(REFIID, LPOLESTR*, UINT, LCID, DISPID*) { + STDMETHOD(GetIDsOfNames)(REFIID, LPOLESTR*, UINT, LCID, DISPID*) override { return E_NOTIMPL; } - STDMETHOD(Invoke)(DISPID, REFIID, LCID, WORD, DISPPARAMS*, VARIANT*, - EXCEPINFO*, UINT*) { + STDMETHOD(Invoke)(DISPID, + REFIID, + LCID, + WORD, + DISPPARAMS*, + VARIANT*, + EXCEPINFO*, + UINT*) override { return E_NOTIMPL; } @@ -72,41 +72,41 @@ class FakeComObject : public IDispatch { TEST(ScopedVariantTest, ScopedVariant) { ScopedVariant var; EXPECT_TRUE(var.type() == VT_EMPTY); - // V_BSTR(&var) = NULL; <- NOTE: Assignment like that is not supported + // V_BSTR(var.ptr()) = NULL; <- NOTE: Assignment like that is not supported. ScopedVariant var_bstr(L"VT_BSTR"); - EXPECT_EQ(VT_BSTR, V_VT(&var_bstr)); - EXPECT_TRUE(V_BSTR(&var_bstr) != NULL); // can't use EXPECT_NE for BSTR + EXPECT_EQ(VT_BSTR, V_VT(var_bstr.ptr())); + EXPECT_TRUE(V_BSTR(var_bstr.ptr()) != NULL); // can't use EXPECT_NE for BSTR var_bstr.Reset(); - EXPECT_NE(VT_BSTR, V_VT(&var_bstr)); + EXPECT_NE(VT_BSTR, V_VT(var_bstr.ptr())); var_bstr.Set(kTestString2); - EXPECT_EQ(VT_BSTR, V_VT(&var_bstr)); + EXPECT_EQ(VT_BSTR, V_VT(var_bstr.ptr())); VARIANT tmp = var_bstr.Release(); - EXPECT_EQ(VT_EMPTY, V_VT(&var_bstr)); + EXPECT_EQ(VT_EMPTY, V_VT(var_bstr.ptr())); EXPECT_EQ(VT_BSTR, V_VT(&tmp)); EXPECT_EQ(0, lstrcmp(V_BSTR(&tmp), kTestString2)); var.Reset(tmp); - EXPECT_EQ(VT_BSTR, V_VT(&var)); - EXPECT_EQ(0, lstrcmpW(V_BSTR(&var), kTestString2)); + EXPECT_EQ(VT_BSTR, V_VT(var.ptr())); + EXPECT_EQ(0, lstrcmpW(V_BSTR(var.ptr()), kTestString2)); var_bstr.Swap(var); - EXPECT_EQ(VT_EMPTY, V_VT(&var)); - EXPECT_EQ(VT_BSTR, V_VT(&var_bstr)); - EXPECT_EQ(0, lstrcmpW(V_BSTR(&var_bstr), kTestString2)); + EXPECT_EQ(VT_EMPTY, V_VT(var.ptr())); + EXPECT_EQ(VT_BSTR, V_VT(var_bstr.ptr())); + EXPECT_EQ(0, lstrcmpW(V_BSTR(var_bstr.ptr()), kTestString2)); var_bstr.Reset(); // Test the Compare and Copy routines. GiveMeAVariant(var_bstr.Receive()); - ScopedVariant var_bstr2(V_BSTR(&var_bstr)); + ScopedVariant var_bstr2(V_BSTR(var_bstr.ptr())); EXPECT_EQ(0, var_bstr.Compare(var_bstr2)); var_bstr2.Reset(); EXPECT_NE(0, var_bstr.Compare(var_bstr2)); var_bstr2.Reset(var_bstr.Copy()); EXPECT_EQ(0, var_bstr.Compare(var_bstr2)); var_bstr2.Reset(); - var_bstr2.Set(V_BSTR(&var_bstr)); + var_bstr2.Set(V_BSTR(var_bstr.ptr())); EXPECT_EQ(0, var_bstr.Compare(var_bstr2)); var_bstr2.Reset(); var_bstr.Reset(); @@ -119,7 +119,7 @@ TEST(ScopedVariantTest, ScopedVariant) { var.Reset(); var.SetDate(date); EXPECT_EQ(VT_DATE, var.type()); - EXPECT_EQ(date, V_DATE(&var)); + EXPECT_EQ(date, V_DATE(var.ptr())); // Simple setter tests. These do not require resetting the variant // after each test since the variant type is not "leakable" (i.e. doesn't @@ -128,75 +128,75 @@ TEST(ScopedVariantTest, ScopedVariant) { // We need static cast here since char defaults to int (!?). var.Set(static_cast<int8>('v')); EXPECT_EQ(VT_I1, var.type()); - EXPECT_EQ('v', V_I1(&var)); + EXPECT_EQ('v', V_I1(var.ptr())); var.Set(static_cast<short>(123)); EXPECT_EQ(VT_I2, var.type()); - EXPECT_EQ(123, V_I2(&var)); + EXPECT_EQ(123, V_I2(var.ptr())); var.Set(static_cast<int32>(123)); EXPECT_EQ(VT_I4, var.type()); - EXPECT_EQ(123, V_I4(&var)); + EXPECT_EQ(123, V_I4(var.ptr())); var.Set(static_cast<int64>(123)); EXPECT_EQ(VT_I8, var.type()); - EXPECT_EQ(123, V_I8(&var)); + EXPECT_EQ(123, V_I8(var.ptr())); var.Set(static_cast<uint8>(123)); EXPECT_EQ(VT_UI1, var.type()); - EXPECT_EQ(123, V_UI1(&var)); + EXPECT_EQ(123, V_UI1(var.ptr())); var.Set(static_cast<unsigned short>(123)); EXPECT_EQ(VT_UI2, var.type()); - EXPECT_EQ(123, V_UI2(&var)); + EXPECT_EQ(123, V_UI2(var.ptr())); var.Set(static_cast<uint32>(123)); EXPECT_EQ(VT_UI4, var.type()); - EXPECT_EQ(123, V_UI4(&var)); + EXPECT_EQ(123, V_UI4(var.ptr())); var.Set(static_cast<uint64>(123)); EXPECT_EQ(VT_UI8, var.type()); - EXPECT_EQ(123, V_UI8(&var)); + EXPECT_EQ(123, V_UI8(var.ptr())); var.Set(123.123f); EXPECT_EQ(VT_R4, var.type()); - EXPECT_EQ(123.123f, V_R4(&var)); + EXPECT_EQ(123.123f, V_R4(var.ptr())); var.Set(static_cast<double>(123.123)); EXPECT_EQ(VT_R8, var.type()); - EXPECT_EQ(123.123, V_R8(&var)); + EXPECT_EQ(123.123, V_R8(var.ptr())); var.Set(true); EXPECT_EQ(VT_BOOL, var.type()); - EXPECT_EQ(VARIANT_TRUE, V_BOOL(&var)); + EXPECT_EQ(VARIANT_TRUE, V_BOOL(var.ptr())); var.Set(false); EXPECT_EQ(VT_BOOL, var.type()); - EXPECT_EQ(VARIANT_FALSE, V_BOOL(&var)); + EXPECT_EQ(VARIANT_FALSE, V_BOOL(var.ptr())); // Com interface tests var.Set(static_cast<IDispatch*>(NULL)); EXPECT_EQ(VT_DISPATCH, var.type()); - EXPECT_EQ(NULL, V_DISPATCH(&var)); + EXPECT_EQ(NULL, V_DISPATCH(var.ptr())); var.Reset(); var.Set(static_cast<IUnknown*>(NULL)); EXPECT_EQ(VT_UNKNOWN, var.type()); - EXPECT_EQ(NULL, V_UNKNOWN(&var)); + EXPECT_EQ(NULL, V_UNKNOWN(var.ptr())); var.Reset(); FakeComObject faker; EXPECT_EQ(0, faker.ref_count()); var.Set(static_cast<IDispatch*>(&faker)); EXPECT_EQ(VT_DISPATCH, var.type()); - EXPECT_EQ(&faker, V_DISPATCH(&var)); + EXPECT_EQ(&faker, V_DISPATCH(var.ptr())); EXPECT_EQ(1, faker.ref_count()); var.Reset(); EXPECT_EQ(0, faker.ref_count()); var.Set(static_cast<IUnknown*>(&faker)); EXPECT_EQ(VT_UNKNOWN, var.type()); - EXPECT_EQ(&faker, V_UNKNOWN(&var)); + EXPECT_EQ(&faker, V_UNKNOWN(var.ptr())); EXPECT_EQ(1, faker.ref_count()); var.Reset(); EXPECT_EQ(0, faker.ref_count()); @@ -204,7 +204,7 @@ TEST(ScopedVariantTest, ScopedVariant) { { ScopedVariant disp_var(&faker); EXPECT_EQ(VT_DISPATCH, disp_var.type()); - EXPECT_EQ(&faker, V_DISPATCH(&disp_var)); + EXPECT_EQ(&faker, V_DISPATCH(disp_var.ptr())); EXPECT_EQ(1, faker.ref_count()); } EXPECT_EQ(0, faker.ref_count()); @@ -223,7 +223,7 @@ TEST(ScopedVariantTest, ScopedVariant) { { ScopedVariant unk_var(static_cast<IUnknown*>(&faker)); EXPECT_EQ(VT_UNKNOWN, unk_var.type()); - EXPECT_EQ(&faker, V_UNKNOWN(&unk_var)); + EXPECT_EQ(&faker, V_UNKNOWN(unk_var.ptr())); EXPECT_EQ(1, faker.ref_count()); } EXPECT_EQ(0, faker.ref_count()); @@ -240,7 +240,7 @@ TEST(ScopedVariantTest, ScopedVariant) { { ScopedVariant number(123); EXPECT_EQ(VT_I4, number.type()); - EXPECT_EQ(123, V_I4(&number)); + EXPECT_EQ(123, V_I4(number.ptr())); } // SAFEARRAY tests @@ -253,7 +253,7 @@ TEST(ScopedVariantTest, ScopedVariant) { var.Set(sa); EXPECT_TRUE(ScopedVariant::IsLeakableVarType(var.type())); EXPECT_EQ(VT_ARRAY | VT_UI1, var.type()); - EXPECT_EQ(sa, V_ARRAY(&var)); + EXPECT_EQ(sa, V_ARRAY(var.ptr())); // The array is destroyed in the destructor of var. } diff --git a/chromium/base/win/shortcut.cc b/chromium/base/win/shortcut.cc index eef299b9440..f8b2182b286 100644 --- a/chromium/base/win/shortcut.cc +++ b/chromium/base/win/shortcut.cc @@ -33,7 +33,7 @@ void InitializeShortcutInterfaces( i_persist_file->Release(); if (FAILED(i_shell_link->CreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER)) || - FAILED(i_persist_file->QueryFrom(*i_shell_link)) || + FAILED(i_persist_file->QueryFrom(i_shell_link->get())) || (shortcut && FAILED((*i_persist_file)->Load(shortcut, STGM_READWRITE)))) { i_shell_link->Release(); i_persist_file->Release(); @@ -42,6 +42,13 @@ void InitializeShortcutInterfaces( } // namespace +ShortcutProperties::ShortcutProperties() + : icon_index(-1), dual_mode(false), options(0U) { +} + +ShortcutProperties::~ShortcutProperties() { +} + bool CreateOrUpdateShortcutLink(const FilePath& shortcut_path, const ShortcutProperties& properties, ShortcutOperation operation) { @@ -129,15 +136,17 @@ bool CreateOrUpdateShortcutLink(const FilePath& shortcut_path, if ((has_app_id || has_dual_mode) && GetVersion() >= VERSION_WIN7) { ScopedComPtr<IPropertyStore> property_store; - if (FAILED(property_store.QueryFrom(i_shell_link)) || !property_store.get()) + if (FAILED(property_store.QueryFrom(i_shell_link.get())) || + !property_store.get()) return false; if (has_app_id && - !SetAppIdForPropertyStore(property_store, properties.app_id.c_str())) { + !SetAppIdForPropertyStore(property_store.get(), + properties.app_id.c_str())) { return false; } if (has_dual_mode && - !SetBooleanValueForPropertyStore(property_store, + !SetBooleanValueForPropertyStore(property_store.get(), PKEY_AppUserModel_IsDualMode, properties.dual_mode)) { return false; @@ -192,7 +201,7 @@ bool ResolveShortcutProperties(const FilePath& shortcut_path, ScopedComPtr<IPersistFile> persist; // Query IShellLink for the IPersistFile interface. - if (FAILED(persist.QueryFrom(i_shell_link))) + if (FAILED(persist.QueryFrom(i_shell_link.get()))) return false; // Load the shell link. @@ -239,7 +248,7 @@ bool ResolveShortcutProperties(const FilePath& shortcut_path, if ((options & ShortcutProperties::PROPERTIES_WIN7) && GetVersion() >= VERSION_WIN7) { ScopedComPtr<IPropertyStore> property_store; - if (FAILED(property_store.QueryFrom(i_shell_link))) + if (FAILED(property_store.QueryFrom(i_shell_link.get()))) return false; if (options & ShortcutProperties::PROPERTIES_APP_ID) { @@ -312,8 +321,8 @@ bool TaskbarPinShortcutLink(const wchar_t* shortcut) { if (GetVersion() < VERSION_WIN7) return false; - int result = reinterpret_cast<int>(ShellExecute(NULL, L"taskbarpin", shortcut, - NULL, NULL, 0)); + intptr_t result = reinterpret_cast<intptr_t>( + ShellExecute(NULL, L"taskbarpin", shortcut, NULL, NULL, 0)); return result > 32; } @@ -324,8 +333,8 @@ bool TaskbarUnpinShortcutLink(const wchar_t* shortcut) { if (GetVersion() < VERSION_WIN7) return false; - int result = reinterpret_cast<int>(ShellExecute(NULL, L"taskbarunpin", - shortcut, NULL, NULL, 0)); + intptr_t result = reinterpret_cast<intptr_t>( + ShellExecute(NULL, L"taskbarunpin", shortcut, NULL, NULL, 0)); return result > 32; } diff --git a/chromium/base/win/shortcut.h b/chromium/base/win/shortcut.h index 2feb4c3aa9e..6f7d10c8ee7 100644 --- a/chromium/base/win/shortcut.h +++ b/chromium/base/win/shortcut.h @@ -7,6 +7,7 @@ #include <windows.h> +#include "base/base_export.h" #include "base/files/file_path.h" #include "base/logging.h" #include "base/strings/string16.h" @@ -29,7 +30,7 @@ enum ShortcutOperation { // creation/update, others will be ignored. // Callers are encouraged to use the setters provided which take care of // setting |options| as desired. -struct ShortcutProperties { +struct BASE_EXPORT ShortcutProperties { enum IndividualProperties { PROPERTIES_TARGET = 1U << 0, PROPERTIES_WORKING_DIR = 1U << 1, @@ -48,7 +49,8 @@ struct ShortcutProperties { PROPERTIES_ALL = PROPERTIES_BASIC | PROPERTIES_WIN7 }; - ShortcutProperties() : icon_index(-1), dual_mode(false), options(0U) {} + ShortcutProperties(); + ~ShortcutProperties(); void set_target(const FilePath& target_in) { target = target_in; diff --git a/chromium/base/win/shortcut_unittest.cc b/chromium/base/win/shortcut_unittest.cc index 4bb227a4701..794be2364f4 100644 --- a/chromium/base/win/shortcut_unittest.cc +++ b/chromium/base/win/shortcut_unittest.cc @@ -25,7 +25,7 @@ static const char kFileContents2[] = "This is another target."; class ShortcutTest : public testing::Test { protected: - virtual void SetUp() override { + void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); ASSERT_TRUE(temp_dir_2_.CreateUniqueTempDir()); diff --git a/chromium/base/win/startup_information.h b/chromium/base/win/startup_information.h index 7cef81f2c8f..3f18ee58efb 100644 --- a/chromium/base/win/startup_information.h +++ b/chromium/base/win/startup_information.h @@ -24,6 +24,8 @@ class BASE_EXPORT StartupInformation { bool InitializeProcThreadAttributeList(DWORD attribute_count); // Sets one entry in the initialized attribute list. + // |value| needs to live at least as long as the StartupInformation object + // this is called on. bool UpdateProcThreadAttribute(DWORD_PTR attribute, void* value, size_t size); @@ -45,5 +47,4 @@ class BASE_EXPORT StartupInformation { } // namespace win } // namespace base -#endif // BASE_WIN_SCOPED_STARTUP_INFO_EX_H_ - +#endif // BASE_WIN_STARTUP_INFORMATION_H_ diff --git a/chromium/base/win/win_util.cc b/chromium/base/win/win_util.cc index f46242d3d25..c5b06c48f8a 100644 --- a/chromium/base/win/win_util.cc +++ b/chromium/base/win/win_util.cc @@ -57,7 +57,7 @@ const wchar_t kWindows8OSKRegPath[] = // Returns true if a physical keyboard is detected on Windows 8 and up. // Uses the Setup APIs to enumerate the attached keyboards and returns true -// if the keyboard count is more than 1. While this will work in most cases +// if the keyboard count is 1 or more.. While this will work in most cases // it won't work if there are devices which expose keyboard interfaces which // are attached to the machine. bool IsKeyboardPresentOnSlate() { @@ -70,6 +70,53 @@ bool IsKeyboardPresentOnSlate() { return true; } + // If the device is docked, the user is treating the device as a PC. + if (GetSystemMetrics(SM_SYSTEMDOCKED) != 0) + return true; + + // To determine whether a keyboard is present on the device, we do the + // following:- + // 1. Check whether the device supports auto rotation. If it does then + // it possibly supports flipping from laptop to slate mode. If it + // does not support auto rotation, then we assume it is a desktop + // or a normal laptop and assume that there is a keyboard. + + // 2. If the device supports auto rotation, then we get its platform role + // and check the system metric SM_CONVERTIBLESLATEMODE to see if it is + // being used in slate mode. If yes then we return false here to ensure + // that the OSK is displayed. + + // 3. If step 1 and 2 fail then we check attached keyboards and return true + // if we find ACPI\* or HID\VID* keyboards. + + typedef BOOL (WINAPI* GetAutoRotationState)(PAR_STATE state); + + GetAutoRotationState get_rotation_state = + reinterpret_cast<GetAutoRotationState>(::GetProcAddress( + GetModuleHandle(L"user32.dll"), "GetAutoRotationState")); + + if (get_rotation_state) { + AR_STATE auto_rotation_state = AR_ENABLED; + get_rotation_state(&auto_rotation_state); + if ((auto_rotation_state & AR_NOSENSOR) || + (auto_rotation_state & AR_NOT_SUPPORTED)) { + // 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; + } + } + + // Check if the device is being used as a laptop or a tablet. This can be + // checked by first checking the role of the device and then the + // corresponding system metric (SM_CONVERTIBLESLATEMODE). If it is being used + // as a tablet then we want the OSK to show up. + POWER_PLATFORM_ROLE role = PowerDeterminePlatformRole(); + + if (((role == PlatformRoleMobile) || (role == PlatformRoleSlate)) && + (GetSystemMetrics(SM_CONVERTIBLESLATEMODE) == 0)) + return false; + const GUID KEYBOARD_CLASS_GUID = { 0x4D36E96B, 0xE325, 0x11CE, { 0xBF, 0xC1, 0x08, 0x00, 0x2B, 0xE1, 0x03, 0x18 } }; @@ -89,6 +136,7 @@ bool IsKeyboardPresentOnSlate() { device_info_data.cbSize = sizeof(device_info_data); if (!SetupDiEnumDeviceInfo(device_info, i, &device_info_data)) break; + // Get the device ID. wchar_t device_id[MAX_DEVICE_ID_LEN]; CONFIGRET status = CM_Get_Device_ID(device_info_data.DevInst, @@ -96,20 +144,18 @@ bool IsKeyboardPresentOnSlate() { MAX_DEVICE_ID_LEN, 0); if (status == CR_SUCCESS) { - // To reduce the scope of the hack we only look for PNP and HID - // keyboards. - if (StartsWith(L"ACPI\\PNP", device_id, false) || - StartsWith(L"HID\\VID", device_id, false)) { + // 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++; } } } - // On a Windows machine, the API's always report 1 keyboard at least - // regardless of whether the machine has a keyboard attached or not. - // The heuristic we are using is to check the count and return true - // if the API's report more than one keyboard. Please note that this + // The heuristic we are using is to check the count of keyboards and return + // true if the API's report one or more keyboards. Please note that this // will break for non keyboard devices which expose a keyboard PDO. - return keyboard_count > 1; + return keyboard_count >= 1; } } // namespace @@ -382,7 +428,7 @@ bool DisplayVirtualKeyboard() { NULL, NULL, SW_SHOW); - return reinterpret_cast<int>(ret) > 32; + return reinterpret_cast<intptr_t>(ret) > 32; } bool DismissVirtualKeyboard() { @@ -448,29 +494,3 @@ bool MaybeHasSHA256Support() { } // namespace win } // namespace base - -#ifdef _MSC_VER - -// There are optimizer bugs in x86 VS2012 pre-Update 1. -#if _MSC_VER == 1700 && defined _M_IX86 && _MSC_FULL_VER < 170051106 - -#pragma message("Relevant defines:") -#define __STR2__(x) #x -#define __STR1__(x) __STR2__(x) -#define __PPOUT__(x) "#define " #x " " __STR1__(x) -#if defined(_M_IX86) - #pragma message(__PPOUT__(_M_IX86)) -#endif -#if defined(_M_X64) - #pragma message(__PPOUT__(_M_X64)) -#endif -#if defined(_MSC_FULL_VER) - #pragma message(__PPOUT__(_MSC_FULL_VER)) -#endif - -#pragma message("Visual Studio 2012 x86 must be updated to at least Update 1") -#error Must install Update 1 to Visual Studio 2012. -#endif - -#endif // _MSC_VER - diff --git a/chromium/base/win/win_util_unittest.cc b/chromium/base/win/win_util_unittest.cc index 8300c1623b0..24141cd68a0 100644 --- a/chromium/base/win/win_util_unittest.cc +++ b/chromium/base/win/win_util_unittest.cc @@ -49,9 +49,9 @@ TEST(BaseWinUtilTest, TestGetUserSidString) { TEST(BaseWinUtilTest, TestGetNonClientMetrics) { NONCLIENTMETRICS_XP metrics = {0}; GetNonClientMetrics(&metrics); - EXPECT_TRUE(metrics.cbSize > 0); - EXPECT_TRUE(metrics.iScrollWidth > 0); - EXPECT_TRUE(metrics.iScrollHeight > 0); + EXPECT_GT(metrics.cbSize, 0u); + EXPECT_GT(metrics.iScrollWidth, 0); + EXPECT_GT(metrics.iScrollHeight, 0); } } // namespace win diff --git a/chromium/base/win/windows_version.cc b/chromium/base/win/windows_version.cc index a99cd1840ac..fc2def39194 100644 --- a/chromium/base/win/windows_version.cc +++ b/chromium/base/win/windows_version.cc @@ -64,6 +64,8 @@ OSInfo::OSInfo() version_ = VERSION_WIN8_1; break; } + } else if (version_number_.major == 10) { + version_ = VERSION_WIN10; } else if (version_number_.major > 6) { NOTREACHED(); version_ = VERSION_WIN_LAST; @@ -84,7 +86,7 @@ OSInfo::OSInfo() GetProductInfoPtr get_product_info; DWORD os_type; - if (version_info.dwMajorVersion == 6) { + if (version_info.dwMajorVersion == 6 || version_info.dwMajorVersion == 10) { // Only present on Vista+. get_product_info = reinterpret_cast<GetProductInfoPtr>( ::GetProcAddress(::GetModuleHandle(L"kernel32.dll"), "GetProductInfo")); diff --git a/chromium/base/win/windows_version.h b/chromium/base/win/windows_version.h index e51840b0db8..a52e64e0742 100644 --- a/chromium/base/win/windows_version.h +++ b/chromium/base/win/windows_version.h @@ -26,7 +26,8 @@ enum Version { VERSION_VISTA, // Also includes Windows Server 2008. VERSION_WIN7, // Also includes Windows Server 2008 R2. VERSION_WIN8, // Also includes Windows Server 2012. - VERSION_WIN8_1, // Code named Windows Blue + VERSION_WIN8_1, // Also includes Windows Server 2012 R2. + VERSION_WIN10, // Also includes Windows 10 Server. VERSION_WIN_LAST, // Indicates error condition. }; |