diff options
author | Jocelyn Turcotte <jocelyn.turcotte@digia.com> | 2014-08-08 14:30:41 +0200 |
---|---|---|
committer | Jocelyn Turcotte <jocelyn.turcotte@digia.com> | 2014-08-12 13:49:54 +0200 |
commit | ab0a50979b9eb4dfa3320eff7e187e41efedf7a9 (patch) | |
tree | 498dfb8a97ff3361a9f7486863a52bb4e26bb898 /chromium/base | |
parent | 4ce69f7403811819800e7c5ae1318b2647e778d1 (diff) |
Update Chromium to beta version 37.0.2062.68
Change-Id: I188e3b5aff1bec75566014291b654eb19f5bc8ca
Reviewed-by: Andras Becsi <andras.becsi@digia.com>
Diffstat (limited to 'chromium/base')
587 files changed, 27035 insertions, 16258 deletions
diff --git a/chromium/base/BUILD.gn b/chromium/base/BUILD.gn new file mode 100644 index 00000000000..53084d5e8f6 --- /dev/null +++ b/chromium/base/BUILD.gn @@ -0,0 +1,1373 @@ +# 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. + +import("//build/config/android/rules.gni") +import("//build/config/ui.gni") + +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/application_status_listener.cc", + "android/application_status_listener.h", + "android/base_jni_registrar.cc", + "android/base_jni_registrar.h", + "android/build_info.cc", + "android/build_info.h", + "android/command_line_android.cc", + "android/command_line_android.h", + "android/content_uri_utils.cc", + "android/content_uri_utils.h", + "android/cpu_features.cc", + "android/event_log.cc", + "android/event_log.h", + "android/fifo_utils.cc", + "android/fifo_utils.h", + "android/important_file_writer_android.cc", + "android/important_file_writer_android.h", + "android/scoped_java_ref.cc", + "android/scoped_java_ref.h", + "android/jni_android.cc", + "android/jni_android.h", + "android/jni_array.cc", + "android/jni_array.h", + "android/jni_registrar.cc", + "android/jni_registrar.h", + "android/jni_string.cc", + "android/jni_string.h", + "android/jni_weak_ref.cc", + "android/jni_weak_ref.h", + "android/library_loader/library_loader_hooks.cc", + "android/library_loader/library_loader_hooks.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/sys_utils.cc", + "android/sys_utils.h", + "android/thread_utils.h", + "android/trace_event_binding.cc", + "android/trace_event_binding.h", + "async_socket_io_handler.h", + "async_socket_io_handler_posix.cc", + "async_socket_io_handler_win.cc", + "at_exit.cc", + "at_exit.h", + "atomic_ref_count.h", + "atomic_sequence_num.h", + "atomicops.h", + "atomicops_internals_gcc.h", + "atomicops_internals_mac.h", + "atomicops_internals_tsan.h", + "atomicops_internals_x86_gcc.cc", + "atomicops_internals_x86_gcc.h", + "atomicops_internals_x86_msvc.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", + "basictypes.h", + "big_endian.cc", + "big_endian.h", + "bind.h", + "bind_helpers.cc", + "bind_helpers.h", + "bind_internal.h", + "bind_internal_win.h", + "bits.h", + "build_time.cc", + "build_time.h", + "callback.h", + "callback_helpers.cc", + "callback_helpers.h", + "callback_internal.cc", + "callback_internal.h", + "cancelable_callback.h", + "command_line.cc", + "command_line.h", + "compiler_specific.h", + "containers/hash_tables.h", + "containers/linked_list.h", + "containers/mru_cache.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/crash_logging.cc", + "debug/crash_logging.h", + "debug/debugger.cc", + "debug/debugger.h", + "debug/debugger_posix.cc", + "debug/debugger_win.cc", + # 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/trace_event.h", + "debug/trace_event_android.cc", + "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", + "file_descriptor_posix.h", + "file_util.cc", + "file_util.h", + "file_util_android.cc", + "file_util_linux.cc", + "file_util_mac.mm", + "file_util_posix.cc", + "file_util_win.cc", + "file_version_info.h", + "file_version_info_mac.h", + "file_version_info_mac.mm", + "file_version_info_win.cc", + "file_version_info_win.h", + "files/dir_reader_fallback.h", + "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", + "files/file_enumerator_win.cc", + "files/file_path.cc", + "files/file_path.h", + "files/file_path_constants.cc", + "files/file_path_watcher.cc", + "files/file_path_watcher.h", + "files/file_path_watcher_fsevents.cc", + "files/file_path_watcher_fsevents.h", + "files/file_path_watcher_kqueue.cc", + "files/file_path_watcher_kqueue.h", + "files/file_path_watcher_linux.cc", + "files/file_path_watcher_mac.cc", + "files/file_path_watcher_win.cc", + "files/file_proxy.cc", + "files/file_proxy.h", + "files/file_util_proxy.cc", + "files/file_util_proxy.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", + "files/memory_mapped_file_win.cc", + "files/scoped_file.cc", + "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", + "guid.h", + "guid_posix.cc", + "guid_win.cc", + "hash.cc", + "hash.h", + "id_map.h", + "ini_parser.cc", + "ini_parser.h", + "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", + "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", + "lazy_instance.cc", + "lazy_instance.h", + "location.cc", + "location.h", + "logging.cc", + "logging.h", + "logging_win.cc", + "logging_win.h", + "mac/authorization_util.h", + "mac/authorization_util.mm", + "mac/bind_objc_block.h", + "mac/bundle_locations.h", + "mac/bundle_locations.mm", + "mac/cocoa_protocols.h", + "mac/foundation_util.h", + "mac/foundation_util.mm", + "mac/launch_services_util.cc", + "mac/launch_services_util.h", + "mac/launchd.cc", + "mac/launchd.h", + "mac/libdispatch_task_runner.cc", + "mac/libdispatch_task_runner.h", + "mac/mac_logging.h", + "mac/mac_logging.cc", + "mac/mac_util.h", + "mac/mac_util.mm", + "mac/mach_logging.cc", + "mac/mach_logging.h", + "mac/objc_property_releaser.h", + "mac/objc_property_releaser.mm", + "mac/os_crash_dumps.cc", + "mac/os_crash_dumps.h", + "mac/scoped_aedesc.h", + "mac/scoped_authorizationref.h", + "mac/scoped_block.h", + "mac/scoped_cftyperef.h", + "mac/scoped_ioobject.h", + "mac/scoped_ioplugininterface.h", + "mac/scoped_launch_data.h", + "mac/scoped_mach_port.cc", + "mac/scoped_mach_port.h", + "mac/scoped_mach_vm.cc", + "mac/scoped_mach_vm.h", + "mac/scoped_nsautorelease_pool.h", + "mac/scoped_nsautorelease_pool.mm", + "mac/scoped_nsexception_enabler.h", + "mac/scoped_nsexception_enabler.mm", + "mac/scoped_nsobject.h", + "mac/scoped_sending_event.h", + "mac/scoped_sending_event.mm", + "mac/sdk_forward_declarations.h", + "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_malloc.cc", + "memory/discardable_memory_malloc.h", + "memory/discardable_memory_manager.cc", + "memory/discardable_memory_manager.h", + "memory/discardable_memory_win.cc", + "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_handle.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", + "message_loop/message_loop.h", + "message_loop/message_loop_proxy.cc", + "message_loop/message_loop_proxy.h", + "message_loop/message_loop_proxy_impl.cc", + "message_loop/message_loop_proxy_impl.h", + "message_loop/message_pump.cc", + "message_loop/message_pump.h", + "message_loop/message_pump_android.cc", + "message_loop/message_pump_android.h", + "message_loop/message_pump_default.cc", + "message_loop/message_pump_default.h", + "message_loop/message_pump_glib.cc", + "message_loop/message_pump_glib.h", + "message_loop/message_pump_io_ios.cc", + "message_loop/message_pump_io_ios.h", + "message_loop/message_pump_libevent.cc", + "message_loop/message_pump_libevent.h", + "message_loop/message_pump_mac.h", + "message_loop/message_pump_mac.mm", + "message_loop/message_pump_observer.h", + "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_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", + "observer_list.h", + "observer_list_threadsafe.h", + "os_compat_android.cc", + "os_compat_android.h", + "os_compat_nacl.cc", + "os_compat_nacl.h", + "path_service.cc", + "path_service.h", + "pending_task.cc", + "pending_task.h", + "pickle.cc", + "pickle.h", + "platform_file.cc", + "platform_file.h", + "platform_file_posix.cc", + "platform_file_win.cc", + "port.h", + "posix/eintr_wrapper.h", + "posix/file_descriptor_shuffle.cc", + "posix/global_descriptors.cc", + "posix/global_descriptors.h", + "posix/unix_domain_socket_linux.cc", + "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_ios.mm", + "power_monitor/power_monitor_device_source_mac.mm", + "power_monitor/power_monitor_device_source_posix.cc", + "power_monitor/power_monitor_device_source_win.cc", + "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_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/scoped_profile.cc", + "profiler/scoped_profile.h", + "profiler/alternate_timer.cc", + "profiler/alternate_timer.h", + "profiler/tracked_time.cc", + "profiler/tracked_time.h", + "rand_util.cc", + "rand_util.h", + "rand_util_nacl.cc", + "rand_util_posix.cc", + "rand_util_win.cc", + "run_loop.cc", + "run_loop.h", + "safe_strerror_posix.cc", + "safe_strerror_posix.h", + "scoped_generic.h", + "scoped_native_library.cc", + "scoped_native_library.h", + "sequence_checker.h", + "sequence_checker_impl.cc", + "sequence_checker_impl.h", + "sequenced_task_runner.cc", + "sequenced_task_runner.h", + "sequenced_task_runner_helpers.h", + "sha1.h", + "sha1_portable.cc", + "sha1_win.cc", + "single_thread_task_runner.h", + "stl_util.h", + "strings/latin1_string_conversions.cc", + "strings/latin1_string_conversions.h", + "strings/nullable_string16.cc", + "strings/nullable_string16.h", + "strings/safe_sprintf.cc", + "strings/safe_sprintf.h", + "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_tokenizer.h", + "strings/string_util.cc", + "strings/string_util.h", + "strings/string_util_constants.cc", + "strings/string_util_posix.h", + "strings/string_util_win.h", + "strings/stringize_macros.h", + "strings/stringprintf.cc", + "strings/stringprintf.h", + "strings/sys_string_conversions.h", + "strings/sys_string_conversions_mac.mm", + "strings/sys_string_conversions_posix.cc", + "strings/sys_string_conversions_win.cc", + "strings/utf_offset_string_conversions.cc", + "strings/utf_offset_string_conversions.h", + "strings/utf_string_conversion_utils.cc", + "strings/utf_string_conversion_utils.h", + "strings/utf_string_conversions.cc", + "strings/utf_string_conversions.h", + "supports_user_data.cc", + "supports_user_data.h", + "sync_socket.h", + "sync_socket_posix.cc", + "sync_socket_win.cc", + "synchronization/cancellation_flag.cc", + "synchronization/cancellation_flag.h", + "synchronization/condition_variable.h", + "synchronization/condition_variable_posix.cc", + "synchronization/condition_variable_win.cc", + "synchronization/lock.cc", + "synchronization/lock.h", + "synchronization/lock_impl.h", + "synchronization/lock_impl_posix.cc", + "synchronization/lock_impl_win.cc", + "synchronization/spin_wait.h", + "synchronization/waitable_event.h", + "synchronization/waitable_event_posix.cc", + "synchronization/waitable_event_watcher.h", + "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", + "sys_info_android.cc", + "sys_info_chromeos.cc", + "sys_info_freebsd.cc", + "sys_info_ios.mm", + "sys_info_linux.cc", + "sys_info_mac.cc", + "sys_info_openbsd.cc", + "sys_info_posix.cc", + "sys_info_win.cc", + "task/cancelable_task_tracker.cc", + "task/cancelable_task_tracker.h", + "task_runner.cc", + "task_runner.h", + "task_runner_util.h", + "template_util.h", + "thread_task_runner_handle.cc", + "thread_task_runner_handle.h", + "threading/non_thread_safe.h", + "threading/non_thread_safe_impl.cc", + "threading/non_thread_safe_impl.h", + "threading/platform_thread.h", + "threading/platform_thread_android.cc", + "threading/platform_thread_linux.cc", + "threading/platform_thread_mac.mm", + "threading/platform_thread_posix.cc", + "threading/platform_thread_win.cc", + "threading/post_task_and_reply_impl.cc", + "threading/post_task_and_reply_impl.h", + "threading/sequenced_worker_pool.cc", + "threading/sequenced_worker_pool.h", + "threading/simple_thread.cc", + "threading/simple_thread.h", + "threading/thread.cc", + "threading/thread.h", + "threading/thread_checker.h", + "threading/thread_checker_impl.cc", + "threading/thread_checker_impl.h", + "threading/thread_collision_warner.cc", + "threading/thread_collision_warner.h", + "threading/thread_id_name_manager.cc", + "threading/thread_id_name_manager.h", + "threading/thread_local.h", + "threading/thread_local_android.cc", + "threading/thread_local_posix.cc", + "threading/thread_local_storage.cc", + "threading/thread_local_storage.h", + "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/watchdog.cc", + "threading/watchdog.h", + "threading/worker_pool.h", + "threading/worker_pool.cc", + "threading/worker_pool_posix.cc", + "threading/worker_pool_posix.h", + "threading/worker_pool_win.cc", + "time/clock.cc", + "time/clock.h", + "time/default_clock.cc", + "time/default_clock.h", + "time/default_tick_clock.cc", + "time/default_tick_clock.h", + "time/tick_clock.cc", + "time/tick_clock.h", + "time/time.cc", + "time/time.h", + "time/time_mac.cc", + "time/time_posix.cc", + "time/time_win.cc", + "timer/elapsed_timer.cc", + "timer/elapsed_timer.h", + "timer/hi_res_timer_manager.h", + "timer/hi_res_timer_manager_posix.cc", + "timer/hi_res_timer_manager_win.cc", + "timer/mock_timer.cc", + "timer/mock_timer.h", + "timer/timer.cc", + "timer/timer.h", + "tracked_objects.cc", + "tracked_objects.h", + "tracking_info.cc", + "tracking_info.h", + "tuple.h", + "values.cc", + "values.h", + "value_conversions.cc", + "value_conversions.h", + "version.cc", + "version.h", + "vlog.cc", + "vlog.h", + "win/enum_variant.cc", + "win/enum_variant.h", + "win/event_trace_consumer.h", + "win/event_trace_controller.cc", + "win/event_trace_controller.h", + "win/event_trace_provider.cc", + "win/event_trace_provider.h", + "win/i18n.cc", + "win/i18n.h", + "win/iat_patch_function.cc", + "win/iat_patch_function.h", + "win/iunknown_impl.cc", + "win/iunknown_impl.h", + "win/message_window.cc", + "win/message_window.h", + "win/metro.cc", + "win/metro.h", + "win/object_watcher.cc", + "win/object_watcher.h", + "win/registry.cc", + "win/registry.h", + "win/resource_util.cc", + "win/resource_util.h", + "win/scoped_bstr.cc", + "win/scoped_bstr.h", + "win/scoped_co_mem.h", + "win/scoped_com_initializer.h", + "win/scoped_comptr.h", + "win/scoped_gdi_object.h", + "win/scoped_handle.cc", + "win/scoped_handle.h", + "win/scoped_hdc.h", + "win/scoped_hglobal.h", + "win/scoped_process_information.cc", + "win/scoped_process_information.h", + "win/scoped_propvariant.h", + "win/scoped_select_object.h", + "win/scoped_variant.cc", + "win/scoped_variant.h", + "win/shortcut.cc", + "win/shortcut.h", + "win/startup_information.cc", + "win/startup_information.h", + "win/win_util.cc", + "win/win_util.h", + "win/windows_version.cc", + "win/windows_version.h", + "win/wrapped_window_proc.cc", + "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", + ] + + deps = [ + ":base_static", + "//base/allocator:allocator_extension_thunks", + "//base/third_party/dynamic_annotations", + "//base/third_party/nspr", + "//third_party/modp_b64", + ] + + 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", + ] + + # 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", + ] + set_sources_assignment_filter(sources_assignment_filter) + + deps += [ + ":base_jni_headers", + "//third_party/ashmem", + "//third_party/android_tools:cpu_features" + ] + + # logging.cc uses the Android logging library. + libs = [ "log" ] + + sources -= [ + "debug/stack_trace_posix.cc", + ] + } + + if (is_nacl) { + # These things would otherwise be built on a Posix build but aren't + # supported on NaCl. + sources -= [ + "debug/stack_trace_posix.cc", + "files/file_enumerator_posix.cc", + "file_util_posix.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", + "sync_socket_posix.cc", + "sys_info_posix.cc", + ] + } else { + # 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 -= [ + "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", + ] + + libs = [ + "netapi32.lib", + "powrprof.lib", + ] + ldflags = [ + "/DELAYLOAD:powrprof.dll", + ] + } else if (!is_nacl) { + # Non-Windows. + deps += [ "//third_party/libevent" ] + } + + # Mac. + if (is_mac) { + sources -= [ + "base_paths_posix.cc", + "native_library_posix.cc", + "strings/sys_string_conversions_posix.cc", + ] + deps += [ "//third_party/mach_override" ] + } else { + # Non-Mac. + sources -= [ + "files/file_path_watcher_fsevents.cc", + "files/file_path_watcher_fsevents.h", + "files/file_path_watcher_kqueue.cc", + "files/file_path_watcher_kqueue.h", + ] + } + + # Linux. + if (is_linux) { + # TODO(brettw) this will need to be parameterized at some point. + linux_configs = [ + "//build/config/linux:glib", + ] + + configs += linux_configs + all_dependent_configs = linux_configs + + # 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", + ] + } else { + # Non-Linux. + sources -= [ + "nix/mime_util_xdg.cc", + "nix/mime_util_xdg.h", + "nix/xdg_util.cc", + "nix/xdg_util.h", + ] + } + + if (!use_glib) { + sources -= [ + "message_loop/message_pump_glib.cc", + "message_loop/message_pump_glib.h", + ] + } +} + +# This is the subset of files from base that should not be used with a dynamic +# library. Note that this library cannot depend on base because base depends on +# base_static. +source_set("base_static") { + sources = [ + "base_switches.cc", + "base_switches.h", + "win/pe_image.cc", + "win/pe_image.h", + ] +} + +component("i18n") { + output_name = "base_i18n" + sources = [ + "i18n/base_i18n_export.h", + "i18n/bidi_line_iterator.cc", + "i18n/bidi_line_iterator.h", + "i18n/break_iterator.cc", + "i18n/break_iterator.h", + "i18n/case_conversion.cc", + "i18n/case_conversion.h", + "i18n/char_iterator.cc", + "i18n/char_iterator.h", + "i18n/file_util_icu.cc", + "i18n/file_util_icu.h", + "i18n/i18n_constants.cc", + "i18n/i18n_constants.h", + "i18n/icu_encoding_detection.cc", + "i18n/icu_encoding_detection.h", + "i18n/icu_string_conversions.cc", + "i18n/icu_string_conversions.h", + "i18n/icu_util.cc", + "i18n/icu_util.h", + "i18n/number_formatting.cc", + "i18n/number_formatting.h", + "i18n/rtl.cc", + "i18n/rtl.h", + "i18n/streaming_utf8_validator.cc", + "i18n/streaming_utf8_validator.h", + "i18n/string_compare.cc", + "i18n/string_compare.h", + "i18n/string_search.cc", + "i18n/string_search.h", + "i18n/time_formatting.cc", + "i18n/time_formatting.h", + "i18n/timezone.cc", + "i18n/timezone.h", + "i18n/utf8_validator_tables.cc", + "i18n/utf8_validator_tables.h", + ] + defines = [ "BASE_I18N_IMPLEMENTATION" ] + configs += [ "//build/config/compiler:wexit_time_destructors" ] + deps = [ + ":base", + "//base/third_party/dynamic_annotations", + "//third_party/icu", + ] +} + +source_set("prefs") { + sources = [ + "prefs/base_prefs_export.h", + "prefs/default_pref_store.cc", + "prefs/default_pref_store.h", + "prefs/json_pref_store.cc", + "prefs/json_pref_store.h", + "prefs/overlay_user_pref_store.cc", + "prefs/overlay_user_pref_store.h", + "prefs/persistent_pref_store.h", + "prefs/pref_change_registrar.cc", + "prefs/pref_change_registrar.h", + "prefs/pref_filter.h", + "prefs/pref_member.cc", + "prefs/pref_member.h", + "prefs/pref_notifier.h", + "prefs/pref_notifier_impl.cc", + "prefs/pref_notifier_impl.h", + "prefs/pref_observer.h", + "prefs/pref_registry.cc", + "prefs/pref_registry.h", + "prefs/pref_registry_simple.cc", + "prefs/pref_registry_simple.h", + "prefs/pref_service.cc", + "prefs/pref_service.h", + "prefs/pref_service_factory.cc", + "prefs/pref_service_factory.h", + "prefs/pref_store.cc", + "prefs/pref_store.h", + "prefs/pref_value_map.cc", + "prefs/pref_value_map.h", + "prefs/pref_value_store.cc", + "prefs/pref_value_store.h", + "prefs/scoped_user_pref_update.cc", + "prefs/scoped_user_pref_update.h", + "prefs/value_map_pref_store.cc", + "prefs/value_map_pref_store.h", + "prefs/writeable_pref_store.h", + ] + + defines = [ "BASE_PREFS_IMPLEMENTATION" ] + + deps = [ ":base" ] +} + +source_set("prefs_test_support") { + sources = [ + "prefs/mock_pref_change_callback.cc", + "prefs/pref_store_observer_mock.cc", + "prefs/pref_store_observer_mock.h", + "prefs/testing_pref_service.cc", + "prefs/testing_pref_service.h", + "prefs/testing_pref_store.cc", + "prefs/testing_pref_store.h", + ] + + deps = [ + ":base", + ":prefs", + "//testing/gmock", + ] +} + +source_set("message_loop_tests") { + sources = [ + "message_loop/message_loop_test.cc", + "message_loop/message_loop_test.h", + ] + + deps = [ + ":base", + "//testing/gtest", + ] +} + +test("base_unittests") { + sources = [ + "android/application_status_listener_unittest.cc", + "android/jni_android_unittest.cc", + "android/jni_array_unittest.cc", + "android/jni_string_unittest.cc", + "android/path_utils_unittest.cc", + "android/scoped_java_ref_unittest.cc", + "android/sys_utils_unittest.cc", + "async_socket_io_handler_unittest.cc", + "at_exit_unittest.cc", + "atomicops_unittest.cc", + "barrier_closure_unittest.cc", + "base64_unittest.cc", + "big_endian_unittest.cc", + "bind_unittest.cc", + "bind_unittest.nc", + "bits_unittest.cc", + "build_time_unittest.cc", + "callback_helpers_unittest.cc", + "callback_list_unittest.cc", + "callback_list_unittest.nc", + "callback_unittest.cc", + "callback_unittest.nc", + "cancelable_callback_unittest.cc", + "command_line_unittest.cc", + "containers/hash_tables_unittest.cc", + "containers/linked_list_unittest.cc", + "containers/mru_cache_unittest.cc", + "containers/small_map_unittest.cc", + "containers/stack_container_unittest.cc", + "cpu_unittest.cc", + "debug/crash_logging_unittest.cc", + "debug/leak_tracker_unittest.cc", + "debug/proc_maps_linux_unittest.cc", + "debug/stack_trace_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_util_unittest.cc", + "file_version_info_unittest.cc", + "files/dir_reader_posix_unittest.cc", + "files/file_path_unittest.cc", + "files/file_proxy_unittest.cc", + "files/file_unittest.cc", + "files/file_util_proxy_unittest.cc", + "files/important_file_writer_unittest.cc", + "files/scoped_temp_dir_unittest.cc", + "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/file_util_icu_unittest.cc", + "i18n/icu_string_conversions_unittest.cc", + "i18n/number_formatting_unittest.cc", + "i18n/rtl_unittest.cc", + "i18n/streaming_utf8_validator_unittest.cc", + "i18n/string_search_unittest.cc", + "i18n/time_formatting_unittest.cc", + "i18n/timezone_unittest.cc", + "ini_parser_unittest.cc", + "ios/device_util_unittest.mm", + "json/json_parser_unittest.cc", + "json/json_reader_unittest.cc", + "json/json_value_converter_unittest.cc", + "json/json_value_serializer_unittest.cc", + "json/json_writer_unittest.cc", + "json/string_escape_unittest.cc", + "lazy_instance_unittest.cc", + "logging_unittest.cc", + "mac/bind_objc_block_unittest.mm", + "mac/foundation_util_unittest.mm", + "mac/libdispatch_task_runner_unittest.cc", + "mac/mac_util_unittest.mm", + "mac/objc_property_releaser_unittest.mm", + "mac/scoped_nsobject_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/linked_ptr_unittest.cc", + "memory/ref_counted_memory_unittest.cc", + "memory/ref_counted_unittest.cc", + "memory/scoped_ptr_unittest.cc", + "memory/scoped_ptr_unittest.nc", + "memory/scoped_vector_unittest.cc", + "memory/shared_memory_unittest.cc", + "memory/singleton_unittest.cc", + "memory/weak_ptr_unittest.cc", + "memory/weak_ptr_unittest.nc", + "message_loop/message_loop_proxy_impl_unittest.cc", + "message_loop/message_loop_proxy_unittest.cc", + "message_loop/message_loop_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/sparse_histogram_unittest.cc", + "metrics/stats_table_unittest.cc", + "metrics/statistics_recorder_unittest.cc", + "observer_list_unittest.cc", + "os_compat_android_unittest.cc", + "path_service_unittest.cc", + "pickle_unittest.cc", + "platform_file_unittest.cc", + "posix/file_descriptor_shuffle_unittest.cc", + "posix/unix_domain_socket_linux_unittest.cc", + "power_monitor/power_monitor_unittest.cc", + "prefs/default_pref_store_unittest.cc", + "prefs/json_pref_store_unittest.cc", + "prefs/mock_pref_change_callback.h", + "prefs/overlay_user_pref_store_unittest.cc", + "prefs/pref_change_registrar_unittest.cc", + "prefs/pref_member_unittest.cc", + "prefs/pref_notifier_impl_unittest.cc", + "prefs/pref_service_unittest.cc", + "prefs/pref_value_map_unittest.cc", + "prefs/pref_value_store_unittest.cc", + "prefs/scoped_user_pref_update_unittest.cc", + "process/memory_unittest.cc", + "process/memory_unittest_mac.h", + "process/memory_unittest_mac.mm", + "process/process_metrics_unittest.cc", + "process/process_metrics_unittest_ios.cc", + "process/process_util_unittest.cc", + "process/process_util_unittest_ios.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", + "scoped_observer.h", + "security_unittest.cc", + "sequence_checker_unittest.cc", + "sha1_unittest.cc", + "stl_util_unittest.cc", + "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/sys_string_conversions_mac_unittest.mm", + "strings/sys_string_conversions_unittest.cc", + "strings/utf_offset_string_conversions_unittest.cc", + "strings/utf_string_conversions_unittest.cc", + "supports_user_data_unittest.cc", + "sync_socket_unittest.cc", + "synchronization/cancellation_flag_unittest.cc", + "synchronization/condition_variable_unittest.cc", + "synchronization/lock_unittest.cc", + "synchronization/waitable_event_unittest.cc", + "synchronization/waitable_event_watcher_unittest.cc", + "sys_info_unittest.cc", + "system_monitor/system_monitor_unittest.cc", + "task/cancelable_task_tracker_unittest.cc", + "task_runner_util_unittest.cc", + "template_util_unittest.cc", + "test/expectations/expectation_unittest.cc", + "test/expectations/parser_unittest.cc", + "test/statistics_delta_reader_unittest.cc", + "test/test_reg_util_win_unittest.cc", + "test/trace_event_analyzer_unittest.cc", + "threading/non_thread_safe_unittest.cc", + "threading/platform_thread_unittest.cc", + "threading/sequenced_worker_pool_unittest.cc", + "threading/simple_thread_unittest.cc", + "threading/thread_checker_unittest.cc", + "threading/thread_collision_warner_unittest.cc", + "threading/thread_id_name_manager_unittest.cc", + "threading/thread_local_storage_unittest.cc", + "threading/thread_local_unittest.cc", + "threading/thread_unittest.cc", + "threading/watchdog_unittest.cc", + "threading/worker_pool_posix_unittest.cc", + "threading/worker_pool_unittest.cc", + "time/pr_time_unittest.cc", + "time/time_unittest.cc", + "time/time_win_unittest.cc", + "timer/hi_res_timer_manager_unittest.cc", + "timer/mock_timer_unittest.cc", + "timer/timer_unittest.cc", + "tools_sanity_unittest.cc", + "tracked_objects_unittest.cc", + "tuple_unittest.cc", + "values_unittest.cc", + "version_unittest.cc", + "vlog_unittest.cc", + "win/dllmain.cc", + "win/enum_variant_unittest.cc", + "win/event_trace_consumer_unittest.cc", + "win/event_trace_controller_unittest.cc", + "win/event_trace_provider_unittest.cc", + "win/i18n_unittest.cc", + "win/iunknown_impl_unittest.cc", + "win/message_window_unittest.cc", + "win/object_watcher_unittest.cc", + "win/pe_image_unittest.cc", + "win/registry_unittest.cc", + "win/scoped_bstr_unittest.cc", + "win/scoped_comptr_unittest.cc", + "win/scoped_process_information_unittest.cc", + "win/scoped_variant_unittest.cc", + "win/shortcut_unittest.cc", + "win/startup_information_unittest.cc", + "win/win_util_unittest.cc", + "win/wrapped_window_proc_unittest.cc", + ] + + deps = [ + ":base", + ":i18n", + ":message_loop_tests", + ":prefs", + ":prefs_test_support", + "//base/allocator", + "//base/test:run_all_unittests", + "//base/test:test_support", + "//base/third_party/dynamic_annotations", + "//testing/gmock", + "//testing/gtest", + "//third_party/icu", + ] + + 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", + "process/process_util_unittest.cc", + ] + + # Pull in specific Mac files for iOS (which have been filtered out by file + # name rules). + set_sources_assignment_filter([]) + sources += [ + "mac/bind_objc_block_unittest.mm", + "mac/foundation_util_unittest.mm", + "mac/objc_property_releaser_unittest.mm", + "mac/scoped_nsobject_unittest.mm", + "sys_string_conversions_mac_unittest.mm", + ] + set_sources_assignment_filter(sources_assignment_filter) + + # TODO(GYP): dep on copy_test_data_ios action. + } + + if (is_linux) { + sources -= [ "file_version_info_unittest.cc" ] + sources += [ "nix/xdg_util_unittest.cc" ] + defines = [ "USE_SYMBOLIZE" ] + configs += [ "//build/config/linux:glib" ] + } + + if (!is_linux || use_ozone) { + sources -= [ "message_loop/message_pump_glib_unittest.cc" ] + } + + if (is_posix || is_ios) { + sources += [ "message_loop/message_pump_libevent_unittest.cc" ] + deps += [ "//third_party/libevent" ] + } + + if (is_android) { + set_sources_assignment_filter([]) + sources += [ "debug/proc_maps_linux_unittest.cc" ] + set_sources_assignment_filter(sources_assignment_filter) + } +} + +if (is_android) { + generate_jni("base_jni_headers") { + sources = [ + "android/java/src/org/chromium/base/ApplicationStatus.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", + "android/java/src/org/chromium/base/CpuFeatures.java", + "android/java/src/org/chromium/base/EventLog.java", + "android/java/src/org/chromium/base/ImportantFileWriterAndroid.java", + "android/java/src/org/chromium/base/library_loader/LibraryLoader.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/ThreadUtils.java", + "android/java/src/org/chromium/base/TraceEvent.java", + ] + jni_package = "base" + } + + java_cpp_template("base_java_application_state") { + sources = [ + "android/java/src/org/chromium/base/ApplicationState.template", + ] + source_prereqs = [ + "android/application_state_list.h" + ] + + package_name = "org/chromium/base" + } + + java_cpp_template("base_java_memory_pressure_level_list") { + sources = [ + "android/java/src/org/chromium/base/MemoryPressureLevelList.template", + ] + source_prereqs = [ + "memory/memory_pressure_level_list.h" + ] + + package_name = "org/chromium/base" + } + + java_cpp_template("base_native_libraries_gen") { + sources = [ + "android/java/templates/NativeLibraries.template", + ] + source_prereqs = [ + "android/java/templates/native_libraries_array.h", + ] + + package_name = "org/chromium/base/library_loader" + include_path = "android/java/templates" + } + + +} diff --git a/chromium/base/DEPS b/chromium/base/DEPS index e1545aee519..2407baaa53c 100644 --- a/chromium/base/DEPS +++ b/chromium/base/DEPS @@ -1,15 +1,18 @@ include_rules = [ "+jni", - "+native_client/src/untrusted/irt/irt.h", "+third_party/ashmem", "+third_party/apple_apsl", "+third_party/libevent", "+third_party/dmg_fp", - "+third_party/GTM", + "+third_party/google_toolbox_for_mac/src", "+third_party/mach_override", "+third_party/modp_b64", "+third_party/tcmalloc", + # These are implicitly brought in from the root, and we don't want them. + "-ipc", + "-url", + # ICU dependendencies must be separate from the rest of base. "-i18n", ] diff --git a/chromium/base/PRESUBMIT.py b/chromium/base/PRESUBMIT.py index 7137c5a0101..1a1cce75def 100644 --- a/chromium/base/PRESUBMIT.py +++ b/chromium/base/PRESUBMIT.py @@ -48,9 +48,11 @@ def CheckChangeOnCommit(input_api, output_api): return results -def GetPreferredTrySlaves(): - return [ - 'linux_rel:sync_integration_tests', - 'mac_rel:sync_integration_tests', - 'win_rel:sync_integration_tests', - ] +def GetPreferredTryMasters(project, change): + return { + 'tryserver.chromium': { + 'linux_chromium_rel': set(['defaulttests']), + 'mac_chromium_rel': set(['defaulttests']), + 'win_chromium_rel': set(['defaulttests']), + } + } diff --git a/chromium/base/allocator/BUILD.gn b/chromium/base/allocator/BUILD.gn new file mode 100644 index 00000000000..d8e3b275a0f --- /dev/null +++ b/chromium/base/allocator/BUILD.gn @@ -0,0 +1,248 @@ +# 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. + +import("//build/config/allocator.gni") + +# Only executables and not libraries should depend on the allocator target; +# only the application (the final executable) knows what allocator makes sense. +# This "allocator" meta-target will forward to the default allocator according +# to the build settings. +group("allocator") { + if (use_allocator == "tcmalloc") { + deps = [ ":tcmalloc" ] + } +} + +# This config and libc modification are only used on Windows. +if (is_win) { + import("//build/config/win/visual_studio_version.gni") + + config("nocmt") { + ldflags = [ + "/NODEFAULTLIB:libcmt", + "/NODEFAULTLIB:libcmtd", + ] + libs = [ rebase_path("$target_gen_dir/allocator/libcmt.lib") ] + } + + action("prep_libc") { + script = "prep_libc.py" + outputs = [ "$target_gen_dir/allocator/libcmt.lib" ] + args = [ + visual_studio_path + "/vc/lib", + rebase_path("$target_gen_dir/allocator"), + cpu_arch, + ] + } +} + +if (!is_android) { + # tcmalloc currently won't compile on Android. + source_set("tcmalloc") { + tcmalloc_dir = "//third_party/tcmalloc/chromium" + + 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", + + # tcmalloc native and forked files. + "$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", + "$tcmalloc_dir/src/base/elf_mem_image.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/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/synchronization_profiling.h", + "$tcmalloc_dir/src/base/sysinfo.cc", + "$tcmalloc_dir/src/base/sysinfo.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", + # #included by debugallocation_shim.cc + #"$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/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/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/maybe_threads.cc", + "$tcmalloc_dir/src/maybe_threads.h", + "$tcmalloc_dir/src/memory_region_map.cc", + "$tcmalloc_dir/src/memory_region_map.h", + "$tcmalloc_dir/src/page_heap.cc", + "$tcmalloc_dir/src/page_heap.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/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", + # #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. + #"generic_allocators.cc", + #"win_allocator.cc", + ] + + # Disable the heap checker in tcmalloc. + defines = [ "NO_HEAP_CHECK" ] + + include_dirs = [ + ".", + "$tcmalloc_dir/src/base", + "$tcmalloc_dir/src", + ] + + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + + deps = [] + + if (is_win) { + sources -= [ + "$tcmalloc_dir/src/base/elf_mem_image.cc", + "$tcmalloc_dir/src/base/elf_mem_image.h", + "$tcmalloc_dir/src/base/linuxthreads.cc", + "$tcmalloc_dir/src/base/linuxthreads.h", + "$tcmalloc_dir/src/base/vdso_support.cc", + "$tcmalloc_dir/src/base/vdso_support.h", + "$tcmalloc_dir/src/maybe_threads.cc", + "$tcmalloc_dir/src/maybe_threads.h", + "$tcmalloc_dir/src/symbolize.h", + "$tcmalloc_dir/src/system-alloc.cc", + "$tcmalloc_dir/src/system-alloc.h", + + # included by allocator_shim.cc + "debugallocation_shim.cc", + + # 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/profiler.cc", + ] + defines += [ "PERFTOOLS_DLL_DECL=" ] + + configs -= [ + # Tcmalloc defines this itself, and we don't want duplicate definition + # warnings. + "//build/config/win:nominmax", + ] + + direct_dependent_configs = [ ":nocmt" ] + + deps += [ + ":prep_libc", + ] + } + + if (is_linux || is_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: + # http://code.google.com/p/google-perftools/source/browse/trunk/Makefile.am + cflags = [ + "-Wno-sign-compare", + "-Wno-unused-result", + ] + + configs -= [ "//build/config/gcc:symbol_visibility_hidden" ] + + ldflags = [ + # 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", + "-Wl,-u_ZN15HeapLeakChecker12IgnoreObjectEPKv,-u_ZN15HeapLeakChecker14UnIgnoreObjectEPKv", + ] + } + + # Make sure the allocation library is optimized as much as possible when + # we"re in release mode. + if (!is_debug) { + configs -= [ "//build/config/compiler:optimize" ] + configs += [ "//build/config/compiler:optimize_max" ] + } + + deps += [ + "//base/third_party/dynamic_annotations", + ] + + if (is_win) { + ldflags = [ "/ignore:4006:4221" ] + } + } +} # !is_android + +source_set("allocator_extension_thunks") { + visibility = "//base/*" + sources = [ + "allocator_extension_thunks.cc", + "allocator_extension_thunks.h", + ] +} diff --git a/chromium/base/allocator/allocator.gyp b/chromium/base/allocator/allocator.gyp index 4841f58c9a4..2de3aca450f 100644 --- a/chromium/base/allocator/allocator.gyp +++ b/chromium/base/allocator/allocator.gyp @@ -4,7 +4,6 @@ { 'variables': { - 'jemalloc_dir': '../../third_party/jemalloc/chromium', 'tcmalloc_dir': '../../third_party/tcmalloc/chromium', 'use_vtable_verify%': 0, }, @@ -198,13 +197,6 @@ '<(tcmalloc_dir)/src/windows/preamble_patcher.h', '<(tcmalloc_dir)/src/windows/preamble_patcher_with_stub.cc', - # jemalloc files - '<(jemalloc_dir)/jemalloc.c', - '<(jemalloc_dir)/jemalloc.h', - '<(jemalloc_dir)/ql.h', - '<(jemalloc_dir)/qr.h', - '<(jemalloc_dir)/rb.h', - 'allocator_shim.cc', 'allocator_shim.h', 'debugallocation_shim.cc', @@ -358,7 +350,6 @@ 'libcmt', ], 'include_dirs': [ - '<(jemalloc_dir)', '<(tcmalloc_dir)/src/windows', ], 'sources!': [ @@ -376,7 +367,10 @@ # included by allocator_shim.cc 'debugallocation_shim.cc', - + ], + }], + ['OS=="win" or profiling!=1', { + 'sources!': [ # cpuprofiler '<(tcmalloc_dir)/src/base/thread_lister.c', '<(tcmalloc_dir)/src/base/thread_lister.h', @@ -395,15 +389,6 @@ # TODO(willchan): Support allocator shim later on. 'allocator_shim.cc', - - # TODO(willchan): support jemalloc on other platforms - # jemalloc files - '<(jemalloc_dir)/jemalloc.c', - '<(jemalloc_dir)/jemalloc.h', - '<(jemalloc_dir)/ql.h', - '<(jemalloc_dir)/qr.h', - '<(jemalloc_dir)/rb.h', - ], # We enable all warnings by default, but upstream disables a few. # Keep "-Wno-*" flags in sync with upstream by comparing against: @@ -426,11 +411,6 @@ '-Wl,-u_ZN15HeapLeakChecker12IgnoreObjectEPKv,-u_ZN15HeapLeakChecker14UnIgnoreObjectEPKv', ]}, }], - # Need to distinguish a non-SDK build for Android WebView - # due to differences in C include files. - ['OS=="android" and android_webview_build==1', { - 'defines': ['ANDROID_NON_SDK_BUILD'], - }], [ 'use_vtable_verify==1', { 'cflags': [ '-fvtable-verify=preinit', @@ -513,7 +493,7 @@ '../..', ], 'sources': [ - 'allocator_unittests.cc', + 'allocator_unittest.cc', '../profiler/alternate_timer.cc', '../profiler/alternate_timer.h', ], @@ -616,7 +596,7 @@ 'sources': [ 'type_profiler_control.cc', 'type_profiler_control.h', - 'type_profiler_unittests.cc', + 'type_profiler_unittest.cc', ], }, { @@ -638,7 +618,7 @@ '../..', ], 'sources': [ - 'type_profiler_map_unittests.cc', + 'type_profiler_map_unittest.cc', '<(tcmalloc_dir)/src/gperftools/type_profiler_map.h', '<(tcmalloc_dir)/src/type_profiler_map.cc', ], diff --git a/chromium/base/allocator/allocator_shim.cc b/chromium/base/allocator/allocator_shim.cc index 9b3b50c4ee4..c0de36e6de2 100644 --- a/chromium/base/allocator/allocator_shim.cc +++ b/chromium/base/allocator/allocator_shim.cc @@ -8,7 +8,6 @@ #include "base/allocator/allocator_extension_thunks.h" #include "base/profiler/alternate_timer.h" #include "base/sysinfo.h" -#include "jemalloc.h" // This shim make it possible to use different allocators via an environment // variable set before running the program. This may reduce the @@ -33,8 +32,7 @@ static int new_mode = 0; typedef enum { TCMALLOC, // TCMalloc is the default allocator. - JEMALLOC, // JEMalloc. - WINHEAP, // Windows Heap (standard Windows allocator). + WINHEAP, // Windows Heap (standard Windows allocator). WINLFH, // Windows LFH Heap. } Allocator; @@ -43,8 +41,8 @@ typedef enum { // See SetupSubprocessAllocator() to specify a default secondary (subprocess) // allocator. // TODO(jar): Switch to using TCMALLOC for the renderer as well. -#if (defined(ADDRESS_SANITIZER) && defined(OS_WIN)) -// The Windows implementation of Asan requires the use of "WINHEAP". +#if defined(SYZYASAN) +// SyzyASan requires the use of "WINHEAP". static Allocator allocator = WINHEAP; #else static Allocator allocator = TCMALLOC; @@ -61,16 +59,6 @@ static const char secondary_name[] = "CHROME_ALLOCATOR_2"; #include "debugallocation_shim.cc" #include "win_allocator.cc" -// Forward declarations from jemalloc. -extern "C" { -void* je_malloc(size_t s); -void* je_realloc(void* p, size_t s); -void je_free(void* s); -size_t je_msize(void* p); -bool je_malloc_init_hard(); -void* je_memalign(size_t a, size_t s); -} - // 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) { @@ -118,9 +106,6 @@ void* malloc(size_t size) __THROW { void* ptr; for (;;) { switch (allocator) { - case JEMALLOC: - ptr = je_malloc(size); - break; case WINHEAP: case WINLFH: ptr = win_heap_malloc(size); @@ -141,9 +126,6 @@ void* malloc(size_t size) __THROW { void free(void* p) __THROW { switch (allocator) { - case JEMALLOC: - je_free(p); - return; case WINHEAP: case WINLFH: win_heap_free(p); @@ -164,9 +146,6 @@ void* realloc(void* ptr, size_t size) __THROW { void* new_ptr; for (;;) { switch (allocator) { - case JEMALLOC: - new_ptr = je_realloc(ptr, size); - break; case WINHEAP: case WINLFH: new_ptr = win_heap_realloc(ptr, size); @@ -191,9 +170,6 @@ void* realloc(void* ptr, size_t size) __THROW { // TODO(mbelshe): Implement this for other allocators. void malloc_stats(void) __THROW { switch (allocator) { - case JEMALLOC: - // No stats. - return; case WINHEAP: case WINLFH: // No stats. @@ -208,8 +184,6 @@ void malloc_stats(void) __THROW { extern "C" size_t _msize(void* p) { switch (allocator) { - case JEMALLOC: - return je_msize(p); case WINHEAP: case WINLFH: return win_heap_msize(p); @@ -226,7 +200,6 @@ extern "C" intptr_t _get_heap_handle() { static bool get_allocator_waste_size_thunk(size_t* size) { switch (allocator) { - case JEMALLOC: case WINHEAP: case WINLFH: // TODO(alexeif): Implement for allocators other than tcmalloc. @@ -255,14 +228,12 @@ static void release_free_memory_thunk() { // The CRT heap initialization stub. extern "C" int _heap_init() { -// Don't use the environment variable if ADDRESS_SANITIZER is defined on -// Windows, as the implementation requires Winheap to be the allocator. -#if !(defined(ADDRESS_SANITIZER) && defined(OS_WIN)) +// 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, "jemalloc")) - allocator = JEMALLOC; - else if (!stricmp(environment_value, "winheap")) + if (!stricmp(environment_value, "winheap")) allocator = WINHEAP; else if (!stricmp(environment_value, "winlfh")) allocator = WINLFH; @@ -272,8 +243,6 @@ extern "C" int _heap_init() { #endif switch (allocator) { - case JEMALLOC: - return je_malloc_init_hard() ? 0 : 1; case WINHEAP: return win_heap_init(false) ? 1 : 0; case WINLFH: @@ -331,9 +300,6 @@ void* _aligned_malloc(size_t size, size_t alignment) { void* ptr; for (;;) { switch (allocator) { - case JEMALLOC: - ptr = je_memalign(alignment, size); - break; case WINHEAP: case WINLFH: ptr = win_heap_memalign(alignment, size); @@ -357,13 +323,10 @@ void* _aligned_malloc(size_t size, size_t alignment) { } void _aligned_free(void* p) { - // Both JEMalloc and TCMalloc return 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. + // 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 JEMALLOC: - je_free(p); - return; case WINHEAP: case WINLFH: win_heap_memalign_free(p); @@ -393,9 +356,9 @@ void SetupSubprocessAllocator() { buffer[sizeof(buffer) - 1] = '\0'; if (secondary_length || !primary_length) { - // Don't use the environment variable if ADDRESS_SANITIZER is defined on - // Windows, as the implementation require Winheap to be the allocator. -#if !(defined(ADDRESS_SANITIZER) && defined(OS_WIN)) +// 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 diff --git a/chromium/base/allocator/allocator_unittests.cc b/chromium/base/allocator/allocator_unittest.cc index cf8b74d7f4e..eb20b38ab61 100644 --- a/chromium/base/allocator/allocator_unittests.cc +++ b/chromium/base/allocator/allocator_unittest.cc @@ -1,10 +1,11 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// 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 <stdio.h> #include <stdlib.h> #include <algorithm> // for min() + #include "base/atomicops.h" #include "testing/gtest/include/gtest/gtest.h" @@ -81,8 +82,6 @@ static int NextSize(int size) { } } -#define GG_ULONGLONG(x) static_cast<uint64>(x) - template <class AtomicType> static void TestAtomicIncrement() { // For now, we just test single threaded execution @@ -164,7 +163,7 @@ static void TestCompareAndSwap() { // 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 = (GG_ULONGLONG(1) << + 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); @@ -187,7 +186,7 @@ static void TestAtomicExchange() { // 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 = (GG_ULONGLONG(1) << + 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); @@ -205,7 +204,7 @@ 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 = GG_ULONGLONG(1) << (NUM_BITS(AtomicType) / 2); + 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); diff --git a/chromium/base/allocator/prep_libc.py b/chromium/base/allocator/prep_libc.py index ba25cea092f..471140cb548 100755 --- a/chromium/base/allocator/prep_libc.py +++ b/chromium/base/allocator/prep_libc.py @@ -12,7 +12,7 @@ # VCLibDir is the path where VC is installed, something like: # C:\Program Files\Microsoft Visual Studio 8\VC\lib # OutputDir is the directory where the modified libcmt file should be stored. -# arch is either 'ia32' or 'x64' +# arch is one of: 'ia32', 'x86' or 'x64'. ia32 and x86 are synonyms. import os import shutil diff --git a/chromium/base/allocator/type_profiler_map_unittests.cc b/chromium/base/allocator/type_profiler_map_unittest.cc index 5ac5dd0c26f..514ec164e81 100644 --- a/chromium/base/allocator/type_profiler_map_unittests.cc +++ b/chromium/base/allocator/type_profiler_map_unittest.cc @@ -1,4 +1,4 @@ -// Copyright 2012 The Chromium Authors. All rights reserved. +// 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. diff --git a/chromium/base/allocator/type_profiler_unittests.cc b/chromium/base/allocator/type_profiler_unittest.cc index e8f06eddd61..3d7369c3802 100644 --- a/chromium/base/allocator/type_profiler_unittests.cc +++ b/chromium/base/allocator/type_profiler_unittest.cc @@ -1,4 +1,4 @@ -// Copyright 2012 The Chromium Authors. All rights reserved. +// 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. diff --git a/chromium/base/android/OWNERS b/chromium/base/android/OWNERS index 87d5d224978..8e73f048ced 100644 --- a/chromium/base/android/OWNERS +++ b/chromium/base/android/OWNERS @@ -1,2 +1,4 @@ bulach@chromium.org +nyquist@chromium.org +rmcilroy@chromium.org yfriedman@chromium.org diff --git a/chromium/base/android/activity_state_list.h b/chromium/base/android/activity_state_list.h deleted file mode 100644 index 43c0f803da3..00000000000 --- a/chromium/base/android/activity_state_list.h +++ /dev/null @@ -1,16 +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. - -// This file intentionally does not have header guards, it's included -// inside a macro to generate enum values. - -#ifndef DEFINE_ACTIVITY_STATE -#error "DEFINE_ACTIVITY_STATE should be defined before including this file" -#endif -DEFINE_ACTIVITY_STATE(CREATED, 1) -DEFINE_ACTIVITY_STATE(STARTED, 2) -DEFINE_ACTIVITY_STATE(RESUMED, 3) -DEFINE_ACTIVITY_STATE(PAUSED, 4) -DEFINE_ACTIVITY_STATE(STOPPED, 5) -DEFINE_ACTIVITY_STATE(DESTROYED, 6) diff --git a/chromium/base/android/activity_status.cc b/chromium/base/android/activity_status.cc deleted file mode 100644 index 4d0be32ef93..00000000000 --- a/chromium/base/android/activity_status.cc +++ /dev/null @@ -1,66 +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/android/activity_status.h" - -#include <jni.h> - -#include "base/memory/singleton.h" -#include "jni/ActivityStatus_jni.h" - -namespace base { -namespace android { - -ActivityStatus::Listener::Listener( - const ActivityStatus::StateChangeCallback& callback) - : callback_(callback) { - ActivityStatus::GetInstance()->RegisterListener(this); -} - -ActivityStatus::Listener::~Listener() { - ActivityStatus::GetInstance()->UnregisterListener(this); -} - -void ActivityStatus::Listener::Notify(ActivityState state) { - callback_.Run(state); -} - -// static -ActivityStatus* ActivityStatus::GetInstance() { - return Singleton<ActivityStatus, - LeakySingletonTraits<ActivityStatus> >::get(); -} - -static void OnActivityStateChange(JNIEnv* env, jclass clazz, int new_state) { - ActivityStatus* activity_status = ActivityStatus::GetInstance(); - ActivityState activity_state = static_cast<ActivityState>(new_state); - activity_status->OnActivityStateChange(activity_state); -} - -bool ActivityStatus::RegisterBindings(JNIEnv* env) { - return RegisterNativesImpl(env); -} - -ActivityStatus::ActivityStatus() - : observers_(new ObserverListThreadSafe<Listener>()) { - Java_ActivityStatus_registerThreadSafeNativeStateListener( - base::android::AttachCurrentThread()); -} - -ActivityStatus::~ActivityStatus() {} - -void ActivityStatus::RegisterListener(Listener* listener) { - observers_->AddObserver(listener); -} - -void ActivityStatus::UnregisterListener(Listener* listener) { - observers_->RemoveObserver(listener); -} - -void ActivityStatus::OnActivityStateChange(ActivityState new_state) { - observers_->Notify(&ActivityStatus::Listener::Notify, new_state); -} - -} // namespace android -} // namespace base diff --git a/chromium/base/android/activity_status.h b/chromium/base/android/activity_status.h deleted file mode 100644 index 7975a789cd0..00000000000 --- a/chromium/base/android/activity_status.h +++ /dev/null @@ -1,98 +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_ANDROID_ACTIVITY_STATUS_H_ -#define BASE_ANDROID_ACTIVITY_STATUS_H_ - -#include <jni.h> - -#include "base/android/jni_android.h" -#include "base/base_export.h" -#include "base/basictypes.h" -#include "base/memory/ref_counted.h" -#include "base/memory/singleton.h" -#include "base/observer_list_threadsafe.h" - -namespace base { -namespace android { - -// Define activity state values like ACTIVITY_STATE_CREATED in a -// way that ensures they're always the same than their Java counterpart. -enum ActivityState { -#define DEFINE_ACTIVITY_STATE(x, y) ACTIVITY_STATE_##x = y, -#include "base/android/activity_state_list.h" -#undef DEFINE_ACTIVITY_STATE -}; - -// A native helper class to listen to state changes of the current -// Android Activity. This mirrors org.chromium.base.ActivityStatus. -// any thread. -// -// To start listening, create a new instance, passing a callback to a -// function that takes an ActivityState parameter. To stop listening, -// simply delete the listener object. The implementation guarantees -// that the callback will always be called on the thread that created -// the listener. -// -// Example: -// -// void OnActivityStateChange(ActivityState state) { -// ... -// } -// -// // Start listening. -// ActivityStatus::Listener* my_listener = -// new ActivityStatus::Listener(base::Bind(&OnActivityStateChange)); -// -// ... -// -// // Stop listening. -// delete my_listener -// -class BASE_EXPORT ActivityStatus { - public: - typedef base::Callback<void(ActivityState)> StateChangeCallback; - - class Listener { - public: - explicit Listener(const StateChangeCallback& callback); - ~Listener(); - - private: - friend class ActivityStatus; - - void Notify(ActivityState state); - - StateChangeCallback callback_; - - DISALLOW_COPY_AND_ASSIGN(Listener); - }; - - // NOTE: The Java ActivityStatus is a singleton too. - static ActivityStatus* GetInstance(); - - // Internal use: must be public to be called from base_jni_registrar.cc - static bool RegisterBindings(JNIEnv* env); - - // Internal use only: must be public to be called from JNI and unit tests. - void OnActivityStateChange(ActivityState new_state); - - private: - friend struct DefaultSingletonTraits<ActivityStatus>; - - ActivityStatus(); - ~ActivityStatus(); - - void RegisterListener(Listener* listener); - void UnregisterListener(Listener* listener); - - scoped_refptr<ObserverListThreadSafe<Listener> > observers_; - - DISALLOW_COPY_AND_ASSIGN(ActivityStatus); -}; - -} // namespace android -} // namespace base - -#endif // BASE_ANDROID_ACTIVITY_STATUS_H_ diff --git a/chromium/base/android/application_state_list.h b/chromium/base/android/application_state_list.h new file mode 100644 index 00000000000..cbc833e54e5 --- /dev/null +++ b/chromium/base/android/application_state_list.h @@ -0,0 +1,17 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file intentionally does not have header guards, it's included +// inside a macro to generate enum values. + +// Note that these states represent the most visible Activity state. +// If there are activities with states paused and stopped, only +// HAS_PAUSED_ACTIVITIES should be returned. +#ifndef DEFINE_APPLICATION_STATE +#error "DEFINE_APPLICATION_STATE should be defined before including this file" +#endif +DEFINE_APPLICATION_STATE(HAS_RUNNING_ACTIVITIES, 1) +DEFINE_APPLICATION_STATE(HAS_PAUSED_ACTIVITIES, 2) +DEFINE_APPLICATION_STATE(HAS_STOPPED_ACTIVITIES, 3) +DEFINE_APPLICATION_STATE(HAS_DESTROYED_ACTIVITIES, 4) diff --git a/chromium/base/android/application_status_listener.cc b/chromium/base/android/application_status_listener.cc new file mode 100644 index 00000000000..f48707282f5 --- /dev/null +++ b/chromium/base/android/application_status_listener.cc @@ -0,0 +1,74 @@ +// 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/application_status_listener.h" + +#include <jni.h> + +#include "base/lazy_instance.h" +#include "base/observer_list_threadsafe.h" +#include "jni/ApplicationStatus_jni.h" + +namespace { +struct LeakyLazyObserverListTraits : + base::internal::LeakyLazyInstanceTraits< + ObserverListThreadSafe<base::android::ApplicationStatusListener> > { + static ObserverListThreadSafe<base::android::ApplicationStatusListener>* + New(void* instance) { + ObserverListThreadSafe<base::android::ApplicationStatusListener>* ret = + base::internal::LeakyLazyInstanceTraits<ObserverListThreadSafe< + base::android::ApplicationStatusListener> >::New(instance); + // Leaky. + ret->AddRef(); + return ret; + } +}; + +base::LazyInstance<ObserverListThreadSafe< + base::android::ApplicationStatusListener>, + LeakyLazyObserverListTraits> g_observers = LAZY_INSTANCE_INITIALIZER; + +} // namespace + +namespace base { +namespace android { + +ApplicationStatusListener::ApplicationStatusListener( + const ApplicationStatusListener::ApplicationStateChangeCallback& callback) + : callback_(callback) { + DCHECK(!callback_.is_null()); + g_observers.Get().AddObserver(this); + + Java_ApplicationStatus_registerThreadSafeNativeApplicationStateListener( + base::android::AttachCurrentThread()); +} + +ApplicationStatusListener::~ApplicationStatusListener() { + g_observers.Get().RemoveObserver(this); +} + +void ApplicationStatusListener::Notify(ApplicationState state) { + callback_.Run(state); +} + +// static +bool ApplicationStatusListener::RegisterBindings(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +// static +void ApplicationStatusListener::NotifyApplicationStateChange( + ApplicationState state) { + g_observers.Get().Notify(&ApplicationStatusListener::Notify, state); +} + +static void OnApplicationStateChange(JNIEnv* env, + jclass clazz, + jint new_state) { + ApplicationState application_state = static_cast<ApplicationState>(new_state); + ApplicationStatusListener::NotifyApplicationStateChange(application_state); +} + +} // namespace android +} // namespace base diff --git a/chromium/base/android/application_status_listener.h b/chromium/base/android/application_status_listener.h new file mode 100644 index 00000000000..ef98985f6bc --- /dev/null +++ b/chromium/base/android/application_status_listener.h @@ -0,0 +1,79 @@ +// 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_APPLICATION_STATUS_LISTENER_H_ +#define BASE_ANDROID_APPLICATION_STATUS_LISTENER_H_ + +#include <jni.h> + +#include "base/android/jni_android.h" +#include "base/base_export.h" +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "base/memory/singleton.h" +#include "base/observer_list_threadsafe.h" + +namespace base { +namespace android { + +// Define application state values like APPLICATION_STATE_VISIBLE in a +// way that ensures they're always the same than their Java counterpart. +enum ApplicationState { +#define DEFINE_APPLICATION_STATE(x, y) APPLICATION_STATE_##x = y, +#include "base/android/application_state_list.h" +#undef DEFINE_APPLICATION_STATE +}; + +// A native helper class to listen to state changes of the Android +// Application. This mirrors org.chromium.base.ApplicationStatus. +// any thread. +// +// To start listening, create a new instance, passing a callback to a +// function that takes an ApplicationState parameter. To stop listening, +// simply delete the listener object. The implementation guarantees +// that the callback will always be called on the thread that created +// the listener. +// +// Example: +// +// void OnApplicationStateChange(ApplicationState state) { +// ... +// } +// +// // Start listening. +// ApplicationStatusListener* my_listener = +// new ApplicationStatusListener( +// base::Bind(&OnApplicationStateChange)); +// +// ... +// +// // Stop listening. +// delete my_listener +// +class BASE_EXPORT ApplicationStatusListener { + public: + typedef base::Callback<void(ApplicationState)> ApplicationStateChangeCallback; + + explicit ApplicationStatusListener( + const ApplicationStateChangeCallback& callback); + ~ApplicationStatusListener(); + + // Internal use: must be public to be called from base_jni_registrar.cc + static bool RegisterBindings(JNIEnv* env); + + // Internal use only: must be public to be called from JNI and unit tests. + static void NotifyApplicationStateChange(ApplicationState state); + + private: + void Notify(ApplicationState state); + + ApplicationStateChangeCallback callback_; + + DISALLOW_COPY_AND_ASSIGN(ApplicationStatusListener); +}; + +} // namespace android +} // namespace base + +#endif // BASE_ANDROID_APPLICATION_STATUS_LISTENER_H_ diff --git a/chromium/base/android/activity_status_unittest.cc b/chromium/base/android/application_status_listener_unittest.cc index 3eb0d10f736..1049628f5a3 100644 --- a/chromium/base/android/activity_status_unittest.cc +++ b/chromium/base/android/application_status_listener_unittest.cc @@ -1,8 +1,8 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. +// 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/activity_status.h" +#include "base/android/application_status_listener.h" #include "base/bind.h" #include "base/callback_forward.h" #include "base/logging.h" @@ -20,11 +20,12 @@ namespace { using base::android::ScopedJavaLocalRef; -// An invalid ActivityState value. -const ActivityState kInvalidActivityState = static_cast<ActivityState>(100); +// An invalid ApplicationState value. +const ApplicationState kInvalidApplicationState = + static_cast<ApplicationState>(100); // Used to generate a callback that stores the new state at a given location. -void StoreStateTo(ActivityState* target, ActivityState state) { +void StoreStateTo(ApplicationState* target, ApplicationState state) { *target = state; } @@ -39,10 +40,9 @@ void RunTasksUntilIdle() { class MultiThreadedTest { public: MultiThreadedTest() - : activity_status_(ActivityStatus::GetInstance()), - state_(kInvalidActivityState), + : state_(kInvalidApplicationState), event_(false, false), - thread_("ActivityStatusTest thread"), + thread_("ApplicationStatusTest thread"), main_() { } @@ -58,14 +58,16 @@ class MultiThreadedTest { event_.Wait(); // Change state, then wait for the thread to modify state. - activity_status_->OnActivityStateChange(ACTIVITY_STATE_CREATED); + ApplicationStatusListener::NotifyApplicationStateChange( + APPLICATION_STATE_HAS_RUNNING_ACTIVITIES); event_.Wait(); - EXPECT_EQ(ACTIVITY_STATE_CREATED, state_); + EXPECT_EQ(APPLICATION_STATE_HAS_RUNNING_ACTIVITIES, state_); // Again - activity_status_->OnActivityStateChange(ACTIVITY_STATE_DESTROYED); + ApplicationStatusListener::NotifyApplicationStateChange( + APPLICATION_STATE_HAS_DESTROYED_ACTIVITIES); event_.Wait(); - EXPECT_EQ(ACTIVITY_STATE_DESTROYED, state_); + EXPECT_EQ(APPLICATION_STATE_HAS_DESTROYED_ACTIVITIES, state_); } private: @@ -75,51 +77,51 @@ class MultiThreadedTest { void RegisterThreadForEvents() { ExpectOnThread(); - listener_.reset(new ActivityStatus::Listener(base::Bind( + listener_.reset(new ApplicationStatusListener(base::Bind( &MultiThreadedTest::StoreStateAndSignal, base::Unretained(this)))); EXPECT_TRUE(listener_.get()); event_.Signal(); } - void StoreStateAndSignal(ActivityState state) { + void StoreStateAndSignal(ApplicationState state) { ExpectOnThread(); state_ = state; event_.Signal(); } - ActivityStatus* const activity_status_; - ActivityState state_; + ApplicationState state_; base::WaitableEvent event_; base::Thread thread_; base::MessageLoop main_; - scoped_ptr<ActivityStatus::Listener> listener_; + scoped_ptr<ApplicationStatusListener> listener_; }; } // namespace -TEST(ActivityStatusTest, SingleThread) { +TEST(ApplicationStatusListenerTest, SingleThread) { MessageLoop message_loop; - ActivityState result = kInvalidActivityState; + ApplicationState result = kInvalidApplicationState; // Create a new listener that stores the new state into |result| on every // state change. - ActivityStatus::Listener listener( + ApplicationStatusListener listener( base::Bind(&StoreStateTo, base::Unretained(&result))); - EXPECT_EQ(kInvalidActivityState, result); + EXPECT_EQ(kInvalidApplicationState, result); - ActivityStatus* const activity_status = ActivityStatus::GetInstance(); - activity_status->OnActivityStateChange(ACTIVITY_STATE_CREATED); + ApplicationStatusListener::NotifyApplicationStateChange( + APPLICATION_STATE_HAS_RUNNING_ACTIVITIES); RunTasksUntilIdle(); - EXPECT_EQ(ACTIVITY_STATE_CREATED, result); + EXPECT_EQ(APPLICATION_STATE_HAS_RUNNING_ACTIVITIES, result); - activity_status->OnActivityStateChange(ACTIVITY_STATE_DESTROYED); + ApplicationStatusListener::NotifyApplicationStateChange( + APPLICATION_STATE_HAS_DESTROYED_ACTIVITIES); RunTasksUntilIdle(); - EXPECT_EQ(ACTIVITY_STATE_DESTROYED, result); + EXPECT_EQ(APPLICATION_STATE_HAS_DESTROYED_ACTIVITIES, result); } -TEST(ActivityStatusTest, TwoThreads) { +TEST(ApplicationStatusListenerTest, TwoThreads) { MultiThreadedTest test; test.Run(); } diff --git a/chromium/base/android/build_info.cc b/chromium/base/android/build_info.cc index cdde6a90052..a1755afe981 100644 --- a/chromium/base/android/build_info.cc +++ b/chromium/base/android/build_info.cc @@ -37,7 +37,9 @@ struct BuildInfoSingletonTraits { } static const bool kRegisterAtExit = false; +#ifndef NDEBUG static const bool kAllowedToAccessOnNonjoinableThread = true; +#endif }; BuildInfo::BuildInfo(JNIEnv* env) @@ -55,6 +57,7 @@ BuildInfo::BuildInfo(JNIEnv* env) env, GetApplicationContext()))), package_name_(StrDupJString(Java_BuildInfo_getPackageName( env, GetApplicationContext()))), + build_type_(StrDupJString(Java_BuildInfo_getBuildType(env))), sdk_int_(Java_BuildInfo_getSdkInt(env)), java_exception_info_(NULL) { } diff --git a/chromium/base/android/build_info.h b/chromium/base/android/build_info.h index a5f44c2e0ac..03dadd99df1 100644 --- a/chromium/base/android/build_info.h +++ b/chromium/base/android/build_info.h @@ -72,6 +72,10 @@ class BASE_EXPORT BuildInfo { return package_name_; } + const char* build_type() const { + return build_type_; + } + int sdk_int() const { return sdk_int_; } @@ -102,6 +106,7 @@ class BASE_EXPORT BuildInfo { const char* const package_version_name_; const char* const package_label_; const char* const package_name_; + const char* const build_type_; const int sdk_int_; // This is set via set_java_exception_info, not at constructor time. const char* java_exception_info_; diff --git a/chromium/base/android/content_uri_utils.cc b/chromium/base/android/content_uri_utils.cc index 64d6ad24226..0e0c0ea6bb1 100644 --- a/chromium/base/android/content_uri_utils.cc +++ b/chromium/base/android/content_uri_utils.cc @@ -6,7 +6,6 @@ #include "base/android/jni_android.h" #include "base/android/jni_string.h" -#include "base/platform_file.h" #include "jni/ContentUriUtils_jni.h" using base::android::ConvertUTF8ToJavaString; @@ -25,15 +24,15 @@ bool ContentUriExists(const FilePath& content_uri) { env, base::android::GetApplicationContext(), j_uri.obj()); } -int OpenContentUriForRead(const FilePath& content_uri) { +File OpenContentUriForRead(const FilePath& content_uri) { JNIEnv* env = base::android::AttachCurrentThread(); ScopedJavaLocalRef<jstring> j_uri = ConvertUTF8ToJavaString(env, content_uri.value()); jint fd = Java_ContentUriUtils_openContentUriForRead( env, base::android::GetApplicationContext(), j_uri.obj()); if (fd < 0) - return base::kInvalidPlatformFileValue; - return fd; + return File(); + return File(fd); } } // namespace base diff --git a/chromium/base/android/content_uri_utils.h b/chromium/base/android/content_uri_utils.h index ec820efbd26..827ec92fa39 100644 --- a/chromium/base/android/content_uri_utils.h +++ b/chromium/base/android/content_uri_utils.h @@ -9,6 +9,7 @@ #include "base/base_export.h" #include "base/basictypes.h" +#include "base/files/file.h" #include "base/files/file_path.h" namespace base { @@ -17,7 +18,7 @@ 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. -BASE_EXPORT int OpenContentUriForRead(const FilePath& content_uri); +BASE_EXPORT File OpenContentUriForRead(const FilePath& content_uri); // Check whether a content uri exists. BASE_EXPORT bool ContentUriExists(const FilePath& content_uri); diff --git a/chromium/base/android/context_types.cc b/chromium/base/android/context_types.cc deleted file mode 100644 index 084b1365ce2..00000000000 --- a/chromium/base/android/context_types.cc +++ /dev/null @@ -1,26 +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/android/context_types.h" - -#include "base/android/jni_android.h" -#include "base/android/scoped_java_ref.h" -#include "base/files/file_path.h" -#include "jni/ContextTypes_jni.h" - -namespace base { -namespace android { - -bool IsRunningInWebapp() { - JNIEnv* env = AttachCurrentThread(); - return static_cast<bool>( - Java_ContextTypes_isRunningInWebapp(env, GetApplicationContext())); -} - -bool RegisterContextTypes(JNIEnv* env) { - return RegisterNativesImpl(env); -} - -} // namespace android -} // namespace base diff --git a/chromium/base/android/context_types.h b/chromium/base/android/context_types.h deleted file mode 100644 index a132167199e..00000000000 --- a/chromium/base/android/context_types.h +++ /dev/null @@ -1,22 +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. - -#ifndef BASE_ANDROID_CONTEXT_TYPES_H_ -#define BASE_ANDROID_CONTEXT_TYPES_H_ - -#include <jni.h> - -#include "base/base_export.h" - -namespace base { -namespace android { - -BASE_EXPORT bool IsRunningInWebapp(); - -bool RegisterContextTypes(JNIEnv* env); - -} // namespace android -} // namespace base - -#endif // BASE_ANDROID_CONTEXT_TYPES_H_ diff --git a/chromium/base/android/event_log.cc b/chromium/base/android/event_log.cc new file mode 100644 index 00000000000..a4b1dd139f2 --- /dev/null +++ b/chromium/base/android/event_log.cc @@ -0,0 +1,20 @@ +// 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/android/event_log.h" +#include "jni/EventLog_jni.h" + +namespace base { +namespace android { + +void EventLogWriteInt(int tag, int value) { + Java_EventLog_writeEvent(AttachCurrentThread(), tag, value); +} + +bool RegisterEventLog(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +} // namespace android +} // namespace base diff --git a/chromium/base/android/event_log.h b/chromium/base/android/event_log.h new file mode 100644 index 00000000000..dad4e4cfbc9 --- /dev/null +++ b/chromium/base/android/event_log.h @@ -0,0 +1,22 @@ +// 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_EVENT_LOG_H_ +#define BASE_ANDROID_EVENT_LOG_H_ + +#include <jni.h> + +#include "base/base_export.h" + +namespace base { +namespace android { + +void BASE_EXPORT EventLogWriteInt(int tag, int value); + +bool RegisterEventLog(JNIEnv* env); + +} // namespace android +} // namespace base + +#endif // BASE_ANDROID_EVENT_LOG_H_ diff --git a/chromium/base/android/jni_generator/jni_generator.gyp b/chromium/base/android/jni_generator/jni_generator.gyp index 4bad0dc4832..2ea36b0b389 100644 --- a/chromium/base/android/jni_generator/jni_generator.gyp +++ b/chromium/base/android/jni_generator/jni_generator.gyp @@ -56,9 +56,6 @@ 'jni_sample_header', 'jni_sample_java', ], - 'include_dirs': [ - '<(SHARED_INTERMEDIATE_DIR)/example', - ], 'sources': [ 'sample_for_tests.cc', ], diff --git a/chromium/base/android/library_loader/library_loader_hooks.cc b/chromium/base/android/library_loader/library_loader_hooks.cc new file mode 100644 index 00000000000..79470108eb3 --- /dev/null +++ b/chromium/base/android/library_loader/library_loader_hooks.cc @@ -0,0 +1,73 @@ +// 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/library_loader/library_loader_hooks.h" + +#include "base/android/jni_string.h" +#include "base/at_exit.h" +#include "base/metrics/histogram.h" +#include "jni/LibraryLoader_jni.h" + +namespace base { +namespace android { + +namespace { + +base::AtExitManager* g_at_exit_manager = NULL; +const char* g_library_version_number = ""; +LibraryLoadedHook* g_registration_callback = NULL; + +} // namespace + +void SetLibraryLoadedHook(LibraryLoadedHook* func) { + g_registration_callback = func; +} + +static jboolean LibraryLoaded(JNIEnv* env, jclass clazz, + jobjectArray init_command_line) { + if(g_registration_callback == NULL) { + return true; + } + return g_registration_callback(env, clazz, init_command_line); +} + +static void RecordChromiumAndroidLinkerHistogram( + JNIEnv* env, + jclass clazz, + jboolean loaded_at_fixed_address_failed, + jboolean is_low_memory_device) { + UMA_HISTOGRAM_BOOLEAN("ChromiumAndroidLinker.LoadedAtFixedAddressFailed", + loaded_at_fixed_address_failed); + UMA_HISTOGRAM_BOOLEAN("ChromiumAndroidLinker.IsLowMemoryDevice", + is_low_memory_device); +} + +void LibraryLoaderExitHook() { + if (g_at_exit_manager) { + delete g_at_exit_manager; + g_at_exit_manager = NULL; + } +} + +bool RegisterLibraryLoaderEntryHook(JNIEnv* env) { + // We need the AtExitManager to be created at the very beginning. + g_at_exit_manager = new base::AtExitManager(); + + return RegisterNativesImpl(env); +} + +void SetVersionNumber(const char* version_number) { + g_library_version_number = strdup(version_number); +} + +jstring GetVersionNumber(JNIEnv* env, jclass clazz) { + return ConvertUTF8ToJavaString(env, g_library_version_number).Release(); +} + +static void RecordNativeLibraryHack(JNIEnv*, jclass, jboolean usedHack) { + UMA_HISTOGRAM_BOOLEAN("LibraryLoader.NativeLibraryHack", usedHack); +} + +} // namespace android +} // namespace base diff --git a/chromium/base/android/library_loader/library_loader_hooks.h b/chromium/base/android/library_loader/library_loader_hooks.h new file mode 100644 index 00000000000..06709f6ba9e --- /dev/null +++ b/chromium/base/android/library_loader/library_loader_hooks.h @@ -0,0 +1,51 @@ +// 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_LIBRARY_LOADER_HOOKS_H_ +#define BASE_ANDROID_LIBRARY_LOADER_HOOKS_H_ + +#include <jni.h> + +#include "base/base_export.h" + +namespace base { +namespace android { + +// Registers the callbacks that allows the entry point of the library to be +// exposed to the calling java code. This handles only registering the +// the callbacks needed by the loader. Any application specific JNI bindings +// should happen once the native library has fully loaded, either in the library +// loaded hook function or later. +BASE_EXPORT bool RegisterLibraryLoaderEntryHook(JNIEnv* env); + +// Typedef for hook function to be called (indirectly from Java) once the +// libraries are loaded. The hook function should register the JNI bindings +// required to start the application. It should return true for success and +// false for failure. +// Note: this can't use base::Callback because there is no way of initializing +// the default callback without using static objects, which we forbid. +typedef bool LibraryLoadedHook(JNIEnv* env, + jclass clazz, + jobjectArray init_command_line); + +// Set the hook function to be called (from Java) once the libraries are loaded. +// SetLibraryLoadedHook may only be called from JNI_OnLoad. The hook function +// should register the JNI bindings required to start the application. + +BASE_EXPORT void SetLibraryLoadedHook(LibraryLoadedHook* func); + +// Pass the version name to the loader. This used to check that the library +// version matches the version expected by Java before completing JNI +// registration. +// Note: argument must remain valid at least until library loading is complete. +BASE_EXPORT void SetVersionNumber(const char* version_number); + +// Call on exit to delete the AtExitManager which OnLibraryLoadedOnUIThread +// created. +BASE_EXPORT void LibraryLoaderExitHook(); + +} // namespace android +} // namespace base + +#endif // BASE_ANDROID_LIBRARY_LOADER_HOOKS_H_ diff --git a/chromium/base/android/linker/DEPS b/chromium/base/android/linker/DEPS new file mode 100644 index 00000000000..15c3afb8668 --- /dev/null +++ b/chromium/base/android/linker/DEPS @@ -0,0 +1,4 @@ +include_rules = [ + # This code cannot depend on anything from base/ + "-base", +] diff --git a/chromium/base/android/path_utils_unittest.cc b/chromium/base/android/path_utils_unittest.cc index c4c12fea133..b6410754eed 100644 --- a/chromium/base/android/path_utils_unittest.cc +++ b/chromium/base/android/path_utils_unittest.cc @@ -39,7 +39,8 @@ TEST_F(PathUtilsTest, TestGetNativeLibraryDirectory) { // the base tests shared object. FilePath path; GetNativeLibraryDirectory(&path); - EXPECT_TRUE(base::PathExists(path.Append(("libbase_unittests.so")))); + EXPECT_TRUE(base::PathExists(path.Append(("libbase_unittests.so"))) || + base::PathExists(path.Append(("libbase_unittests.cr.so")))); } } // namespace android diff --git a/chromium/base/android/scoped_java_ref.cc b/chromium/base/android/scoped_java_ref.cc index 21b466e9584..bb6f5032fe3 100644 --- a/chromium/base/android/scoped_java_ref.cc +++ b/chromium/base/android/scoped_java_ref.cc @@ -9,6 +9,24 @@ namespace base { namespace android { +namespace { + +const int kDefaultLocalFrameCapacity = 16; + +} // namespace + +ScopedJavaLocalFrame::ScopedJavaLocalFrame(JNIEnv* env) : env_(env) { + int failed = env_->PushLocalFrame(kDefaultLocalFrameCapacity); + DCHECK(!failed); +} + +ScopedJavaLocalFrame::ScopedJavaLocalFrame(JNIEnv* env, int capacity) + : env_(env) { + int failed = env_->PushLocalFrame(capacity); + DCHECK(!failed); +} + +ScopedJavaLocalFrame::~ScopedJavaLocalFrame() { env_->PopLocalFrame(NULL); } JavaRef<jobject>::JavaRef() : obj_(NULL) {} diff --git a/chromium/base/android/scoped_java_ref.h b/chromium/base/android/scoped_java_ref.h index a5d71e2d23b..7863c0bef3a 100644 --- a/chromium/base/android/scoped_java_ref.h +++ b/chromium/base/android/scoped_java_ref.h @@ -14,6 +14,23 @@ namespace base { namespace android { +// Creates a new local reference frame, in which at least a given number of +// local references can be created. Note that local references already created +// in previous local frames are still valid in the current local frame. +class BASE_EXPORT ScopedJavaLocalFrame { + public: + explicit ScopedJavaLocalFrame(JNIEnv* env); + ScopedJavaLocalFrame(JNIEnv* env, int capacity); + ~ScopedJavaLocalFrame(); + + private: + // This class is only good for use on the thread it was created on so + // it's safe to cache the non-threadsafe JNIEnv* inside this object. + JNIEnv* env_; + + DISALLOW_COPY_AND_ASSIGN(ScopedJavaLocalFrame); +}; + // Forward declare the generic java reference template class. template<typename T> class JavaRef; diff --git a/chromium/base/android/sys_utils.h b/chromium/base/android/sys_utils.h index 9b3152b466c..0841d0a7888 100644 --- a/chromium/base/android/sys_utils.h +++ b/chromium/base/android/sys_utils.h @@ -17,7 +17,7 @@ class BASE_EXPORT SysUtils { // Returns true iff this is a low-end device. static bool IsLowEndDevice(); - // Return the device's RAM size in kilo-bytes. Used for testing. + // Return the device's RAM size in kilo-bytes. static size_t AmountOfPhysicalMemoryKB(); private: diff --git a/chromium/base/android/trace_event_binding.cc b/chromium/base/android/trace_event_binding.cc new file mode 100644 index 00000000000..e261411f08b --- /dev/null +++ b/chromium/base/android/trace_event_binding.cc @@ -0,0 +1,169 @@ +// 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/trace_event_binding.h" + +#include <jni.h> + +#include <set> + +#include "base/debug/trace_event.h" +#include "base/debug/trace_event_impl.h" +#include "base/lazy_instance.h" +#include "jni/TraceEvent_jni.h" + +namespace base { +namespace android { + +namespace { + +const char kJavaCategory[] = "Java"; +const char kToplevelCategory[] = "toplevel"; +const char kLooperDispatchMessage[] = "Looper.dispatchMessage"; + +// Boilerplate for safely converting Java data to TRACE_EVENT data. +class TraceEventDataConverter { + public: + TraceEventDataConverter(JNIEnv* env, + jstring jname, + jstring jarg) + : env_(env), + jname_(jname), + jarg_(jarg), + name_(env->GetStringUTFChars(jname, NULL)), + arg_(jarg ? env->GetStringUTFChars(jarg, NULL) : NULL) { + } + ~TraceEventDataConverter() { + env_->ReleaseStringUTFChars(jname_, name_); + if (jarg_) + env_->ReleaseStringUTFChars(jarg_, arg_); + } + + // Return saves values to pass to TRACE_EVENT macros. + const char* name() { return name_; } + const char* arg_name() { return arg_ ? "arg" : NULL; } + const char* arg() { return arg_; } + + private: + JNIEnv* env_; + jstring jname_; + jstring jarg_; + const char* name_; + const char* arg_; + + DISALLOW_COPY_AND_ASSIGN(TraceEventDataConverter); +}; + +class TraceEnabledObserver : public debug::TraceLog::EnabledStateObserver { + public: + virtual void OnTraceLogEnabled() OVERRIDE { + JNIEnv* env = base::android::AttachCurrentThread(); + base::android::Java_TraceEvent_setEnabled(env, true); + } + virtual void OnTraceLogDisabled() OVERRIDE { + JNIEnv* env = base::android::AttachCurrentThread(); + base::android::Java_TraceEvent_setEnabled(env, false); + } +}; + +base::LazyInstance<TraceEnabledObserver>::Leaky g_trace_enabled_state_observer_; + +} // namespace + +static void RegisterEnabledObserver(JNIEnv* env, jclass clazz) { + bool enabled = debug::TraceLog::GetInstance()->IsEnabled(); + base::android::Java_TraceEvent_setEnabled(env, enabled); + debug::TraceLog::GetInstance()->AddEnabledStateObserver( + g_trace_enabled_state_observer_.Pointer()); +} + +static void StartATrace(JNIEnv* env, jclass clazz) { + base::debug::TraceLog::GetInstance()->StartATrace(); +} + +static void StopATrace(JNIEnv* env, jclass clazz) { + base::debug::TraceLog::GetInstance()->StopATrace(); +} + +static void Instant(JNIEnv* env, jclass clazz, + jstring jname, jstring jarg) { + TraceEventDataConverter converter(env, jname, jarg); + if (converter.arg()) { + TRACE_EVENT_COPY_INSTANT1(kJavaCategory, converter.name(), + TRACE_EVENT_SCOPE_THREAD, + converter.arg_name(), converter.arg()); + } else { + TRACE_EVENT_COPY_INSTANT0(kJavaCategory, converter.name(), + TRACE_EVENT_SCOPE_THREAD); + } +} + +static void Begin(JNIEnv* env, jclass clazz, + jstring jname, jstring jarg) { + TraceEventDataConverter converter(env, jname, jarg); + if (converter.arg()) { + TRACE_EVENT_COPY_BEGIN1(kJavaCategory, converter.name(), + converter.arg_name(), converter.arg()); + } else { + TRACE_EVENT_COPY_BEGIN0(kJavaCategory, converter.name()); + } +} + +static void End(JNIEnv* env, jclass clazz, + jstring jname, jstring jarg) { + TraceEventDataConverter converter(env, jname, jarg); + if (converter.arg()) { + TRACE_EVENT_COPY_END1(kJavaCategory, converter.name(), + converter.arg_name(), converter.arg()); + } else { + TRACE_EVENT_COPY_END0(kJavaCategory, converter.name()); + } +} + +static void BeginToplevel(JNIEnv* env, jclass clazz) { + TRACE_EVENT_BEGIN0(kToplevelCategory, kLooperDispatchMessage); +} + +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 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); + } +} + +bool RegisterTraceEvent(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +} // namespace android +} // namespace base diff --git a/chromium/base/android/trace_event_binding.h b/chromium/base/android/trace_event_binding.h new file mode 100644 index 00000000000..ed0626620aa --- /dev/null +++ b/chromium/base/android/trace_event_binding.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_TRACE_EVENT_H_ +#define BASE_ANDROID_TRACE_EVENT_H_ + +#include <jni.h> + +namespace base { +namespace android { + +extern bool RegisterTraceEvent(JNIEnv* env); + +} // namespace android +} // namespace base + +#endif // CONTENT_COMMON_ANDROID_TRACE_EVENT_H_ diff --git a/chromium/base/atomicops.h b/chromium/base/atomicops.h index 7f03492e849..cb737cd3704 100644 --- a/chromium/base/atomicops.h +++ b/chromium/base/atomicops.h @@ -28,7 +28,8 @@ #ifndef BASE_ATOMICOPS_H_ #define BASE_ATOMICOPS_H_ -#include "base/basictypes.h" +#include <stdint.h> + #include "build/build_config.h" #if defined(OS_WIN) && defined(ARCH_CPU_64_BITS) @@ -43,7 +44,7 @@ namespace base { namespace subtle { -typedef int32 Atomic32; +typedef int32_t Atomic32; #ifdef ARCH_CPU_64_BITS // We need to be able to go between Atomic64 and AtomicWord implicitly. This // means Atomic64 and AtomicWord should be the same type on 64-bit. @@ -133,7 +134,7 @@ Atomic64 Acquire_Load(volatile const Atomic64* ptr); Atomic64 Release_Load(volatile const Atomic64* ptr); #endif // ARCH_CPU_64_BITS -} // namespace base::subtle +} // namespace subtle } // namespace base // Include our platform specific implementation. @@ -145,8 +146,10 @@ Atomic64 Release_Load(volatile const Atomic64* ptr); #include "base/atomicops_internals_mac.h" #elif defined(OS_NACL) #include "base/atomicops_internals_gcc.h" -#elif defined(COMPILER_GCC) && defined(ARCH_CPU_ARM_FAMILY) +#elif defined(COMPILER_GCC) && defined(ARCH_CPU_ARMEL) #include "base/atomicops_internals_arm_gcc.h" +#elif defined(COMPILER_GCC) && defined(ARCH_CPU_ARM64) +#include "base/atomicops_internals_arm64_gcc.h" #elif defined(COMPILER_GCC) && defined(ARCH_CPU_X86_FAMILY) #include "base/atomicops_internals_x86_gcc.h" #elif defined(COMPILER_GCC) && defined(ARCH_CPU_MIPS_FAMILY) diff --git a/chromium/base/atomicops_internals_arm64_gcc.h b/chromium/base/atomicops_internals_arm64_gcc.h new file mode 100644 index 00000000000..bb6a346eccb --- /dev/null +++ b/chromium/base/atomicops_internals_arm64_gcc.h @@ -0,0 +1,307 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file is an internal atomic implementation, use base/atomicops.h instead. + +// TODO(rmcilroy): Investigate whether we can use __sync__ intrinsics instead of +// the hand coded assembly without introducing perf regressions. +// TODO(rmcilroy): Investigate whether we can use acquire / release versions of +// exclusive load / store assembly instructions and do away with +// the barriers. + +#ifndef BASE_ATOMICOPS_INTERNALS_ARM64_GCC_H_ +#define BASE_ATOMICOPS_INTERNALS_ARM64_GCC_H_ + +#if defined(OS_QNX) +#include <sys/cpuinline.h> +#endif + +namespace base { +namespace subtle { + +inline void MemoryBarrier() { + __asm__ __volatile__ ("dmb ish" ::: "memory"); // NOLINT +} + +// NoBarrier versions of the operation include "memory" in the clobber list. +// This is not required for direct usage of the NoBarrier versions of the +// operations. However this is required for correctness when they are used as +// part of the Acquire or Release versions, to ensure that nothing from outside +// the call is reordered between the operation and the memory barrier. This does +// not change the code generated, so has no or minimal impact on the +// NoBarrier operations. + +inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + Atomic32 prev; + int32_t temp; + + __asm__ __volatile__ ( // NOLINT + "0: \n\t" + "ldxr %w[prev], %[ptr] \n\t" // Load the previous value. + "cmp %w[prev], %w[old_value] \n\t" + "bne 1f \n\t" + "stxr %w[temp], %w[new_value], %[ptr] \n\t" // Try to store the new value. + "cbnz %w[temp], 0b \n\t" // Retry if it did not work. + "1: \n\t" + : [prev]"=&r" (prev), + [temp]"=&r" (temp), + [ptr]"+Q" (*ptr) + : [old_value]"IJr" (old_value), + [new_value]"r" (new_value) + : "cc", "memory" + ); // NOLINT + + return prev; +} + +inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, + Atomic32 new_value) { + Atomic32 result; + int32_t temp; + + __asm__ __volatile__ ( // NOLINT + "0: \n\t" + "ldxr %w[result], %[ptr] \n\t" // Load the previous value. + "stxr %w[temp], %w[new_value], %[ptr] \n\t" // Try to store the new value. + "cbnz %w[temp], 0b \n\t" // Retry if it did not work. + : [result]"=&r" (result), + [temp]"=&r" (temp), + [ptr]"+Q" (*ptr) + : [new_value]"r" (new_value) + : "memory" + ); // NOLINT + + return result; +} + +inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, + Atomic32 increment) { + Atomic32 result; + int32_t temp; + + __asm__ __volatile__ ( // NOLINT + "0: \n\t" + "ldxr %w[result], %[ptr] \n\t" // Load the previous value. + "add %w[result], %w[result], %w[increment]\n\t" + "stxr %w[temp], %w[result], %[ptr] \n\t" // Try to store the result. + "cbnz %w[temp], 0b \n\t" // Retry on failure. + : [result]"=&r" (result), + [temp]"=&r" (temp), + [ptr]"+Q" (*ptr) + : [increment]"IJr" (increment) + : "memory" + ); // NOLINT + + return result; +} + +inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, + Atomic32 increment) { + MemoryBarrier(); + Atomic32 result = NoBarrier_AtomicIncrement(ptr, increment); + MemoryBarrier(); + + return result; +} + +inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + Atomic32 prev = NoBarrier_CompareAndSwap(ptr, old_value, new_value); + MemoryBarrier(); + + return prev; +} + +inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + MemoryBarrier(); + Atomic32 prev = NoBarrier_CompareAndSwap(ptr, old_value, new_value); + + return prev; +} + +inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) { + *ptr = value; +} + +inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) { + *ptr = value; + MemoryBarrier(); +} + +inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) { + __asm__ __volatile__ ( // NOLINT + "stlr %w[value], %[ptr] \n\t" + : [ptr]"=Q" (*ptr) + : [value]"r" (value) + : "memory" + ); // NOLINT +} + +inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) { + return *ptr; +} + +inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) { + Atomic32 value; + + __asm__ __volatile__ ( // NOLINT + "ldar %w[value], %[ptr] \n\t" + : [value]"=r" (value) + : [ptr]"Q" (*ptr) + : "memory" + ); // NOLINT + + return value; +} + +inline Atomic32 Release_Load(volatile const Atomic32* ptr) { + MemoryBarrier(); + return *ptr; +} + +// 64-bit versions of the operations. +// See the 32-bit versions for comments. + +inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value) { + Atomic64 prev; + int32_t temp; + + __asm__ __volatile__ ( // NOLINT + "0: \n\t" + "ldxr %[prev], %[ptr] \n\t" + "cmp %[prev], %[old_value] \n\t" + "bne 1f \n\t" + "stxr %w[temp], %[new_value], %[ptr] \n\t" + "cbnz %w[temp], 0b \n\t" + "1: \n\t" + : [prev]"=&r" (prev), + [temp]"=&r" (temp), + [ptr]"+Q" (*ptr) + : [old_value]"IJr" (old_value), + [new_value]"r" (new_value) + : "cc", "memory" + ); // NOLINT + + return prev; +} + +inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr, + Atomic64 new_value) { + Atomic64 result; + int32_t temp; + + __asm__ __volatile__ ( // NOLINT + "0: \n\t" + "ldxr %[result], %[ptr] \n\t" + "stxr %w[temp], %[new_value], %[ptr] \n\t" + "cbnz %w[temp], 0b \n\t" + : [result]"=&r" (result), + [temp]"=&r" (temp), + [ptr]"+Q" (*ptr) + : [new_value]"r" (new_value) + : "memory" + ); // NOLINT + + return result; +} + +inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr, + Atomic64 increment) { + Atomic64 result; + int32_t temp; + + __asm__ __volatile__ ( // NOLINT + "0: \n\t" + "ldxr %[result], %[ptr] \n\t" + "add %[result], %[result], %[increment] \n\t" + "stxr %w[temp], %[result], %[ptr] \n\t" + "cbnz %w[temp], 0b \n\t" + : [result]"=&r" (result), + [temp]"=&r" (temp), + [ptr]"+Q" (*ptr) + : [increment]"IJr" (increment) + : "memory" + ); // NOLINT + + return result; +} + +inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr, + Atomic64 increment) { + MemoryBarrier(); + Atomic64 result = NoBarrier_AtomicIncrement(ptr, increment); + MemoryBarrier(); + + return result; +} + +inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value) { + Atomic64 prev = NoBarrier_CompareAndSwap(ptr, old_value, new_value); + MemoryBarrier(); + + return prev; +} + +inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value) { + MemoryBarrier(); + Atomic64 prev = NoBarrier_CompareAndSwap(ptr, old_value, new_value); + + return prev; +} + +inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) { + *ptr = value; +} + +inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) { + *ptr = value; + MemoryBarrier(); +} + +inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) { + __asm__ __volatile__ ( // NOLINT + "stlr %x[value], %[ptr] \n\t" + : [ptr]"=Q" (*ptr) + : [value]"r" (value) + : "memory" + ); // NOLINT +} + +inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) { + return *ptr; +} + +inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) { + Atomic64 value; + + __asm__ __volatile__ ( // NOLINT + "ldar %x[value], %[ptr] \n\t" + : [value]"=r" (value) + : [ptr]"Q" (*ptr) + : "memory" + ); // NOLINT + + return value; +} + +inline Atomic64 Release_Load(volatile const Atomic64* ptr) { + MemoryBarrier(); + return *ptr; +} + +} // namespace base::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 9f4fe2e586e..e654afa7d27 100644 --- a/chromium/base/atomicops_internals_arm_gcc.h +++ b/chromium/base/atomicops_internals_arm_gcc.h @@ -9,6 +9,10 @@ #ifndef BASE_ATOMICOPS_INTERNALS_ARM_GCC_H_ #define BASE_ATOMICOPS_INTERNALS_ARM_GCC_H_ +#if defined(OS_QNX) +#include <sys/cpuinline.h> +#endif + namespace base { namespace subtle { @@ -40,10 +44,15 @@ namespace subtle { // inline void MemoryBarrier() { - // Note: This is a function call, which is also an implicit compiler - // barrier. +#if defined(OS_LINUX) || defined(OS_ANDROID) + // Note: This is a function call, which is also an implicit compiler barrier. typedef void (*KernelMemoryBarrierFunc)(); ((KernelMemoryBarrierFunc)0xffff0fa0)(); +#elif defined(OS_QNX) + __cpu_membarrier(); +#else +#error MemoryBarrier() is not implemented on this platform. +#endif } // An ARM toolchain would only define one of these depending on which @@ -54,7 +63,7 @@ inline void MemoryBarrier() { defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || \ defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || \ defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || \ - defined(__ARM_ARCH_6KZ__) || defined(__ARM_ARCH_6T2__) + defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, Atomic32 old_value, diff --git a/chromium/base/atomicops_internals_mac.h b/chromium/base/atomicops_internals_mac.h index 658ed54879f..ccbb896e4cb 100644 --- a/chromium/base/atomicops_internals_mac.h +++ b/chromium/base/atomicops_internals_mac.h @@ -12,7 +12,7 @@ namespace base { namespace subtle { -inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32 *ptr, +inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, Atomic32 old_value, Atomic32 new_value) { Atomic32 prev_value; @@ -26,7 +26,7 @@ inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32 *ptr, return prev_value; } -inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32 *ptr, +inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, Atomic32 new_value) { Atomic32 old_value; do { @@ -36,13 +36,13 @@ inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32 *ptr, return old_value; } -inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32 *ptr, +inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, Atomic32 increment) { return OSAtomicAdd32(increment, const_cast<Atomic32*>(ptr)); } -inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32 *ptr, - Atomic32 increment) { +inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, + Atomic32 increment) { return OSAtomicAdd32Barrier(increment, const_cast<Atomic32*>(ptr)); } @@ -50,7 +50,7 @@ inline void MemoryBarrier() { OSMemoryBarrier(); } -inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32 *ptr, +inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr, Atomic32 old_value, Atomic32 new_value) { Atomic32 prev_value; @@ -64,7 +64,7 @@ inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32 *ptr, return prev_value; } -inline Atomic32 Release_CompareAndSwap(volatile Atomic32 *ptr, +inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr, Atomic32 old_value, Atomic32 new_value) { return Acquire_CompareAndSwap(ptr, old_value, new_value); @@ -74,12 +74,12 @@ inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) { *ptr = value; } -inline void Acquire_Store(volatile Atomic32 *ptr, Atomic32 value) { +inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) { *ptr = value; MemoryBarrier(); } -inline void Release_Store(volatile Atomic32 *ptr, Atomic32 value) { +inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) { MemoryBarrier(); *ptr = value; } @@ -88,13 +88,13 @@ inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) { return *ptr; } -inline Atomic32 Acquire_Load(volatile const Atomic32 *ptr) { +inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) { Atomic32 value = *ptr; MemoryBarrier(); return value; } -inline Atomic32 Release_Load(volatile const Atomic32 *ptr) { +inline Atomic32 Release_Load(volatile const Atomic32* ptr) { MemoryBarrier(); return *ptr; } @@ -103,7 +103,7 @@ inline Atomic32 Release_Load(volatile const Atomic32 *ptr) { // 64-bit implementation on 64-bit platform -inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64 *ptr, +inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr, Atomic64 old_value, Atomic64 new_value) { Atomic64 prev_value; @@ -117,7 +117,7 @@ inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64 *ptr, return prev_value; } -inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64 *ptr, +inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr, Atomic64 new_value) { Atomic64 old_value; do { @@ -127,18 +127,18 @@ inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64 *ptr, return old_value; } -inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64 *ptr, +inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr, Atomic64 increment) { return OSAtomicAdd64(increment, reinterpret_cast<volatile int64_t*>(ptr)); } -inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64 *ptr, +inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr, Atomic64 increment) { return OSAtomicAdd64Barrier(increment, reinterpret_cast<volatile int64_t*>(ptr)); } -inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64 *ptr, +inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr, Atomic64 old_value, Atomic64 new_value) { Atomic64 prev_value; @@ -152,7 +152,7 @@ inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64 *ptr, return prev_value; } -inline Atomic64 Release_CompareAndSwap(volatile Atomic64 *ptr, +inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr, Atomic64 old_value, Atomic64 new_value) { // The lib kern interface does not distinguish between @@ -164,12 +164,12 @@ inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) { *ptr = value; } -inline void Acquire_Store(volatile Atomic64 *ptr, Atomic64 value) { +inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) { *ptr = value; MemoryBarrier(); } -inline void Release_Store(volatile Atomic64 *ptr, Atomic64 value) { +inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) { MemoryBarrier(); *ptr = value; } @@ -178,13 +178,13 @@ inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) { return *ptr; } -inline Atomic64 Acquire_Load(volatile const Atomic64 *ptr) { +inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) { Atomic64 value = *ptr; MemoryBarrier(); return value; } -inline Atomic64 Release_Load(volatile const Atomic64 *ptr) { +inline Atomic64 Release_Load(volatile const Atomic64* ptr) { MemoryBarrier(); return *ptr; } diff --git a/chromium/base/atomicops_internals_tsan.h b/chromium/base/atomicops_internals_tsan.h index 44d64009c70..24382fd9c01 100644 --- a/chromium/base/atomicops_internals_tsan.h +++ b/chromium/base/atomicops_internals_tsan.h @@ -8,358 +8,168 @@ #ifndef BASE_ATOMICOPS_INTERNALS_TSAN_H_ #define BASE_ATOMICOPS_INTERNALS_TSAN_H_ -#include "base/base_export.h" - -// This struct is not part of the public API of this module; clients may not -// use it. (However, it's exported via BASE_EXPORT because clients implicitly -// do use it at link time by inlining these functions.) -// Features of this x86. Values may not be correct before main() is run, -// but are set conservatively. -struct AtomicOps_x86CPUFeatureStruct { - bool has_amd_lock_mb_bug; // Processor has AMD memory-barrier bug; do lfence - // after acquire compare-and-swap. - bool has_sse2; // Processor has SSE2. -}; -BASE_EXPORT extern struct AtomicOps_x86CPUFeatureStruct - AtomicOps_Internalx86CPUFeatures; - -#define ATOMICOPS_COMPILER_BARRIER() __asm__ __volatile__("" : : : "memory") +#include <sanitizer/tsan_interface_atomic.h> namespace base { namespace subtle { -#ifndef TSAN_INTERFACE_ATOMIC_H -#define TSAN_INTERFACE_ATOMIC_H - -#ifdef __cplusplus -extern "C" { -#endif - -typedef char __tsan_atomic8; -typedef short __tsan_atomic16; // NOLINT -typedef int __tsan_atomic32; -typedef long __tsan_atomic64; // NOLINT - -#if defined(__SIZEOF_INT128__) \ - || (__clang_major__ * 100 + __clang_minor__ >= 302) -typedef __int128 __tsan_atomic128; -#define __TSAN_HAS_INT128 1 -#else -typedef char __tsan_atomic128; -#define __TSAN_HAS_INT128 0 -#endif - -typedef enum { - __tsan_memory_order_relaxed, - __tsan_memory_order_consume, - __tsan_memory_order_acquire, - __tsan_memory_order_release, - __tsan_memory_order_acq_rel, - __tsan_memory_order_seq_cst, -} __tsan_memory_order; - -__tsan_atomic8 __tsan_atomic8_load(const volatile __tsan_atomic8 *a, - __tsan_memory_order mo); -__tsan_atomic16 __tsan_atomic16_load(const volatile __tsan_atomic16 *a, - __tsan_memory_order mo); -__tsan_atomic32 __tsan_atomic32_load(const volatile __tsan_atomic32 *a, - __tsan_memory_order mo); -__tsan_atomic64 __tsan_atomic64_load(const volatile __tsan_atomic64 *a, - __tsan_memory_order mo); -__tsan_atomic128 __tsan_atomic128_load(const volatile __tsan_atomic128 *a, - __tsan_memory_order mo); - -void __tsan_atomic8_store(volatile __tsan_atomic8 *a, __tsan_atomic8 v, - __tsan_memory_order mo); -void __tsan_atomic16_store(volatile __tsan_atomic16 *a, __tsan_atomic16 v, - __tsan_memory_order mo); -void __tsan_atomic32_store(volatile __tsan_atomic32 *a, __tsan_atomic32 v, - __tsan_memory_order mo); -void __tsan_atomic64_store(volatile __tsan_atomic64 *a, __tsan_atomic64 v, - __tsan_memory_order mo); -void __tsan_atomic128_store(volatile __tsan_atomic128 *a, __tsan_atomic128 v, - __tsan_memory_order mo); - -__tsan_atomic8 __tsan_atomic8_exchange(volatile __tsan_atomic8 *a, - __tsan_atomic8 v, __tsan_memory_order mo); -__tsan_atomic16 __tsan_atomic16_exchange(volatile __tsan_atomic16 *a, - __tsan_atomic16 v, __tsan_memory_order mo); -__tsan_atomic32 __tsan_atomic32_exchange(volatile __tsan_atomic32 *a, - __tsan_atomic32 v, __tsan_memory_order mo); -__tsan_atomic64 __tsan_atomic64_exchange(volatile __tsan_atomic64 *a, - __tsan_atomic64 v, __tsan_memory_order mo); -__tsan_atomic128 __tsan_atomic128_exchange(volatile __tsan_atomic128 *a, - __tsan_atomic128 v, __tsan_memory_order mo); - -__tsan_atomic8 __tsan_atomic8_fetch_add(volatile __tsan_atomic8 *a, - __tsan_atomic8 v, __tsan_memory_order mo); -__tsan_atomic16 __tsan_atomic16_fetch_add(volatile __tsan_atomic16 *a, - __tsan_atomic16 v, __tsan_memory_order mo); -__tsan_atomic32 __tsan_atomic32_fetch_add(volatile __tsan_atomic32 *a, - __tsan_atomic32 v, __tsan_memory_order mo); -__tsan_atomic64 __tsan_atomic64_fetch_add(volatile __tsan_atomic64 *a, - __tsan_atomic64 v, __tsan_memory_order mo); -__tsan_atomic128 __tsan_atomic128_fetch_add(volatile __tsan_atomic128 *a, - __tsan_atomic128 v, __tsan_memory_order mo); - -__tsan_atomic8 __tsan_atomic8_fetch_and(volatile __tsan_atomic8 *a, - __tsan_atomic8 v, __tsan_memory_order mo); -__tsan_atomic16 __tsan_atomic16_fetch_and(volatile __tsan_atomic16 *a, - __tsan_atomic16 v, __tsan_memory_order mo); -__tsan_atomic32 __tsan_atomic32_fetch_and(volatile __tsan_atomic32 *a, - __tsan_atomic32 v, __tsan_memory_order mo); -__tsan_atomic64 __tsan_atomic64_fetch_and(volatile __tsan_atomic64 *a, - __tsan_atomic64 v, __tsan_memory_order mo); -__tsan_atomic128 __tsan_atomic128_fetch_and(volatile __tsan_atomic128 *a, - __tsan_atomic128 v, __tsan_memory_order mo); - -__tsan_atomic8 __tsan_atomic8_fetch_or(volatile __tsan_atomic8 *a, - __tsan_atomic8 v, __tsan_memory_order mo); -__tsan_atomic16 __tsan_atomic16_fetch_or(volatile __tsan_atomic16 *a, - __tsan_atomic16 v, __tsan_memory_order mo); -__tsan_atomic32 __tsan_atomic32_fetch_or(volatile __tsan_atomic32 *a, - __tsan_atomic32 v, __tsan_memory_order mo); -__tsan_atomic64 __tsan_atomic64_fetch_or(volatile __tsan_atomic64 *a, - __tsan_atomic64 v, __tsan_memory_order mo); -__tsan_atomic128 __tsan_atomic128_fetch_or(volatile __tsan_atomic128 *a, - __tsan_atomic128 v, __tsan_memory_order mo); - -__tsan_atomic8 __tsan_atomic8_fetch_xor(volatile __tsan_atomic8 *a, - __tsan_atomic8 v, __tsan_memory_order mo); -__tsan_atomic16 __tsan_atomic16_fetch_xor(volatile __tsan_atomic16 *a, - __tsan_atomic16 v, __tsan_memory_order mo); -__tsan_atomic32 __tsan_atomic32_fetch_xor(volatile __tsan_atomic32 *a, - __tsan_atomic32 v, __tsan_memory_order mo); -__tsan_atomic64 __tsan_atomic64_fetch_xor(volatile __tsan_atomic64 *a, - __tsan_atomic64 v, __tsan_memory_order mo); -__tsan_atomic128 __tsan_atomic128_fetch_xor(volatile __tsan_atomic128 *a, - __tsan_atomic128 v, __tsan_memory_order mo); - -__tsan_atomic8 __tsan_atomic8_fetch_nand(volatile __tsan_atomic8 *a, - __tsan_atomic8 v, __tsan_memory_order mo); -__tsan_atomic16 __tsan_atomic16_fetch_nand(volatile __tsan_atomic16 *a, - __tsan_atomic16 v, __tsan_memory_order mo); -__tsan_atomic32 __tsan_atomic32_fetch_nand(volatile __tsan_atomic32 *a, - __tsan_atomic32 v, __tsan_memory_order mo); -__tsan_atomic64 __tsan_atomic64_fetch_nand(volatile __tsan_atomic64 *a, - __tsan_atomic64 v, __tsan_memory_order mo); -__tsan_atomic128 __tsan_atomic128_fetch_nand(volatile __tsan_atomic128 *a, - __tsan_atomic128 v, __tsan_memory_order mo); - -int __tsan_atomic8_compare_exchange_weak(volatile __tsan_atomic8 *a, - __tsan_atomic8 *c, __tsan_atomic8 v, __tsan_memory_order mo, - __tsan_memory_order fail_mo); -int __tsan_atomic16_compare_exchange_weak(volatile __tsan_atomic16 *a, - __tsan_atomic16 *c, __tsan_atomic16 v, __tsan_memory_order mo, - __tsan_memory_order fail_mo); -int __tsan_atomic32_compare_exchange_weak(volatile __tsan_atomic32 *a, - __tsan_atomic32 *c, __tsan_atomic32 v, __tsan_memory_order mo, - __tsan_memory_order fail_mo); -int __tsan_atomic64_compare_exchange_weak(volatile __tsan_atomic64 *a, - __tsan_atomic64 *c, __tsan_atomic64 v, __tsan_memory_order mo, - __tsan_memory_order fail_mo); -int __tsan_atomic128_compare_exchange_weak(volatile __tsan_atomic128 *a, - __tsan_atomic128 *c, __tsan_atomic128 v, __tsan_memory_order mo, - __tsan_memory_order fail_mo); - -int __tsan_atomic8_compare_exchange_strong(volatile __tsan_atomic8 *a, - __tsan_atomic8 *c, __tsan_atomic8 v, __tsan_memory_order mo, - __tsan_memory_order fail_mo); -int __tsan_atomic16_compare_exchange_strong(volatile __tsan_atomic16 *a, - __tsan_atomic16 *c, __tsan_atomic16 v, __tsan_memory_order mo, - __tsan_memory_order fail_mo); -int __tsan_atomic32_compare_exchange_strong(volatile __tsan_atomic32 *a, - __tsan_atomic32 *c, __tsan_atomic32 v, __tsan_memory_order mo, - __tsan_memory_order fail_mo); -int __tsan_atomic64_compare_exchange_strong(volatile __tsan_atomic64 *a, - __tsan_atomic64 *c, __tsan_atomic64 v, __tsan_memory_order mo, - __tsan_memory_order fail_mo); -int __tsan_atomic128_compare_exchange_strong(volatile __tsan_atomic128 *a, - __tsan_atomic128 *c, __tsan_atomic128 v, __tsan_memory_order mo, - __tsan_memory_order fail_mo); - -__tsan_atomic8 __tsan_atomic8_compare_exchange_val( - volatile __tsan_atomic8 *a, __tsan_atomic8 c, __tsan_atomic8 v, - __tsan_memory_order mo, __tsan_memory_order fail_mo); -__tsan_atomic16 __tsan_atomic16_compare_exchange_val( - volatile __tsan_atomic16 *a, __tsan_atomic16 c, __tsan_atomic16 v, - __tsan_memory_order mo, __tsan_memory_order fail_mo); -__tsan_atomic32 __tsan_atomic32_compare_exchange_val( - volatile __tsan_atomic32 *a, __tsan_atomic32 c, __tsan_atomic32 v, - __tsan_memory_order mo, __tsan_memory_order fail_mo); -__tsan_atomic64 __tsan_atomic64_compare_exchange_val( - volatile __tsan_atomic64 *a, __tsan_atomic64 c, __tsan_atomic64 v, - __tsan_memory_order mo, __tsan_memory_order fail_mo); -__tsan_atomic128 __tsan_atomic128_compare_exchange_val( - volatile __tsan_atomic128 *a, __tsan_atomic128 c, __tsan_atomic128 v, - __tsan_memory_order mo, __tsan_memory_order fail_mo); - -void __tsan_atomic_thread_fence(__tsan_memory_order mo); -void __tsan_atomic_signal_fence(__tsan_memory_order mo); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // #ifndef TSAN_INTERFACE_ATOMIC_H - -inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32 *ptr, - Atomic32 old_value, - Atomic32 new_value) { +inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { Atomic32 cmp = old_value; __tsan_atomic32_compare_exchange_strong(ptr, &cmp, new_value, __tsan_memory_order_relaxed, __tsan_memory_order_relaxed); return cmp; } -inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32 *ptr, - Atomic32 new_value) { +inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, + Atomic32 new_value) { return __tsan_atomic32_exchange(ptr, new_value, __tsan_memory_order_relaxed); } -inline Atomic32 Acquire_AtomicExchange(volatile Atomic32 *ptr, - Atomic32 new_value) { +inline Atomic32 Acquire_AtomicExchange(volatile Atomic32* ptr, + Atomic32 new_value) { return __tsan_atomic32_exchange(ptr, new_value, __tsan_memory_order_acquire); } -inline Atomic32 Release_AtomicExchange(volatile Atomic32 *ptr, - Atomic32 new_value) { +inline Atomic32 Release_AtomicExchange(volatile Atomic32* ptr, + Atomic32 new_value) { return __tsan_atomic32_exchange(ptr, new_value, __tsan_memory_order_release); } -inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32 *ptr, - Atomic32 increment) { +inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, + Atomic32 increment) { return increment + __tsan_atomic32_fetch_add(ptr, increment, __tsan_memory_order_relaxed); } -inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32 *ptr, - Atomic32 increment) { +inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, + Atomic32 increment) { return increment + __tsan_atomic32_fetch_add(ptr, increment, __tsan_memory_order_acq_rel); } -inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32 *ptr, - Atomic32 old_value, - Atomic32 new_value) { +inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { Atomic32 cmp = old_value; __tsan_atomic32_compare_exchange_strong(ptr, &cmp, new_value, __tsan_memory_order_acquire, __tsan_memory_order_acquire); return cmp; } -inline Atomic32 Release_CompareAndSwap(volatile Atomic32 *ptr, - Atomic32 old_value, - Atomic32 new_value) { +inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { Atomic32 cmp = old_value; __tsan_atomic32_compare_exchange_strong(ptr, &cmp, new_value, __tsan_memory_order_release, __tsan_memory_order_relaxed); return cmp; } -inline void NoBarrier_Store(volatile Atomic32 *ptr, Atomic32 value) { +inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) { __tsan_atomic32_store(ptr, value, __tsan_memory_order_relaxed); } -inline void Acquire_Store(volatile Atomic32 *ptr, Atomic32 value) { +inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) { __tsan_atomic32_store(ptr, value, __tsan_memory_order_relaxed); __tsan_atomic_thread_fence(__tsan_memory_order_seq_cst); } -inline void Release_Store(volatile Atomic32 *ptr, Atomic32 value) { +inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) { __tsan_atomic32_store(ptr, value, __tsan_memory_order_release); } -inline Atomic32 NoBarrier_Load(volatile const Atomic32 *ptr) { +inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) { return __tsan_atomic32_load(ptr, __tsan_memory_order_relaxed); } -inline Atomic32 Acquire_Load(volatile const Atomic32 *ptr) { +inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) { return __tsan_atomic32_load(ptr, __tsan_memory_order_acquire); } -inline Atomic32 Release_Load(volatile const Atomic32 *ptr) { +inline Atomic32 Release_Load(volatile const Atomic32* ptr) { __tsan_atomic_thread_fence(__tsan_memory_order_seq_cst); return __tsan_atomic32_load(ptr, __tsan_memory_order_relaxed); } -inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64 *ptr, - Atomic64 old_value, - Atomic64 new_value) { +inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value) { Atomic64 cmp = old_value; __tsan_atomic64_compare_exchange_strong(ptr, &cmp, new_value, __tsan_memory_order_relaxed, __tsan_memory_order_relaxed); return cmp; } -inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64 *ptr, - Atomic64 new_value) { +inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr, + Atomic64 new_value) { return __tsan_atomic64_exchange(ptr, new_value, __tsan_memory_order_relaxed); } -inline Atomic64 Acquire_AtomicExchange(volatile Atomic64 *ptr, - Atomic64 new_value) { +inline Atomic64 Acquire_AtomicExchange(volatile Atomic64* ptr, + Atomic64 new_value) { return __tsan_atomic64_exchange(ptr, new_value, __tsan_memory_order_acquire); } -inline Atomic64 Release_AtomicExchange(volatile Atomic64 *ptr, - Atomic64 new_value) { +inline Atomic64 Release_AtomicExchange(volatile Atomic64* ptr, + Atomic64 new_value) { return __tsan_atomic64_exchange(ptr, new_value, __tsan_memory_order_release); } -inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64 *ptr, - Atomic64 increment) { +inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr, + Atomic64 increment) { return increment + __tsan_atomic64_fetch_add(ptr, increment, __tsan_memory_order_relaxed); } -inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64 *ptr, - Atomic64 increment) { +inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr, + Atomic64 increment) { return increment + __tsan_atomic64_fetch_add(ptr, increment, __tsan_memory_order_acq_rel); } -inline void NoBarrier_Store(volatile Atomic64 *ptr, Atomic64 value) { +inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) { __tsan_atomic64_store(ptr, value, __tsan_memory_order_relaxed); } -inline void Acquire_Store(volatile Atomic64 *ptr, Atomic64 value) { +inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) { __tsan_atomic64_store(ptr, value, __tsan_memory_order_relaxed); __tsan_atomic_thread_fence(__tsan_memory_order_seq_cst); } -inline void Release_Store(volatile Atomic64 *ptr, Atomic64 value) { +inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) { __tsan_atomic64_store(ptr, value, __tsan_memory_order_release); } -inline Atomic64 NoBarrier_Load(volatile const Atomic64 *ptr) { +inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) { return __tsan_atomic64_load(ptr, __tsan_memory_order_relaxed); } -inline Atomic64 Acquire_Load(volatile const Atomic64 *ptr) { +inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) { return __tsan_atomic64_load(ptr, __tsan_memory_order_acquire); } -inline Atomic64 Release_Load(volatile const Atomic64 *ptr) { +inline Atomic64 Release_Load(volatile const Atomic64* ptr) { __tsan_atomic_thread_fence(__tsan_memory_order_seq_cst); return __tsan_atomic64_load(ptr, __tsan_memory_order_relaxed); } -inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64 *ptr, - Atomic64 old_value, - Atomic64 new_value) { +inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value) { Atomic64 cmp = old_value; __tsan_atomic64_compare_exchange_strong(ptr, &cmp, new_value, __tsan_memory_order_acquire, __tsan_memory_order_acquire); return cmp; } -inline Atomic64 Release_CompareAndSwap(volatile Atomic64 *ptr, - Atomic64 old_value, - Atomic64 new_value) { +inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value) { Atomic64 cmp = old_value; __tsan_atomic64_compare_exchange_strong(ptr, &cmp, new_value, __tsan_memory_order_release, __tsan_memory_order_relaxed); @@ -373,6 +183,4 @@ inline void MemoryBarrier() { } // namespace base::subtle } // namespace base -#undef ATOMICOPS_COMPILER_BARRIER - #endif // BASE_ATOMICOPS_INTERNALS_TSAN_H_ diff --git a/chromium/base/atomicops_internals_x86_gcc.cc b/chromium/base/atomicops_internals_x86_gcc.cc index 933ca51896d..3f47458ad19 100644 --- a/chromium/base/atomicops_internals_x86_gcc.cc +++ b/chromium/base/atomicops_internals_x86_gcc.cc @@ -5,10 +5,10 @@ // This module gets enough CPU information to optimize the // atomicops module on x86. +#include <stdint.h> #include <string.h> #include "base/atomicops.h" -#include "base/basictypes.h" // This file only makes sense with atomicops_internals_x86_gcc.h -- it // depends on structs that are defined in that file. If atomicops.h @@ -21,16 +21,16 @@ // must preserve that register's value across cpuid instructions. #if defined(__i386__) #define cpuid(a, b, c, d, inp) \ - asm ("mov %%ebx, %%edi\n" \ - "cpuid\n" \ - "xchg %%edi, %%ebx\n" \ - : "=a" (a), "=D" (b), "=c" (c), "=d" (d) : "a" (inp)) -#elif defined (__x86_64__) + asm("mov %%ebx, %%edi\n" \ + "cpuid\n" \ + "xchg %%edi, %%ebx\n" \ + : "=a" (a), "=D" (b), "=c" (c), "=d" (d) : "a" (inp)) +#elif defined(__x86_64__) #define cpuid(a, b, c, d, inp) \ - asm ("mov %%rbx, %%rdi\n" \ - "cpuid\n" \ - "xchg %%rdi, %%rbx\n" \ - : "=a" (a), "=D" (b), "=c" (c), "=d" (d) : "a" (inp)) + asm("mov %%rbx, %%rdi\n" \ + "cpuid\n" \ + "xchg %%rdi, %%rbx\n" \ + : "=a" (a), "=D" (b), "=c" (c), "=d" (d) : "a" (inp)) #endif #if defined(cpuid) // initialize the struct only on x86 @@ -40,15 +40,16 @@ // default values should hopefully be pretty safe. struct AtomicOps_x86CPUFeatureStruct AtomicOps_Internalx86CPUFeatures = { false, // bug can't exist before process spawns multiple threads - false, // no SSE2 }; +namespace { + // Initialize the AtomicOps_Internalx86CPUFeatures struct. -static void AtomicOps_Internalx86CPUFeaturesInit() { - uint32 eax; - uint32 ebx; - uint32 ecx; - uint32 edx; +void AtomicOps_Internalx86CPUFeaturesInit() { + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; // Get vendor string (issue CPUID with eax = 0) cpuid(eax, ebx, ecx, edx, 0); @@ -80,13 +81,8 @@ static void AtomicOps_Internalx86CPUFeaturesInit() { } else { AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug = false; } - - // edx bit 26 is SSE2 which we use to tell use whether we can use mfence - AtomicOps_Internalx86CPUFeatures.has_sse2 = ((edx >> 26) & 1); } -namespace { - class AtomicOpsx86Initializer { public: AtomicOpsx86Initializer() { diff --git a/chromium/base/atomicops_internals_x86_gcc.h b/chromium/base/atomicops_internals_x86_gcc.h index ac02b17f5db..7386fabea9c 100644 --- a/chromium/base/atomicops_internals_x86_gcc.h +++ b/chromium/base/atomicops_internals_x86_gcc.h @@ -17,7 +17,6 @@ struct AtomicOps_x86CPUFeatureStruct { bool has_amd_lock_mb_bug; // Processor has AMD memory-barrier bug; do lfence // after acquire compare-and-swap. - bool has_sse2; // Processor has SSE2. }; BASE_EXPORT extern struct AtomicOps_x86CPUFeatureStruct AtomicOps_Internalx86CPUFeatures; @@ -92,10 +91,6 @@ inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) { *ptr = value; } -#if defined(__x86_64__) - -// 64-bit implementations of memory barrier can be simpler, because it -// "mfence" is guaranteed to exist. inline void MemoryBarrier() { __asm__ __volatile__("mfence" : : : "memory"); } @@ -105,28 +100,6 @@ inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) { MemoryBarrier(); } -#else - -inline void MemoryBarrier() { - if (AtomicOps_Internalx86CPUFeatures.has_sse2) { - __asm__ __volatile__("mfence" : : : "memory"); - } else { // mfence is faster but not present on PIII - Atomic32 x = 0; - NoBarrier_AtomicExchange(&x, 0); // acts as a barrier on PIII - } -} - -inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) { - if (AtomicOps_Internalx86CPUFeatures.has_sse2) { - *ptr = value; - __asm__ __volatile__("mfence" : : : "memory"); - } else { - NoBarrier_AtomicExchange(ptr, value); - // acts as a barrier on PIII - } -} -#endif - inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) { ATOMICOPS_COMPILER_BARRIER(); *ptr = value; // An x86 store acts as a release barrier. diff --git a/chromium/base/atomicops_internals_x86_msvc.h b/chromium/base/atomicops_internals_x86_msvc.h index 3a2c72ddb47..0269d894a1f 100644 --- a/chromium/base/atomicops_internals_x86_msvc.h +++ b/chromium/base/atomicops_internals_x86_msvc.h @@ -9,6 +9,10 @@ #include <windows.h> +#include <intrin.h> + +#include "base/macros.h" + #if defined(ARCH_CPU_64_BITS) // windows.h #defines this (only on x64). This causes problems because the // public API also uses MemoryBarrier at the public name for this fence. So, on @@ -24,7 +28,7 @@ namespace subtle { inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, Atomic32 old_value, Atomic32 new_value) { - LONG result = InterlockedCompareExchange( + LONG result = _InterlockedCompareExchange( reinterpret_cast<volatile LONG*>(ptr), static_cast<LONG>(new_value), static_cast<LONG>(old_value)); @@ -33,7 +37,7 @@ inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, Atomic32 new_value) { - LONG result = InterlockedExchange( + LONG result = _InterlockedExchange( reinterpret_cast<volatile LONG*>(ptr), static_cast<LONG>(new_value)); return static_cast<Atomic32>(result); @@ -41,7 +45,7 @@ inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, Atomic32 increment) { - return InterlockedExchangeAdd( + return _InterlockedExchangeAdd( reinterpret_cast<volatile LONG*>(ptr), static_cast<LONG>(increment)) + increment; } diff --git a/chromium/base/atomicops_unittest.cc b/chromium/base/atomicops_unittest.cc index d73a098c488..3fd55974722 100644 --- a/chromium/base/atomicops_unittest.cc +++ b/chromium/base/atomicops_unittest.cc @@ -4,9 +4,9 @@ #include "base/atomicops.h" +#include <stdint.h> #include <string.h> -#include "base/port.h" #include "testing/gtest/include/gtest/gtest.h" template <class AtomicType> @@ -91,7 +91,7 @@ static void TestCompareAndSwap() { // 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 = (GG_ULONGLONG(1) << + 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); @@ -114,7 +114,7 @@ static void TestAtomicExchange() { // 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 = (GG_ULONGLONG(1) << + 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); @@ -131,7 +131,7 @@ static void TestAtomicExchange() { template <class AtomicType> static void TestAtomicIncrementBounds() { // Test at rollover boundary between int_max and int_min - AtomicType test_val = (GG_ULONGLONG(1) << + AtomicType test_val = (static_cast<uint64_t>(1) << (NUM_BITS(AtomicType) - 1)); AtomicType value = -1 ^ test_val; AtomicType new_value = base::subtle::NoBarrier_AtomicIncrement(&value, 1); @@ -142,7 +142,7 @@ static void TestAtomicIncrementBounds() { EXPECT_EQ(-1 ^ test_val, value); // Test at 32-bit boundary for 64-bit atomic type. - test_val = GG_ULONGLONG(1) << (NUM_BITS(AtomicType) / 2); + test_val = static_cast<uint64_t>(1) << (NUM_BITS(AtomicType) / 2); value = test_val - 1; new_value = base::subtle::NoBarrier_AtomicIncrement(&value, 1); EXPECT_EQ(test_val, value); diff --git a/chromium/base/base.gyp b/chromium/base/base.gyp index 407d49d0582..5544a4b5a27 100644 --- a/chromium/base/base.gyp +++ b/chromium/base/base.gyp @@ -40,14 +40,6 @@ ['chromeos==1', { 'sources/': [ ['include', '_chromeos\\.cc$'] ] }], - ['toolkit_uses_gtk==1', { - 'dependencies': [ - '../build/linux/system.gyp:gtk', - ], - 'export_dependent_settings': [ - '../build/linux/system.gyp:gtk', - ], - }], ], 'dependencies': [ 'symbolize', @@ -56,9 +48,6 @@ 'defines': [ 'USE_SYMBOLIZE', ], - 'cflags': [ - '-Wno-write-strings', - ], }, { # desktop_linux == 0 and chromeos == 0 'sources/': [ ['exclude', '/xdg_user_dirs/'], @@ -73,22 +62,6 @@ '../build/linux/system.gyp:glib', ], }], - ['use_x11==1', { - 'dependencies': [ - '../build/linux/system.gyp:x11', - ], - 'export_dependent_settings': [ - '../build/linux/system.gyp:x11', - ], - }], - ['use_aura==1 and use_x11==1', { - 'dependencies': [ - '../build/linux/system.gyp:xrandr', - ], - 'export_dependent_settings': [ - '../build/linux/system.gyp:xrandr', - ], - }], ['OS == "android" and _toolset == "host"', { # Always build base as a static_library for host toolset, even if # we're doing a component build. Specifically, we only care about the @@ -105,19 +78,6 @@ # hence the *_android.cc files are included but the actual code # doesn't have OS_ANDROID / ANDROID defined. 'conditions': [ - # Host build on linux depends on system.gyp::gtk as - # default linux build has TOOLKIT_GTK defined. - ['host_os == "linux"', { - 'sources/': [ - ['include', '^atomicops_internals_x86_gcc\\.cc$'], - ], - 'dependencies': [ - '../build/linux/system.gyp:gtk', - ], - 'export_dependent_settings': [ - '../build/linux/system.gyp:gtk', - ], - }], ['host_os == "mac"', { 'sources/': [ ['exclude', '^native_library_linux\\.cc$'], @@ -131,7 +91,7 @@ }], ['OS == "android" and _toolset == "target"', { 'conditions': [ - ['target_arch == "ia32"', { + ['target_arch == "ia32" or target_arch == "x64"', { 'sources/': [ ['include', '^atomicops_internals_x86_gcc\\.cc$'], ], @@ -146,9 +106,6 @@ 'base_jni_headers', '../third_party/ashmem/ashmem.gyp:ashmem', ], - 'include_dirs': [ - '<(SHARED_INTERMEDIATE_DIR)/base', - ], 'link_settings': { 'libraries': [ '-llog', @@ -186,7 +143,7 @@ ], }, 'conditions': [ - ['linux_use_tcmalloc==0', { + ['use_allocator!="tcmalloc"', { 'defines': [ 'NO_TCMALLOC', ], @@ -198,6 +155,32 @@ }], ], }], + ['OS == "win"', { + # Specify delayload for base.dll. + 'msvs_settings': { + 'VCLinkerTool': { + 'DelayLoadDLLs': [ + 'powrprof.dll', + ], + 'AdditionalDependencies': [ + 'powrprof.lib', + ], + }, + }, + # Specify delayload for components that link with base.lib. + 'all_dependent_settings': { + 'msvs_settings': { + 'VCLinkerTool': { + 'DelayLoadDLLs': [ + 'powrprof.dll', + ], + 'AdditionalDependencies': [ + 'powrprof.lib', + ], + }, + }, + }, + }], ['OS == "mac" or (OS == "ios" and _toolset == "host")', { 'link_settings': { 'libraries': [ @@ -237,16 +220,8 @@ }], ], }], - ['use_system_nspr==1', { - 'dependencies': [ - 'third_party/nspr/nspr.gyp:nspr', - ], - }], ], 'sources': [ - 'third_party/nspr/prcpucfg.h', - 'third_party/nspr/prcpucfg_win.h', - 'third_party/nspr/prtypes.h', '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', @@ -258,14 +233,10 @@ 'event_recorder_win.cc', 'linux_util.cc', 'linux_util.h', - 'md5.cc', - 'md5.h', 'message_loop/message_pump_android.cc', 'message_loop/message_pump_android.h', 'message_loop/message_pump_glib.cc', 'message_loop/message_pump_glib.h', - 'message_loop/message_pump_gtk.cc', - 'message_loop/message_pump_gtk.h', 'message_loop/message_pump_io_ios.cc', 'message_loop/message_pump_io_ios.h', 'message_loop/message_pump_observer.h', @@ -273,8 +244,6 @@ 'message_loop/message_pump_libevent.h', 'message_loop/message_pump_mac.h', 'message_loop/message_pump_mac.mm', - 'message_loop/message_pump_x11.cc', - 'message_loop/message_pump_x11.h', 'metrics/field_trial.cc', 'metrics/field_trial.h', 'posix/file_descriptor_shuffle.cc', @@ -290,6 +259,7 @@ 'variables': { 'enable_wexit_time_destructors': 1, 'optimize': 'max', + 'base_i18n_target': 1, }, 'dependencies': [ 'base', @@ -298,58 +268,29 @@ '../third_party/icu/icu.gyp:icuuc', ], 'conditions': [ - ['toolkit_uses_gtk==1', { - 'dependencies': [ - # i18n/rtl.cc uses gtk - '../build/linux/system.gyp:gtk', - ], - }], ['OS == "win"', { # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. 'msvs_disabled_warnings': [ 4267, ], }], + ['icu_use_data_file_flag==1', { + 'defines': ['ICU_UTIL_DATA_IMPL=ICU_UTIL_DATA_FILE'], + }, { # else icu_use_data_file_flag !=1 + 'conditions': [ + ['OS=="win"', { + 'defines': ['ICU_UTIL_DATA_IMPL=ICU_UTIL_DATA_SHARED'], + }, { + 'defines': ['ICU_UTIL_DATA_IMPL=ICU_UTIL_DATA_STATIC'], + }], + ], + }], ], 'export_dependent_settings': [ 'base', ], - 'defines': [ - 'BASE_I18N_IMPLEMENTATION', - ], - 'sources': [ - 'i18n/base_i18n_export.h', - 'i18n/bidi_line_iterator.cc', - 'i18n/bidi_line_iterator.h', - 'i18n/break_iterator.cc', - 'i18n/break_iterator.h', - 'i18n/char_iterator.cc', - 'i18n/char_iterator.h', - 'i18n/case_conversion.cc', - 'i18n/case_conversion.h', - 'i18n/file_util_icu.cc', - 'i18n/file_util_icu.h', - 'i18n/i18n_constants.cc', - 'i18n/i18n_constants.h', - 'i18n/icu_encoding_detection.cc', - 'i18n/icu_encoding_detection.h', - 'i18n/icu_string_conversions.cc', - 'i18n/icu_string_conversions.h', - 'i18n/icu_util.cc', - 'i18n/icu_util.h', - 'i18n/number_formatting.cc', - 'i18n/number_formatting.h', - 'i18n/rtl.cc', - 'i18n/rtl.h', - 'i18n/string_compare.cc', - 'i18n/string_compare.h', - 'i18n/string_search.cc', - 'i18n/string_search.h', - 'i18n/time_formatting.cc', - 'i18n/time_formatting.h', - 'i18n/timezone.cc', - 'i18n/timezone.h', - ], + + }, { 'target_name': 'base_message_loop_tests', @@ -390,6 +331,7 @@ 'prefs/persistent_pref_store.h', 'prefs/pref_change_registrar.cc', 'prefs/pref_change_registrar.h', + 'prefs/pref_filter.h', 'prefs/pref_member.cc', 'prefs/pref_member.h', 'prefs/pref_notifier.h', @@ -414,6 +356,7 @@ 'prefs/scoped_user_pref_update.h', 'prefs/value_map_pref_store.cc', 'prefs/value_map_pref_store.h', + 'prefs/writeable_pref_store.h', ], }, { @@ -471,8 +414,7 @@ 'target_name': 'base_unittests', 'type': '<(gtest_target_type)', 'sources': [ - # Tests. - 'android/activity_status_unittest.cc', + 'android/application_status_listener_unittest.cc', 'android/jni_android_unittest.cc', 'android/jni_array_unittest.cc', 'android/jni_string_unittest.cc', @@ -484,6 +426,7 @@ 'atomicops_unittest.cc', 'barrier_closure_unittest.cc', 'base64_unittest.cc', + 'big_endian_unittest.cc', 'bind_unittest.cc', 'bind_unittest.nc', 'bits_unittest.cc', @@ -506,6 +449,7 @@ 'debug/proc_maps_linux_unittest.cc', 'debug/stack_trace_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', @@ -516,12 +460,14 @@ 'file_version_info_unittest.cc', 'files/dir_reader_posix_unittest.cc', 'files/file_path_unittest.cc', + 'files/file_proxy_unittest.cc', 'files/file_unittest.cc', 'files/file_util_proxy_unittest.cc', 'files/important_file_writer_unittest.cc', 'files/scoped_temp_dir_unittest.cc', 'gmock_unittest.cc', 'guid_unittest.cc', + 'hash_unittest.cc', 'id_map_unittest.cc', 'i18n/break_iterator_unittest.cc', 'i18n/char_iterator_unittest.cc', @@ -530,6 +476,7 @@ 'i18n/icu_string_conversions_unittest.cc', 'i18n/number_formatting_unittest.cc', 'i18n/rtl_unittest.cc', + 'i18n/streaming_utf8_validator_unittest.cc', 'i18n/string_search_unittest.cc', 'i18n/time_formatting_unittest.cc', 'i18n/timezone_unittest.cc', @@ -552,9 +499,8 @@ 'mac/scoped_sending_event_unittest.mm', 'md5_unittest.cc', 'memory/aligned_memory_unittest.cc', - 'memory/discardable_memory_allocator_android_unittest.cc', + 'memory/discardable_memory_manager_unittest.cc', 'memory/discardable_memory_unittest.cc', - 'memory/discardable_memory_provider_unittest.cc', 'memory/linked_ptr_unittest.cc', 'memory/ref_counted_memory_unittest.cc', 'memory/ref_counted_unittest.cc', @@ -577,6 +523,7 @@ '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/sparse_histogram_unittest.cc', 'metrics/stats_table_unittest.cc', @@ -608,9 +555,9 @@ 'process/process_util_unittest.cc', 'profiler/tracked_time_unittest.cc', 'rand_util_unittest.cc', - 'safe_numerics_unittest.cc', - 'safe_numerics_unittest.nc', + 'numerics/safe_numerics_unittest.cc', 'scoped_clear_errno_unittest.cc', + 'scoped_generic_unittest.cc', 'scoped_native_library_unittest.cc', 'scoped_observer.h', 'security_unittest.cc', @@ -631,6 +578,7 @@ 'strings/sys_string_conversions_unittest.cc', 'strings/utf_offset_string_conversions_unittest.cc', 'strings/utf_string_conversions_unittest.cc', + 'supports_user_data_unittest.cc', 'sync_socket_unittest.cc', 'synchronization/cancellation_flag_unittest.cc', 'synchronization/condition_variable_unittest.cc', @@ -639,10 +587,12 @@ 'synchronization/waitable_event_watcher_unittest.cc', 'sys_info_unittest.cc', 'system_monitor/system_monitor_unittest.cc', + 'task/cancelable_task_tracker_unittest.cc', 'task_runner_util_unittest.cc', 'template_util_unittest.cc', 'test/expectations/expectation_unittest.cc', 'test/expectations/parser_unittest.cc', + 'test/statistics_delta_reader_unittest.cc', 'test/test_reg_util_win_unittest.cc', 'test/trace_event_analyzer_unittest.cc', 'threading/non_thread_safe_unittest.cc', @@ -662,6 +612,7 @@ 'time/time_unittest.cc', 'time/time_win_unittest.cc', 'timer/hi_res_timer_manager_unittest.cc', + 'timer/mock_timer_unittest.cc', 'timer/timer_unittest.cc', 'tools_sanity_unittest.cc', 'tracked_objects_unittest.cc', @@ -710,21 +661,10 @@ 'module_dir': 'base' }, 'conditions': [ - ['desktop_linux == 1 or chromeos == 1', { - 'defines': [ - 'USE_SYMBOLIZE', - ], - }], ['OS == "android"', { 'dependencies': [ 'android/jni_generator/jni_generator.gyp:jni_generator_tests', - ], - 'conditions': [ - ['gtest_target_type == "shared_library"', { - 'dependencies': [ - '../testing/android/native_test.gyp:native_test_native_code', - ], - }], + '../testing/android/native_test.gyp:native_test_native_code', ], }], ['OS == "ios" and _toolset != "host"', { @@ -752,27 +692,19 @@ ], }], ['desktop_linux == 1 or chromeos == 1', { + 'defines': [ + 'USE_SYMBOLIZE', + ], 'sources!': [ 'file_version_info_unittest.cc', ], 'conditions': [ - [ 'toolkit_uses_gtk==1', { + [ 'desktop_linux==1', { 'sources': [ 'nix/xdg_util_unittest.cc', ], - 'dependencies': [ - '../build/linux/system.gyp:gtk', - ] }], ], - 'dependencies': [ - '../build/linux/system.gyp:ssl', - ], - }], - ['use_x11 == 1', { - 'dependencies': [ - '../tools/xdisplaycheck/xdisplaycheck.gyp:xdisplaycheck', - ], }], ['use_glib == 1', { 'dependencies': [ @@ -788,18 +720,13 @@ 'message_loop/message_pump_glib_unittest.cc', ] }], - ['OS == "linux" and linux_use_tcmalloc==1', { + ['OS == "linux" and use_allocator!="none"', { 'dependencies': [ 'allocator/allocator.gyp:allocator', ], }, ], ['OS == "win"', { - # This is needed to trigger the dll copy step on windows. - # TODO(mark): This should not be necessary. - 'dependencies': [ - '../third_party/icu/icu.gyp:icudata', - ], 'sources!': [ 'file_descriptor_shuffle_unittest.cc', 'files/dir_reader_posix_unittest.cc', @@ -810,38 +737,29 @@ 'msvs_disabled_warnings': [ 4267, ], - # This is needed so base_unittests uses the allocator shim, as - # SecurityTest.MemoryAllocationRestriction* tests are dependent - # on tcmalloc. - # TODO(wfh): crbug.com/246278 Move tcmalloc specific tests into - # their own test suite. 'conditions': [ + # This is needed so base_unittests uses the allocator shim, as + # SecurityTest.MemoryAllocationRestriction* tests are dependent + # on tcmalloc. + # TODO(wfh): crbug.com/246278 Move tcmalloc specific tests into + # their own test suite. ['win_use_allocator_shim==1', { 'dependencies': [ 'allocator/allocator.gyp:allocator', ], }], + ['icu_use_data_file_flag==0', { + # This is needed to trigger the dll copy step on windows. + # TODO(mark): This should not be necessary. + 'dependencies': [ + '../third_party/icu/icu.gyp:icudata', + ], + }], ], }, { # OS != "win" 'dependencies': [ '../third_party/libevent/libevent.gyp:libevent' ], - 'sources/': [ - ['exclude', '^win/'], - ], - 'sources!': [ - 'win/win_util_unittest.cc', - ], - }], - ['use_aura==1 and use_x11==1', { - 'sources': [ - 'x11/edid_parser_x11_unittest.cc', - ], - }], - ['use_system_nspr==1', { - 'dependencies': [ - 'third_party/nspr/nspr.gyp:nspr', - ], }], ], # conditions 'target_conditions': [ @@ -849,12 +767,18 @@ 'sources/': [ # Pull in specific Mac files for iOS (which have been filtered out # by file name rules). - ['include', '^mac/objc_property_releaser_unittest\\.mm$'], ['include', '^mac/bind_objc_block_unittest\\.mm$'], + ['include', '^mac/foundation_util_unittest\\.mm$',], + ['include', '^mac/objc_property_releaser_unittest\\.mm$'], ['include', '^mac/scoped_nsobject_unittest\\.mm$'], ['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$'], @@ -863,6 +787,41 @@ ], # target_conditions }, { + 'target_name': 'base_perftests', + 'type': '<(gtest_target_type)', + 'dependencies': [ + 'base', + 'test_support_base', + '../testing/gtest.gyp:gtest', + ], + 'sources': [ + 'threading/thread_perftest.cc', + 'test/run_all_unittests.cc', + '../testing/perf/perf_test.cc' + ], + 'conditions': [ + ['OS == "android"', { + 'dependencies': [ + '../testing/android/native_test.gyp:native_test_native_code', + ], + }], + ], + }, + { + 'target_name': 'base_i18n_perftests', + 'type': '<(gtest_target_type)', + 'dependencies': [ + 'test_support_base', + 'test_support_perf', + '../testing/gtest.gyp:gtest', + 'base_i18n', + 'base', + ], + 'sources': [ + 'i18n/streaming_utf8_validator_perftest.cc', + ], + }, + { 'target_name': 'test_support_base', 'type': 'static_library', 'dependencies': [ @@ -878,12 +837,6 @@ 'base', ], 'conditions': [ - ['toolkit_uses_gtk==1', { - 'dependencies': [ - # test_suite initializes GTK. - '../build/linux/system.gyp:gtk', - ], - }], ['os_posix==0', { 'sources!': [ 'test/scoped_locale.cc', @@ -900,9 +853,6 @@ 'base_unittests_jni_headers', 'base_java_unittest_support', ], - 'include_dirs': [ - '<(SHARED_INTERMEDIATE_DIR)/base', - ], }], ], 'sources': [ @@ -952,6 +902,8 @@ 'test/simple_test_clock.h', 'test/simple_test_tick_clock.cc', 'test/simple_test_tick_clock.h', + 'test/statistics_delta_reader.cc', + 'test/statistics_delta_reader.h', 'test/task_runner_test_template.cc', 'test/task_runner_test_template.h', 'test/test_file_util.cc', @@ -1020,17 +972,51 @@ 'PERF_TEST', ], }, + }, + { + 'target_name': 'sanitizer_options', + 'type': 'static_library', + 'toolsets': ['host', 'target'], + 'variables': { + # Every target is going to depend on sanitizer_options, so allow + # this one to depend on itself. + 'prune_self_dependency': 1, + # Do not let 'none' targets depend on this one, they don't need to. + 'link_dependency': 1, + }, + 'sources': [ + 'debug/sanitizer_options.cc', + ], + 'include_dirs': [ + '..', + ], + # Some targets may want to opt-out from ASan, TSan and MSan and link + # without the corresponding runtime libraries. We drop the libc++ + # dependency and omit the compiler flags to avoid bringing instrumented + # code to those targets. 'conditions': [ - ['toolkit_uses_gtk==1', { - 'dependencies': [ - # Needed to handle the #include chain: - # base/test/perf_test_suite.h - # base/test/test_suite.h - # gtk/gtk.h - '../build/linux/system.gyp:gtk', + ['use_custom_libcxx==1', { + 'dependencies!': [ + '../third_party/libc++/libc++.gyp:libcxx_proxy', + ], + }], + ['tsan==1', { + 'sources': [ + 'debug/tsan_suppressions.cc', ], }], ], + 'cflags!': [ + '-fsanitize=address', + '-fsanitize=thread', + '-fsanitize=memory', + '-fsanitize-memory-track-origins', + ], + 'direct_dependent_settings': { + 'ldflags': [ + '-Wl,-u_sanitizer_options_link_helper', + ], + }, }, ], 'conditions': [ @@ -1046,12 +1032,26 @@ 'base', ], }, + { + 'target_name': 'build_utf8_validator_tables', + 'type': 'executable', + 'toolsets': ['host'], + 'dependencies': [ + 'base', + '../third_party/icu/icu.gyp:icuuc', + ], + 'sources': [ + 'i18n/build_utf8_validator_tables.cc' + ], + }, ], }], ['OS == "win" and target_arch=="ia32"', { 'targets': [ + # The base_win64 target here allows us to use base for Win64 targets + # (the normal build is 32 bits). { - 'target_name': 'base_nacl_win64', + 'target_name': 'base_win64', 'type': '<(component)', 'variables': { 'base_target': 1, @@ -1059,6 +1059,7 @@ 'dependencies': [ 'base_static_win64', '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', ], # TODO(gregoryd): direct_dependent_settings should be shared with the @@ -1069,12 +1070,9 @@ ], }, 'defines': [ + 'BASE_WIN64', '<@(nacl_win64_defines)', ], - 'sources!': [ - # base64.cc depends on modp_b64. - 'base64.cc', - ], 'configurations': { 'Common_Base': { 'msvs_target_platform': 'x64', @@ -1087,6 +1085,61 @@ ], }], ], + # Specify delayload for base_win64.dll. + 'msvs_settings': { + 'VCLinkerTool': { + 'DelayLoadDLLs': [ + 'powrprof.dll', + ], + 'AdditionalDependencies': [ + 'powrprof.lib', + ], + }, + }, + # Specify delayload for components that link with base_win64.lib. + 'all_dependent_settings': { + 'msvs_settings': { + 'VCLinkerTool': { + 'DelayLoadDLLs': [ + 'powrprof.dll', + ], + 'AdditionalDependencies': [ + 'powrprof.lib', + ], + }, + }, + }, + # TODO(rvargas): Bug 78117. Remove this. + 'msvs_disabled_warnings': [ + 4244, + 4996, + 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', + 'md5.h', + 'message_loop/message_pump_observer.h', + 'message_loop/message_pump_libevent.cc', + 'message_loop/message_pump_libevent.h', + 'metrics/field_trial.cc', + 'metrics/field_trial.h', + 'posix/file_descriptor_shuffle.cc', + 'posix/file_descriptor_shuffle.h', + 'sync_socket.h', + 'sync_socket_win.cc', + 'sync_socket_posix.cc', + ], }, { 'target_name': 'base_i18n_nacl_win64', @@ -1138,7 +1191,7 @@ }, }, 'defines': [ - 'NACL_WIN64', + '<@(nacl_win64_defines)', ], # TODO(rvargas): Bug 78117. Remove this. 'msvs_disabled_warnings': [ @@ -1170,6 +1223,9 @@ 'cflags!': [ '-Wextra', ], + 'defines': [ + 'GLOG_BUILD_CONFIG_INCLUDE="build/build_config.h"', + ], 'sources': [ 'third_party/symbolize/config.h', 'third_party/symbolize/demangle.cc', @@ -1221,12 +1277,14 @@ 'target_name': 'base_jni_headers', 'type': 'none', 'sources': [ - 'android/java/src/org/chromium/base/ActivityStatus.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', 'android/java/src/org/chromium/base/ContentUriUtils.java', 'android/java/src/org/chromium/base/CpuFeatures.java', + 'android/java/src/org/chromium/base/EventLog.java', 'android/java/src/org/chromium/base/ImportantFileWriterAndroid.java', + 'android/java/src/org/chromium/base/library_loader/LibraryLoader.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', @@ -1235,17 +1293,10 @@ 'android/java/src/org/chromium/base/SystemMessageHandler.java', 'android/java/src/org/chromium/base/SysUtils.java', 'android/java/src/org/chromium/base/ThreadUtils.java', - ], - 'conditions': [ - ['google_tv==1', { - 'sources': [ - 'android/java/src/org/chromium/base/ContextTypes.java', - ], - }], + 'android/java/src/org/chromium/base/TraceEvent.java', ], 'variables': { 'jni_gen_package': 'base', - 'jni_generator_ptr_type': 'long', }, 'includes': [ '../build/jni_generator.gypi' ], }, @@ -1261,14 +1312,31 @@ 'includes': [ '../build/jni_generator.gypi' ], }, { + 'target_name': 'base_native_libraries_gen', + 'type': 'none', + 'sources': [ + 'android/java/templates/NativeLibraries.template', + ], + 'variables': { + 'package_name': 'org/chromium/base/library_loader', + 'include_path': 'android/java/templates', + 'template_deps': [ + 'android/java/templates/native_libraries_array.h' + ], + }, + 'includes': [ '../build/android/java_cpp_template.gypi' ], + }, + { 'target_name': 'base_java', 'type': 'none', 'variables': { 'java_in_dir': '../base/android/java', + 'jar_excluded_classes': [ '*/NativeLibraries.class' ], }, 'dependencies': [ - 'base_java_activity_state', + 'base_java_application_state', 'base_java_memory_pressure_level_list', + 'base_native_libraries_gen', ], 'includes': [ '../build/java.gypi' ], 'conditions': [ @@ -1291,18 +1359,18 @@ 'includes': [ '../build/java.gypi' ], }, { - 'target_name': 'base_java_activity_state', + 'target_name': 'base_java_application_state', 'type': 'none', - # This target is used to auto-generate ActivityState.java + # This target is used to auto-generate ApplicationState.java # from a template file. The source file contains a list of # Java constant declarations matching the ones in - # android/activity_state_list.h. + # android/application_state_list.h. 'sources': [ - 'android/java/src/org/chromium/base/ActivityState.template', + 'android/java/src/org/chromium/base/ApplicationState.template', ], 'variables': { 'package_name': 'org/chromium/base', - 'template_deps': ['android/activity_state_list.h'], + 'template_deps': ['android/application_state_list.h'], }, 'includes': [ '../build/android/java_cpp_template.gypi' ], }, @@ -1341,6 +1409,48 @@ }, 'includes': [ '../build/java.gypi' ], }, + { + 'target_name': 'chromium_android_linker', + 'type': 'shared_library', + 'conditions': [ + ['android_webview_build == 0 and target_arch != "x64" and \ + target_arch != "arm64"', { + # Avoid breaking the webview/64-bit build because they + # don't have <(android_ndk_root)/crazy_linker.gyp. + # Note that webview never uses the linker anyway. + # Note there is no 64-bit support in the 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', + ], + }], + ], + }, + + ], + }], + ['OS == "android"', { + 'targets': [ + { + 'target_name': 'base_perftests_apk', + 'type': 'none', + 'dependencies': [ + 'base_perftests', + ], + 'variables': { + 'test_suite_name': 'base_perftests', + }, + 'includes': [ '../build/apk_test.gypi' ], + }, ], }], ['OS == "win"', { @@ -1359,13 +1469,7 @@ }, ], }], - # Special target to wrap a gtest_target_type == shared_library - # base_unittests into an android apk for execution. - # TODO(jrg): lib.target comes from _InstallableTargetInstallPath() - # in the gyp make generator. What is the correct way to extract - # this path from gyp and into 'raw' for input to antfiles? - # Hard-coding in the gypfile seems a poor choice. - ['OS == "android" and gtest_target_type == "shared_library"', { + ['OS == "android"', { 'targets': [ { 'target_name': 'base_unittests_apk', @@ -1376,7 +1480,6 @@ ], 'variables': { 'test_suite_name': 'base_unittests', - 'input_shlib_path': '<(SHARED_LIB_DIR)/<(SHARED_LIB_PREFIX)base_unittests<(SHARED_LIB_SUFFIX)', }, 'includes': [ '../build/apk_test.gypi' ], }, diff --git a/chromium/base/base.gypi b/chromium/base/base.gypi index 11edab00968..c033dcbce1b 100644 --- a/chromium/base/base.gypi +++ b/chromium/base/base.gypi @@ -6,6 +6,7 @@ 'target_defaults': { 'variables': { 'base_target': 0, + 'base_i18n_target': 0, }, 'target_conditions': [ # This part is shared between the targets defined below. @@ -17,24 +18,16 @@ 'third_party/dmg_fp/dtoa_wrapper.cc', 'third_party/icu/icu_utf.cc', 'third_party/icu/icu_utf.h', - 'third_party/nspr/prcpucfg.h', - 'third_party/nspr/prcpucfg_freebsd.h', - 'third_party/nspr/prcpucfg_linux.h', - 'third_party/nspr/prcpucfg_mac.h', - 'third_party/nspr/prcpucfg_nacl.h', - 'third_party/nspr/prcpucfg_openbsd.h', - 'third_party/nspr/prcpucfg_solaris.h', - 'third_party/nspr/prcpucfg_win.h', 'third_party/nspr/prtime.cc', 'third_party/nspr/prtime.h', - 'third_party/nspr/prtypes.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/activity_status.cc', - 'android/activity_status.h', + 'android/application_status_listener.cc', + 'android/application_status_listener.h', 'android/base_jni_registrar.cc', 'android/base_jni_registrar.h', 'android/build_info.cc', @@ -44,6 +37,8 @@ 'android/content_uri_utils.cc', 'android/content_uri_utils.h', 'android/cpu_features.cc', + 'android/event_log.cc', + 'android/event_log.h', 'android/fifo_utils.cc', 'android/fifo_utils.h', 'android/important_file_writer_android.cc', @@ -54,12 +49,14 @@ 'android/jni_android.h', 'android/jni_array.cc', 'android/jni_array.h', - 'android/jni_helper.cc', - 'android/jni_helper.h', 'android/jni_registrar.cc', 'android/jni_registrar.h', 'android/jni_string.cc', 'android/jni_string.h', + 'android/jni_weak_ref.cc', + 'android/jni_weak_ref.h', + 'android/library_loader/library_loader_hooks.cc', + 'android/library_loader/library_loader_hooks.h', 'android/memory_pressure_listener_android.cc', 'android/memory_pressure_listener_android.h', 'android/java_handler_thread.cc', @@ -71,6 +68,8 @@ 'android/sys_utils.cc', 'android/sys_utils.h', 'android/thread_utils.h', + 'android/trace_event_binding.cc', + 'android/trace_event_binding.h', 'at_exit.cc', 'at_exit.h', 'atomic_ref_count.h', @@ -84,6 +83,8 @@ 'atomicops_internals_x86_msvc.h', 'barrier_closure.cc', 'barrier_closure.h', + 'base64.cc', + 'base64.h', 'base_export.h', 'base_paths.cc', 'base_paths.h', @@ -96,9 +97,9 @@ 'base_paths_win.cc', 'base_paths_win.h', 'base_switches.h', - 'base64.cc', - 'base64.h', 'basictypes.h', + 'big_endian.cc', + 'big_endian.h', 'bind.h', 'bind_helpers.cc', 'bind_helpers.h', @@ -126,17 +127,21 @@ 'cpu.cc', 'cpu.h', 'critical_closure.h', - 'critical_closure_ios.mm', + '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/debug_on_start_win.cc', - 'debug/debug_on_start_win.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). @@ -149,7 +154,6 @@ 'debug/stack_trace.cc', 'debug/stack_trace.h', 'debug/stack_trace_android.cc', - 'debug/stack_trace_ios.mm', 'debug/stack_trace_posix.cc', 'debug/stack_trace_win.cc', 'debug/trace_event.h', @@ -157,6 +161,8 @@ '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', @@ -192,11 +198,17 @@ 'files/file_path_constants.cc', 'files/file_path_watcher.cc', 'files/file_path_watcher.h', + 'files/file_path_watcher_fsevents.cc', + 'files/file_path_watcher_fsevents.h', 'files/file_path_watcher_kqueue.cc', + 'files/file_path_watcher_kqueue.h', 'files/file_path_watcher_linux.cc', + 'files/file_path_watcher_mac.cc', 'files/file_path_watcher_stub.cc', 'files/file_path_watcher_win.cc', 'files/file_posix.cc', + 'files/file_proxy.cc', + 'files/file_proxy.h', 'files/file_util_proxy.cc', 'files/file_util_proxy.h', 'files/file_win.cc', @@ -206,6 +218,8 @@ 'files/memory_mapped_file.h', 'files/memory_mapped_file_posix.cc', 'files/memory_mapped_file_win.cc', + 'files/scoped_file.cc', + 'files/scoped_file.h', 'files/scoped_platform_file_closer.cc', 'files/scoped_platform_file_closer.h', 'files/scoped_temp_dir.cc', @@ -268,6 +282,8 @@ 'mac/mac_logging.cc', 'mac/mac_util.h', 'mac/mac_util.mm', + 'mac/mach_logging.cc', + 'mac/mach_logging.h', 'mac/objc_property_releaser.h', 'mac/objc_property_releaser.mm', 'mac/os_crash_dumps.cc', @@ -281,6 +297,8 @@ 'mac/scoped_launch_data.h', 'mac/scoped_mach_port.cc', 'mac/scoped_mach_port.h', + 'mac/scoped_mach_vm.cc', + 'mac/scoped_mach_vm.h', 'mac/scoped_nsautorelease_pool.h', 'mac/scoped_nsautorelease_pool.mm', 'mac/scoped_nsexception_enabler.h', @@ -288,20 +306,25 @@ 'mac/scoped_nsobject.h', 'mac/scoped_sending_event.h', 'mac/scoped_sending_event.mm', + 'mac/scoped_typeref.h', '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_allocator_android.cc', - 'memory/discardable_memory_allocator_android.h', 'memory/discardable_memory_android.cc', - 'memory/discardable_memory_android.h', 'memory/discardable_memory_emulated.cc', 'memory/discardable_memory_emulated.h', 'memory/discardable_memory_linux.cc', 'memory/discardable_memory_mac.cc', - 'memory/discardable_memory_provider.cc', - 'memory/discardable_memory_provider.h', + 'memory/discardable_memory_malloc.cc', + 'memory/discardable_memory_malloc.h', + 'memory/discardable_memory_manager.cc', + 'memory/discardable_memory_manager.h', 'memory/discardable_memory_win.cc', 'memory/linked_ptr.h', 'memory/manual_constructor.h', @@ -313,7 +336,6 @@ 'memory/ref_counted_delete_on_message_loop.h', 'memory/ref_counted_memory.cc', 'memory/ref_counted_memory.h', - 'memory/scoped_handle.h', 'memory/scoped_open_process.h', 'memory/scoped_policy.h', 'memory/scoped_ptr.h', @@ -341,10 +363,9 @@ 'message_loop/message_pump_android.h', 'message_loop/message_pump_default.cc', 'message_loop/message_pump_default.h', - 'message_loop/message_pump_ozone.cc', - 'message_loop/message_pump_ozone.h', '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', @@ -370,6 +391,9 @@ '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_mac.mm', @@ -428,6 +452,7 @@ 'process/launch_posix.cc', 'process/launch_win.cc', 'process/memory.h', + 'process/memory.cc', 'process/memory_linux.cc', 'process/memory_mac.mm', 'process/memory_win.cc', @@ -474,9 +499,13 @@ 'rand_util_win.cc', 'run_loop.cc', 'run_loop.h', - 'safe_numerics.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', 'scoped_native_library.cc', 'scoped_native_library.h', 'sequence_checker.h', @@ -557,6 +586,8 @@ 'sys_info_openbsd.cc', 'sys_info_posix.cc', 'sys_info_win.cc', + 'task/cancelable_task_tracker.cc', + 'task/cancelable_task_tracker.h', 'task_runner.cc', 'task_runner.h', 'task_runner_util.h', @@ -588,7 +619,9 @@ 'threading/thread_id_name_manager.cc', 'threading/thread_id_name_manager.h', 'threading/thread_local.h', + 'threading/thread_local_android.cc', 'threading/thread_local_posix.cc', + 'threading/thread_local_storage.cc', 'threading/thread_local_storage.h', 'threading/thread_local_storage_posix.cc', 'threading/thread_local_storage_win.cc', @@ -620,6 +653,8 @@ 'timer/hi_res_timer_manager.h', 'timer/hi_res_timer_manager_posix.cc', 'timer/hi_res_timer_manager_win.cc', + 'timer/mock_timer.cc', + 'timer/mock_timer.h', 'timer/timer.cc', 'timer/timer.h', 'tracked_objects.cc', @@ -678,31 +713,12 @@ 'win/shortcut.h', 'win/startup_information.cc', 'win/startup_information.h', - 'win/text_services_message_filter.cc', - 'win/text_services_message_filter.h', 'win/win_util.cc', 'win/win_util.h', 'win/windows_version.cc', 'win/windows_version.h', 'win/wrapped_window_proc.cc', 'win/wrapped_window_proc.h', - 'x11/x11_error_tracker.cc', - 'x11/x11_error_tracker.h', - 'x11/x11_error_tracker_gtk.cc', - ], - 'conditions': [ - ['use_aura==1 and use_x11==1', { - 'sources': [ - 'x11/edid_parser_x11.cc', - 'x11/edid_parser_x11.h', - ], - }], - ['google_tv==1', { - 'sources': [ - 'android/context_types.cc', - 'android/context_types.h', - ], - }], ], 'defines': [ 'BASE_IMPLEMENTATION', @@ -727,14 +743,6 @@ 'message_loop/message_pump_glib.cc', ], }], - ['<(use_x11)==0 or >(nacl_untrusted_build)==1', { - 'sources!': [ - 'message_loop/message_pump_x11.cc', - ], - }], - ['<(toolkit_uses_gtk)==0 or >(nacl_untrusted_build)==1', { - 'sources!': ['message_loop/message_pump_gtk.cc'], - }], ['(OS != "linux" and <(os_bsd) != 1 and OS != "android") or >(nacl_untrusted_build)==1', { 'sources!': [ # Not automatically excluded by the *linux.cc rules. @@ -752,7 +760,11 @@ 'file_util.cc', 'file_util_posix.cc', 'files/file_enumerator_posix.cc', + 'files/file_path_watcher_fsevents.cc', + 'files/file_path_watcher_fsevents.h', 'files/file_path_watcher_kqueue.cc', + 'files/file_path_watcher_kqueue.h', + 'files/file_proxy.cc', 'files/file_util_proxy.cc', 'memory/shared_memory_posix.cc', 'native_library_posix.cc', @@ -769,15 +781,24 @@ 'third_party/dynamic_annotations/dynamic_annotations.c', ], 'sources/': [ - # Metrics won't work in the NaCl sandbox. - ['exclude', '^metrics/'], ['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', + 'files/file_path_watcher_fsevents.cc', + 'files/file_path_watcher_fsevents.h', 'files/file_path_watcher_kqueue.cc', + 'files/file_path_watcher_kqueue.h', 'files/file_path_watcher_stub.cc', 'power_monitor/power_monitor_device_source_posix.cc', ], @@ -797,8 +818,12 @@ ], }], ['OS == "android" and _toolset == "host" and host_os == "linux"', { + 'defines': [ + 'OS_ANDROID_HOST=Linux', + ], 'sources/': [ # Pull in specific files for host builds. + ['include', '^atomicops_internals_x86_gcc\\.cc$'], ['include', '^threading/platform_thread_linux\\.cc$'], ], }], @@ -820,8 +845,10 @@ ['include', '^mac/bundle_locations\\.'], ['include', '^mac/foundation_util\\.'], ['include', '^mac/mac_logging\\.'], + ['include', '^mac/mach_logging\\.'], ['include', '^mac/objc_property_releaser\\.'], ['include', '^mac/scoped_mach_port\\.'], + ['include', '^mac/scoped_mach_vm\\.'], ['include', '^mac/scoped_nsautorelease_pool\\.'], ['include', '^mac/scoped_nsobject\\.'], ['include', '^memory/discardable_memory_mac\\.'], @@ -836,6 +863,9 @@ ['include', '^process/.*_ios\.(cc|mm)$'], ['include', '^process/memory_stubs\.cc$'], ['include', '^process/process_handle_posix\.cc$'], + ['exclude', 'files/file_path_watcher_fsevents.cc'], + ['exclude', 'files/file_path_watcher_fsevents.h'], + ['include', 'files/file_path_watcher_mac.cc'], ], 'sources': [ 'process/memory_stubs.cc', @@ -851,13 +881,11 @@ ['include', '(^|/)(cocoa|mac)/'], ['exclude', '_ios(_unittest)?\\.(h|cc|mm?)$'], ['exclude', '(^|/)ios/'], + ['exclude', 'files/file_path_watcher_fsevents.cc'], + ['exclude', 'files/file_path_watcher_fsevents.h'], + ['include', 'files/file_path_watcher_mac.cc'], ] }], - ['OS != "mac" or >(nacl_untrusted_build)==1', { - 'sources!': [ - 'mac/scoped_aedesc.h' - ], - }], # For now, just test the *BSD platforms enough to exclude them. # Subsequent changes will include them further. ['OS != "freebsd" or >(nacl_untrusted_build)==1', { @@ -868,21 +896,16 @@ 'sources/': [ ['exclude', '_openbsd\\.cc$'] ], }, ], - ['OS != "win" or >(nacl_untrusted_build)==1', { - 'sources/': [ ['exclude', '^win/'] ], - }, - ], - ['OS != "android" or >(nacl_untrusted_build)==1', { - 'sources/': [ ['exclude', '^android/'] ], - }, - ], ['OS == "win" and >(nacl_untrusted_build)==0', { 'include_dirs': [ '<(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', + 'files/file_path_watcher_kqueue.h', 'files/file_path_watcher_stub.cc', 'message_loop/message_pump_libevent.cc', 'posix/file_descriptor_shuffle.cc', @@ -895,12 +918,14 @@ ['<(use_ozone) == 1', { 'sources!': [ 'message_loop/message_pump_glib.cc', - 'message_loop/message_pump_x11.cc', ] }], ['OS == "linux" and >(nacl_untrusted_build)==0', { 'sources!': [ + 'files/file_path_watcher_fsevents.cc', + 'files/file_path_watcher_fsevents.h', 'files/file_path_watcher_kqueue.cc', + 'files/file_path_watcher_kqueue.h', 'files/file_path_watcher_stub.cc', ], }], @@ -924,28 +949,55 @@ ['exclude', '^sys_info_linux\\.cc$'], ], }], - ['<(chromeos)!=1 or >(nacl_untrusted_build)==1', { - 'sources/': [ - ['exclude', '^chromeos/'], - ], - }], # Remove all unnecessary files for build_nexe.py to avoid exceeding # command-line-string limitation when building NaCl on Windows. ['OS == "win" and >(nacl_untrusted_build)==1', { 'sources/': [ ['exclude', '\\.h$'] ], }], - ['<(use_system_nspr)==1 and >(nacl_untrusted_build)==0', { - 'sources/': [ - ['exclude', '^third_party/nspr/'], - ], - }], - ['<(toolkit_uses_gtk) == 1', { - 'sources!': [ - 'x11/x11_error_tracker.cc', - ], - }], ], }], + ['base_i18n_target==1', { + 'defines': [ + 'BASE_I18N_IMPLEMENTATION', + ], + 'sources': [ + 'i18n/base_i18n_export.h', + 'i18n/bidi_line_iterator.cc', + 'i18n/bidi_line_iterator.h', + 'i18n/break_iterator.cc', + 'i18n/break_iterator.h', + 'i18n/case_conversion.cc', + 'i18n/case_conversion.h', + 'i18n/char_iterator.cc', + 'i18n/char_iterator.h', + 'i18n/file_util_icu.cc', + 'i18n/file_util_icu.h', + 'i18n/i18n_constants.cc', + 'i18n/i18n_constants.h', + 'i18n/icu_encoding_detection.cc', + 'i18n/icu_encoding_detection.h', + 'i18n/icu_string_conversions.cc', + 'i18n/icu_string_conversions.h', + 'i18n/icu_util.cc', + 'i18n/icu_util.h', + 'i18n/number_formatting.cc', + 'i18n/number_formatting.h', + 'i18n/rtl.cc', + 'i18n/rtl.h', + 'i18n/streaming_utf8_validator.cc', + 'i18n/streaming_utf8_validator.h', + 'i18n/string_compare.cc', + 'i18n/string_compare.h', + 'i18n/string_search.cc', + 'i18n/string_search.h', + 'i18n/time_formatting.cc', + 'i18n/time_formatting.h', + 'i18n/timezone.cc', + 'i18n/timezone.h', + 'i18n/utf8_validator_tables.cc', + 'i18n/utf8_validator_tables.h', + ], + }] ], }, } diff --git a/chromium/base/base.isolate b/chromium/base/base.isolate new file mode 100644 index 00000000000..aeab5ef3c2e --- /dev/null +++ b/chromium/base/base.isolate @@ -0,0 +1,29 @@ +# 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. +{ + 'includes': [ + # While the target 'base' doesn't depend on ../third_party/icu/icu.gyp + # itself, virtually all targets using it has to include icu. The only + # exception is the Windows sandbox (?). + '../third_party/icu/icu.isolate', + ], + 'conditions': [ + ['OS=="win" and component=="shared_library"', { + 'variables': { + 'isolate_dependency_tracked': [ + # Copy the VS runtime DLLs into the isolate so that they + # don't have to be preinstalled on the target machine. Note + # that depending on whether the build is Debug or Release, + # half of these dependencies will be broken, so these + # dependencies require --ignore-broken-items to be passed to + # the isolate driver. + '<(PRODUCT_DIR)/msvcp120d.dll', + '<(PRODUCT_DIR)/msvcp120.dll', + '<(PRODUCT_DIR)/msvcr120d.dll', + '<(PRODUCT_DIR)/msvcr120.dll', + ], + }, + }], + ], +} diff --git a/chromium/base/base_nacl.gyp b/chromium/base/base_nacl.gyp new file mode 100644 index 00000000000..5ef664ce5be --- /dev/null +++ b/chromium/base/base_nacl.gyp @@ -0,0 +1,71 @@ +# 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. + +{ + 'variables': { + 'chromium_code': 1, + }, + 'includes': [ + '../build/common_untrusted.gypi', + 'base.gypi', + ], + 'conditions': [ + ['disable_nacl==0 and disable_nacl_untrusted==0', { + 'targets': [ + { + 'target_name': 'base_nacl', + 'type': 'none', + 'variables': { + 'base_target': 1, + 'nacl_untrusted_build': 1, + 'nlib_target': 'libbase_nacl.a', + 'build_glibc': 1, + 'build_newlib': 1, + 'build_irt': 1, + 'build_pnacl_newlib': 1, + 'sources': [ + 'base_switches.cc', + 'base_switches.h', + 'strings/string16.cc', + 'sync_socket_nacl.cc', + 'time/time_posix.cc', + ], + 'gcc_compile_flags': [ + '-fno-strict-aliasing', + ], + }, + 'dependencies': [ + '<(DEPTH)/native_client/tools.gyp:prep_toolchain', + ], + }, + { + 'target_name': 'base_i18n_nacl', + 'type': 'none', + 'variables': { + 'base_i18n_target': 1, + 'nacl_untrusted_build': 1, + 'nlib_target': 'libbase_i18n_nacl.a', + 'build_glibc': 0, + 'build_newlib': 1, + 'build_irt': 0, + 'build_pnacl_newlib': 1, + 'sources': [ + 'base_switches.cc', + 'base_switches.h', + 'strings/string16.cc', + 'sync_socket_nacl.cc', + 'time/time_posix.cc', + ], + }, + 'dependencies': [ + '<(DEPTH)/third_party/icu/icu_nacl.gyp:icudata_nacl', + '<(DEPTH)/third_party/icu/icu_nacl.gyp:icui18n_nacl', + '<(DEPTH)/third_party/icu/icu_nacl.gyp:icuuc_nacl', + '<(DEPTH)/native_client/tools.gyp:prep_toolchain', + ], + }, + ], + }], + ], +} diff --git a/chromium/base/base_paths.cc b/chromium/base/base_paths.cc index b4fc28b6c75..1b99e850758 100644 --- a/chromium/base/base_paths.cc +++ b/chromium/base/base_paths.cc @@ -13,35 +13,34 @@ namespace base { bool PathProvider(int key, FilePath* result) { // NOTE: DIR_CURRENT is a special case in PathService::Get - FilePath cur; switch (key) { case DIR_EXE: - PathService::Get(FILE_EXE, &cur); - cur = cur.DirName(); - break; + PathService::Get(FILE_EXE, result); + *result = result->DirName(); + return true; case DIR_MODULE: - PathService::Get(FILE_MODULE, &cur); - cur = cur.DirName(); - break; + PathService::Get(FILE_MODULE, result); + *result = result->DirName(); + return true; case DIR_TEMP: - if (!base::GetTempDir(&cur)) + if (!GetTempDir(result)) return false; - break; + return true; + case base::DIR_HOME: + *result = GetHomeDir(); + return true; case DIR_TEST_DATA: - if (!PathService::Get(DIR_SOURCE_ROOT, &cur)) + if (!PathService::Get(DIR_SOURCE_ROOT, result)) return false; - cur = cur.Append(FILE_PATH_LITERAL("base")); - cur = cur.Append(FILE_PATH_LITERAL("test")); - cur = cur.Append(FILE_PATH_LITERAL("data")); - if (!base::PathExists(cur)) // We don't want to create this. + *result = result->Append(FILE_PATH_LITERAL("base")); + *result = result->Append(FILE_PATH_LITERAL("test")); + *result = result->Append(FILE_PATH_LITERAL("data")); + if (!PathExists(*result)) // We don't want to create this. return false; - break; + return true; default: return false; } - - *result = cur; - return true; } } // namespace base diff --git a/chromium/base/base_paths.h b/chromium/base/base_paths.h index f9601a290ae..26b2fd4c9a1 100644 --- a/chromium/base/base_paths.h +++ b/chromium/base/base_paths.h @@ -31,6 +31,10 @@ enum BasePathKey { DIR_EXE, // Directory containing FILE_EXE. DIR_MODULE, // Directory containing FILE_MODULE. DIR_TEMP, // Temporary directory. + DIR_HOME, // User's root home directory. On Windows this will look + // like "C:\Users\you" (or on XP + // "C:\Document and Settings\you") which isn't necessarily + // a great place to put files. FILE_EXE, // Path and filename of the current executable. FILE_MODULE, // Path and filename of the module containing the code for // the PathService (which could differ from FILE_EXE if the diff --git a/chromium/base/base_paths_android.cc b/chromium/base/base_paths_android.cc index 68cc5a72cef..27be24ea982 100644 --- a/chromium/base/base_paths_android.cc +++ b/chromium/base/base_paths_android.cc @@ -47,9 +47,6 @@ bool PathProviderAndroid(int key, FilePath* result) { return base::android::GetCacheDirectory(result); case base::DIR_ANDROID_APP_DATA: return base::android::GetDataDirectory(result); - case base::DIR_HOME: - *result = GetHomeDir(); - return true; case base::DIR_ANDROID_EXTERNAL_STORAGE: return base::android::GetExternalStorageDirectory(result); default: diff --git a/chromium/base/base_paths_mac.mm b/chromium/base/base_paths_mac.mm index 86d6a80e9ad..3930c44c3b7 100644 --- a/chromium/base/base_paths_mac.mm +++ b/chromium/base/base_paths_mac.mm @@ -106,9 +106,6 @@ bool PathProviderMac(int key, base::FilePath* result) { #endif case base::DIR_CACHE: return base::mac::GetUserDirectory(NSCachesDirectory, result); - case base::DIR_HOME: - *result = base::mac::NSStringToFilePath(NSHomeDirectory()); - return true; default: return false; } diff --git a/chromium/base/base_paths_posix.cc b/chromium/base/base_paths_posix.cc index b4147e99327..cf136d47fa3 100644 --- a/chromium/base/base_paths_posix.cc +++ b/chromium/base/base_paths_posix.cc @@ -109,9 +109,6 @@ bool PathProviderPosix(int key, FilePath* result) { *result = cache_dir; return true; } - case base::DIR_HOME: - *result = GetHomeDir(); - return true; } return false; } diff --git a/chromium/base/base_paths_posix.h b/chromium/base/base_paths_posix.h index 811c8cb8948..ef002aeb0bc 100644 --- a/chromium/base/base_paths_posix.h +++ b/chromium/base/base_paths_posix.h @@ -19,8 +19,6 @@ enum { // browser cache can be a subdirectory. // This is $XDG_CACHE_HOME on Linux and // ~/Library/Caches on Mac. - DIR_HOME, // $HOME on POSIX-like systems. - PATH_POSIX_END }; diff --git a/chromium/base/base_paths_win.cc b/chromium/base/base_paths_win.cc index bc883358add..509d5fd7ef1 100644 --- a/chromium/base/base_paths_win.cc +++ b/chromium/base/base_paths_win.cc @@ -127,12 +127,6 @@ bool PathProviderWin(int key, FilePath* result) { return false; cur = FilePath(system_buffer); break; - case base::DIR_PROFILE: - if (FAILED(SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT, - system_buffer))) - return false; - cur = FilePath(system_buffer); - break; case base::DIR_LOCAL_APP_DATA_LOW: if (win::GetVersion() < win::VERSION_VISTA) return false; @@ -197,6 +191,13 @@ bool PathProviderWin(int key, FilePath* result) { cur = cur.AppendASCII("User Pinned"); cur = cur.AppendASCII("TaskBar"); break; + case base::DIR_WINDOWS_FONTS: + if (FAILED(SHGetFolderPath( + NULL, CSIDL_FONTS, NULL, SHGFP_TYPE_CURRENT, system_buffer))) { + return false; + } + cur = FilePath(system_buffer); + break; default: return false; } diff --git a/chromium/base/base_paths_win.h b/chromium/base/base_paths_win.h index 11bc111d66c..b042d087379 100644 --- a/chromium/base/base_paths_win.h +++ b/chromium/base/base_paths_win.h @@ -25,7 +25,6 @@ enum { DIR_START_MENU, // Usually "C:\Documents and Settings\<user>\ // Start Menu\Programs" DIR_APP_DATA, // Application Data directory under the user profile. - DIR_PROFILE, // Usually "C:\Documents and settings\<user>. DIR_LOCAL_APP_DATA_LOW, // Local AppData directory for low integrity level. DIR_LOCAL_APP_DATA, // "Local Settings\Application Data" directory under // the user profile. @@ -42,6 +41,7 @@ enum { // of the Default user. DIR_TASKBAR_PINS, // Directory for the shortcuts pinned to taskbar via // base::win::TaskbarPinShortcutLink(). + DIR_WINDOWS_FONTS, // Usually C:\Windows\Fonts. PATH_WIN_END }; diff --git a/chromium/base/base_switches.cc b/chromium/base/base_switches.cc index 91e401ca497..81698cdfc13 100644 --- a/chromium/base/base_switches.cc +++ b/chromium/base/base_switches.cc @@ -6,12 +6,6 @@ namespace switches { -// If the program includes base/debug/debug_on_start_win.h, the process will -// (on Windows only) start the JIT system-registered debugger on itself and -// will wait for 60 seconds for the debugger to attach to itself. Then a break -// point will be hit. -const char kDebugOnStart[] = "debug-on-start"; - // Disables the crash reporting. const char kDisableBreakpad[] = "disable-breakpad"; @@ -20,9 +14,6 @@ const char kDisableBreakpad[] = "disable-breakpad"; // generated internally. const char kEnableCrashReporter[] = "enable-crash-reporter"; -// Enable DCHECKs in release mode. -const char kEnableDCHECK[] = "enable-dcheck"; - // Generates full memory crash dump. const char kFullMemoryCrashReport[] = "full-memory-crash-report"; diff --git a/chromium/base/base_switches.h b/chromium/base/base_switches.h index b679e6d9ebf..6e391a45fcf 100644 --- a/chromium/base/base_switches.h +++ b/chromium/base/base_switches.h @@ -11,10 +11,8 @@ namespace switches { -extern const char kDebugOnStart[]; extern const char kDisableBreakpad[]; extern const char kEnableCrashReporter[]; -extern const char kEnableDCHECK[]; extern const char kFullMemoryCrashReport[]; extern const char kNoErrorDialogs[]; extern const char kProfilerTiming[]; diff --git a/chromium/base/base_unittests.isolate b/chromium/base/base_unittests.isolate index 166cf801eca..b5464be8e37 100644 --- a/chromium/base/base_unittests.isolate +++ b/chromium/base/base_unittests.isolate @@ -32,9 +32,7 @@ '../testing/test_env.py', '<(PRODUCT_DIR)/base_unittests<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_untracked': [ - '../tools/swarming_client/', - ], + 'read_only': 1, }, }], ['OS=="mac" or OS=="win"', { @@ -50,9 +48,11 @@ ['OS=="win"', { 'variables': { 'isolate_dependency_tracked': [ - '<(PRODUCT_DIR)/icudt.dll', ], }, }], ], + 'includes': [ + 'base.isolate', + ], } diff --git a/chromium/base/base_untrusted.gyp b/chromium/base/base_untrusted.gyp deleted file mode 100644 index e4b005bc291..00000000000 --- a/chromium/base/base_untrusted.gyp +++ /dev/null @@ -1,41 +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. - -{ - 'variables': { - 'chromium_code': 1, - }, - 'includes': [ - '../build/common_untrusted.gypi', - 'base.gypi', - ], - 'conditions': [ - ['disable_nacl==0 and disable_nacl_untrusted==0', { - 'targets': [ - { - 'target_name': 'base_untrusted', - 'type': 'none', - 'variables': { - 'base_target': 1, - 'nacl_untrusted_build': 1, - 'nlib_target': 'libbase_untrusted.a', - 'build_glibc': 1, - 'build_newlib': 1, - 'build_irt': 1, - 'sources': [ - 'base_switches.cc', - 'base_switches.h', - 'strings/string16.cc', - 'sync_socket_nacl.cc', - 'time/time_posix.cc', - ], - }, - 'dependencies': [ - '<(DEPTH)/native_client/tools.gyp:prep_toolchain', - ], - }, - ], - }], - ], -} diff --git a/chromium/base/basictypes.h b/chromium/base/basictypes.h index e77d7b10f24..b0019f2a193 100644 --- a/chromium/base/basictypes.h +++ b/chromium/base/basictypes.h @@ -1,377 +1,58 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// This file contains definitions of our old basic integral types +// ((u)int{8,16,32,64}) and further includes. I recommend that you use the C99 +// standard types instead, and include <stdint.h>/<stddef.h>/etc. as needed. +// Note that the macros and macro-like constructs that were formerly defined in +// this file are now available separately in base/macros.h. + #ifndef BASE_BASICTYPES_H_ #define BASE_BASICTYPES_H_ -#include <limits.h> // So we can set the bounds of our types -#include <stddef.h> // For size_t -#include <string.h> // for memcpy - -#include "base/compiler_specific.h" -#include "base/port.h" // Types that only need exist on certain systems +#include <limits.h> // So we can set the bounds of our types. +#include <stddef.h> // For size_t. +#include <stdint.h> // For intptr_t. -#ifndef COMPILER_MSVC -// stdint.h is part of C99 but MSVC doesn't have it. -#include <stdint.h> // For intptr_t. -#endif +#include "base/macros.h" +#include "base/port.h" // Types that only need exist on certain systems. -typedef signed char schar; -typedef signed char int8; -typedef short int16; -typedef int int32; +// DEPRECATED: Please use (u)int{8,16,32,64}_t instead (and include <stdint.h>). +typedef int8_t int8; +typedef uint8_t uint8; +typedef int16_t int16; +typedef int32_t int32; +typedef uint16_t uint16; +typedef uint32_t uint32; +// TODO(vtl): Figure what's up with the 64-bit types. Can we just define them as +// |int64_t|/|uint64_t|? // The NSPR system headers define 64-bit as |long| when possible, except on // Mac OS X. In order to not have typedef mismatches, we do the same on LP64. // // On Mac OS X, |long long| is used for 64-bit types for compatibility with // <inttypes.h> format macros even in the LP64 model. #if defined(__LP64__) && !defined(OS_MACOSX) && !defined(OS_OPENBSD) -typedef long int64; -#else -typedef long long int64; -#endif - -// NOTE: It is DANGEROUS to compare signed with unsigned types in loop -// conditions and other conditional expressions, and it is DANGEROUS to -// compute object/allocation sizes, indices, and offsets with signed types. -// Integer overflow behavior for signed types is UNDEFINED in the C/C++ -// standards, but is defined for unsigned types. -// -// Use the unsigned types if your variable represents a bit pattern (e.g. a -// hash value), object or allocation size, object count, offset, -// array/vector index, etc. -// -// Do NOT use 'unsigned' to express "this value should always be positive"; -// use assertions for this. -// -// See the Chromium style guide for more information. -// https://sites.google.com/a/chromium.org/dev/developers/coding-style - -typedef unsigned char uint8; -typedef unsigned short uint16; -typedef unsigned int uint32; - -// See the comment above about NSPR and 64-bit. -#if defined(__LP64__) && !defined(OS_MACOSX) && !defined(OS_OPENBSD) +typedef long int64; typedef unsigned long uint64; #else +typedef long long int64; typedef unsigned long long uint64; #endif -// A type to represent a Unicode code-point value. As of Unicode 4.0, -// such values require up to 21 bits. -// (For type-checking on pointers, make this explicitly signed, -// and it should always be the signed version of whatever int32 is.) -typedef signed int char32; - +// DEPRECATED: Please use std::numeric_limits (from <limits>) instead. const uint8 kuint8max = (( uint8) 0xFF); const uint16 kuint16max = ((uint16) 0xFFFF); const uint32 kuint32max = ((uint32) 0xFFFFFFFF); -const uint64 kuint64max = ((uint64) GG_LONGLONG(0xFFFFFFFFFFFFFFFF)); +const uint64 kuint64max = ((uint64) 0xFFFFFFFFFFFFFFFFULL); const int8 kint8min = (( int8) 0x80); const int8 kint8max = (( int8) 0x7F); const int16 kint16min = (( int16) 0x8000); const int16 kint16max = (( int16) 0x7FFF); const int32 kint32min = (( int32) 0x80000000); const int32 kint32max = (( int32) 0x7FFFFFFF); -const int64 kint64min = (( int64) GG_LONGLONG(0x8000000000000000)); -const int64 kint64max = (( int64) GG_LONGLONG(0x7FFFFFFFFFFFFFFF)); - -// Put this in the private: declarations for a class to be uncopyable. -#define DISALLOW_COPY(TypeName) \ - TypeName(const TypeName&) - -// Put this in the private: declarations for a class to be unassignable. -#define DISALLOW_ASSIGN(TypeName) \ - void operator=(const TypeName&) - -// A macro to disallow the copy constructor and operator= functions -// This should be used in the private: declarations for a class -#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName&); \ - void operator=(const TypeName&) - -// An older, deprecated, politically incorrect name for the above. -// NOTE: The usage of this macro was banned from our code base, but some -// third_party libraries are yet using it. -// TODO(tfarina): Figure out how to fix the usage of this macro in the -// third_party libraries and get rid of it. -#define DISALLOW_EVIL_CONSTRUCTORS(TypeName) DISALLOW_COPY_AND_ASSIGN(TypeName) - -// A macro to disallow all the implicit constructors, namely the -// default constructor, copy constructor and operator= functions. -// -// This should be used in the private: declarations for a class -// that wants to prevent anyone from instantiating it. This is -// especially useful for classes containing only static methods. -#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ - TypeName(); \ - DISALLOW_COPY_AND_ASSIGN(TypeName) - -// The arraysize(arr) macro returns the # of elements in an array arr. -// The expression is a compile-time constant, and therefore can be -// used in defining new arrays, for example. If you use arraysize on -// a pointer by mistake, you will get a compile-time error. -// -// One caveat is that arraysize() doesn't accept any array of an -// anonymous type or a type defined inside a function. In these rare -// cases, you have to use the unsafe ARRAYSIZE_UNSAFE() macro below. This is -// due to a limitation in C++'s template system. The limitation might -// eventually be removed, but it hasn't happened yet. - -// 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 - -#define arraysize(array) (sizeof(ArraySizeHelper(array))) - -// ARRAYSIZE_UNSAFE performs essentially the same calculation as arraysize, -// but can be used on anonymous types or types defined inside -// functions. It's less safe than arraysize as it accepts some -// (although not all) pointers. Therefore, you should use arraysize -// whenever possible. -// -// The expression ARRAYSIZE_UNSAFE(a) is a compile-time constant of type -// size_t. -// -// ARRAYSIZE_UNSAFE catches a few type errors. If you see a compiler error -// -// "warning: division by zero in ..." -// -// when using ARRAYSIZE_UNSAFE, you are (wrongfully) giving it a pointer. -// You should only use ARRAYSIZE_UNSAFE on statically allocated arrays. -// -// The following comments are on the implementation details, and can -// be ignored by the users. -// -// ARRAYSIZE_UNSAFE(arr) works by inspecting sizeof(arr) (the # of bytes in -// the array) and sizeof(*(arr)) (the # of bytes in one array -// element). If the former is divisible by the latter, perhaps arr is -// indeed an array, in which case the division result is the # of -// elements in the array. Otherwise, arr cannot possibly be an array, -// and we generate a compiler error to prevent the code from -// compiling. -// -// Since the size of bool is implementation-defined, we need to cast -// !(sizeof(a) & sizeof(*(a))) to size_t in order to ensure the final -// result has type size_t. -// -// This macro is not perfect as it wrongfully accepts certain -// pointers, namely where the pointer size is divisible by the pointee -// size. Since all our code has to go through a 32-bit compiler, -// where a pointer is 4 bytes, this means all pointers to a type whose -// size is 3 or greater than 4 will be (righteously) rejected. - -#define ARRAYSIZE_UNSAFE(a) \ - ((sizeof(a) / sizeof(*(a))) / \ - static_cast<size_t>(!(sizeof(a) % sizeof(*(a))))) - - -// Use implicit_cast as a safe version of static_cast or const_cast -// for upcasting in the type hierarchy (i.e. casting a pointer to Foo -// to a pointer to SuperclassOfFoo or casting a pointer to Foo to -// a const pointer to Foo). -// When you use implicit_cast, the compiler checks that the cast is safe. -// Such explicit implicit_casts are necessary in surprisingly many -// situations where C++ demands an exact type match instead of an -// argument type convertible to a target type. -// -// The From type can be inferred, so the preferred syntax for using -// implicit_cast is the same as for static_cast etc.: -// -// implicit_cast<ToType>(expr) -// -// implicit_cast would have been part of the C++ standard library, -// but the proposal was submitted too late. It will probably make -// its way into the language in the future. -template<typename To, typename From> -inline To implicit_cast(From const &f) { - return f; -} - -// The COMPILE_ASSERT macro can be used to verify that a compile time -// expression is true. For example, you could use it to verify the -// size of a static array: -// -// COMPILE_ASSERT(ARRAYSIZE_UNSAFE(content_type_names) == CONTENT_NUM_TYPES, -// content_type_names_incorrect_size); -// -// or to make sure a struct is smaller than a certain size: -// -// COMPILE_ASSERT(sizeof(foo) < 128, foo_too_large); -// -// The second argument to the macro is the name of the variable. If -// the expression is false, most compilers will issue a warning/error -// containing the name of the variable. - -#undef COMPILE_ASSERT - -#if __cplusplus >= 201103L - -// Under C++11, just use static_assert. -#define COMPILE_ASSERT(expr, msg) static_assert(expr, #msg) - -#else - -template <bool> -struct CompileAssert { -}; - -#define COMPILE_ASSERT(expr, msg) \ - typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] ALLOW_UNUSED - -// Implementation details of COMPILE_ASSERT: -// -// - COMPILE_ASSERT works by defining an array type that has -1 -// elements (and thus is invalid) when the expression is false. -// -// - The simpler definition -// -// #define COMPILE_ASSERT(expr, msg) typedef char msg[(expr) ? 1 : -1] -// -// does not work, as gcc supports variable-length arrays whose sizes -// are determined at run-time (this is gcc's extension and not part -// of the C++ standard). As a result, gcc fails to reject the -// following code with the simple definition: -// -// int foo; -// COMPILE_ASSERT(foo, msg); // not supposed to compile as foo is -// // not a compile-time constant. -// -// - By using the type CompileAssert<(bool(expr))>, we ensures that -// expr is a compile-time constant. (Template arguments must be -// determined at compile-time.) -// -// - The outer parentheses in CompileAssert<(bool(expr))> are necessary -// to work around a bug in gcc 3.4.4 and 4.0.1. If we had written -// -// CompileAssert<bool(expr)> -// -// instead, these compilers will refuse to compile -// -// COMPILE_ASSERT(5 > 0, some_message); -// -// (They seem to think the ">" in "5 > 0" marks the end of the -// template argument list.) -// -// - The array size is (bool(expr) ? 1 : -1), instead of simply -// -// ((expr) ? 1 : -1). -// -// This is to avoid running into a bug in MS VC 7.1, which -// causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1. - -#endif - -// bit_cast<Dest,Source> is a template function that implements the -// equivalent of "*reinterpret_cast<Dest*>(&source)". We need this in -// very low-level functions like the protobuf library and fast math -// support. -// -// float f = 3.14159265358979; -// int i = bit_cast<int32>(f); -// // i = 0x40490fdb -// -// The classical address-casting method is: -// -// // WRONG -// float f = 3.14159265358979; // WRONG -// int i = * reinterpret_cast<int*>(&f); // WRONG -// -// The address-casting method actually produces undefined behavior -// according to ISO C++ specification section 3.10 -15 -. Roughly, this -// section says: if an object in memory has one type, and a program -// accesses it with a different type, then the result is undefined -// behavior for most values of "different type". -// -// This is true for any cast syntax, either *(int*)&f or -// *reinterpret_cast<int*>(&f). And it is particularly true for -// conversions between integral lvalues and floating-point lvalues. -// -// The purpose of 3.10 -15- is to allow optimizing compilers to assume -// that expressions with different types refer to different memory. gcc -// 4.0.1 has an optimizer that takes advantage of this. So a -// non-conforming program quietly produces wildly incorrect output. -// -// The problem is not the use of reinterpret_cast. The problem is type -// punning: holding an object in memory of one type and reading its bits -// back using a different type. -// -// The C++ standard is more subtle and complex than this, but that -// is the basic idea. -// -// Anyways ... -// -// bit_cast<> calls memcpy() which is blessed by the standard, -// especially by the example in section 3.9 . Also, of course, -// bit_cast<> wraps up the nasty logic in one place. -// -// Fortunately memcpy() is very fast. In optimized mode, with a -// constant size, gcc 2.95.3, gcc 4.0.1, and msvc 7.1 produce inline -// code with the minimal amount of data movement. On a 32-bit system, -// memcpy(d,s,4) compiles to one load and one store, and memcpy(d,s,8) -// compiles to two loads and two stores. -// -// I tested this code with gcc 2.95.3, gcc 4.0.1, icc 8.1, and msvc 7.1. -// -// WARNING: if Dest or Source is a non-POD type, the result of the memcpy -// is likely to surprise you. - -template <class Dest, class Source> -inline Dest bit_cast(const Source& source) { - COMPILE_ASSERT(sizeof(Dest) == sizeof(Source), VerifySizesAreEqual); - - Dest dest; - memcpy(&dest, &source, sizeof(dest)); - return dest; -} - -// Used to explicitly mark the return value of a function as unused. If you are -// really sure you don't want to do anything with the return value of a function -// that has been marked WARN_UNUSED_RESULT, wrap it with this. Example: -// -// scoped_ptr<MyType> my_var = ...; -// if (TakeOwnership(my_var.get()) == SUCCESS) -// ignore_result(my_var.release()); -// -template<typename T> -inline void ignore_result(const T&) { -} - -// The following enum should be used only as a constructor argument to indicate -// that the variable has static storage class, and that the constructor should -// do nothing to its state. It indicates to the reader that it is legal to -// declare a static instance of the class, provided the constructor is given -// the base::LINKER_INITIALIZED argument. Normally, it is unsafe to declare a -// static variable that has a constructor or a destructor because invocation -// order is undefined. However, IF the type can be initialized by filling with -// zeroes (which the loader does for static variables), AND the destructor also -// does nothing to the storage, AND there are no virtual methods, then a -// constructor declared as -// explicit MyClass(base::LinkerInitialized x) {} -// and invoked as -// static MyClass my_variable_name(base::LINKER_INITIALIZED); -namespace base { -enum LinkerInitialized { LINKER_INITIALIZED }; - -// Use these to declare and define a static local variable (static T;) so that -// it is leaked so that its destructors are not called at exit. If you need -// thread-safe initialization, use base/lazy_instance.h instead. -#define CR_DEFINE_STATIC_LOCAL(type, name, arguments) \ - static type& name = *new type arguments - -} // base +const int64 kint64min = (( int64) 0x8000000000000000LL); +const int64 kint64max = (( int64) 0x7FFFFFFFFFFFFFFFLL); #endif // BASE_BASICTYPES_H_ diff --git a/chromium/base/big_endian.cc b/chromium/base/big_endian.cc new file mode 100644 index 00000000000..fd9e7600ac1 --- /dev/null +++ b/chromium/base/big_endian.cc @@ -0,0 +1,97 @@ +// 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/big_endian.h" + +#include "base/strings/string_piece.h" + +namespace base { + +BigEndianReader::BigEndianReader(const char* buf, size_t len) + : ptr_(buf), end_(ptr_ + len) {} + +bool BigEndianReader::Skip(size_t len) { + if (ptr_ + len > end_) + return false; + ptr_ += len; + return true; +} + +bool BigEndianReader::ReadBytes(void* out, size_t len) { + if (ptr_ + len > end_) + return false; + memcpy(out, ptr_, len); + ptr_ += len; + return true; +} + +bool BigEndianReader::ReadPiece(base::StringPiece* out, size_t len) { + if (ptr_ + len > end_) + return false; + *out = base::StringPiece(ptr_, len); + ptr_ += len; + return true; +} + +template<typename T> +bool BigEndianReader::Read(T* value) { + if (ptr_ + sizeof(T) > end_) + return false; + ReadBigEndian<T>(ptr_, value); + ptr_ += sizeof(T); + return true; +} + +bool BigEndianReader::ReadU8(uint8* value) { + return Read(value); +} + +bool BigEndianReader::ReadU16(uint16* value) { + return Read(value); +} + +bool BigEndianReader::ReadU32(uint32* value) { + return Read(value); +} + +BigEndianWriter::BigEndianWriter(char* buf, size_t len) + : ptr_(buf), end_(ptr_ + len) {} + +bool BigEndianWriter::Skip(size_t len) { + if (ptr_ + len > end_) + return false; + ptr_ += len; + return true; +} + +bool BigEndianWriter::WriteBytes(const void* buf, size_t len) { + if (ptr_ + len > end_) + return false; + memcpy(ptr_, buf, len); + ptr_ += len; + return true; +} + +template<typename T> +bool BigEndianWriter::Write(T value) { + if (ptr_ + sizeof(T) > end_) + return false; + WriteBigEndian<T>(ptr_, value); + ptr_ += sizeof(T); + return true; +} + +bool BigEndianWriter::WriteU8(uint8 value) { + return Write(value); +} + +bool BigEndianWriter::WriteU16(uint16 value) { + return Write(value); +} + +bool BigEndianWriter::WriteU32(uint32 value) { + return Write(value); +} + +} // namespace base diff --git a/chromium/base/big_endian.h b/chromium/base/big_endian.h new file mode 100644 index 00000000000..35df5e79776 --- /dev/null +++ b/chromium/base/big_endian.h @@ -0,0 +1,102 @@ +// 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_BIG_ENDIAN_H_ +#define BASE_BIG_ENDIAN_H_ + +#include "base/base_export.h" +#include "base/basictypes.h" +#include "base/strings/string_piece.h" + +namespace base { + +// Read an integer (signed or unsigned) from |buf| in Big Endian order. +// Note: this loop is unrolled with -O1 and above. +// NOTE(szym): glibc dns-canon.c and SpdyFrameBuilder use +// ntohs(*(uint16_t*)ptr) which is potentially unaligned. +// This would cause SIGBUS on ARMv5 or earlier and ARMv6-M. +template<typename T> +inline void ReadBigEndian(const char buf[], T* out) { + *out = buf[0]; + for (size_t i = 1; i < sizeof(T); ++i) { + *out <<= 8; + // Must cast to uint8 to avoid clobbering by sign extension. + *out |= static_cast<uint8>(buf[i]); + } +} + +// Write an integer (signed or unsigned) |val| to |buf| in Big Endian order. +// Note: this loop is unrolled with -O1 and above. +template<typename T> +inline void WriteBigEndian(char buf[], T val) { + for (size_t i = 0; i < sizeof(T); ++i) { + buf[sizeof(T)-i-1] = static_cast<char>(val & 0xFF); + val >>= 8; + } +} + +// Specializations to make clang happy about the (dead code) shifts above. +template<> +inline void ReadBigEndian<uint8>(const char buf[], uint8* out) { + *out = buf[0]; +} + +template<> +inline void WriteBigEndian<uint8>(char buf[], uint8 val) { + buf[0] = static_cast<char>(val); +} + +// Allows reading integers in network order (big endian) while iterating over +// an underlying buffer. All the reading functions advance the internal pointer. +class BASE_EXPORT BigEndianReader { + public: + BigEndianReader(const char* buf, size_t len); + + const char* ptr() const { return ptr_; } + int remaining() const { return end_ - ptr_; } + + bool Skip(size_t len); + bool ReadBytes(void* out, size_t len); + // Creates a StringPiece in |out| that points to the underlying buffer. + bool ReadPiece(base::StringPiece* out, size_t len); + bool ReadU8(uint8* value); + bool ReadU16(uint16* value); + bool ReadU32(uint32* value); + + private: + // Hidden to promote type safety. + template<typename T> + bool Read(T* v); + + const char* ptr_; + const char* end_; +}; + +// Allows writing integers in network order (big endian) while iterating over +// an underlying buffer. All the writing functions advance the internal pointer. +class BASE_EXPORT BigEndianWriter { + public: + BigEndianWriter(char* buf, size_t len); + + char* ptr() const { return ptr_; } + int remaining() const { return end_ - ptr_; } + + bool Skip(size_t len); + bool WriteBytes(const void* buf, size_t len); + bool WriteU8(uint8 value); + bool WriteU16(uint16 value); + bool WriteU32(uint32 value); + + private: + // Hidden to promote type safety. + template<typename T> + bool Write(T v); + + char* ptr_; + char* end_; +}; + +} // namespace base + +#endif // BASE_BIG_ENDIAN_H_ diff --git a/chromium/base/big_endian_unittest.cc b/chromium/base/big_endian_unittest.cc new file mode 100644 index 00000000000..b57aeb29ac9 --- /dev/null +++ b/chromium/base/big_endian_unittest.cc @@ -0,0 +1,100 @@ +// 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/big_endian.h" + +#include "base/strings/string_piece.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { + +TEST(BigEndianReaderTest, ReadsValues) { + char data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC }; + char buf[2]; + uint8 u8; + uint16 u16; + uint32 u32; + base::StringPiece piece; + BigEndianReader reader(data, sizeof(data)); + + EXPECT_TRUE(reader.Skip(2)); + EXPECT_EQ(data + 2, reader.ptr()); + EXPECT_EQ(reader.remaining(), static_cast<int>(sizeof(data)) - 2); + EXPECT_TRUE(reader.ReadBytes(buf, sizeof(buf))); + EXPECT_EQ(0x2, buf[0]); + EXPECT_EQ(0x3, buf[1]); + EXPECT_TRUE(reader.ReadU8(&u8)); + EXPECT_EQ(0x4, u8); + EXPECT_TRUE(reader.ReadU16(&u16)); + EXPECT_EQ(0x0506, u16); + EXPECT_TRUE(reader.ReadU32(&u32)); + EXPECT_EQ(0x0708090Au, u32); + base::StringPiece expected(reader.ptr(), 2); + EXPECT_TRUE(reader.ReadPiece(&piece, 2)); + EXPECT_EQ(2u, piece.size()); + EXPECT_EQ(expected.data(), piece.data()); +} + +TEST(BigEndianReaderTest, RespectsLength) { + char data[4]; + char buf[2]; + uint8 u8; + uint16 u16; + uint32 u32; + base::StringPiece piece; + BigEndianReader reader(data, sizeof(data)); + // 4 left + EXPECT_FALSE(reader.Skip(6)); + EXPECT_TRUE(reader.Skip(1)); + // 3 left + EXPECT_FALSE(reader.ReadU32(&u32)); + EXPECT_FALSE(reader.ReadPiece(&piece, 4)); + EXPECT_TRUE(reader.Skip(2)); + // 1 left + EXPECT_FALSE(reader.ReadU16(&u16)); + EXPECT_FALSE(reader.ReadBytes(buf, 2)); + EXPECT_TRUE(reader.Skip(1)); + // 0 left + EXPECT_FALSE(reader.ReadU8(&u8)); + EXPECT_EQ(0, reader.remaining()); +} + +TEST(BigEndianWriterTest, WritesValues) { + char expected[] = { 0, 0, 2, 3, 4, 5, 6, 7, 8, 9, 0xA }; + char data[sizeof(expected)]; + char buf[] = { 0x2, 0x3 }; + memset(data, 0, sizeof(data)); + BigEndianWriter writer(data, sizeof(data)); + + EXPECT_TRUE(writer.Skip(2)); + EXPECT_TRUE(writer.WriteBytes(buf, sizeof(buf))); + EXPECT_TRUE(writer.WriteU8(0x4)); + EXPECT_TRUE(writer.WriteU16(0x0506)); + EXPECT_TRUE(writer.WriteU32(0x0708090A)); + EXPECT_EQ(0, memcmp(expected, data, sizeof(expected))); +} + +TEST(BigEndianWriterTest, RespectsLength) { + char data[4]; + char buf[2]; + uint8 u8 = 0; + uint16 u16 = 0; + uint32 u32 = 0; + BigEndianWriter writer(data, sizeof(data)); + // 4 left + EXPECT_FALSE(writer.Skip(6)); + EXPECT_TRUE(writer.Skip(1)); + // 3 left + EXPECT_FALSE(writer.WriteU32(u32)); + EXPECT_TRUE(writer.Skip(2)); + // 1 left + EXPECT_FALSE(writer.WriteU16(u16)); + EXPECT_FALSE(writer.WriteBytes(buf, 2)); + EXPECT_TRUE(writer.Skip(1)); + // 0 left + EXPECT_FALSE(writer.WriteU8(u8)); + EXPECT_EQ(0, writer.remaining()); +} + +} // namespace base diff --git a/chromium/base/bind.h b/chromium/base/bind.h index 5cf124df32f..b14f70c109f 100644 --- a/chromium/base/bind.h +++ b/chromium/base/bind.h @@ -65,12 +65,6 @@ Bind(Functor 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; - typedef internal::BindState<RunnableType, RunType, void()> BindState; diff --git a/chromium/base/bind.h.pump b/chromium/base/bind.h.pump index b321649aea6..2cdd7d5e498 100644 --- a/chromium/base/bind.h.pump +++ b/chromium/base/bind.h.pump @@ -93,14 +93,14 @@ $if ARITY > 0 [[, ]] $for ARG , [[const P$(ARG)& p$(ARG)]]) { 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; -$if ARITY > 0 [[ - // 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 diff --git a/chromium/base/bind_helpers.h b/chromium/base/bind_helpers.h index d717892c8f5..e31ef05ff1a 100644 --- a/chromium/base/bind_helpers.h +++ b/chromium/base/bind_helpers.h @@ -12,7 +12,7 @@ // // ARGUMENT BINDING WRAPPERS // -// The wrapper functions are base::Unretained(), base::Owned(), bass::Passed(), +// The wrapper functions are base::Unretained(), base::Owned(), base::Passed(), // base::ConstRef(), and base::IgnoreResult(). // // Unretained() allows Bind() to bind a non-refcounted class, and to disable diff --git a/chromium/base/bind_unittest.cc b/chromium/base/bind_unittest.cc index 2c93d53ee4b..e1f15cb2011 100644 --- a/chromium/base/bind_unittest.cc +++ b/chromium/base/bind_unittest.cc @@ -127,10 +127,6 @@ class CopyCounter { return *copies_; } - int assigns() const { - return *assigns_; - } - private: int* copies_; int* assigns_; diff --git a/chromium/base/bind_unittest.nc b/chromium/base/bind_unittest.nc index 033acfaa057..de81646fbff 100644 --- a/chromium/base/bind_unittest.nc +++ b/chromium/base/bind_unittest.nc @@ -9,8 +9,7 @@ namespace base { // Do not put everything inside an anonymous namespace. If you do, many of the -// helper function declarations will generate unused definition warnings unless -// unused definition warnings. +// helper function declarations will generate unused definition warnings. static const int kParentValue = 1; static const int kChildValue = 2; diff --git a/chromium/base/callback_list.h b/chromium/base/callback_list.h index d12a1e95baa..5b911fd4867 100644 --- a/chromium/base/callback_list.h +++ b/chromium/base/callback_list.h @@ -89,10 +89,13 @@ class CallbackListBase { } ~Subscription() { - if (list_->active_iterator_count_) + if (list_->active_iterator_count_) { iter_->Reset(); - else + } else { list_->callbacks_.erase(iter_); + if (!list_->removal_callback_.is_null()) + list_->removal_callback_.Run(); + } } private: @@ -111,6 +114,18 @@ class CallbackListBase { 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 { @@ -167,17 +182,24 @@ class CallbackListBase { // iteration. void Compact() { typename std::list<CallbackType>::iterator it = callbacks_.begin(); + bool updated = false; while (it != callbacks_.end()) { - if ((*it).is_null()) + if ((*it).is_null()) { + updated = true; it = callbacks_.erase(it); - else + } 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); }; diff --git a/chromium/base/callback_list.h.pump b/chromium/base/callback_list.h.pump index ea2103ebeef..d7f84736c15 100644 --- a/chromium/base/callback_list.h.pump +++ b/chromium/base/callback_list.h.pump @@ -94,10 +94,13 @@ class CallbackListBase { } ~Subscription() { - if (list_->active_iterator_count_) + if (list_->active_iterator_count_) { iter_->Reset(); - else + } else { list_->callbacks_.erase(iter_); + if (!list_->removal_callback_.is_null()) + list_->removal_callback_.Run(); + } } private: @@ -116,6 +119,18 @@ class CallbackListBase { 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 { @@ -172,17 +187,24 @@ class CallbackListBase { // iteration. void Compact() { typename std::list<CallbackType>::iterator it = callbacks_.begin(); + bool updated = false; while (it != callbacks_.end()) { - if ((*it).is_null()) + if ((*it).is_null()) { + updated = true; it = callbacks_.erase(it); - else + } 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); }; diff --git a/chromium/base/cancelable_callback_unittest.cc b/chromium/base/cancelable_callback_unittest.cc index 89c603c9537..fcbe23c1de3 100644 --- a/chromium/base/cancelable_callback_unittest.cc +++ b/chromium/base/cancelable_callback_unittest.cc @@ -159,7 +159,7 @@ TEST(CancelableCallbackTest, IsNull) { // CancelableCallback posted to a MessageLoop with PostTask. // - Callbacks posted to a MessageLoop can be cancelled. TEST(CancelableCallbackTest, PostTask) { - MessageLoop loop(MessageLoop::TYPE_DEFAULT); + MessageLoop loop; int count = 0; CancelableClosure cancelable(base::Bind(&Increment, diff --git a/chromium/base/command_line.cc b/chromium/base/command_line.cc index e00eee6bd89..6e37d6bf72b 100644 --- a/chromium/base/command_line.cc +++ b/chromium/base/command_line.cc @@ -20,11 +20,12 @@ #include <shellapi.h> #endif -using base::FilePath; +namespace base { CommandLine* CommandLine::current_process_commandline_ = NULL; namespace { + const CommandLine::CharType kSwitchTerminator[] = FILE_PATH_LITERAL("--"); const CommandLine::CharType kSwitchValueSeparator[] = FILE_PATH_LITERAL("="); @@ -81,7 +82,8 @@ 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(WideToASCII(switch_string), switch_value); + command_line.AppendSwitchNative(UTF16ToASCII(switch_string), + switch_value); #elif defined(OS_POSIX) command_line.AppendSwitchNative(switch_string, switch_value); #endif @@ -308,7 +310,7 @@ std::string CommandLine::GetSwitchValueASCII( return std::string(); } #if defined(OS_WIN) - return WideToASCII(value); + return UTF16ToASCII(value); #else return value; #endif @@ -413,7 +415,7 @@ void CommandLine::PrependWrapper(const CommandLine::StringType& wrapper) { // The wrapper may have embedded arguments (like "gdb --args"). In this case, // we don't pretend to do anything fancy, we just split on spaces. StringVector wrapper_argv; - base::SplitString(wrapper, FILE_PATH_LITERAL(' '), &wrapper_argv); + SplitString(wrapper, FILE_PATH_LITERAL(' '), &wrapper_argv); // Prepend the wrapper and update the switches/arguments |begin_args_|. argv_.insert(argv_.begin(), wrapper_argv.begin(), wrapper_argv.end()); begin_args_ += wrapper_argv.size(); @@ -431,8 +433,10 @@ void CommandLine::ParseFromString(const std::wstring& command_line) { args = ::CommandLineToArgvW(command_line_string.c_str(), &num_args); DPLOG_IF(FATAL, !args) << "CommandLineToArgvW failed on command line: " - << command_line; + << UTF16ToUTF8(command_line); InitFromArgv(num_args, args); LocalFree(args); } #endif + +} // namespace base diff --git a/chromium/base/command_line.h b/chromium/base/command_line.h index 81bd4b73761..fd06e61c774 100644 --- a/chromium/base/command_line.h +++ b/chromium/base/command_line.h @@ -24,8 +24,8 @@ #include "build/build_config.h" namespace base { + class FilePath; -} class BASE_EXPORT CommandLine { public: @@ -45,7 +45,7 @@ class BASE_EXPORT CommandLine { explicit CommandLine(NoProgram no_program); // Construct a new command line with |program| as argv[0]. - explicit CommandLine(const base::FilePath& program); + explicit CommandLine(const FilePath& program); // Construct a new command line from an argument list. CommandLine(int argc, const CharType* const* argv); @@ -108,8 +108,8 @@ class BASE_EXPORT CommandLine { const StringVector& argv() const { return argv_; } // Get and Set the program part of the command line string (the first item). - base::FilePath GetProgram() const; - void SetProgram(const base::FilePath& program); + FilePath GetProgram() const; + void SetProgram(const FilePath& program); // Returns true if this command line contains the given switch. // (Switch names are case-insensitive). @@ -118,7 +118,7 @@ class BASE_EXPORT CommandLine { // Returns the value associated with the given switch. If the switch has no // value or isn't present, this method returns the empty string. std::string GetSwitchValueASCII(const std::string& switch_string) const; - base::FilePath GetSwitchValuePath(const std::string& switch_string) const; + FilePath GetSwitchValuePath(const std::string& switch_string) const; StringType GetSwitchValueNative(const std::string& switch_string) const; // Get a copy of all switches, along with their values. @@ -128,7 +128,7 @@ class BASE_EXPORT CommandLine { // Note: Switches will precede arguments regardless of appending order. void AppendSwitch(const std::string& switch_string); void AppendSwitchPath(const std::string& switch_string, - const base::FilePath& path); + const FilePath& path); void AppendSwitchNative(const std::string& switch_string, const StringType& value); void AppendSwitchASCII(const std::string& switch_string, @@ -148,7 +148,7 @@ class BASE_EXPORT CommandLine { // AppendArg is primarily for ASCII; non-ASCII input is interpreted as UTF-8. // Note: Switches will precede arguments regardless of appending order. void AppendArg(const std::string& value); - void AppendArgPath(const base::FilePath& value); + void AppendArgPath(const FilePath& value); void AppendArgNative(const StringType& value); // Append the switches and arguments from another command line to this one. @@ -186,4 +186,9 @@ class BASE_EXPORT CommandLine { size_t begin_args_; }; +} // 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 2c947fc39ad..06c5006505a 100644 --- a/chromium/base/command_line_unittest.cc +++ b/chromium/base/command_line_unittest.cc @@ -198,10 +198,14 @@ TEST(CommandLineTest, GetArgumentsString) { cl.AppendArg(kFourthArgName); #if defined(OS_WIN) - 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_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)); #elif defined(OS_POSIX) CommandLine::StringType expected_first_arg(kFirstArgName); CommandLine::StringType expected_second_arg(kSecondArgName); diff --git a/chromium/base/compiler_specific.h b/chromium/base/compiler_specific.h index dc4b2334987..9e2111db6b6 100644 --- a/chromium/base/compiler_specific.h +++ b/chromium/base/compiler_specific.h @@ -137,9 +137,7 @@ // method in the parent class. // Use like: // virtual void foo() OVERRIDE; -#if defined(COMPILER_MSVC) -#define OVERRIDE override -#elif defined(__clang__) +#if defined(__clang__) || defined(COMPILER_MSVC) #define OVERRIDE override #elif defined(COMPILER_GCC) && __cplusplus >= 201103 && \ (__GNUC__ * 10000 + __GNUC_MINOR__ * 100) >= 40700 @@ -154,10 +152,7 @@ // Use like: // virtual void foo() FINAL; // class B FINAL : public A {}; -#if defined(COMPILER_MSVC) -// TODO(jered): Change this to "final" when chromium no longer uses MSVC 2010. -#define FINAL sealed -#elif defined(__clang__) +#if defined(__clang__) || defined(COMPILER_MSVC) #define FINAL final #elif defined(COMPILER_GCC) && __cplusplus >= 201103 && \ (__GNUC__ * 10000 + __GNUC_MINOR__ * 100) >= 40700 @@ -196,12 +191,9 @@ // If available, it would look like: // __attribute__((format(wprintf, format_param, dots_param))) - // MemorySanitizer annotations. -#ifdef MEMORY_SANITIZER -extern "C" { -void __msan_unpoison(const void *p, unsigned long s); -} // extern "C" +#if defined(MEMORY_SANITIZER) && !defined(OS_NACL) +#include <sanitizer/msan_interface.h> // Mark a memory region fully initialized. // Use this to annotate code that deliberately reads uninitialized data, for @@ -211,4 +203,22 @@ void __msan_unpoison(const void *p, unsigned long s); #define MSAN_UNPOISON(p, s) #endif // MEMORY_SANITIZER +// Macro useful for writing cross-platform function pointers. +#if !defined(CDECL) +#if defined(OS_WIN) +#define CDECL __cdecl +#else // defined(OS_WIN) +#define CDECL +#endif // defined(OS_WIN) +#endif // !defined(CDECL) + +// Macro for hinting that an expression is likely to be false. +#if !defined(UNLIKELY) +#if defined(COMPILER_GCC) +#define UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define UNLIKELY(x) (x) +#endif // defined(COMPILER_GCC) +#endif // !defined(UNLIKELY) + #endif // BASE_COMPILER_SPECIFIC_H_ diff --git a/chromium/base/containers/hash_tables.h b/chromium/base/containers/hash_tables.h index 365b586ba93..6f37c49c3e4 100644 --- a/chromium/base/containers/hash_tables.h +++ b/chromium/base/containers/hash_tables.h @@ -140,7 +140,7 @@ inline std::size_t HashInts32(uint32 value1, uint32 value2) { hash64 = hash64 * odd_random + shift_random; std::size_t high_bits = static_cast<std::size_t>( - hash64 >> (sizeof(uint64) - sizeof(std::size_t))); + hash64 >> (8 * (sizeof(uint64) - sizeof(std::size_t)))); return high_bits; } @@ -175,7 +175,7 @@ inline std::size_t HashInts64(uint64 value1, uint64 value2) { hash64 = hash64 * odd_random + shift_random; std::size_t high_bits = static_cast<std::size_t>( - hash64 >> (sizeof(uint64) - sizeof(std::size_t))); + hash64 >> (8 * (sizeof(uint64) - sizeof(std::size_t)))); return high_bits; } diff --git a/chromium/base/containers/scoped_ptr_hash_map.h b/chromium/base/containers/scoped_ptr_hash_map.h index b636e604a5a..dedf21365be 100644 --- a/chromium/base/containers/scoped_ptr_hash_map.h +++ b/chromium/base/containers/scoped_ptr_hash_map.h @@ -24,6 +24,9 @@ class ScopedPtrHashMap { typedef base::hash_map<Key, Value*> Container; public: + typedef typename Container::key_type key_type; + typedef typename Container::mapped_type mapped_type; + typedef typename Container::value_type value_type; typedef typename Container::iterator iterator; typedef typename Container::const_iterator const_iterator; @@ -35,30 +38,29 @@ class ScopedPtrHashMap { data_.swap(other.data_); } - std::pair<iterator, bool> insert( - std::pair<Key, const scoped_ptr<Value> > pair) { - return data_.insert( - std::pair<Key, Value*>(pair.first, pair.second.release())); - } - // Replaces value but not key if key is already present. - std::pair<iterator, bool> set(Key key, scoped_ptr<Value> data) { + iterator set(const Key& key, scoped_ptr<Value> data) { iterator it = find(key); - if (it != end()) - erase(it); - Value* raw_ptr = data.release(); - return data_.insert(std::pair<Key, Value*>(key, raw_ptr)); + if (it != end()) { + delete it->second; + it->second = data.release(); + return it; + } + + return data_.insert(std::make_pair(key, data.release())).first; } // Does nothing if key is already present - std::pair<iterator, bool> add(Key key, scoped_ptr<Value> data) { - Value* raw_ptr = data.release(); - return data_.insert(std::pair<Key, Value*>(key, raw_ptr)); + std::pair<iterator, bool> add(const Key& key, scoped_ptr<Value> data) { + std::pair<iterator, bool> result = + data_.insert(std::make_pair(key, data.get())); + if (result.second) + ignore_result(data.release()); + return result; } void erase(iterator it) { - if (it->second) - delete it->second; + delete it->second; data_.erase(it); } @@ -75,10 +77,8 @@ class ScopedPtrHashMap { if (it == data_.end()) return scoped_ptr<Value>(); - Key key = it->first; scoped_ptr<Value> ret(it->second); - data_.erase(it); - data_.insert(std::pair<Key, Value*>(key, static_cast<Value*>(NULL))); + it->second = NULL; return ret.Pass(); } @@ -108,12 +108,12 @@ class ScopedPtrHashMap { return take_and_erase(it); } - // Returns the first element in the hash_map that matches the given key. + // 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 { const_iterator it = find(k); if (it == end()) - return 0; + return NULL; return it->second; } diff --git a/chromium/base/containers/small_map_unittest.cc b/chromium/base/containers/small_map_unittest.cc index b911bee655d..bc76e370397 100644 --- a/chromium/base/containers/small_map_unittest.cc +++ b/chromium/base/containers/small_map_unittest.cc @@ -138,30 +138,21 @@ TEST(SmallMap, CopyConstructor) { } template<class inner> -static void SmallMapToMap(SmallMap<inner> const& src, inner* dest) { +static bool SmallMapIsSubset(SmallMap<inner> const& a, + SmallMap<inner> const& b) { typename SmallMap<inner>::const_iterator it; - for (it = src.begin(); it != src.end(); ++it) { - dest->insert(std::make_pair(it->first, it->second)); + for (it = a.begin(); it != a.end(); ++it) { + typename SmallMap<inner>::const_iterator it_in_b = b.find(it->first); + if (it_in_b == b.end() || it_in_b->second != it->second) + return false; } + return true; } template<class inner> static bool SmallMapEqual(SmallMap<inner> const& a, SmallMap<inner> const& b) { - inner ia, ib; - SmallMapToMap(a, &ia); - SmallMapToMap(b, &ib); - - // On most systems we can use operator== here, but under some lesser STL - // implementations it doesn't seem to work. So we manually compare. - if (ia.size() != ib.size()) - return false; - for (typename inner::iterator ia_it = ia.begin(), ib_it = ib.begin(); - ia_it != ia.end(); ++ia_it, ++ib_it) { - if (*ia_it != *ib_it) - return false; - } - return true; + return SmallMapIsSubset(a, b) && SmallMapIsSubset(b, a); } TEST(SmallMap, AssignmentOperator) { diff --git a/chromium/base/containers/stack_container.h b/chromium/base/containers/stack_container.h index f0106d73f21..87fa0369b6a 100644 --- a/chromium/base/containers/stack_container.h +++ b/chromium/base/containers/stack_container.h @@ -90,6 +90,13 @@ class StackAllocator : public std::allocator<T> { : source_(NULL) { } + // This constructor must exist. It creates a default allocator that doesn't + // actually have a stack buffer. glibc's std::string() will compare the + // current allocator against the default-constructed allocator, so this + // should be fast. + StackAllocator() : source_(NULL) { + } + explicit StackAllocator(Source* source) : source_(source) { } diff --git a/chromium/base/cpu.cc b/chromium/base/cpu.cc index 66207a1e0b8..dcba3b6e73e 100644 --- a/chromium/base/cpu.cc +++ b/chromium/base/cpu.cc @@ -11,6 +11,11 @@ #include "base/basictypes.h" #include "build/build_config.h" +#if defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX)) +#include "base/file_util.h" +#include "base/lazy_instance.h" +#endif + #if defined(ARCH_CPU_X86_FAMILY) #if defined(_MSC_VER) #include <intrin.h> @@ -84,6 +89,56 @@ uint64 _xgetbv(uint32 xcr) { #endif // !_MSC_VER #endif // ARCH_CPU_X86_FAMILY +#if defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX)) + +// Returns the string found in /proc/cpuinfo under the key "model name" or +// "Processor". "model name" is used in Linux 3.8 and later (3.7 and later for +// arm64) and is shown once per CPU. "Processor" is used in earler versions and +// is shown only once at the top of /proc/cpuinfo regardless of the number CPUs. +std::string ParseCpuInfo() { + const char kModelNamePrefix[] = "model name\t: "; + const char kProcessorPrefix[] = "Processor\t: "; + std::string contents; + ReadFileToString(FilePath("/proc/cpuinfo"), &contents); + DCHECK(!contents.empty()); + std::string cpu_brand; + if (!contents.empty()) { + std::istringstream iss(contents); + std::string line; + while (std::getline(iss, line)) { + if (line.compare(0, strlen(kModelNamePrefix), kModelNamePrefix) == 0) { + cpu_brand.assign(line.substr(strlen(kModelNamePrefix))); + break; + } + if (line.compare(0, strlen(kProcessorPrefix), kProcessorPrefix) == 0) { + cpu_brand.assign(line.substr(strlen(kProcessorPrefix))); + break; + } + } + } + return cpu_brand; +} + +class LazyCpuInfoValue { + public: + LazyCpuInfoValue() : value_(ParseCpuInfo()) {} + const std::string& value() { return value_; } + + private: + const std::string value_; + DISALLOW_COPY_AND_ASSIGN(LazyCpuInfoValue); +}; + +base::LazyInstance<LazyCpuInfoValue> g_lazy_cpu_brand = + LAZY_INSTANCE_INITIALIZER; + +const std::string& CpuBrandInfo() { + return g_lazy_cpu_brand.Get().value(); +} + +#endif // defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || + // defined(OS_LINUX)) + } // anonymous namespace void CPU::Initialize() { @@ -128,8 +183,14 @@ void CPU::Initialize() { // b) XSAVE is supported by the CPU and // c) XSAVE is enabled by the kernel. // See http://software.intel.com/en-us/blogs/2011/04/14/is-avx-enabled + // + // In addition, we have observed some crashes with the xgetbv instruction + // even after following Intel's example code. (See crbug.com/375968.) + // Because of that, we also test the XSAVE bit because its description in + // the CPUID documentation suggests that it signals xgetbv support. has_avx_ = has_avx_hardware_ && + (cpu_info[2] & 0x04000000) != 0 /* XSAVE */ && (cpu_info[2] & 0x08000000) != 0 /* OSXSAVE */ && (_xgetbv(0) & 6) == 6 /* XSAVE enabled by kernel */; has_aesni_ = (cpu_info[2] & 0x02000000) != 0; @@ -157,13 +218,8 @@ void CPU::Initialize() { __cpuid(cpu_info, parameter_containing_non_stop_time_stamp_counter); has_non_stop_time_stamp_counter_ = (cpu_info[3] & (1 << 8)) != 0; } -#elif defined(ARCH_CPU_ARM_FAMILY) - // TODO(piman): Expand this. ARM has a CPUID register, but it's not available - // in user mode. /proc/cpuinfo has some information, but it's non standard, - // platform-specific, and not accessible from the sandbox. - // For some purposes, this first approximation is enough. - // crbug.com/313454 - cpu_brand_.assign("ARM"); +#elif defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX)) + cpu_brand_.assign(CpuBrandInfo()); #endif } diff --git a/chromium/base/critical_closure.h b/chromium/base/critical_closure.h index ca51ed5cef2..ac07911089d 100644 --- a/chromium/base/critical_closure.h +++ b/chromium/base/critical_closure.h @@ -7,9 +7,46 @@ #include "base/callback.h" +#if defined(OS_IOS) +#include "base/bind.h" +#include "base/ios/scoped_critical_action.h" +#endif + namespace base { -// Returns a closure that will continue to run for a period of time when the +namespace internal { + +#if defined(OS_IOS) +// Returns true if multi-tasking is supported on this iOS device. +bool IsMultiTaskingSupported(); + +// This class wraps a closure so it can continue to run for a period of time +// when the application goes to the background by using +// |ios::ScopedCriticalAction|. +template <typename R> +class CriticalClosure { + public: + explicit CriticalClosure(const Callback<R(void)>& closure) + : closure_(closure) {} + + ~CriticalClosure() {} + + R Run() { + return closure_.Run(); + } + + private: + ios::ScopedCriticalAction critical_action_; + Callback<R(void)> closure_; + + DISALLOW_COPY_AND_ASSIGN(CriticalClosure); +}; +#endif // defined(OS_IOS) + +} // namespace internal + +// Returns a closure (which may return a result, but must not require any extra +// arguments) that will continue to run for a period of time when the // application goes to the background if possible on platforms where // applications don't execute while backgrounded, otherwise the original task is // returned. @@ -23,14 +60,20 @@ namespace base { // background running time, |MakeCriticalClosure| should be applied on them // before posting. #if defined(OS_IOS) -base::Closure MakeCriticalClosure(const base::Closure& closure); -#else -inline base::Closure MakeCriticalClosure(const base::Closure& closure) { +template <typename R> +Callback<R(void)> MakeCriticalClosure(const Callback<R(void)>& closure) { + DCHECK(internal::IsMultiTaskingSupported()); + return base::Bind(&internal::CriticalClosure<R>::Run, + Owned(new internal::CriticalClosure<R>(closure))); +} +#else // defined(OS_IOS) +template <typename R> +inline Callback<R(void)> MakeCriticalClosure(const Callback<R(void)>& closure) { // No-op for platforms where the application does not need to acquire // background time for closures to finish when it goes into the background. return closure; } -#endif // !defined(OS_IOS) +#endif // defined(OS_IOS) } // namespace base diff --git a/chromium/base/critical_closure_internal_ios.mm b/chromium/base/critical_closure_internal_ios.mm new file mode 100644 index 00000000000..b8fec141b21 --- /dev/null +++ b/chromium/base/critical_closure_internal_ios.mm @@ -0,0 +1,17 @@ +// 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/critical_closure.h" + +#import <UIKit/UIKit.h> + +namespace base { +namespace internal { + +bool IsMultiTaskingSupported() { + return [[UIDevice currentDevice] isMultitaskingSupported]; +} + +} // namespace internal +} // namespace base diff --git a/chromium/base/critical_closure_ios.mm b/chromium/base/critical_closure_ios.mm deleted file mode 100644 index d605cad0a20..00000000000 --- a/chromium/base/critical_closure_ios.mm +++ /dev/null @@ -1,49 +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/critical_closure.h" - -#import <UIKit/UIKit.h> - -#include "base/bind.h" -#include "base/ios/scoped_critical_action.h" -#include "base/memory/ref_counted.h" - -namespace { - -// This class wraps a closure so it can continue to run for a period of time -// when the application goes to the background by using -// |base::ios::ScopedCriticalAction|. -class CriticalClosure : public base::RefCountedThreadSafe<CriticalClosure> { - public: - explicit CriticalClosure(base::Closure* closure) : closure_(closure) { - } - - void Run() { - closure_->Run(); - } - - private: - friend class base::RefCountedThreadSafe<CriticalClosure>; - - virtual ~CriticalClosure() {} - - base::ios::ScopedCriticalAction criticial_action_; - scoped_ptr<base::Closure> closure_; - - DISALLOW_COPY_AND_ASSIGN(CriticalClosure); -}; - -} // namespace - -namespace base { - -base::Closure MakeCriticalClosure(const base::Closure& closure) { - DCHECK([[UIDevice currentDevice] isMultitaskingSupported]); - scoped_refptr<CriticalClosure> critical_closure( - new CriticalClosure(new base::Closure(closure))); - return base::Bind(&CriticalClosure::Run, critical_closure.get()); -} - -} // namespace base diff --git a/chromium/base/debug/OWNERS b/chromium/base/debug/OWNERS index 4976ab1e773..5dcc3e9d737 100644 --- a/chromium/base/debug/OWNERS +++ b/chromium/base/debug/OWNERS @@ -1,3 +1,5 @@ +per-file sanitizer_options.cc=glider@chromium.org per-file trace_event*=nduca@chromium.org per-file trace_event*=dsinclair@chromium.org per-file trace_event_android.cc=wangxianzhu@chromium.org +per-file tsan_suppressions.cc=* diff --git a/chromium/base/debug/asan_invalid_access.cc b/chromium/base/debug/asan_invalid_access.cc new file mode 100644 index 00000000000..ff7788a39cc --- /dev/null +++ b/chromium/base/debug/asan_invalid_access.cc @@ -0,0 +1,94 @@ +// 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. + +#if defined(OS_WIN) +#include <windows.h> +#endif + +#include "base/debug/alias.h" +#include "base/debug/asan_invalid_access.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" + +namespace base { +namespace debug { + +namespace { + +#if defined(SYZYASAN) +// Corrupt a memory block and make sure that the corruption gets detected either +// when we free it or when another crash happens (if |induce_crash| is set to +// true). +NOINLINE void CorruptMemoryBlock(bool induce_crash) { + // NOTE(sebmarchand): We intentionally corrupt a memory block here in order to + // trigger an Address Sanitizer (ASAN) error report. + static const int kArraySize = 5; + int* array = new int[kArraySize]; + // Encapsulate the invalid memory access into a try-catch statement to prevent + // this function from being instrumented. This way the underflow won't be + // detected but the corruption will (as the allocator will still be hooked). + try { + // Declares the dummy value as volatile to make sure it doesn't get + // optimized away. + int volatile dummy = array[-1]--; + base::debug::Alias(const_cast<int*>(&dummy)); + } catch (...) { + } + if (induce_crash) + CHECK(false); + delete[] array; +} +#endif + +} // namespace + +#if defined(ADDRESS_SANITIZER) || defined(SYZYASAN) +// NOTE(sebmarchand): We intentionally perform some invalid heap access here in +// order to trigger an AddressSanitizer (ASan) error report. + +static const int kArraySize = 5; + +void AsanHeapOverflow() { + scoped_ptr<int[]> array(new int[kArraySize]); + // Declares the dummy value as volatile to make sure it doesn't get optimized + // away. + int volatile dummy = 0; + dummy = array[kArraySize]; + base::debug::Alias(const_cast<int*>(&dummy)); +} + +void AsanHeapUnderflow() { + scoped_ptr<int[]> array(new int[kArraySize]); + // Declares the dummy value as volatile to make sure it doesn't get optimized + // away. + int volatile dummy = 0; + dummy = array[-1]; + base::debug::Alias(const_cast<int*>(&dummy)); +} + +void AsanHeapUseAfterFree() { + scoped_ptr<int[]> array(new int[kArraySize]); + // Declares the dummy value as volatile to make sure it doesn't get optimized + // away. + int volatile dummy = 0; + int* dangling = array.get(); + array.reset(); + dummy = dangling[kArraySize / 2]; + base::debug::Alias(const_cast<int*>(&dummy)); +} + +#endif // ADDRESS_SANITIZER || SYZYASAN + +#if defined(SYZYASAN) +void AsanCorruptHeapBlock() { + CorruptMemoryBlock(false); +} + +void AsanCorruptHeap() { + CorruptMemoryBlock(true); +} +#endif // SYZYASAN + +} // namespace debug +} // namespace base diff --git a/chromium/base/debug/asan_invalid_access.h b/chromium/base/debug/asan_invalid_access.h new file mode 100644 index 00000000000..65519878f12 --- /dev/null +++ b/chromium/base/debug/asan_invalid_access.h @@ -0,0 +1,47 @@ +// 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. +// +// Defines some functions that intentionally do an invalid memory access in +// order to trigger an AddressSanitizer (ASan) error report. + +#ifndef BASE_DEBUG_ASAN_INVALID_ACCESS_H_ +#define BASE_DEBUG_ASAN_INVALID_ACCESS_H_ + +#include "base/base_export.h" +#include "base/compiler_specific.h" + +namespace base { +namespace debug { + +#if defined(ADDRESS_SANITIZER) || defined(SYZYASAN) + +// Generates an heap buffer overflow. +BASE_EXPORT NOINLINE void AsanHeapOverflow(); + +// Generates an heap buffer underflow. +BASE_EXPORT NOINLINE void AsanHeapUnderflow(); + +// Generates an use after free. +BASE_EXPORT NOINLINE void AsanHeapUseAfterFree(); + +#endif // ADDRESS_SANITIZER || SYZYASAN + +// The "corrupt-block" and "corrupt-heap" classes of bugs is specific to +// SyzyASan. +#if defined(SYZYASAN) + +// Corrupts a memory block and makes sure that the corruption gets detected when +// we try to free this block. +BASE_EXPORT NOINLINE void AsanCorruptHeapBlock(); + +// Corrupts the heap and makes sure that the corruption gets detected when a +// crash occur. +BASE_EXPORT NOINLINE void AsanCorruptHeap(); + +#endif // SYZYASAN + +} // namespace debug +} // namespace base + +#endif // BASE_DEBUG_ASAN_INVALID_ACCESS_H_ diff --git a/chromium/base/debug/debug_on_start_win.cc b/chromium/base/debug/debug_on_start_win.cc deleted file mode 100644 index 6ca88dde20c..00000000000 --- a/chromium/base/debug/debug_on_start_win.cc +++ /dev/null @@ -1,74 +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/debug/debug_on_start_win.h" - -#include <windows.h> - -#include "base/base_switches.h" -#include "base/basictypes.h" -#include "base/debug/debugger.h" - -namespace base { -namespace debug { - -// Minimalist implementation to try to find a command line argument. We can use -// kernel32 exported functions but not the CRT functions because we're too early -// in the process startup. -// The code is not that bright and will find things like ---argument or -// /-/argument. -// Note: command_line is non-destructively modified. -bool DebugOnStart::FindArgument(wchar_t* command_line, const char* argument_c) { - wchar_t argument[50] = {}; - for (int i = 0; argument_c[i]; ++i) - argument[i] = argument_c[i]; - - int argument_len = lstrlen(argument); - int command_line_len = lstrlen(command_line); - while (command_line_len > argument_len) { - wchar_t first_char = command_line[0]; - wchar_t last_char = command_line[argument_len+1]; - // Try to find an argument. - if ((first_char == L'-' || first_char == L'/') && - (last_char == L' ' || last_char == 0 || last_char == L'=')) { - command_line[argument_len+1] = 0; - // Skip the - or / - if (lstrcmpi(command_line+1, argument) == 0) { - // Found it. - command_line[argument_len+1] = last_char; - return true; - } - // Fix back. - command_line[argument_len+1] = last_char; - } - // Continue searching. - ++command_line; - --command_line_len; - } - return false; -} - -// static -int __cdecl DebugOnStart::Init() { - // Try to find the argument. - if (FindArgument(GetCommandLine(), switches::kDebugOnStart)) { - // We can do 2 things here: - // - Ask for a debugger to attach to us. This involve reading the registry - // key and creating the process. - // - Do a int3. - - // It will fails if we run in a sandbox. That is expected. - base::debug::SpawnDebuggerOnProcess(GetCurrentProcessId()); - - // Wait for a debugger to come take us. - base::debug::WaitForDebugger(60, false); - } else if (FindArgument(GetCommandLine(), switches::kWaitForDebugger)) { - // Wait for a debugger to come take us. - base::debug::WaitForDebugger(60, true); - } - return 0; -} - -} // namespace debug -} // namespace base diff --git a/chromium/base/debug/debug_on_start_win.h b/chromium/base/debug/debug_on_start_win.h deleted file mode 100644 index edcaa0aa953..00000000000 --- a/chromium/base/debug/debug_on_start_win.h +++ /dev/null @@ -1,83 +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. - -// Define the necessary code and global data to look for kDebugOnStart command -// line argument. When the command line argument is detected, it invokes the -// debugger, if no system-wide debugger is registered, a debug break is done. - -#ifndef BASE_DEBUG_DEBUG_ON_START_WIN_H_ -#define BASE_DEBUG_DEBUG_ON_START_WIN_H_ - -#include "base/basictypes.h" -#include "build/build_config.h" - -// This only works on Windows. It's legal to include on other platforms, but -// will be a NOP. -#if defined(OS_WIN) - -#ifndef DECLSPEC_SELECTANY -#define DECLSPEC_SELECTANY __declspec(selectany) -#endif - -namespace base { -namespace debug { - -// There is no way for this code, as currently implemented, to work across DLLs. -// TODO(rvargas): It looks like we really don't use this code, at least not for -// Chrome. Figure out if it's really worth implementing something simpler. -#if !defined(COMPONENT_BUILD) - -// Debug on start functions and data. -class DebugOnStart { - public: - // Expected function type in the .CRT$XI* section. - // Note: See VC\crt\src\internal.h for reference. - typedef int (__cdecl *PIFV)(void); - - // Looks at the command line for kDebugOnStart argument. If found, it invokes - // the debugger, if this fails, it crashes. - static int __cdecl Init(); - - // Returns true if the 'argument' is present in the 'command_line'. It does - // not use the CRT, only Kernel32 functions. - static bool FindArgument(wchar_t* command_line, const char* argument); -}; - -// Set the function pointer to our function to look for a crash on start. The -// XIB section is started pretty early in the program initialization so in -// theory it should be called before any user created global variable -// initialization code and CRT initialization code. -// Note: See VC\crt\src\defsects.inc and VC\crt\src\crt0.c for reference. -#ifdef _WIN64 - -// "Fix" the segment. On x64, the .CRT segment is merged into the .rdata segment -// so it contains const data only. -#pragma const_seg(push, ".CRT$XIB") -// Declare the pointer so the CRT will find it. -extern const DebugOnStart::PIFV debug_on_start; -DECLSPEC_SELECTANY const DebugOnStart::PIFV debug_on_start = - &DebugOnStart::Init; -// Fix back the segment. -#pragma const_seg(pop) - -#else // _WIN64 - -// "Fix" the segment. On x86, the .CRT segment is merged into the .data segment -// so it contains non-const data only. -#pragma data_seg(push, ".CRT$XIB") -// Declare the pointer so the CRT will find it. -DECLSPEC_SELECTANY DebugOnStart::PIFV debug_on_start = &DebugOnStart::Init; -// Fix back the segment. -#pragma data_seg(pop) - -#endif // _WIN64 - -#endif // defined(COMPONENT_BUILD) - -} // namespace debug -} // namespace base - -#endif // defined(OS_WIN) - -#endif // BASE_DEBUG_DEBUG_ON_START_WIN_H_ diff --git a/chromium/base/debug/debugger.h b/chromium/base/debug/debugger.h index 4f368d986d4..d62ea3f7e17 100644 --- a/chromium/base/debug/debugger.h +++ b/chromium/base/debug/debugger.h @@ -14,10 +14,6 @@ namespace base { namespace debug { -// Starts the registered system-wide JIT debugger to attach it to specified -// process. -BASE_EXPORT bool SpawnDebuggerOnProcess(unsigned process_id); - // Waits wait_seconds seconds for a debugger to attach to the current process. // When silent is false, an exception is thrown when a debugger is detected. BASE_EXPORT bool WaitForDebugger(int wait_seconds, bool silent); diff --git a/chromium/base/debug/debugger_posix.cc b/chromium/base/debug/debugger_posix.cc index 60ad5218308..48393f4f415 100644 --- a/chromium/base/debug/debugger_posix.cc +++ b/chromium/base/debug/debugger_posix.cc @@ -14,7 +14,6 @@ #include <sys/types.h> #include <unistd.h> -#include <string> #include <vector> #if defined(__GLIBCXX__) @@ -41,7 +40,6 @@ #include "base/posix/eintr_wrapper.h" #include "base/safe_strerror_posix.h" #include "base/strings/string_piece.h" -#include "base/strings/stringprintf.h" #if defined(USE_SYMBOLIZE) #include "base/third_party/symbolize/symbolize.h" @@ -54,22 +52,6 @@ namespace base { namespace debug { -bool SpawnDebuggerOnProcess(unsigned process_id) { -#if OS_ANDROID || OS_NACL - NOTIMPLEMENTED(); - return false; -#else - const std::string debug_cmd = - StringPrintf("xterm -e 'gdb --pid=%u' &", process_id); - LOG(WARNING) << "Starting debugger on pid " << process_id - << " with command `" << debug_cmd << "`"; - int ret = system(debug_cmd.c_str()); - if (ret == -1) - return false; - return true; -#endif -} - #if defined(OS_MACOSX) || defined(OS_BSD) // Based on Apple's recommended method as described in @@ -199,8 +181,10 @@ bool BeingDebugged() { // SIGABRT // Mac: Always send SIGTRAP. -#if defined(ARCH_CPU_ARM_FAMILY) +#if defined(ARCH_CPU_ARMEL) #define DEBUG_BREAK_ASM() asm("bkpt 0") +#elif defined(ARCH_CPU_ARM64) +#define DEBUG_BREAK_ASM() asm("brk 0") #elif defined(ARCH_CPU_MIPS_FAMILY) #define DEBUG_BREAK_ASM() asm("break 2") #elif defined(ARCH_CPU_X86_FAMILY) diff --git a/chromium/base/debug/debugger_win.cc b/chromium/base/debug/debugger_win.cc index b13dbfd1483..ccc9c1604bc 100644 --- a/chromium/base/debug/debugger_win.cc +++ b/chromium/base/debug/debugger_win.cc @@ -4,99 +4,12 @@ #include "base/debug/debugger.h" +#include <stdlib.h> #include <windows.h> -#include <dbghelp.h> - -#include "base/basictypes.h" -#include "base/logging.h" namespace base { namespace debug { -namespace { - -// Minimalist key reader. -// Note: Does not use the CRT. -bool RegReadString(HKEY root, const wchar_t* subkey, - const wchar_t* value_name, wchar_t* buffer, int* len) { - HKEY key = NULL; - DWORD res = RegOpenKeyEx(root, subkey, 0, KEY_READ, &key); - if (ERROR_SUCCESS != res || key == NULL) - return false; - - DWORD type = 0; - DWORD buffer_size = *len * sizeof(wchar_t); - // We don't support REG_EXPAND_SZ. - res = RegQueryValueEx(key, value_name, NULL, &type, - reinterpret_cast<BYTE*>(buffer), &buffer_size); - if (ERROR_SUCCESS == res && buffer_size != 0 && type == REG_SZ) { - // Make sure the buffer is NULL terminated. - buffer[*len - 1] = 0; - *len = lstrlen(buffer); - RegCloseKey(key); - return true; - } - RegCloseKey(key); - return false; -} - -// Replaces each "%ld" in input per a value. Not efficient but it works. -// Note: Does not use the CRT. -bool StringReplace(const wchar_t* input, int value, wchar_t* output, - int output_len) { - memset(output, 0, output_len*sizeof(wchar_t)); - int input_len = lstrlen(input); - - for (int i = 0; i < input_len; ++i) { - int current_output_len = lstrlen(output); - - if (input[i] == L'%' && input[i + 1] == L'l' && input[i + 2] == L'd') { - // Make sure we have enough place left. - if ((current_output_len + 12) >= output_len) - return false; - - // Cheap _itow(). - wsprintf(output+current_output_len, L"%d", value); - i += 2; - } else { - if (current_output_len >= output_len) - return false; - output[current_output_len] = input[i]; - } - } - return true; -} - -} // namespace - -// Note: Does not use the CRT. -bool SpawnDebuggerOnProcess(unsigned process_id) { - wchar_t reg_value[1026]; - int len = arraysize(reg_value); - if (RegReadString(HKEY_LOCAL_MACHINE, - L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug", - L"Debugger", reg_value, &len)) { - wchar_t command_line[1026]; - if (StringReplace(reg_value, process_id, command_line, - arraysize(command_line))) { - // We don't mind if the debugger is present because it will simply fail - // to attach to this process. - STARTUPINFO startup_info = {0}; - startup_info.cb = sizeof(startup_info); - PROCESS_INFORMATION process_info = {0}; - - if (CreateProcess(NULL, command_line, NULL, NULL, FALSE, 0, NULL, NULL, - &startup_info, &process_info)) { - CloseHandle(process_info.hThread); - WaitForInputIdle(process_info.hProcess, 10000); - CloseHandle(process_info.hProcess); - return true; - } - } - } - return false; -} - bool BeingDebugged() { return ::IsDebuggerPresent() != 0; } diff --git a/chromium/base/debug/dump_without_crashing.cc b/chromium/base/debug/dump_without_crashing.cc new file mode 100644 index 00000000000..47fd873c19f --- /dev/null +++ b/chromium/base/debug/dump_without_crashing.cc @@ -0,0 +1,32 @@ +// 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/debug/dump_without_crashing.h" + +#include "base/logging.h" + +namespace { + +// Pointer to the function that's called by DumpWithoutCrashing() to dump the +// process's memory. +void (CDECL *dump_without_crashing_function_)() = NULL; + +} // namespace + +namespace base { + +namespace debug { + +void DumpWithoutCrashing() { + if (dump_without_crashing_function_) + (*dump_without_crashing_function_)(); +} + +void SetDumpWithoutCrashingFunction(void (CDECL *function)()) { + dump_without_crashing_function_ = function; +} + +} // namespace debug + +} // namespace base diff --git a/chromium/base/debug/dump_without_crashing.h b/chromium/base/debug/dump_without_crashing.h new file mode 100644 index 00000000000..b8ed17414ac --- /dev/null +++ b/chromium/base/debug/dump_without_crashing.h @@ -0,0 +1,30 @@ +// 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_DEBUG_DUMP_WITHOUT_CRASHING_H_ +#define BASE_DEBUG_DUMP_WITHOUT_CRASHING_H_ + +#include "base/base_export.h" +#include "base/compiler_specific.h" +#include "build/build_config.h" + +namespace base { + +namespace debug { + +// Handler to silently dump the current process without crashing. +// Before calling this function, call SetDumpWithoutCrashingFunction to pass a +// function pointer, typically chrome!DumpProcessWithoutCrash. See example code +// in chrome_main.cc that does this for chrome.dll. +BASE_EXPORT void DumpWithoutCrashing(); + +// Sets a function that'll be invoked to dump the current process when +// DumpWithoutCrashing() is called. +BASE_EXPORT void SetDumpWithoutCrashingFunction(void (CDECL *function)()); + +} // namespace debug + +} // namespace base + +#endif // BASE_DEBUG_DUMP_WITHOUT_CRASHING_H_ diff --git a/chromium/base/debug/gdi_debug_util_win.cc b/chromium/base/debug/gdi_debug_util_win.cc new file mode 100644 index 00000000000..4bac759d8c7 --- /dev/null +++ b/chromium/base/debug/gdi_debug_util_win.cc @@ -0,0 +1,129 @@ +// 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/gdi_debug_util_win.h" + +#include <cmath> + +#include <psapi.h> +#include <TlHelp32.h> + +#include "base/debug/alias.h" +#include "base/logging.h" +#include "base/win/scoped_handle.h" + +namespace { + +void CollectChildGDIUsageAndDie(DWORD parent_pid) { + HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) ; + if(snapshot == INVALID_HANDLE_VALUE) + CHECK(false); + + int child_count = 0; + base::debug::Alias(&child_count); + int peak_gdi_count = 0; + base::debug::Alias(&peak_gdi_count); + int sum_gdi_count = 0; + base::debug::Alias(&sum_gdi_count); + int sum_user_count = 0; + base::debug::Alias(&sum_user_count); + + PROCESSENTRY32 proc_entry = {0}; + proc_entry.dwSize = sizeof(PROCESSENTRY32) ; + if(!Process32First(snapshot, &proc_entry)) + CHECK(false); + + do { + if (parent_pid != proc_entry.th32ParentProcessID) + continue; + // Got a child process. Compute GDI usage. + base::win::ScopedHandle process( + ::OpenProcess(PROCESS_QUERY_INFORMATION, + FALSE, + proc_entry.th32ParentProcessID)); + if (!process) + continue; + + int num_gdi_handles = ::GetGuiResources(process.Get(), GR_GDIOBJECTS); + int num_user_handles = ::GetGuiResources(process.Get(), GR_USEROBJECTS); + + // Compute sum and peak counts. + ++child_count; + sum_user_count += num_user_handles; + sum_gdi_count += num_gdi_handles; + if (peak_gdi_count < num_gdi_handles) + peak_gdi_count = num_gdi_handles; + + } while(Process32Next(snapshot, &proc_entry)); + + ::CloseHandle(snapshot) ; + CHECK(false); +} + +} // namespace + +namespace base { +namespace debug { + +void GDIBitmapAllocFailure(BITMAPINFOHEADER* header, HANDLE shared_section) { + // Make sure parameters are saved in the minidump. + DWORD last_error = ::GetLastError(); + + LONG width = header->biWidth; + LONG heigth = header->biHeight; + + base::debug::Alias(&last_error); + base::debug::Alias(&width); + base::debug::Alias(&heigth); + base::debug::Alias(&shared_section); + + int num_user_handles = GetGuiResources(GetCurrentProcess(), + GR_USEROBJECTS); + + int num_gdi_handles = GetGuiResources(GetCurrentProcess(), + GR_GDIOBJECTS); + if (num_gdi_handles == 0) { + DWORD get_gui_resources_error = GetLastError(); + base::debug::Alias(&get_gui_resources_error); + CHECK(false); + } + + base::debug::Alias(&num_gdi_handles); + base::debug::Alias(&num_user_handles); + + const DWORD kLotsOfHandles = 9990; + if (num_gdi_handles > kLotsOfHandles) + CHECK(false); + + PROCESS_MEMORY_COUNTERS_EX pmc; + pmc.cb = sizeof(pmc); + if (!GetProcessMemoryInfo(GetCurrentProcess(), + reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&pmc), + sizeof(pmc))) { + CHECK(false); + } + const size_t kLotsOfMemory = 1500 * 1024 * 1024; // 1.5GB + if (pmc.PagefileUsage > kLotsOfMemory) + CHECK(false); + if (pmc.PrivateUsage > kLotsOfMemory) + CHECK(false); + + void* small_data = NULL; + base::debug::Alias(&small_data); + + if (std::abs(heigth) * width > 100) { + // Huh, that's weird. We don't have crazy handle count, we don't have + // ridiculous memory usage. Try to allocate a small bitmap and see if that + // fails too. + header->biWidth = 5; + header->biHeight = -5; + HBITMAP small_bitmap = CreateDIBSection( + NULL, reinterpret_cast<BITMAPINFO*>(&header), + 0, &small_data, shared_section, 0); + } + // Maybe the child processes are the ones leaking GDI or USER resouces. + CollectChildGDIUsageAndDie(::GetCurrentProcessId()); +} + +} // namespace debug +} // namespace base diff --git a/chromium/base/debug/gdi_debug_util_win.h b/chromium/base/debug/gdi_debug_util_win.h new file mode 100644 index 00000000000..5887ecb8464 --- /dev/null +++ b/chromium/base/debug/gdi_debug_util_win.h @@ -0,0 +1,23 @@ +// 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_DEBUG_GDI_DEBUG_UTIL_WIN_H_ +#define BASE_DEBUG_GDI_DEBUG_UTIL_WIN_H_ + +#include <windows.h> + +#include "base/base_export.h" + +namespace base { +namespace debug { + +// Crashes the process leaving valuable information on the dump via +// debug::alias so we can find what is causing the allocation failures. +void BASE_EXPORT GDIBitmapAllocFailure(BITMAPINFOHEADER* header, + HANDLE shared_section); + +} // namespace debug +} // namespace base + +#endif // BASE_DEBUG_GDI_DEBUG_UTIL_WIN_H_ diff --git a/chromium/base/debug/leak_tracker.h b/chromium/base/debug/leak_tracker.h index e5f0cb1778b..8c5aaf31c2f 100644 --- a/chromium/base/debug/leak_tracker.h +++ b/chromium/base/debug/leak_tracker.h @@ -5,8 +5,10 @@ #ifndef BASE_DEBUG_LEAK_TRACKER_H_ #define BASE_DEBUG_LEAK_TRACKER_H_ -// Only enable leak tracking in debug builds. -#ifndef NDEBUG +#include "build/build_config.h" + +// Only enable leak tracking in non-uClibc debug builds. +#if !defined(NDEBUG) && !defined(__UCLIBC__) #define ENABLE_LEAK_TRACKER #endif diff --git a/chromium/base/debug/proc_maps_linux.cc b/chromium/base/debug/proc_maps_linux.cc index b7a5862f747..1e0209e9bf7 100644 --- a/chromium/base/debug/proc_maps_linux.cc +++ b/chromium/base/debug/proc_maps_linux.cc @@ -6,16 +6,18 @@ #include <fcntl.h> -#if defined(OS_LINUX) +#if defined(OS_LINUX) || defined(OS_ANDROID) #include <inttypes.h> #endif #include "base/file_util.h" +#include "base/files/scoped_file.h" #include "base/strings/string_split.h" -#if defined(OS_ANDROID) -// Bionic's inttypes.h defines PRI/SCNxPTR as an unsigned long int, which -// is incompatible with Bionic's stdint.h defining uintptr_t as a unsigned int: +#if defined(OS_ANDROID) && !defined(__LP64__) +// In 32-bit mode, Bionic's inttypes.h defines PRI/SCNxPTR as an +// unsigned long int, which is incompatible with Bionic's stdint.h +// defining uintptr_t as an unsigned int: // https://code.google.com/p/android/issues/detail?id=57218 #undef SCNxPTR #define SCNxPTR "x" @@ -45,12 +47,11 @@ bool ReadProcMaps(std::string* proc_maps) { // file for details. const long kReadSize = sysconf(_SC_PAGESIZE); - int fd = HANDLE_EINTR(open("/proc/self/maps", O_RDONLY)); - if (fd == -1) { + base::ScopedFD fd(HANDLE_EINTR(open("/proc/self/maps", O_RDONLY))); + if (!fd.is_valid()) { DPLOG(ERROR) << "Couldn't open /proc/self/maps"; return false; } - file_util::ScopedFD fd_closer(&fd); proc_maps->clear(); while (true) { @@ -60,7 +61,7 @@ bool ReadProcMaps(std::string* proc_maps) { proc_maps->resize(pos + kReadSize); void* buffer = &(*proc_maps)[pos]; - ssize_t bytes_read = HANDLE_EINTR(read(fd, buffer, kReadSize)); + ssize_t bytes_read = HANDLE_EINTR(read(fd.get(), buffer, kReadSize)); if (bytes_read < 0) { DPLOG(ERROR) << "Couldn't read /proc/self/maps"; proc_maps->clear(); @@ -90,6 +91,7 @@ bool ReadProcMaps(std::string* proc_maps) { bool ParseProcMaps(const std::string& input, std::vector<MappedMemoryRegion>* regions_out) { + CHECK(regions_out); std::vector<MappedMemoryRegion> regions; // This isn't async safe nor terribly efficient, but it doesn't need to be at @@ -100,8 +102,10 @@ bool ParseProcMaps(const std::string& input, for (size_t i = 0; i < lines.size(); ++i) { // Due to splitting on '\n' the last line should be empty. if (i == lines.size() - 1) { - if (!lines[i].empty()) + if (!lines[i].empty()) { + DLOG(WARNING) << "Last line not empty"; return false; + } break; } @@ -124,6 +128,7 @@ bool ParseProcMaps(const std::string& input, if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " %4c %llx %hhx:%hhx %ld %n", ®ion.start, ®ion.end, permissions, ®ion.offset, &dev_major, &dev_minor, &inode, &path_index) < 7) { + DPLOG(WARNING) << "sscanf failed for line: " << line; return false; } diff --git a/chromium/base/debug/proc_maps_linux_unittest.cc b/chromium/base/debug/proc_maps_linux_unittest.cc index 7c2929f212a..fc8ced6aa05 100644 --- a/chromium/base/debug/proc_maps_linux_unittest.cc +++ b/chromium/base/debug/proc_maps_linux_unittest.cc @@ -277,5 +277,37 @@ TEST(ProcMapsTest, InvalidInput) { } } +TEST(ProcMapsTest, ParseProcMapsEmptyString) { + std::vector<MappedMemoryRegion> regions; + EXPECT_TRUE(ParseProcMaps("", ®ions)); + EXPECT_EQ(0ULL, regions.size()); +} + +// Testing a couple of remotely possible weird things in the input: +// - Line ending with \r\n or \n\r. +// - File name contains quotes. +// - File name has whitespaces. +TEST(ProcMapsTest, ParseProcMapsWeirdCorrectInput) { + std::vector<MappedMemoryRegion> regions; + const std::string kContents = + "00400000-0040b000 r-xp 00000000 fc:00 2106562 " + " /bin/cat\r\n" + "7f53b7dad000-7f53b7f62000 r-xp 00000000 fc:00 263011 " + " /lib/x86_64-linux-gnu/libc-2.15.so\n\r" + "7f53b816d000-7f53b818f000 r-xp 00000000 fc:00 264284 " + " /lib/x86_64-linux-gnu/ld-2.15.so\n" + "7fff9c7ff000-7fff9c800000 r-xp 00000000 00:00 0 " + " \"vd so\"\n" + "ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 " + " [vsys call]\n"; + EXPECT_TRUE(ParseProcMaps(kContents, ®ions)); + EXPECT_EQ(5ULL, regions.size()); + EXPECT_EQ("/bin/cat", regions[0].path); + EXPECT_EQ("/lib/x86_64-linux-gnu/libc-2.15.so", regions[1].path); + EXPECT_EQ("/lib/x86_64-linux-gnu/ld-2.15.so", regions[2].path); + EXPECT_EQ("\"vd so\"", regions[3].path); + EXPECT_EQ("[vsys call]", regions[4].path); +} + } // namespace debug } // namespace base diff --git a/chromium/base/debug/sanitizer_options.cc b/chromium/base/debug/sanitizer_options.cc new file mode 100644 index 00000000000..7b26876ea85 --- /dev/null +++ b/chromium/base/debug/sanitizer_options.cc @@ -0,0 +1,117 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// This file contains the default options for various compiler-based dynamic +// tools. + +#include "build/build_config.h" + +// Functions returning default options are declared weak in the tools' runtime +// libraries. To make the linker pick the strong replacements for those +// functions from this module, we explicitly force its inclusion by passing +// -Wl,-u_sanitizer_options_link_helper +extern "C" +void _sanitizer_options_link_helper() { } + +#if defined(ADDRESS_SANITIZER) +// Default options for AddressSanitizer in various configurations: +// strict_memcmp=1 - disable the strict memcmp() checking +// (http://crbug.com/178677 and http://crbug.com/178404). +// malloc_context_size=5 - limit the size of stack traces collected by ASan +// for each malloc/free by 5 frames. These stack traces tend to accumulate +// very fast in applications using JIT (v8 in Chrome's case), see +// https://code.google.com/p/address-sanitizer/issues/detail?id=177 +// symbolize=false - disable the in-process symbolization, which isn't 100% +// compatible with the existing sandboxes and doesn't make much sense for +// stripped official binaries. +// legacy_pthread_cond=1 - run in the libpthread 2.2.5 compatibility mode to +// work around libGL.so using the obsolete API, see +// http://crbug.com/341805. This may break if pthread_cond_t objects are +// accessed by both instrumented and non-instrumented binaries (e.g. if +// they reside in shared memory). This option is going to be deprecated in +// upstream AddressSanitizer and must not be used anywhere except the +// official builds. +// replace_intrin=0 - do not intercept memcpy(), memmove() and memset() to +// work around http://crbug.com/162461 (ASan report in OpenCL on Mac). +// check_printf=1 - check the memory accesses to printf (and other formatted +// output routines) arguments. +// use_sigaltstack=1 - handle signals on an alternate signal stack. Useful +// for stack overflow detection. +// strip_path_prefix=Release/../../ - prefixes up to and including this +// substring will be stripped from source file paths in symbolized reports +// (if symbolize=true, which is set when running with LeakSanitizer). +#if defined(OS_LINUX) +#if defined(GOOGLE_CHROME_BUILD) +// Default AddressSanitizer options for the official build. These do not affect +// tests on buildbots (which don't set GOOGLE_CHROME_BUILD) or non-official +// Chromium builds. +const char kAsanDefaultOptions[] = + "legacy_pthread_cond=1 malloc_context_size=5 strict_memcmp=0 " + "symbolize=false check_printf=1 use_sigaltstack=1 detect_leaks=0 " + "strip_path_prefix=Release/../../ "; +#else +// Default AddressSanitizer options for buildbots and non-official builds. +const char *kAsanDefaultOptions = + "strict_memcmp=0 symbolize=false check_printf=1 use_sigaltstack=1 " + "detect_leaks=0 strip_path_prefix=Release/../../ "; +#endif // GOOGLE_CHROME_BUILD + +#elif defined(OS_MACOSX) +const char *kAsanDefaultOptions = + "strict_memcmp=0 replace_intrin=0 check_printf=1 use_sigaltstack=1 " + "strip_path_prefix=Release/../../ "; +#endif // OS_LINUX + +#if defined(OS_LINUX) || defined(OS_MACOSX) +extern "C" +__attribute__((no_sanitize_address)) +__attribute__((visibility("default"))) +// The function isn't referenced from the executable itself. Make sure it isn't +// stripped by the linker. +__attribute__((used)) +const char *__asan_default_options() { + return kAsanDefaultOptions; +} +#endif // OS_LINUX || OS_MACOSX +#endif // ADDRESS_SANITIZER + +#if defined(THREAD_SANITIZER) && defined(OS_LINUX) +// Default options for ThreadSanitizer in various configurations: +// detect_deadlocks=1 - enable deadlock (lock inversion) detection. +// second_deadlock_stack=1 - more verbose deadlock reports. +// report_signal_unsafe=0 - do not report async-signal-unsafe functions +// called from signal handlers. +// report_thread_leaks=0 - do not report unjoined threads at the end of +// the program execution. +// print_suppressions=1 - print the list of matched suppressions. +// strip_path_prefix=Release/../../ - prefixes up to and including this +// substring will be stripped from source file paths in symbolized reports. +const char kTsanDefaultOptions[] = + "detect_deadlocks=1 second_deadlock_stack=1 report_signal_unsafe=0 " + "report_thread_leaks=0 print_suppressions=1 " + "strip_path_prefix=Release/../../ "; + +extern "C" +__attribute__((no_sanitize_thread)) +__attribute__((visibility("default"))) +// The function isn't referenced from the executable itself. Make sure it isn't +// stripped by the linker. +__attribute__((used)) +const char *__tsan_default_options() { + return kTsanDefaultOptions; +} + +extern "C" char kTSanDefaultSuppressions[]; + +extern "C" +__attribute__((no_sanitize_thread)) +__attribute__((visibility("default"))) +// The function isn't referenced from the executable itself. Make sure it isn't +// stripped by the linker. +__attribute__((used)) +const char *__tsan_default_suppressions() { + return kTSanDefaultSuppressions; +} + +#endif // THREAD_SANITIZER && OS_LINUX diff --git a/chromium/base/debug/stack_trace.cc b/chromium/base/debug/stack_trace.cc index 6fab1835034..ce9e9ad5500 100644 --- a/chromium/base/debug/stack_trace.cc +++ b/chromium/base/debug/stack_trace.cc @@ -33,7 +33,9 @@ const void *const *StackTrace::Addresses(size_t* count) const { std::string StackTrace::ToString() const { std::stringstream stream; +#if !defined(__UCLIBC__) OutputToStream(&stream); +#endif return stream.str(); } diff --git a/chromium/base/debug/stack_trace.h b/chromium/base/debug/stack_trace.h index b0883c1fc92..7c2ac3cb87c 100644 --- a/chromium/base/debug/stack_trace.h +++ b/chromium/base/debug/stack_trace.h @@ -27,6 +27,15 @@ namespace debug { // unit_tests only! This is not thread-safe: only call from main thread. BASE_EXPORT bool EnableInProcessStackDumping(); +// A different version of EnableInProcessStackDumping that also works for +// sandboxed processes. For more details take a look at the description +// of EnableInProcessStackDumping. +// Calling this function on Linux opens /proc/self/maps and caches its +// contents. In DEBUG builds, this function also opens the object files that +// are loaded in memory and caches their file descriptors (this cannot be +// done in official builds because it has security implications). +BASE_EXPORT bool EnableInProcessStackDumpingForSandbox(); + // A stacktrace can be helpful in debugging. For example, you can include a // stacktrace member in a object (probably around #ifndef NDEBUG) so that you // can later see where the given object was created from. @@ -44,7 +53,7 @@ class BASE_EXPORT StackTrace { // Creates a stacktrace for an exception. // Note: this function will throw an import not found (StackWalk64) exception // on system without dbghelp 5.1. - StackTrace(_EXCEPTION_POINTERS* exception_pointers); + StackTrace(const _EXCEPTION_POINTERS* exception_pointers); #endif // Copying and assignment are allowed with the default functions. @@ -55,11 +64,13 @@ class BASE_EXPORT StackTrace { // number of elements in the returned array. const void* const* Addresses(size_t* count) const; +#if !defined(__UCLIBC__) // Prints the stack trace to stderr. void Print() const; // Resolves backtrace to symbols and write to stream. void OutputToStream(std::ostream* os) const; +#endif // Resolves backtrace to symbols and returns as string. std::string ToString() const; diff --git a/chromium/base/debug/stack_trace_android.cc b/chromium/base/debug/stack_trace_android.cc index 257e82309e7..c07f34a9957 100644 --- a/chromium/base/debug/stack_trace_android.cc +++ b/chromium/base/debug/stack_trace_android.cc @@ -11,6 +11,12 @@ #include "base/strings/stringprintf.h" #include "base/threading/thread_restrictions.h" +#ifdef __LP64__ +#define FMT_ADDR "0x%016lx" +#else +#define FMT_ADDR "0x%08x" +#endif + namespace { struct StackCrawlState { @@ -104,12 +110,12 @@ void StackTrace::OutputToStream(std::ostream* os) const { ++iter; } - *os << base::StringPrintf("#%02d 0x%08x ", i, address); + *os << base::StringPrintf("#%02zd " FMT_ADDR " ", i, address); if (iter != regions.end()) { uintptr_t rel_pc = address - iter->start + iter->offset; const char* path = iter->path.c_str(); - *os << base::StringPrintf("%s+0x%08x", path, rel_pc); + *os << base::StringPrintf("%s+" FMT_ADDR, path, rel_pc); } else { *os << "<unknown>"; } diff --git a/chromium/base/debug/stack_trace_ios.mm b/chromium/base/debug/stack_trace_ios.mm deleted file mode 100644 index 998dd0dfc69..00000000000 --- a/chromium/base/debug/stack_trace_ios.mm +++ /dev/null @@ -1,53 +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. - -#import <Foundation/Foundation.h> -#include <mach/task.h> -#include <stdio.h> - -#include "base/logging.h" - -// This is just enough of a shim to let the support needed by test_support -// link. - -namespace base { -namespace debug { - -namespace { - -void StackDumpSignalHandler(int signal) { - // TODO(phajdan.jr): Fix async-signal unsafety. - LOG(ERROR) << "Received signal " << signal; - NSArray *stack_symbols = [NSThread callStackSymbols]; - for (NSString* stack_symbol in stack_symbols) { - fprintf(stderr, "\t%s\n", [stack_symbol UTF8String]); - } - _exit(1); -} - -} // namespace - -// TODO(phajdan.jr): Deduplicate, see copy in stack_trace_posix.cc. -bool EnableInProcessStackDumping() { - // When running in an application, our code typically expects SIGPIPE - // to be ignored. Therefore, when testing that same code, it should run - // with SIGPIPE ignored as well. - struct sigaction action; - action.sa_handler = SIG_IGN; - action.sa_flags = 0; - sigemptyset(&action.sa_mask); - bool success = (sigaction(SIGPIPE, &action, NULL) == 0); - - success &= (signal(SIGILL, &StackDumpSignalHandler) != SIG_ERR); - success &= (signal(SIGABRT, &StackDumpSignalHandler) != SIG_ERR); - success &= (signal(SIGFPE, &StackDumpSignalHandler) != SIG_ERR); - success &= (signal(SIGBUS, &StackDumpSignalHandler) != SIG_ERR); - success &= (signal(SIGSEGV, &StackDumpSignalHandler) != SIG_ERR); - success &= (signal(SIGSYS, &StackDumpSignalHandler) != SIG_ERR); - - return success; -} - -} // namespace debug -} // namespace base diff --git a/chromium/base/debug/stack_trace_posix.cc b/chromium/base/debug/stack_trace_posix.cc index ed1a91889b1..261ce9587d3 100644 --- a/chromium/base/debug/stack_trace_posix.cc +++ b/chromium/base/debug/stack_trace_posix.cc @@ -5,7 +5,6 @@ #include "base/debug/stack_trace.h" #include <errno.h> -#include <execinfo.h> #include <fcntl.h> #include <signal.h> #include <stdio.h> @@ -15,11 +14,17 @@ #include <sys/types.h> #include <unistd.h> +#include <map> #include <ostream> +#include <string> +#include <vector> #if defined(__GLIBCXX__) #include <cxxabi.h> #endif +#if !defined(__UCLIBC__) +#include <execinfo.h> +#endif #if defined(OS_MACOSX) #include <AvailabilityMacros.h> @@ -27,10 +32,14 @@ #include "base/basictypes.h" #include "base/debug/debugger.h" +#include "base/debug/proc_maps_linux.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" +#include "base/memory/singleton.h" +#include "base/numerics/safe_conversions.h" #include "base/posix/eintr_wrapper.h" #include "base/strings/string_number_conversions.h" +#include "build/build_config.h" #if defined(USE_SYMBOLIZE) #include "base/third_party/symbolize/symbolize.h" @@ -64,7 +73,7 @@ void DemangleSymbols(std::string* text) { // Note: code in this function is NOT async-signal safe (std::string uses // malloc internally). -#if defined(__GLIBCXX__) +#if defined(__GLIBCXX__) && !defined(__UCLIBC__) std::string::size_type search_from = 0; while (search_from < text->size()) { @@ -86,7 +95,7 @@ void DemangleSymbols(std::string* text) { // Try to demangle the mangled symbol candidate. int status = 0; - scoped_ptr_malloc<char> demangled_symbol( + scoped_ptr<char, base::FreeDeleter> demangled_symbol( abi::__cxa_demangle(mangled_symbol.c_str(), NULL, 0, &status)); if (status == 0) { // Demangling is successful. // Remove the mangled symbol. @@ -101,7 +110,7 @@ void DemangleSymbols(std::string* text) { } } -#endif // defined(__GLIBCXX__) +#endif // defined(__GLIBCXX__) && !defined(__UCLIBC__) } #endif // !defined(USE_SYMBOLIZE) @@ -114,22 +123,37 @@ class BacktraceOutputHandler { }; void OutputPointer(void* pointer, BacktraceOutputHandler* handler) { - char buf[1024] = { '\0' }; - handler->HandleOutput(" [0x"); + // This should be more than enough to store a 64-bit number in hex: + // 16 hex digits + 1 for null-terminator. + char buf[17] = { '\0' }; + handler->HandleOutput("0x"); internal::itoa_r(reinterpret_cast<intptr_t>(pointer), buf, sizeof(buf), 16, 12); handler->HandleOutput(buf); - handler->HandleOutput("]"); } +#if defined(USE_SYMBOLIZE) +void OutputFrameId(intptr_t frame_id, BacktraceOutputHandler* handler) { + // Max unsigned 64-bit number in decimal has 20 digits (18446744073709551615). + // Hence, 30 digits should be more than enough to represent it in decimal + // (including the null-terminator). + char buf[30] = { '\0' }; + handler->HandleOutput("#"); + internal::itoa_r(frame_id, buf, sizeof(buf), 10, 1); + handler->HandleOutput(buf); +} +#endif // defined(USE_SYMBOLIZE) + void ProcessBacktrace(void *const *trace, - int size, + size_t size, BacktraceOutputHandler* handler) { // NOTE: This code MUST be async-signal safe (it's used by in-process // stack dumping signal handler). NO malloc or stdio is allowed here. #if defined(USE_SYMBOLIZE) - for (int i = 0; i < size; ++i) { + for (size_t i = 0; i < size; ++i) { + OutputFrameId(i, handler); + handler->HandleOutput(" "); OutputPointer(trace[i], handler); handler->HandleOutput(" "); @@ -145,15 +169,16 @@ void ProcessBacktrace(void *const *trace, handler->HandleOutput("\n"); } -#else +#elif !defined(__UCLIBC__) bool printed = false; // Below part is async-signal unsafe (uses malloc), so execute it only // when we are not executing the signal handler. if (in_signal_handler == 0) { - scoped_ptr_malloc<char*> trace_symbols(backtrace_symbols(trace, size)); + scoped_ptr<char*, FreeDeleter> + trace_symbols(backtrace_symbols(trace, size)); if (trace_symbols.get()) { - for (int i = 0; i < size; ++i) { + for (size_t i = 0; i < size; ++i) { std::string trace_symbol = trace_symbols.get()[i]; DemangleSymbols(&trace_symbol); handler->HandleOutput(trace_symbol.c_str()); @@ -165,9 +190,10 @@ void ProcessBacktrace(void *const *trace, } if (!printed) { - for (int i = 0; i < size; ++i) { + for (size_t i = 0; i < size; ++i) { + handler->HandleOutput(" ["); OutputPointer(trace[i], handler); - handler->HandleOutput("\n"); + handler->HandleOutput("]\n"); } } #endif // defined(USE_SYMBOLIZE) @@ -179,7 +205,6 @@ void PrintToStderr(const char* output) { ignore_result(HANDLE_EINTR(write(STDERR_FILENO, output, strlen(output)))); } -#if !defined(OS_IOS) void StackDumpSignalHandler(int signal, siginfo_t* info, void* void_context) { // NOTE: This code MUST be async-signal safe. // NO malloc or stdio is allowed here. @@ -256,7 +281,9 @@ void StackDumpSignalHandler(int signal, siginfo_t* info, void* void_context) { } PrintToStderr("\n"); +#if !defined(__UCLIBC__) debug::StackTrace().Print(); +#endif #if defined(OS_LINUX) #if ARCH_CPU_X86_FAMILY @@ -330,7 +357,7 @@ void StackDumpSignalHandler(int signal, siginfo_t* info, void* void_context) { PrintToStderr("\n"); #endif #elif defined(OS_MACOSX) - // TODO(shess): Port to 64-bit. + // TODO(shess): Port to 64-bit, and ARM architecture (32 and 64-bit). #if ARCH_CPU_X86_FAMILY && ARCH_CPU_32_BITS ucontext_t* context = reinterpret_cast<ucontext_t*>(void_context); size_t len; @@ -372,7 +399,6 @@ void StackDumpSignalHandler(int signal, siginfo_t* info, void* void_context) { #endif // defined(OS_MACOSX) _exit(1); } -#endif // !defined(OS_IOS) class PrintBacktraceOutputHandler : public BacktraceOutputHandler { public: @@ -403,7 +429,6 @@ class StreamBacktraceOutputHandler : public BacktraceOutputHandler { DISALLOW_COPY_AND_ASSIGN(StreamBacktraceOutputHandler); }; -#if !defined(OS_IOS) void WarmUpBacktrace() { // Warm up stack trace infrastructure. It turns out that on the first // call glibc initializes some internal data structures using pthread_once, @@ -436,11 +461,251 @@ void WarmUpBacktrace() { // #22 <signal handler called> StackTrace stack_trace; } -#endif // !defined(OS_IOS) } // namespace -#if !defined(OS_IOS) +#if defined(USE_SYMBOLIZE) + +// class SandboxSymbolizeHelper. +// +// The purpose of this class is to prepare and install a "file open" callback +// needed by the stack trace symbolization code +// (base/third_party/symbolize/symbolize.h) so that it can function properly +// in a sandboxed process. The caveat is that this class must be instantiated +// before the sandboxing is enabled so that it can get the chance to open all +// the object files that are loaded in the virtual address space of the current +// process. +class SandboxSymbolizeHelper { + public: + // Returns the singleton instance. + static SandboxSymbolizeHelper* GetInstance() { + return Singleton<SandboxSymbolizeHelper>::get(); + } + + private: + friend struct DefaultSingletonTraits<SandboxSymbolizeHelper>; + + SandboxSymbolizeHelper() + : is_initialized_(false) { + Init(); + } + + ~SandboxSymbolizeHelper() { + UnregisterCallback(); + CloseObjectFiles(); + } + + // Returns a O_RDONLY file descriptor for |file_path| if it was opened + // sucessfully during the initialization. The file is repositioned at + // offset 0. + // IMPORTANT: This function must be async-signal-safe because it can be + // called from a signal handler (symbolizing stack frames for a crash). + int GetFileDescriptor(const char* file_path) { + int fd = -1; + +#if !defined(NDEBUG) + if (file_path) { + // The assumption here is that iterating over std::map<std::string, int> + // using a const_iterator does not allocate dynamic memory, hense it is + // async-signal-safe. + std::map<std::string, int>::const_iterator it; + for (it = modules_.begin(); it != modules_.end(); ++it) { + if (strcmp((it->first).c_str(), file_path) == 0) { + // POSIX.1-2004 requires an implementation to guarantee that dup() + // is async-signal-safe. + fd = dup(it->second); + break; + } + } + // POSIX.1-2004 requires an implementation to guarantee that lseek() + // is async-signal-safe. + if (fd >= 0 && lseek(fd, 0, SEEK_SET) < 0) { + // Failed to seek. + fd = -1; + } + } +#endif // !defined(NDEBUG) + + return fd; + } + + // Searches for the object file (from /proc/self/maps) that contains + // the specified pc. If found, sets |start_address| to the start address + // of where this object file is mapped in memory, sets the module base + // address into |base_address|, copies the object file name into + // |out_file_name|, and attempts to open the object file. If the object + // file is opened successfully, returns the file descriptor. Otherwise, + // returns -1. |out_file_name_size| is the size of the file name buffer + // (including the null terminator). + // IMPORTANT: This function must be async-signal-safe because it can be + // called from a signal handler (symbolizing stack frames for a crash). + static int OpenObjectFileContainingPc(uint64_t pc, uint64_t& start_address, + uint64_t& base_address, char* file_path, + int file_path_size) { + // This method can only be called after the singleton is instantiated. + // This is ensured by the following facts: + // * This is the only static method in this class, it is private, and + // the class has no friends (except for the DefaultSingletonTraits). + // The compiler guarantees that it can only be called after the + // singleton is instantiated. + // * This method is used as a callback for the stack tracing code and + // the callback registration is done in the constructor, so logically + // it cannot be called before the singleton is created. + SandboxSymbolizeHelper* instance = GetInstance(); + + // The assumption here is that iterating over + // std::vector<MappedMemoryRegion> using a const_iterator does not allocate + // dynamic memory, hence it is async-signal-safe. + std::vector<MappedMemoryRegion>::const_iterator it; + bool is_first = true; + for (it = instance->regions_.begin(); it != instance->regions_.end(); + ++it, is_first = false) { + const MappedMemoryRegion& region = *it; + if (region.start <= pc && pc < region.end) { + start_address = region.start; + // Don't subtract 'start_address' from the first entry: + // * If a binary is compiled w/o -pie, then the first entry in + // process maps is likely the binary itself (all dynamic libs + // are mapped higher in address space). For such a binary, + // instruction offset in binary coincides with the actual + // instruction address in virtual memory (as code section + // is mapped to a fixed memory range). + // * If a binary is compiled with -pie, all the modules are + // mapped high at address space (in particular, higher than + // shadow memory of the tool), so the module can't be the + // first entry. + base_address = (is_first ? 0U : start_address) - region.offset; + if (file_path && file_path_size > 0) { + strncpy(file_path, region.path.c_str(), file_path_size); + // Ensure null termination. + file_path[file_path_size - 1] = '\0'; + } + return instance->GetFileDescriptor(region.path.c_str()); + } + } + return -1; + } + + // Parses /proc/self/maps in order to compile a list of all object file names + // for the modules that are loaded in the current process. + // Returns true on success. + bool CacheMemoryRegions() { + // Reads /proc/self/maps. + std::string contents; + if (!ReadProcMaps(&contents)) { + LOG(ERROR) << "Failed to read /proc/self/maps"; + return false; + } + + // Parses /proc/self/maps. + if (!ParseProcMaps(contents, ®ions_)) { + LOG(ERROR) << "Failed to parse the contents of /proc/self/maps"; + return false; + } + + is_initialized_ = true; + return true; + } + + // Opens all object files and caches their file descriptors. + void OpenSymbolFiles() { + // Pre-opening and caching the file descriptors of all loaded modules is + // not considered safe for retail builds. Hence it is only done in debug + // builds. For more details, take a look at: http://crbug.com/341966 + // Enabling this to release mode would require approval from the security + // team. +#if !defined(NDEBUG) + // Open the object files for all read-only executable regions and cache + // their file descriptors. + std::vector<MappedMemoryRegion>::const_iterator it; + for (it = regions_.begin(); it != regions_.end(); ++it) { + const MappedMemoryRegion& region = *it; + // Only interesed in read-only executable regions. + if ((region.permissions & MappedMemoryRegion::READ) == + MappedMemoryRegion::READ && + (region.permissions & MappedMemoryRegion::WRITE) == 0 && + (region.permissions & MappedMemoryRegion::EXECUTE) == + MappedMemoryRegion::EXECUTE) { + if (region.path.empty()) { + // Skip regions with empty file names. + continue; + } + if (region.path[0] == '[') { + // Skip pseudo-paths, like [stack], [vdso], [heap], etc ... + continue; + } + // Avoid duplicates. + if (modules_.find(region.path) == modules_.end()) { + int fd = open(region.path.c_str(), O_RDONLY | O_CLOEXEC); + if (fd >= 0) { + modules_.insert(std::make_pair(region.path, fd)); + } else { + LOG(WARNING) << "Failed to open file: " << region.path + << "\n Error: " << strerror(errno); + } + } + } + } +#endif // !defined(NDEBUG) + } + + // Initializes and installs the symbolization callback. + void Init() { + if (CacheMemoryRegions()) { + OpenSymbolFiles(); + google::InstallSymbolizeOpenObjectFileCallback( + &OpenObjectFileContainingPc); + } + } + + // Unregister symbolization callback. + void UnregisterCallback() { + if (is_initialized_) { + google::InstallSymbolizeOpenObjectFileCallback(NULL); + is_initialized_ = false; + } + } + + // Closes all file descriptors owned by this instance. + void CloseObjectFiles() { +#if !defined(NDEBUG) + std::map<std::string, int>::iterator it; + for (it = modules_.begin(); it != modules_.end(); ++it) { + int ret = IGNORE_EINTR(close(it->second)); + DCHECK(!ret); + it->second = -1; + } + modules_.clear(); +#endif // !defined(NDEBUG) + } + + // Set to true upon successful initialization. + bool is_initialized_; + +#if !defined(NDEBUG) + // Mapping from file name to file descriptor. Includes file descriptors + // for all successfully opened object files and the file descriptor for + // /proc/self/maps. This code is not safe for release builds so + // this is only done for DEBUG builds. + std::map<std::string, int> modules_; +#endif // !defined(NDEBUG) + + // Cache for the process memory regions. Produced by parsing the contents + // of /proc/self/maps cache. + std::vector<MappedMemoryRegion> regions_; + + DISALLOW_COPY_AND_ASSIGN(SandboxSymbolizeHelper); +}; +#endif // USE_SYMBOLIZE + +bool EnableInProcessStackDumpingForSandbox() { +#if defined(USE_SYMBOLIZE) + SandboxSymbolizeHelper::GetInstance(); +#endif // USE_SYMBOLIZE + + return EnableInProcessStackDumping(); +} + bool EnableInProcessStackDumping() { // When running in an application, our code typically expects SIGPIPE // to be ignored. Therefore, when testing that same code, it should run @@ -465,21 +730,28 @@ bool EnableInProcessStackDumping() { success &= (sigaction(SIGFPE, &action, NULL) == 0); success &= (sigaction(SIGBUS, &action, NULL) == 0); success &= (sigaction(SIGSEGV, &action, NULL) == 0); +// On Linux, SIGSYS is reserved by the kernel for seccomp-bpf sandboxing. +#if !defined(OS_LINUX) success &= (sigaction(SIGSYS, &action, NULL) == 0); +#endif // !defined(OS_LINUX) return success; } -#endif // !defined(OS_IOS) StackTrace::StackTrace() { // NOTE: This code MUST be async-signal safe (it's used by in-process // stack dumping signal handler). NO malloc or stdio is allowed here. +#if !defined(__UCLIBC__) // Though the backtrace API man page does not list any possible negative // return values, we take no chance. - count_ = std::max(backtrace(trace_, arraysize(trace_)), 0); + count_ = base::saturated_cast<size_t>(backtrace(trace_, arraysize(trace_))); +#else + count_ = 0; +#endif } +#if !defined(__UCLIBC__) void StackTrace::Print() const { // NOTE: This code MUST be async-signal safe (it's used by in-process // stack dumping signal handler). NO malloc or stdio is allowed here. @@ -492,6 +764,7 @@ void StackTrace::OutputToStream(std::ostream* os) const { StreamBacktraceOutputHandler handler(os); ProcessBacktrace(trace_, count_, &handler); } +#endif namespace internal { diff --git a/chromium/base/debug/stack_trace_unittest.cc b/chromium/base/debug/stack_trace_unittest.cc index 701ebc47a1f..eb0bd9ad7f3 100644 --- a/chromium/base/debug/stack_trace_unittest.cc +++ b/chromium/base/debug/stack_trace_unittest.cc @@ -35,6 +35,7 @@ typedef testing::Test StackTraceTest; #else #define MAYBE_OutputToStream OutputToStream #endif +#if !defined(__UCLIBC__) TEST_F(StackTraceTest, MAYBE_OutputToStream) { StackTrace trace; @@ -130,6 +131,7 @@ TEST_F(StackTraceTest, DebugOutputToStream) { TEST_F(StackTraceTest, DebugPrintBacktrace) { StackTrace().Print(); } +#endif // !defined(__UCLIBC__) #if defined(OS_POSIX) && !defined(OS_ANDROID) #if !defined(OS_IOS) @@ -144,7 +146,7 @@ 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 = this->SpawnChild("MismatchedMallocChildProcess", false); + ProcessHandle child = SpawnChild("MismatchedMallocChildProcess"); ASSERT_NE(kNullProcessHandle, child); ASSERT_TRUE(WaitForSingleProcess(child, TestTimeouts::action_timeout())); } diff --git a/chromium/base/debug/stack_trace_win.cc b/chromium/base/debug/stack_trace_win.cc index eb35b6ab91b..a3327afcea8 100644 --- a/chromium/base/debug/stack_trace_win.cc +++ b/chromium/base/debug/stack_trace_win.cc @@ -211,22 +211,25 @@ StackTrace::StackTrace() { #pragma optimize("", on) #endif -StackTrace::StackTrace(EXCEPTION_POINTERS* exception_pointers) { +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; // 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 = exception_pointers->ContextRecord->Rip; - stack_frame.AddrFrame.Offset = exception_pointers->ContextRecord->Rbp; - stack_frame.AddrStack.Offset = exception_pointers->ContextRecord->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 = exception_pointers->ContextRecord->Eip; - stack_frame.AddrFrame.Offset = exception_pointers->ContextRecord->Ebp; - stack_frame.AddrStack.Offset = exception_pointers->ContextRecord->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; @@ -235,7 +238,7 @@ StackTrace::StackTrace(EXCEPTION_POINTERS* exception_pointers) { GetCurrentProcess(), GetCurrentThread(), &stack_frame, - exception_pointers->ContextRecord, + &context_record, NULL, &SymFunctionTableAccess64, &SymGetModuleBase64, diff --git a/chromium/base/debug/trace_event.h b/chromium/base/debug/trace_event.h index 18feb33f3d0..686bd38790c 100644 --- a/chromium/base/debug/trace_event.h +++ b/chromium/base/debug/trace_event.h @@ -195,6 +195,7 @@ #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 "build/build_config.h" // By default, const char* argument values are assumed to have long-lived scope @@ -725,12 +726,16 @@ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_DELETE_OBJECT, \ 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)) // Macro to efficiently determine if a given category group is enabled. #define TRACE_EVENT_CATEGORY_GROUP_ENABLED(category_group, ret) \ do { \ INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \ - if (*INTERNAL_TRACE_EVENT_UID(category_group_enabled)) { \ + if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \ *ret = true; \ } else { \ *ret = false; \ @@ -850,7 +855,7 @@ TRACE_EVENT_API_CLASS_EXPORT extern \ category_group_enabled = \ reinterpret_cast<const unsigned char*>(TRACE_EVENT_API_ATOMIC_LOAD( \ atomic)); \ - if (!category_group_enabled) { \ + if (UNLIKELY(!category_group_enabled)) { \ category_group_enabled = \ TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(category_group); \ TRACE_EVENT_API_ATOMIC_STORE(atomic, \ @@ -870,7 +875,7 @@ TRACE_EVENT_API_CLASS_EXPORT extern \ #define INTERNAL_TRACE_EVENT_ADD(phase, category_group, name, flags, ...) \ do { \ INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \ - if (*INTERNAL_TRACE_EVENT_UID(category_group_enabled)) { \ + if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \ trace_event_internal::AddTraceEvent( \ phase, INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \ trace_event_internal::kNoEventId, flags, ##__VA_ARGS__); \ @@ -883,7 +888,7 @@ TRACE_EVENT_API_CLASS_EXPORT extern \ #define INTERNAL_TRACE_EVENT_ADD_SCOPED(category_group, name, ...) \ INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \ trace_event_internal::ScopedTracer INTERNAL_TRACE_EVENT_UID(tracer); \ - if (*INTERNAL_TRACE_EVENT_UID(category_group_enabled)) { \ + 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), \ @@ -899,7 +904,7 @@ TRACE_EVENT_API_CLASS_EXPORT extern \ flags, ...) \ do { \ INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \ - if (*INTERNAL_TRACE_EVENT_UID(category_group_enabled)) { \ + if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \ unsigned char trace_event_flags = flags | TRACE_EVENT_FLAG_HAS_ID; \ trace_event_internal::TraceID trace_event_trace_id( \ id, &trace_event_flags); \ @@ -916,7 +921,7 @@ TRACE_EVENT_API_CLASS_EXPORT extern \ category_group, name, id, thread_id, timestamp, flags, ...) \ do { \ INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \ - if (*INTERNAL_TRACE_EVENT_UID(category_group_enabled)) { \ + if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \ unsigned char trace_event_flags = flags | TRACE_EVENT_FLAG_HAS_ID; \ trace_event_internal::TraceID trace_event_trace_id( \ id, &trace_event_flags); \ @@ -1092,7 +1097,7 @@ union TraceValueUnion { class TraceStringWithCopy { public: explicit TraceStringWithCopy(const char* str) : str_(str) {} - operator const char* () const { return str_; } + const char* str() const { return str_; } private: const char* str_; }; @@ -1101,6 +1106,7 @@ class TraceStringWithCopy { // value in the return arguments. This allows this API to avoid declaring any // structures so that it is portable to third_party libraries. #define INTERNAL_DECLARE_SET_TRACE_VALUE(actual_type, \ + arg_expression, \ union_member, \ value_type_id) \ static inline void SetTraceValue( \ @@ -1108,7 +1114,7 @@ class TraceStringWithCopy { unsigned char* type, \ unsigned long long* value) { \ TraceValueUnion type_value; \ - type_value.union_member = arg; \ + type_value.union_member = arg_expression; \ *type = value_type_id; \ *value = type_value.as_uint; \ } @@ -1133,14 +1139,15 @@ INTERNAL_DECLARE_SET_TRACE_VALUE_INT(long, TRACE_VALUE_TYPE_INT) INTERNAL_DECLARE_SET_TRACE_VALUE_INT(int, TRACE_VALUE_TYPE_INT) INTERNAL_DECLARE_SET_TRACE_VALUE_INT(short, TRACE_VALUE_TYPE_INT) INTERNAL_DECLARE_SET_TRACE_VALUE_INT(signed char, TRACE_VALUE_TYPE_INT) -INTERNAL_DECLARE_SET_TRACE_VALUE(bool, as_bool, TRACE_VALUE_TYPE_BOOL) -INTERNAL_DECLARE_SET_TRACE_VALUE(double, as_double, TRACE_VALUE_TYPE_DOUBLE) -INTERNAL_DECLARE_SET_TRACE_VALUE(const void*, as_pointer, +INTERNAL_DECLARE_SET_TRACE_VALUE(bool, arg, as_bool, TRACE_VALUE_TYPE_BOOL) +INTERNAL_DECLARE_SET_TRACE_VALUE(double, arg, as_double, + TRACE_VALUE_TYPE_DOUBLE) +INTERNAL_DECLARE_SET_TRACE_VALUE(const void*, arg, as_pointer, TRACE_VALUE_TYPE_POINTER) -INTERNAL_DECLARE_SET_TRACE_VALUE(const char*, as_string, +INTERNAL_DECLARE_SET_TRACE_VALUE(const char*, arg, as_string, TRACE_VALUE_TYPE_STRING) -INTERNAL_DECLARE_SET_TRACE_VALUE(const TraceStringWithCopy&, as_string, - TRACE_VALUE_TYPE_COPY_STRING) +INTERNAL_DECLARE_SET_TRACE_VALUE(const TraceStringWithCopy&, arg.str(), + as_string, TRACE_VALUE_TYPE_COPY_STRING) #undef INTERNAL_DECLARE_SET_TRACE_VALUE #undef INTERNAL_DECLARE_SET_TRACE_VALUE_INT @@ -1155,6 +1162,22 @@ static inline void SetTraceValue(const std::string& arg, *value = type_value.as_uint; } +// base::Time and base::TimeTicks version of SetTraceValue to make it easier to +// trace these types. +static inline void SetTraceValue(const base::Time arg, + unsigned char* type, + unsigned long long* value) { + *type = TRACE_VALUE_TYPE_INT; + *value = arg.ToInternalValue(); +} + +static inline void SetTraceValue(const base::TimeTicks arg, + unsigned char* type, + unsigned long long* value) { + *type = TRACE_VALUE_TYPE_INT; + *value = arg.ToInternalValue(); +} + // These AddTraceEvent and AddTraceEventWithThreadIdAndTimestamp template // functions are defined here instead of in the macro, because the arg_values // could be temporary objects, such as std::string. In order to store diff --git a/chromium/base/debug/trace_event_android.cc b/chromium/base/debug/trace_event_android.cc index 567c48eb293..ed6b20d0bf7 100644 --- a/chromium/base/debug/trace_event_android.cc +++ b/chromium/base/debug/trace_event_android.cc @@ -97,6 +97,7 @@ void TraceLog::StartATrace() { return; } SetEnabled(CategoryFilter(CategoryFilter::kDefaultCategoryFilterString), + base::debug::TraceLog::RECORDING_MODE, RECORD_CONTINUOUSLY); } diff --git a/chromium/base/debug/trace_event_impl.cc b/chromium/base/debug/trace_event_impl.cc index e774f621e04..8bcbe0e45ef 100644 --- a/chromium/base/debug/trace_event_impl.cc +++ b/chromium/base/debug/trace_event_impl.cc @@ -11,6 +11,8 @@ #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" @@ -69,8 +71,12 @@ const size_t kEchoToConsoleTraceEventBufferChunks = 256; const int kThreadFlushTimeoutMs = 3000; +#if !defined(OS_NACL) // These categories will cause deadlock when ECHO_TO_CONSOLE. crbug.com/325575. const char kEchoToConsoleCategoryFilter[] = "-ipc,-task"; +#endif + +const char kSyntheticDelayCategoryFilterPrefix[] = "DELAY("; #define MAX_CATEGORY_GROUPS 100 @@ -81,6 +87,7 @@ const char kEchoToConsoleCategoryFilter[] = "-ipc,-task"; // convert internally to determine the category name from the char enabled // pointer. const char* g_category_groups[MAX_CATEGORY_GROUPS] = { + "toplevel", "tracing already shutdown", "tracing categories exhausted; must increase MAX_CATEGORY_GROUPS", "__metadata", @@ -89,12 +96,14 @@ const char* g_category_groups[MAX_CATEGORY_GROUPS] = { // The enabled flag is char instead of bool so that the API can be used from C. unsigned char g_category_group_enabled[MAX_CATEGORY_GROUPS] = { 0 }; -const int g_category_already_shutdown = 0; -const int g_category_categories_exhausted = 1; -const int g_category_metadata = 2; -const int g_category_trace_event_overhead = 3; -const int g_num_builtin_categories = 4; -int g_category_index = g_num_builtin_categories; // Skip default categories. +// Indexes here have to match the g_category_groups array indexes above. +const int g_category_already_shutdown = 1; +const int g_category_categories_exhausted = 2; +const int g_category_metadata = 3; +const int g_category_trace_event_overhead = 4; +const int g_num_builtin_categories = 5; +// Skip default categories. +base::subtle::AtomicWord g_category_index = g_num_builtin_categories; // The name of the current thread. This is used to decide if the current // thread name has changed. We combine all the seen thread names into the @@ -632,24 +641,35 @@ void TraceEvent::AppendValueAsJSON(unsigned char type, case TRACE_VALUE_TYPE_DOUBLE: { // FIXME: base/json/json_writer.cc is using the same code, // should be made into a common method. - std::string real = DoubleToString(value.as_double); - // 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 - // real rather than an int. - if (real.find('.') == std::string::npos && - real.find('e') == std::string::npos && - real.find('E') == std::string::npos) { - real.append(".0"); - } - // The JSON spec requires that non-integer values in the range (-1,1) - // have a zero before the decimal point - ".52" is not valid, "0.52" is. - if (real[0] == '.') { - real.insert(0, "0"); - } else if (real.length() > 1 && real[0] == '-' && real[1] == '.') { - // "-.1" bad "-0.1" good - real.insert(1, "0"); + std::string real; + double val = value.as_double; + if (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 + // real rather than an int. + if (real.find('.') == std::string::npos && + real.find('e') == std::string::npos && + real.find('E') == std::string::npos) { + real.append(".0"); + } + // The JSON spec requires that non-integer values in the range (-1,1) + // have a zero before the decimal point - ".52" is not valid, "0.52" is. + if (real[0] == '.') { + real.insert(0, "0"); + } else if (real.length() > 1 && real[0] == '-' && real[1] == '.') { + // "-.1" bad "-0.1" good + real.insert(1, "0"); + } + } else if (IsNaN(val)){ + // The JSON spec doesn't allow NaN and Infinity (since these are + // objects in EcmaScript). Use strings instead. + real = "\"NaN\""; + } else if (val < 0) { + real = "\"-Infinity\""; + } else { + real = "\"Infinity\""; } - StringAppendF(out, "%s", real.c_str()); break; } @@ -1094,7 +1114,7 @@ void TraceLog::ThreadLocalEventBuffer::FlushWhileLocked() { trace_log_->lock_.AssertAcquired(); if (trace_log_->CheckGeneration(generation_)) { - // Return the chunk to the buffer only if the generation matches, + // Return the chunk to the buffer only if the generation matches. trace_log_->logged_events_->ReturnChunk(chunk_index_, chunk_.Pass()); } // Otherwise this method may be called from the destructor, or TraceLog will @@ -1107,7 +1127,7 @@ TraceLog* TraceLog::GetInstance() { } TraceLog::TraceLog() - : enabled_(false), + : mode_(DISABLED), num_traces_recorded_(0), event_callback_(0), dispatching_to_observer_list_(false), @@ -1153,7 +1173,7 @@ TraceLog::TraceLog() LOG(ERROR) << "Start " << switches::kTraceToConsole << " with CategoryFilter '" << filter << "'."; - SetEnabled(CategoryFilter(filter), ECHO_TO_CONSOLE); + SetEnabled(CategoryFilter(filter), RECORDING_MODE, ECHO_TO_CONSOLE); } #endif @@ -1192,8 +1212,12 @@ const char* TraceLog::GetCategoryGroupName( void TraceLog::UpdateCategoryGroupEnabledFlag(int category_index) { unsigned char enabled_flag = 0; const char* category_group = g_category_groups[category_index]; - if (enabled_ && category_filter_.IsCategoryGroupEnabled(category_group)) + if (mode_ == RECORDING_MODE && + category_filter_.IsCategoryGroupEnabled(category_group)) enabled_flag |= ENABLED_FOR_RECORDING; + else if (mode_ == MONITORING_MODE && + category_filter_.IsCategoryGroupEnabled(category_group)) + enabled_flag |= ENABLED_FOR_MONITORING; if (event_callback_ && event_callback_category_filter_.IsCategoryGroupEnabled(category_group)) enabled_flag |= ENABLED_FOR_EVENT_CALLBACK; @@ -1201,48 +1225,89 @@ void TraceLog::UpdateCategoryGroupEnabledFlag(int category_index) { } void TraceLog::UpdateCategoryGroupEnabledFlags() { - for (int i = 0; i < g_category_index; i++) + int category_index = base::subtle::NoBarrier_Load(&g_category_index); + for (int i = 0; i < category_index; i++) UpdateCategoryGroupEnabledFlag(i); } +void TraceLog::UpdateSyntheticDelaysFromCategoryFilter() { + ResetTraceEventSyntheticDelays(); + const CategoryFilter::StringList& delays = + category_filter_.GetSyntheticDelayValues(); + CategoryFilter::StringList::const_iterator ci; + for (ci = delays.begin(); ci != delays.end(); ++ci) { + StringTokenizer tokens(*ci, ";"); + if (!tokens.GetNext()) + continue; + TraceEventSyntheticDelay* delay = + TraceEventSyntheticDelay::Lookup(tokens.token()); + while (tokens.GetNext()) { + std::string token = tokens.token(); + char* duration_end; + double target_duration = strtod(token.c_str(), &duration_end); + if (duration_end != token.c_str()) { + delay->SetTargetDuration( + TimeDelta::FromMicroseconds(target_duration * 1e6)); + } else if (token == "static") { + delay->SetMode(TraceEventSyntheticDelay::STATIC); + } else if (token == "oneshot") { + delay->SetMode(TraceEventSyntheticDelay::ONE_SHOT); + } else if (token == "alternating") { + delay->SetMode(TraceEventSyntheticDelay::ALTERNATING); + } + } + } +} + const unsigned char* TraceLog::GetCategoryGroupEnabledInternal( const char* category_group) { DCHECK(!strchr(category_group, '"')) << "Category groups may not contain double quote"; - AutoLock lock(lock_); + // The g_category_groups is append only, avoid using a lock for the fast path. + int current_category_index = base::subtle::Acquire_Load(&g_category_index); - unsigned char* category_group_enabled = NULL; // Search for pre-existing category group. - for (int i = 0; i < g_category_index; i++) { + for (int i = 0; i < current_category_index; ++i) { if (strcmp(g_category_groups[i], category_group) == 0) { - category_group_enabled = &g_category_group_enabled[i]; - break; + return &g_category_group_enabled[i]; } } - if (!category_group_enabled) { - // Create a new category group - DCHECK(g_category_index < MAX_CATEGORY_GROUPS) << - "must increase MAX_CATEGORY_GROUPS"; - if (g_category_index < MAX_CATEGORY_GROUPS) { - int new_index = g_category_index++; - // Don't hold on to the category_group pointer, so that we can create - // category groups with strings not known at compile time (this is - // required by SetWatchEvent). - const char* new_group = strdup(category_group); - ANNOTATE_LEAKING_OBJECT_PTR(new_group); - g_category_groups[new_index] = new_group; - DCHECK(!g_category_group_enabled[new_index]); - // Note that if both included and excluded patterns in the - // CategoryFilter are empty, we exclude nothing, - // thereby enabling this category group. - UpdateCategoryGroupEnabledFlag(new_index); - category_group_enabled = &g_category_group_enabled[new_index]; - } else { - category_group_enabled = - &g_category_group_enabled[g_category_categories_exhausted]; + unsigned char* category_group_enabled = NULL; + // This is the slow path: the lock is not held in the case above, so more + // than one thread could have reached here trying to add the same category. + // Only hold to lock when actually appending a new category, and + // check the categories groups again. + AutoLock lock(lock_); + int category_index = base::subtle::Acquire_Load(&g_category_index); + for (int i = 0; i < category_index; ++i) { + if (strcmp(g_category_groups[i], category_group) == 0) { + return &g_category_group_enabled[i]; } } + + // Create a new category group. + DCHECK(category_index < MAX_CATEGORY_GROUPS) << + "must increase MAX_CATEGORY_GROUPS"; + if (category_index < MAX_CATEGORY_GROUPS) { + // Don't hold on to the category_group pointer, so that we can create + // category groups with strings not known at compile time (this is + // required by SetWatchEvent). + const char* new_group = strdup(category_group); + ANNOTATE_LEAKING_OBJECT_PTR(new_group); + g_category_groups[category_index] = new_group; + DCHECK(!g_category_group_enabled[category_index]); + // Note that if both included and excluded patterns in the + // CategoryFilter are empty, we exclude nothing, + // thereby enabling this category group. + UpdateCategoryGroupEnabledFlag(category_index); + category_group_enabled = &g_category_group_enabled[category_index]; + // Update the max index now. + base::subtle::Release_Store(&g_category_index, category_index + 1); + } else { + category_group_enabled = + &g_category_group_enabled[g_category_categories_exhausted]; + } return category_group_enabled; } @@ -1251,11 +1316,13 @@ void TraceLog::GetKnownCategoryGroups( AutoLock lock(lock_); category_groups->push_back( g_category_groups[g_category_trace_event_overhead]); - for (int i = g_num_builtin_categories; i < g_category_index; i++) + int category_index = base::subtle::NoBarrier_Load(&g_category_index); + for (int i = g_num_builtin_categories; i < category_index; i++) category_groups->push_back(g_category_groups[i]); } void TraceLog::SetEnabled(const CategoryFilter& category_filter, + Mode mode, Options options) { std::vector<EnabledStateObserver*> observer_list; { @@ -1266,12 +1333,16 @@ void TraceLog::SetEnabled(const CategoryFilter& category_filter, Options old_options = trace_options(); - if (enabled_) { + if (IsEnabled()) { if (options != old_options) { - DLOG(ERROR) << "Attemting to re-enable tracing with a different " + DLOG(ERROR) << "Attempting to re-enable tracing with a different " << "set of options."; } + if (mode != mode_) { + DLOG(ERROR) << "Attempting to re-enable tracing with a different mode."; + } + category_filter_.Merge(category_filter); UpdateCategoryGroupEnabledFlags(); return; @@ -1283,7 +1354,7 @@ void TraceLog::SetEnabled(const CategoryFilter& category_filter, return; } - enabled_ = true; + mode_ = mode; if (options != old_options) { subtle::NoBarrier_Store(&trace_options_, options); @@ -1294,8 +1365,9 @@ void TraceLog::SetEnabled(const CategoryFilter& category_filter, category_filter_ = CategoryFilter(category_filter); UpdateCategoryGroupEnabledFlags(); + UpdateSyntheticDelaysFromCategoryFilter(); - if ((options & ENABLE_SAMPLING) || (options & MONITOR_SAMPLING)) { + if (options & ENABLE_SAMPLING) { sampling_thread_.reset(new TraceSamplingThread); sampling_thread_->RegisterSampleBucket( &g_trace_state[0], @@ -1341,7 +1413,7 @@ void TraceLog::SetDisabled() { void TraceLog::SetDisabledWhileLocked() { lock_.AssertAcquired(); - if (!enabled_) + if (!IsEnabled()) return; if (dispatching_to_observer_list_) { @@ -1350,7 +1422,7 @@ void TraceLog::SetDisabledWhileLocked() { return; } - enabled_ = false; + mode_ = DISABLED; if (sampling_thread_.get()) { // Stop the sampling thread. @@ -1384,7 +1456,7 @@ void TraceLog::SetDisabledWhileLocked() { int TraceLog::GetNumTracesRecorded() { AutoLock lock(lock_); - if (!enabled_) + if (!IsEnabled()) return -1; return num_traces_recorded_; } @@ -1425,7 +1497,7 @@ TraceBuffer* TraceLog::CreateTraceBuffer() { Options options = trace_options(); if (options & RECORD_CONTINUOUSLY) return new TraceBufferRingBuffer(kTraceEventRingBufferChunks); - else if (options & MONITOR_SAMPLING) + else if ((options & ENABLE_SAMPLING) && mode_ == MONITORING_MODE) return new TraceBufferRingBuffer(kMonitorTraceEventBufferChunks); else if (options & ECHO_TO_CONSOLE) return new TraceBufferRingBuffer(kEchoToConsoleTraceEventBufferChunks); @@ -1769,7 +1841,8 @@ TraceEventHandle TraceLog::AddTraceEventWithThreadIdAndTimestamp( } std::string console_message; - if ((*category_group_enabled & ENABLED_FOR_RECORDING)) { + if (*category_group_enabled & + (ENABLED_FOR_RECORDING | ENABLED_FOR_MONITORING)) { OptionalAutoLock lock(lock_); TraceEvent* trace_event = NULL; @@ -1898,8 +1971,7 @@ void TraceLog::AddTraceEventEtw(char phase, void TraceLog::AddTraceEventEtw(char phase, const char* name, const void* id, - const std::string& extra) -{ + const std::string& extra) { #if defined(OS_WIN) TraceEventETWProvider::Trace(name, phase, id, extra); #endif @@ -1977,6 +2049,14 @@ void TraceLog::CancelWatchEvent() { void TraceLog::AddMetadataEventsWhileLocked() { lock_.AssertAcquired(); +#if !defined(OS_NACL) // NaCl shouldn't expose the process id. + InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, false), + 0, + "num_cpus", "number", + base::SysInfo::NumberOfProcessors()); +#endif + + int current_thread_id = static_cast<int>(base::PlatformThread::CurrentId()); if (process_sort_index_ != 0) { InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, false), @@ -2166,7 +2246,8 @@ CategoryFilter::CategoryFilter(const std::string& filter_string) { CategoryFilter::CategoryFilter(const CategoryFilter& cf) : included_(cf.included_), disabled_(cf.disabled_), - excluded_(cf.excluded_) { + excluded_(cf.excluded_), + delays_(cf.delays_) { } CategoryFilter::~CategoryFilter() { @@ -2179,6 +2260,7 @@ CategoryFilter& CategoryFilter::operator=(const CategoryFilter& rhs) { included_ = rhs.included_; disabled_ = rhs.disabled_; excluded_ = rhs.excluded_; + delays_ = rhs.delays_; return *this; } @@ -2191,8 +2273,19 @@ void CategoryFilter::Initialize(const std::string& filter_string) { // Ignore empty categories. if (category.empty()) continue; - // Excluded categories start with '-'. - if (category.at(0) == '-') { + // Synthetic delays are of the form 'DELAY(delay;option;option;...)'. + if (category.find(kSyntheticDelayCategoryFilterPrefix) == 0 && + category.at(category.size() - 1) == ')') { + category = category.substr( + strlen(kSyntheticDelayCategoryFilterPrefix), + category.size() - strlen(kSyntheticDelayCategoryFilterPrefix) - 1); + size_t name_length = category.find(';'); + if (name_length != std::string::npos && name_length > 0 && + name_length != category.size() - 1) { + delays_.push_back(category); + } + } else if (category.at(0) == '-') { + // Excluded categories start with '-'. // Remove '-' from category string. category = category.substr(1); excluded_.push_back(category); @@ -2219,11 +2312,26 @@ void CategoryFilter::WriteString(const StringList& values, } } +void CategoryFilter::WriteString(const StringList& delays, + std::string* out) const { + bool prepend_comma = !out->empty(); + int token_cnt = 0; + for (StringList::const_iterator ci = delays.begin(); + ci != delays.end(); ++ci) { + if (token_cnt > 0 || prepend_comma) + StringAppendF(out, ","); + StringAppendF(out, "%s%s)", kSyntheticDelayCategoryFilterPrefix, + ci->c_str()); + ++token_cnt; + } +} + std::string CategoryFilter::ToString() const { std::string filter_string; WriteString(included_, &filter_string, true); WriteString(disabled_, &filter_string, true); WriteString(excluded_, &filter_string, false); + WriteString(delays_, &filter_string); return filter_string; } @@ -2279,6 +2387,9 @@ void CategoryFilter::Merge(const CategoryFilter& nested_filter) { excluded_.insert(excluded_.end(), nested_filter.excluded_.begin(), nested_filter.excluded_.end()); + delays_.insert(delays_.end(), + nested_filter.delays_.begin(), + nested_filter.delays_.end()); } void CategoryFilter::Clear() { @@ -2287,6 +2398,11 @@ void CategoryFilter::Clear() { excluded_.clear(); } +const CategoryFilter::StringList& + CategoryFilter::GetSyntheticDelayValues() const { + return delays_; +} + } // namespace debug } // namespace base diff --git a/chromium/base/debug/trace_event_impl.h b/chromium/base/debug/trace_event_impl.h index 61794807a41..e5a6dbd71f9 100644 --- a/chromium/base/debug/trace_event_impl.h +++ b/chromium/base/debug/trace_event_impl.h @@ -125,10 +125,6 @@ class BASE_EXPORT TraceEvent { void UpdateDuration(const TimeTicks& now, const TimeTicks& thread_now); // Serialize event data to JSON - static void AppendEventsAsJSON(const std::vector<TraceEvent>& events, - size_t start, - size_t count, - std::string* out); void AppendAsJSON(std::string* out) const; void AppendPrettyPrinted(std::ostringstream* out) const; @@ -283,6 +279,8 @@ class BASE_EXPORT TraceResultBuffer { class BASE_EXPORT CategoryFilter { public: + typedef std::vector<std::string> StringList; + // The default category filter, used when none is provided. // Allows all categories through, except if they end in the suffix 'Debug' or // 'Test'. @@ -298,6 +296,17 @@ class BASE_EXPORT CategoryFilter { // Example: CategoryFilter("-excluded_category1,-excluded_category2"); // Example: CategoryFilter("-*,webkit"); would disable everything but webkit. // Example: CategoryFilter("-webkit"); would enable everything but webkit. + // + // Category filters can also be used to configure synthetic delays. + // + // Example: CategoryFilter("DELAY(gpu.PresentingFrame;16)"); would make swap + // buffers always take at least 16 ms. + // Example: CategoryFilter("DELAY(gpu.PresentingFrame;16;oneshot)"); would + // make swap buffers take at least 16 ms the first time it is + // called. + // Example: CategoryFilter("DELAY(gpu.PresentingFrame;16;alternating)"); + // would make swap buffers take at least 16 ms every other time it + // is called. explicit CategoryFilter(const std::string& filter_string); CategoryFilter(const CategoryFilter& cf); @@ -317,6 +326,9 @@ class BASE_EXPORT CategoryFilter { // disabled by this category filter. bool IsCategoryGroupEnabled(const char* category_group) const; + // Return a list of the synthetic delays specified in this category filter. + const StringList& GetSyntheticDelayValues() const; + // Merges nested_filter with the current CategoryFilter void Merge(const CategoryFilter& nested_filter); @@ -334,12 +346,11 @@ class BASE_EXPORT CategoryFilter { static bool IsEmptyOrContainsLeadingOrTrailingWhitespace( const std::string& str); - typedef std::vector<std::string> StringList; - void Initialize(const std::string& filter_string); void WriteString(const StringList& values, std::string* out, bool included) const; + void WriteString(const StringList& delays, std::string* out) const; bool HasIncludedPatterns() const; bool DoesCategoryGroupContainCategory(const char* category_group, @@ -348,12 +359,19 @@ class BASE_EXPORT CategoryFilter { StringList included_; StringList disabled_; StringList excluded_; + StringList delays_; }; class TraceSamplingThread; class BASE_EXPORT TraceLog { public: + enum Mode { + DISABLED = 0, + RECORDING_MODE, + MONITORING_MODE, + }; + // Options determines how the trace buffer stores data. enum Options { // Record until the trace buffer is full. @@ -366,21 +384,21 @@ class BASE_EXPORT TraceLog { // Enable the sampling profiler in the recording mode. ENABLE_SAMPLING = 1 << 2, - // Enable the sampling profiler in the monitoring mode. - MONITOR_SAMPLING = 1 << 3, - // Echo to console. Events are discarded. - ECHO_TO_CONSOLE = 1 << 4, + ECHO_TO_CONSOLE = 1 << 3, }; // The pointer returned from GetCategoryGroupEnabledInternal() points to a // value with zero or more of the following bits. Used in this class only. // The TRACE_EVENT macros should only use the value as a bool. + // These values must be in sync with macro values in TraceEvent.h in Blink. enum CategoryGroupEnabledFlags { - // Normal enabled flag for category groups enabled by SetEnabled(). + // Category group enabled for the recording mode. ENABLED_FOR_RECORDING = 1 << 0, + // Category group enabled for the monitoring mode. + ENABLED_FOR_MONITORING = 1 << 1, // Category group enabled by SetEventCallbackEnabled(). - ENABLED_FOR_EVENT_CALLBACK = 1 << 1, + ENABLED_FOR_EVENT_CALLBACK = 1 << 2, }; static TraceLog* GetInstance(); @@ -400,12 +418,13 @@ class BASE_EXPORT TraceLog { // See CategoryFilter comments for details on how to control what categories // will be traced. If tracing has already been enabled, |category_filter| will // be merged into the current category filter. - void SetEnabled(const CategoryFilter& category_filter, Options options); + void SetEnabled(const CategoryFilter& category_filter, + Mode mode, Options options); // Disables normal tracing for all categories. void SetDisabled(); - bool IsEnabled() { return enabled_; } + bool IsEnabled() { return mode_ != DISABLED; } // The number of times we have begun recording traces. If tracing is off, // returns -1. If tracing is on, then it returns the number of times we have @@ -426,7 +445,7 @@ class BASE_EXPORT TraceLog { class EnabledStateObserver { public: // Called just after the tracing system becomes enabled, outside of the - // |lock_|. TraceLog::IsEnabled() is true at this point. + // |lock_|. TraceLog::IsEnabled() is true at this point. virtual void OnTraceLogEnabled() = 0; // Called just after the tracing system disables, outside of the |lock_|. @@ -591,14 +610,18 @@ class BASE_EXPORT TraceLog { // by the Singleton class. friend struct DefaultSingletonTraits<TraceLog>; - // Enable/disable each category group based on the current enabled_, + // Enable/disable each category group based on the current mode_, // category_filter_, event_callback_ and event_callback_category_filter_. - // Enable the category group if enabled_ is true and category_filter_ matches + // Enable the category group in the enabled mode if category_filter_ matches // the category group, or event_callback_ is not null and // event_callback_category_filter_ matches the category group. void UpdateCategoryGroupEnabledFlags(); void UpdateCategoryGroupEnabledFlag(int category_index); + // Configure synthetic delays based on the values set in the current + // category filter. + void UpdateSyntheticDelaysFromCategoryFilter(); + class ThreadLocalEventBuffer; class OptionalAutoLock; @@ -652,7 +675,7 @@ class BASE_EXPORT TraceLog { // and thread_colors_. Lock thread_info_lock_; int locked_line_; - bool enabled_; + Mode mode_; int num_traces_recorded_; scoped_ptr<TraceBuffer> logged_events_; subtle::AtomicWord /* EventCallback */ event_callback_; diff --git a/chromium/base/debug/trace_event_memory.cc b/chromium/base/debug/trace_event_memory.cc index 4b2b050df8e..3c468278a15 100644 --- a/chromium/base/debug/trace_event_memory.cc +++ b/chromium/base/debug/trace_event_memory.cc @@ -409,9 +409,9 @@ bool AppendHeapProfileLineAsTraceFormat(const std::string& line, // TODO(jamescook): Report the trace category and name separately to the // trace viewer and allow it to decide what decorations to apply. For now - // just hard-code a decoration for posted tasks. + // just hard-code a decoration for posted tasks (toplevel). std::string trace_string(trace_name); - if (!strcmp(trace_category, "task")) + if (!strcmp(trace_category, "toplevel")) trace_string.append("->PostTask"); // Some trace name strings have double quotes, convert them to single. diff --git a/chromium/base/debug/trace_event_memory_unittest.cc b/chromium/base/debug/trace_event_memory_unittest.cc index c0a1568e357..3f5cad3ede2 100644 --- a/chromium/base/debug/trace_event_memory_unittest.cc +++ b/chromium/base/debug/trace_event_memory_unittest.cc @@ -164,10 +164,10 @@ TEST_F(TraceMemoryTest, AppendHeapProfileLineAsTraceFormat) { AppendHeapProfileLineAsTraceFormat(input.str().c_str(), &output)); EXPECT_EQ(kExpectedOutput, output); - // Input with with the category "task". + // Input with with the category "toplevel". // TODO(jamescook): Eliminate this special case and move the logic to the // trace viewer code. - const char kTaskCategory[] = "task"; + const char kTaskCategory[] = "toplevel"; const char kTaskName[] = "TaskName"; std::ostringstream input2; input2 << " 68: 4195 [ 1087: 98009] @ " << &kTaskCategory << " " diff --git a/chromium/base/debug/trace_event_synthetic_delay.cc b/chromium/base/debug/trace_event_synthetic_delay.cc new file mode 100644 index 00000000000..ab1880941ba --- /dev/null +++ b/chromium/base/debug/trace_event_synthetic_delay.cc @@ -0,0 +1,233 @@ +// 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/trace_event_synthetic_delay.h" +#include "base/memory/singleton.h" + +namespace { +const int kMaxSyntheticDelays = 32; +} // namespace + +namespace base { +namespace debug { + +TraceEventSyntheticDelayClock::TraceEventSyntheticDelayClock() {} +TraceEventSyntheticDelayClock::~TraceEventSyntheticDelayClock() {} + +class TraceEventSyntheticDelayRegistry : public TraceEventSyntheticDelayClock { + public: + static TraceEventSyntheticDelayRegistry* GetInstance(); + + TraceEventSyntheticDelay* GetOrCreateDelay(const char* name); + void ResetAllDelays(); + + // TraceEventSyntheticDelayClock implementation. + virtual base::TimeTicks Now() OVERRIDE; + + private: + TraceEventSyntheticDelayRegistry(); + + friend struct DefaultSingletonTraits<TraceEventSyntheticDelayRegistry>; + + Lock lock_; + TraceEventSyntheticDelay delays_[kMaxSyntheticDelays]; + TraceEventSyntheticDelay dummy_delay_; + base::subtle::Atomic32 delay_count_; + + DISALLOW_COPY_AND_ASSIGN(TraceEventSyntheticDelayRegistry); +}; + +TraceEventSyntheticDelay::TraceEventSyntheticDelay() + : mode_(STATIC), begin_count_(0), trigger_count_(0), clock_(NULL) {} + +TraceEventSyntheticDelay::~TraceEventSyntheticDelay() {} + +TraceEventSyntheticDelay* TraceEventSyntheticDelay::Lookup( + const std::string& name) { + return TraceEventSyntheticDelayRegistry::GetInstance()->GetOrCreateDelay( + name.c_str()); +} + +void TraceEventSyntheticDelay::Initialize( + const std::string& name, + TraceEventSyntheticDelayClock* clock) { + name_ = name; + clock_ = clock; +} + +void TraceEventSyntheticDelay::SetTargetDuration( + base::TimeDelta target_duration) { + AutoLock lock(lock_); + target_duration_ = target_duration; + trigger_count_ = 0; + begin_count_ = 0; +} + +void TraceEventSyntheticDelay::SetMode(Mode mode) { + AutoLock lock(lock_); + mode_ = mode; +} + +void TraceEventSyntheticDelay::SetClock(TraceEventSyntheticDelayClock* clock) { + AutoLock lock(lock_); + clock_ = clock; +} + +void TraceEventSyntheticDelay::Begin() { + // Note that we check for a non-zero target duration without locking to keep + // things quick for the common case when delays are disabled. Since the delay + // calculation is done with a lock held, it will always be correct. The only + // downside of this is that we may fail to apply some delays when the target + // duration changes. + ANNOTATE_BENIGN_RACE(&target_duration_, "Synthetic delay duration"); + if (!target_duration_.ToInternalValue()) + return; + + base::TimeTicks start_time = clock_->Now(); + { + AutoLock lock(lock_); + if (++begin_count_ != 1) + return; + end_time_ = CalculateEndTimeLocked(start_time); + } +} + +void TraceEventSyntheticDelay::BeginParallel(base::TimeTicks* out_end_time) { + // See note in Begin(). + ANNOTATE_BENIGN_RACE(&target_duration_, "Synthetic delay duration"); + if (!target_duration_.ToInternalValue()) { + *out_end_time = base::TimeTicks(); + return; + } + + base::TimeTicks start_time = clock_->Now(); + { + AutoLock lock(lock_); + *out_end_time = CalculateEndTimeLocked(start_time); + } +} + +void TraceEventSyntheticDelay::End() { + // See note in Begin(). + ANNOTATE_BENIGN_RACE(&target_duration_, "Synthetic delay duration"); + if (!target_duration_.ToInternalValue()) + return; + + base::TimeTicks end_time; + { + AutoLock lock(lock_); + if (!begin_count_ || --begin_count_ != 0) + return; + end_time = end_time_; + } + if (!end_time.is_null()) + ApplyDelay(end_time); +} + +void TraceEventSyntheticDelay::EndParallel(base::TimeTicks end_time) { + if (!end_time.is_null()) + ApplyDelay(end_time); +} + +base::TimeTicks TraceEventSyntheticDelay::CalculateEndTimeLocked( + base::TimeTicks start_time) { + if (mode_ == ONE_SHOT && trigger_count_++) + return base::TimeTicks(); + else if (mode_ == ALTERNATING && trigger_count_++ % 2) + return base::TimeTicks(); + return start_time + target_duration_; +} + +void TraceEventSyntheticDelay::ApplyDelay(base::TimeTicks end_time) { + TRACE_EVENT0("synthetic_delay", name_.c_str()); + while (clock_->Now() < end_time) { + // Busy loop. + } +} + +TraceEventSyntheticDelayRegistry* +TraceEventSyntheticDelayRegistry::GetInstance() { + return Singleton< + TraceEventSyntheticDelayRegistry, + LeakySingletonTraits<TraceEventSyntheticDelayRegistry> >::get(); +} + +TraceEventSyntheticDelayRegistry::TraceEventSyntheticDelayRegistry() + : delay_count_(0) {} + +TraceEventSyntheticDelay* TraceEventSyntheticDelayRegistry::GetOrCreateDelay( + const char* name) { + // Try to find an existing delay first without locking to make the common case + // fast. + int delay_count = base::subtle::Acquire_Load(&delay_count_); + for (int i = 0; i < delay_count; ++i) { + if (!strcmp(name, delays_[i].name_.c_str())) + return &delays_[i]; + } + + AutoLock lock(lock_); + delay_count = base::subtle::Acquire_Load(&delay_count_); + for (int i = 0; i < delay_count; ++i) { + if (!strcmp(name, delays_[i].name_.c_str())) + return &delays_[i]; + } + + DCHECK(delay_count < kMaxSyntheticDelays) + << "must increase kMaxSyntheticDelays"; + if (delay_count >= kMaxSyntheticDelays) + return &dummy_delay_; + + delays_[delay_count].Initialize(std::string(name), this); + base::subtle::Release_Store(&delay_count_, delay_count + 1); + return &delays_[delay_count]; +} + +base::TimeTicks TraceEventSyntheticDelayRegistry::Now() { + return base::TimeTicks::HighResNow(); +} + +void TraceEventSyntheticDelayRegistry::ResetAllDelays() { + AutoLock lock(lock_); + int delay_count = base::subtle::Acquire_Load(&delay_count_); + for (int i = 0; i < delay_count; ++i) { + delays_[i].SetTargetDuration(base::TimeDelta()); + delays_[i].SetClock(this); + } +} + +void ResetTraceEventSyntheticDelays() { + TraceEventSyntheticDelayRegistry::GetInstance()->ResetAllDelays(); +} + +} // namespace debug +} // namespace base + +namespace trace_event_internal { + +ScopedSyntheticDelay::ScopedSyntheticDelay(const char* name, + base::subtle::AtomicWord* impl_ptr) + : delay_impl_(GetOrCreateDelay(name, impl_ptr)) { + delay_impl_->BeginParallel(&end_time_); +} + +ScopedSyntheticDelay::~ScopedSyntheticDelay() { + delay_impl_->EndParallel(end_time_); +} + +base::debug::TraceEventSyntheticDelay* GetOrCreateDelay( + const char* name, + base::subtle::AtomicWord* impl_ptr) { + base::debug::TraceEventSyntheticDelay* delay_impl = + reinterpret_cast<base::debug::TraceEventSyntheticDelay*>( + base::subtle::NoBarrier_Load(impl_ptr)); + if (!delay_impl) { + delay_impl = base::debug::TraceEventSyntheticDelayRegistry::GetInstance() + ->GetOrCreateDelay(name); + base::subtle::NoBarrier_Store( + impl_ptr, reinterpret_cast<base::subtle::AtomicWord>(delay_impl)); + } + return delay_impl; +} + +} // namespace trace_event_internal diff --git a/chromium/base/debug/trace_event_synthetic_delay.h b/chromium/base/debug/trace_event_synthetic_delay.h new file mode 100644 index 00000000000..06d6cdea1f9 --- /dev/null +++ b/chromium/base/debug/trace_event_synthetic_delay.h @@ -0,0 +1,166 @@ +// 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. + +// The synthetic delay framework makes it possible to dynamically inject +// arbitrary delays into into different parts of the codebase. This can be used, +// for instance, for testing various task scheduling algorithms. +// +// The delays are specified in terms of a target duration for a given block of +// code. If the code executes faster than the duration, the thread is made to +// sleep until the deadline is met. +// +// Code can be instrumented for delays with two sets of macros. First, for +// delays that should apply within a scope, use the following macro: +// +// TRACE_EVENT_SYNTHETIC_DELAY("cc.LayerTreeHost.DrawAndSwap"); +// +// For delaying operations that span multiple scopes, use: +// +// TRACE_EVENT_SYNTHETIC_DELAY_BEGIN("cc.Scheduler.BeginMainFrame"); +// ... +// TRACE_EVENT_SYNTHETIC_DELAY_END("cc.Scheduler.BeginMainFrame"); +// +// Here BEGIN establishes the start time for the delay and END executes the +// delay based on the remaining time. If BEGIN is called multiple times in a +// row, END should be called a corresponding number of times. Only the last +// call to END will have an effect. +// +// 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_ + +#include "base/atomicops.h" +#include "base/debug/trace_event.h" +#include "base/synchronization/lock.h" +#include "base/time/time.h" + +// Apply a named delay in the current scope. +#define TRACE_EVENT_SYNTHETIC_DELAY(name) \ + static base::subtle::AtomicWord INTERNAL_TRACE_EVENT_UID(impl_ptr) = 0; \ + trace_event_internal::ScopedSyntheticDelay INTERNAL_TRACE_EVENT_UID(delay)( \ + name, &INTERNAL_TRACE_EVENT_UID(impl_ptr)); + +// Begin a named delay, establishing its timing start point. May be called +// multiple times as long as the calls to TRACE_EVENT_SYNTHETIC_DELAY_END are +// balanced. Only the first call records the timing start point. +#define TRACE_EVENT_SYNTHETIC_DELAY_BEGIN(name) \ + do { \ + static base::subtle::AtomicWord impl_ptr = 0; \ + trace_event_internal::GetOrCreateDelay(name, &impl_ptr)->Begin(); \ + } while (false) + +// End a named delay. The delay is applied only if this call matches the +// first corresponding call to TRACE_EVENT_SYNTHETIC_DELAY_BEGIN with the +// same delay. +#define TRACE_EVENT_SYNTHETIC_DELAY_END(name) \ + do { \ + static base::subtle::AtomicWord impl_ptr = 0; \ + trace_event_internal::GetOrCreateDelay(name, &impl_ptr)->End(); \ + } while (false) + +template <typename Type> +struct DefaultSingletonTraits; + +namespace base { +namespace debug { + +// Time source for computing delay durations. Used for testing. +class TRACE_EVENT_API_CLASS_EXPORT TraceEventSyntheticDelayClock { + public: + TraceEventSyntheticDelayClock(); + virtual ~TraceEventSyntheticDelayClock(); + virtual base::TimeTicks Now() = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(TraceEventSyntheticDelayClock); +}; + +// Single delay point instance. +class TRACE_EVENT_API_CLASS_EXPORT TraceEventSyntheticDelay { + public: + enum Mode { + STATIC, // Apply the configured delay every time. + ONE_SHOT, // Apply the configured delay just once. + ALTERNATING // Apply the configured delay every other time. + }; + + // Returns an existing named delay instance or creates a new one with |name|. + static TraceEventSyntheticDelay* Lookup(const std::string& name); + + void SetTargetDuration(TimeDelta target_duration); + void SetMode(Mode mode); + void SetClock(TraceEventSyntheticDelayClock* clock); + + // Begin the delay, establishing its timing start point. May be called + // multiple times as long as the calls to End() are balanced. Only the first + // call records the timing start point. + void Begin(); + + // End the delay. The delay is applied only if this call matches the first + // corresponding call to Begin() with the same delay. + void End(); + + // Begin a parallel instance of the delay. Several parallel instances may be + // active simultaneously and will complete independently. The computed end + // time for the delay is stored in |out_end_time|, which should later be + // passed to EndParallel(). + void BeginParallel(base::TimeTicks* out_end_time); + + // End a previously started parallel delay. |end_time| is the delay end point + // computed by BeginParallel(). + void EndParallel(base::TimeTicks end_time); + + private: + TraceEventSyntheticDelay(); + ~TraceEventSyntheticDelay(); + friend class TraceEventSyntheticDelayRegistry; + + void Initialize(const std::string& name, + TraceEventSyntheticDelayClock* clock); + base::TimeTicks CalculateEndTimeLocked(base::TimeTicks start_time); + void ApplyDelay(base::TimeTicks end_time); + + Lock lock_; + Mode mode_; + std::string name_; + int begin_count_; + int trigger_count_; + base::TimeTicks end_time_; + base::TimeDelta target_duration_; + TraceEventSyntheticDelayClock* clock_; + + DISALLOW_COPY_AND_ASSIGN(TraceEventSyntheticDelay); +}; + +// Set the target durations of all registered synthetic delay points to zero. +TRACE_EVENT_API_CLASS_EXPORT void ResetTraceEventSyntheticDelays(); + +} // namespace debug +} // namespace base + +namespace trace_event_internal { + +// Helper class for scoped delays. Do not use directly. +class TRACE_EVENT_API_CLASS_EXPORT ScopedSyntheticDelay { + public: + explicit ScopedSyntheticDelay(const char* name, + base::subtle::AtomicWord* impl_ptr); + ~ScopedSyntheticDelay(); + + private: + base::debug::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* + GetOrCreateDelay(const char* name, base::subtle::AtomicWord* impl_ptr); + +} // namespace trace_event_internal + +#endif /* BASE_DEBUG_TRACE_EVENT_SYNTHETIC_DELAY_H_ */ diff --git a/chromium/base/debug/trace_event_synthetic_delay_unittest.cc b/chromium/base/debug/trace_event_synthetic_delay_unittest.cc new file mode 100644 index 00000000000..7833e7bbcde --- /dev/null +++ b/chromium/base/debug/trace_event_synthetic_delay_unittest.cc @@ -0,0 +1,156 @@ +// 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/trace_event_synthetic_delay.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { +namespace debug { +namespace { + +const int kTargetDurationMs = 100; +// Allow some leeway in timings to make it possible to run these tests with a +// wall clock time source too. +const int kShortDurationMs = 10; + +} // namespace + +class TraceEventSyntheticDelayTest : public testing::Test, + public TraceEventSyntheticDelayClock { + public: + TraceEventSyntheticDelayTest() {} + virtual ~TraceEventSyntheticDelayTest() { + ResetTraceEventSyntheticDelays(); + } + + // TraceEventSyntheticDelayClock implementation. + virtual base::TimeTicks Now() OVERRIDE { + AdvanceTime(base::TimeDelta::FromMilliseconds(kShortDurationMs / 10)); + return now_; + } + + TraceEventSyntheticDelay* ConfigureDelay(const char* name) { + TraceEventSyntheticDelay* delay = TraceEventSyntheticDelay::Lookup(name); + delay->SetClock(this); + delay->SetTargetDuration( + base::TimeDelta::FromMilliseconds(kTargetDurationMs)); + return delay; + } + + void AdvanceTime(base::TimeDelta delta) { now_ += delta; } + + int TestFunction() { + base::TimeTicks start = Now(); + { TRACE_EVENT_SYNTHETIC_DELAY("test.Delay"); } + return (Now() - start).InMilliseconds(); + } + + int AsyncTestFunctionBegin() { + base::TimeTicks start = Now(); + { TRACE_EVENT_SYNTHETIC_DELAY_BEGIN("test.AsyncDelay"); } + return (Now() - start).InMilliseconds(); + } + + int AsyncTestFunctionEnd() { + base::TimeTicks start = Now(); + { TRACE_EVENT_SYNTHETIC_DELAY_END("test.AsyncDelay"); } + return (Now() - start).InMilliseconds(); + } + + private: + base::TimeTicks now_; + + DISALLOW_COPY_AND_ASSIGN(TraceEventSyntheticDelayTest); +}; + +TEST_F(TraceEventSyntheticDelayTest, StaticDelay) { + TraceEventSyntheticDelay* delay = ConfigureDelay("test.Delay"); + delay->SetMode(TraceEventSyntheticDelay::STATIC); + EXPECT_GE(TestFunction(), kTargetDurationMs); +} + +TEST_F(TraceEventSyntheticDelayTest, OneShotDelay) { + TraceEventSyntheticDelay* delay = ConfigureDelay("test.Delay"); + delay->SetMode(TraceEventSyntheticDelay::ONE_SHOT); + EXPECT_GE(TestFunction(), kTargetDurationMs); + EXPECT_LT(TestFunction(), kShortDurationMs); + + delay->SetTargetDuration( + base::TimeDelta::FromMilliseconds(kTargetDurationMs)); + EXPECT_GE(TestFunction(), kTargetDurationMs); +} + +TEST_F(TraceEventSyntheticDelayTest, AlternatingDelay) { + TraceEventSyntheticDelay* delay = ConfigureDelay("test.Delay"); + delay->SetMode(TraceEventSyntheticDelay::ALTERNATING); + EXPECT_GE(TestFunction(), kTargetDurationMs); + EXPECT_LT(TestFunction(), kShortDurationMs); + EXPECT_GE(TestFunction(), kTargetDurationMs); + EXPECT_LT(TestFunction(), kShortDurationMs); +} + +TEST_F(TraceEventSyntheticDelayTest, AsyncDelay) { + ConfigureDelay("test.AsyncDelay"); + EXPECT_LT(AsyncTestFunctionBegin(), kShortDurationMs); + EXPECT_GE(AsyncTestFunctionEnd(), kTargetDurationMs / 2); +} + +TEST_F(TraceEventSyntheticDelayTest, AsyncDelayExceeded) { + ConfigureDelay("test.AsyncDelay"); + EXPECT_LT(AsyncTestFunctionBegin(), kShortDurationMs); + AdvanceTime(base::TimeDelta::FromMilliseconds(kTargetDurationMs)); + EXPECT_LT(AsyncTestFunctionEnd(), kShortDurationMs); +} + +TEST_F(TraceEventSyntheticDelayTest, AsyncDelayNoActivation) { + ConfigureDelay("test.AsyncDelay"); + EXPECT_LT(AsyncTestFunctionEnd(), kShortDurationMs); +} + +TEST_F(TraceEventSyntheticDelayTest, AsyncDelayNested) { + ConfigureDelay("test.AsyncDelay"); + EXPECT_LT(AsyncTestFunctionBegin(), kShortDurationMs); + EXPECT_LT(AsyncTestFunctionBegin(), kShortDurationMs); + EXPECT_LT(AsyncTestFunctionEnd(), kShortDurationMs); + EXPECT_GE(AsyncTestFunctionEnd(), kTargetDurationMs / 2); +} + +TEST_F(TraceEventSyntheticDelayTest, AsyncDelayUnbalanced) { + ConfigureDelay("test.AsyncDelay"); + EXPECT_LT(AsyncTestFunctionBegin(), kShortDurationMs); + EXPECT_GE(AsyncTestFunctionEnd(), kTargetDurationMs / 2); + EXPECT_LT(AsyncTestFunctionEnd(), kShortDurationMs); + + EXPECT_LT(AsyncTestFunctionBegin(), kShortDurationMs); + EXPECT_GE(AsyncTestFunctionEnd(), kTargetDurationMs / 2); +} + +TEST_F(TraceEventSyntheticDelayTest, ResetDelays) { + ConfigureDelay("test.Delay"); + ResetTraceEventSyntheticDelays(); + EXPECT_LT(TestFunction(), kShortDurationMs); +} + +TEST_F(TraceEventSyntheticDelayTest, BeginParallel) { + TraceEventSyntheticDelay* delay = ConfigureDelay("test.AsyncDelay"); + base::TimeTicks end_times[2]; + base::TimeTicks start_time = Now(); + + delay->BeginParallel(&end_times[0]); + EXPECT_FALSE(end_times[0].is_null()); + + delay->BeginParallel(&end_times[1]); + EXPECT_FALSE(end_times[1].is_null()); + + delay->EndParallel(end_times[0]); + EXPECT_GE((Now() - start_time).InMilliseconds(), kTargetDurationMs); + + start_time = Now(); + delay->EndParallel(end_times[1]); + EXPECT_LT((Now() - start_time).InMilliseconds(), kShortDurationMs); +} + +} // namespace debug +} // namespace base diff --git a/chromium/base/debug/trace_event_unittest.cc b/chromium/base/debug/trace_event_unittest.cc index aef2c43b6fa..c843c25f540 100644 --- a/chromium/base/debug/trace_event_unittest.cc +++ b/chromium/base/debug/trace_event_unittest.cc @@ -4,11 +4,13 @@ #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/memory/ref_counted_memory.h" @@ -19,6 +21,7 @@ #include "base/synchronization/waitable_event.h" #include "base/threading/platform_thread.h" #include "base/threading/thread.h" +#include "base/time/time.h" #include "base/values.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -62,6 +65,7 @@ class TraceEventTestFixture : public testing::Test { const char* phase, const char* key, const char* value); + void DropTracedMetadataRecords(); bool FindMatchingValue(const char* key, const char* value); bool FindNonMatchingValue(const char* key, @@ -78,6 +82,7 @@ class TraceEventTestFixture : public testing::Test { void BeginSpecificTrace(const std::string& filter) { event_watch_notification_ = 0; TraceLog::GetInstance()->SetEnabled(CategoryFilter(filter), + base::debug::TraceLog::RECORDING_MODE, TraceLog::RECORD_UNTIL_FULL); } @@ -247,6 +252,28 @@ DictionaryValue* TraceEventTestFixture::FindMatchingTraceEntry( return NULL; } +void TraceEventTestFixture::DropTracedMetadataRecords() { + + scoped_ptr<ListValue> old_trace_parsed(trace_parsed_.DeepCopy()); + size_t old_trace_parsed_size = old_trace_parsed->GetSize(); + trace_parsed_.Clear(); + + for (size_t i = 0; i < old_trace_parsed_size; i++) { + Value* value = NULL; + old_trace_parsed->Get(i, &value); + if (!value || value->GetType() != Value::TYPE_DICTIONARY) { + trace_parsed_.Append(value->DeepCopy()); + continue; + } + DictionaryValue* dict = static_cast<DictionaryValue*>(value); + std::string tmp; + if(dict->GetString("ph", &tmp) && tmp == "M") + continue; + + trace_parsed_.Append(value->DeepCopy()); + } +} + DictionaryValue* TraceEventTestFixture::FindNamePhase(const char* name, const char* phase) { JsonKeyValue key_values[] = { @@ -853,6 +880,7 @@ void HighResSleepForTraceTest(base::TimeDelta elapsed) { // Simple Test for emitting data and validating it was received. TEST_F(TraceEventTestFixture, DataCaptured) { TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"), + base::debug::TraceLog::RECORDING_MODE, TraceLog::RECORD_UNTIL_FULL); TraceWithAllMacroVariants(NULL); @@ -876,6 +904,7 @@ TEST_F(TraceEventTestFixture, EnabledObserverFiresOnEnable) { EXPECT_CALL(observer, OnTraceLogEnabled()) .Times(1); TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"), + base::debug::TraceLog::RECORDING_MODE, TraceLog::RECORD_UNTIL_FULL); testing::Mock::VerifyAndClear(&observer); EXPECT_TRUE(TraceLog::GetInstance()->IsEnabled()); @@ -887,6 +916,7 @@ TEST_F(TraceEventTestFixture, EnabledObserverFiresOnEnable) { TEST_F(TraceEventTestFixture, EnabledObserverDoesntFireOnSecondEnable) { TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"), + base::debug::TraceLog::RECORDING_MODE, TraceLog::RECORD_UNTIL_FULL); testing::StrictMock<MockEnabledStateChangedObserver> observer; @@ -897,6 +927,7 @@ TEST_F(TraceEventTestFixture, EnabledObserverDoesntFireOnSecondEnable) { EXPECT_CALL(observer, OnTraceLogDisabled()) .Times(0); TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"), + base::debug::TraceLog::RECORDING_MODE, TraceLog::RECORD_UNTIL_FULL); testing::Mock::VerifyAndClear(&observer); EXPECT_TRUE(TraceLog::GetInstance()->IsEnabled()); @@ -909,8 +940,12 @@ TEST_F(TraceEventTestFixture, EnabledObserverDoesntFireOnSecondEnable) { TEST_F(TraceEventTestFixture, EnabledObserverFiresOnFirstDisable) { CategoryFilter cf_inc_all("*"); - TraceLog::GetInstance()->SetEnabled(cf_inc_all, TraceLog::RECORD_UNTIL_FULL); - TraceLog::GetInstance()->SetEnabled(cf_inc_all, TraceLog::RECORD_UNTIL_FULL); + TraceLog::GetInstance()->SetEnabled(cf_inc_all, + base::debug::TraceLog::RECORDING_MODE, + TraceLog::RECORD_UNTIL_FULL); + TraceLog::GetInstance()->SetEnabled(cf_inc_all, + base::debug::TraceLog::RECORDING_MODE, + TraceLog::RECORD_UNTIL_FULL); testing::StrictMock<MockEnabledStateChangedObserver> observer; TraceLog::GetInstance()->AddEnabledStateObserver(&observer); @@ -929,6 +964,7 @@ TEST_F(TraceEventTestFixture, EnabledObserverFiresOnFirstDisable) { TEST_F(TraceEventTestFixture, EnabledObserverFiresOnDisable) { TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"), + base::debug::TraceLog::RECORDING_MODE, TraceLog::RECORD_UNTIL_FULL); MockEnabledStateChangedObserver observer; @@ -965,6 +1001,7 @@ TEST_F(TraceEventTestFixture, ObserversFireAfterStateChange) { TraceLog::GetInstance()->AddEnabledStateObserver(&observer); TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"), + base::debug::TraceLog::RECORDING_MODE, TraceLog::RECORD_UNTIL_FULL); EXPECT_TRUE(TraceLog::GetInstance()->IsEnabled()); @@ -997,6 +1034,7 @@ TEST_F(TraceEventTestFixture, SelfRemovingObserver) { EXPECT_EQ(1u, TraceLog::GetInstance()->GetObserverCountForTest()); TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"), + base::debug::TraceLog::RECORDING_MODE, TraceLog::RECORD_UNTIL_FULL); TraceLog::GetInstance()->SetDisabled(); // The observer removed itself on disable. @@ -1012,6 +1050,7 @@ bool IsNewTrace() { TEST_F(TraceEventTestFixture, NewTraceRecording) { ASSERT_FALSE(IsNewTrace()); TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"), + base::debug::TraceLog::RECORDING_MODE, TraceLog::RECORD_UNTIL_FULL); // First call to IsNewTrace() should succeed. But, the second shouldn't. ASSERT_TRUE(IsNewTrace()); @@ -1024,6 +1063,7 @@ TEST_F(TraceEventTestFixture, NewTraceRecording) { // Start another trace. IsNewTrace() should become true again, briefly, as // before. TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"), + base::debug::TraceLog::RECORDING_MODE, TraceLog::RECORD_UNTIL_FULL); ASSERT_TRUE(IsNewTrace()); ASSERT_FALSE(IsNewTrace()); @@ -1080,20 +1120,24 @@ TEST_F(TraceEventTestFixture, Categories) { Clear(); included_categories.clear(); TraceLog::GetInstance()->SetEnabled(CategoryFilter("not_found823564786"), + base::debug::TraceLog::RECORDING_MODE, TraceLog::RECORD_UNTIL_FULL); TRACE_EVENT_INSTANT0("cat1", "name", TRACE_EVENT_SCOPE_THREAD); TRACE_EVENT_INSTANT0("cat2", "name", TRACE_EVENT_SCOPE_THREAD); EndTraceAndFlush(); + DropTracedMetadataRecords(); EXPECT_TRUE(trace_parsed_.empty()); // Include existent category -> only events of that category Clear(); included_categories.clear(); TraceLog::GetInstance()->SetEnabled(CategoryFilter("inc"), + base::debug::TraceLog::RECORDING_MODE, TraceLog::RECORD_UNTIL_FULL); TRACE_EVENT_INSTANT0("inc", "name", TRACE_EVENT_SCOPE_THREAD); TRACE_EVENT_INSTANT0("inc2", "name", TRACE_EVENT_SCOPE_THREAD); EndTraceAndFlush(); + DropTracedMetadataRecords(); EXPECT_TRUE(FindMatchingValue("cat", "inc")); EXPECT_FALSE(FindNonMatchingValue("cat", "inc")); @@ -1102,6 +1146,7 @@ TEST_F(TraceEventTestFixture, Categories) { included_categories.clear(); TraceLog::GetInstance()->SetEnabled( CategoryFilter("inc_wildcard_*,inc_wildchar_?_end"), + base::debug::TraceLog::RECORDING_MODE, TraceLog::RECORD_UNTIL_FULL); TRACE_EVENT_INSTANT0("inc_wildcard_abc", "included", TRACE_EVENT_SCOPE_THREAD); @@ -1131,6 +1176,7 @@ TEST_F(TraceEventTestFixture, Categories) { // Exclude nonexistent category -> all events Clear(); TraceLog::GetInstance()->SetEnabled(CategoryFilter("-not_found823564786"), + base::debug::TraceLog::RECORDING_MODE, TraceLog::RECORD_UNTIL_FULL); TRACE_EVENT_INSTANT0("cat1", "name", TRACE_EVENT_SCOPE_THREAD); TRACE_EVENT_INSTANT0("cat2", "name", TRACE_EVENT_SCOPE_THREAD); @@ -1143,6 +1189,7 @@ TEST_F(TraceEventTestFixture, Categories) { // Exclude existent category -> only events of other categories Clear(); TraceLog::GetInstance()->SetEnabled(CategoryFilter("-inc"), + base::debug::TraceLog::RECORDING_MODE, TraceLog::RECORD_UNTIL_FULL); TRACE_EVENT_INSTANT0("inc", "name", TRACE_EVENT_SCOPE_THREAD); TRACE_EVENT_INSTANT0("inc2", "name", TRACE_EVENT_SCOPE_THREAD); @@ -1158,6 +1205,7 @@ TEST_F(TraceEventTestFixture, Categories) { Clear(); TraceLog::GetInstance()->SetEnabled( CategoryFilter("-inc_wildcard_*,-inc_wildchar_?_end"), + base::debug::TraceLog::RECORDING_MODE, TraceLog::RECORD_UNTIL_FULL); TRACE_EVENT_INSTANT0("inc_wildcard_abc", "not_inc", TRACE_EVENT_SCOPE_THREAD); @@ -1390,7 +1438,7 @@ TEST_F(TraceEventTestFixture, DataCapturedManyThreads) { Thread* threads[num_threads]; WaitableEvent* task_complete_events[num_threads]; for (int i = 0; i < num_threads; i++) { - threads[i] = new Thread(StringPrintf("Thread %d", i).c_str()); + threads[i] = new Thread(StringPrintf("Thread %d", i)); task_complete_events[i] = new WaitableEvent(false, false); threads[i]->Start(); threads[i]->message_loop()->PostTask( @@ -1430,7 +1478,7 @@ TEST_F(TraceEventTestFixture, ThreadNames) { Thread* threads[num_threads]; PlatformThreadId thread_ids[num_threads]; for (int i = 0; i < num_threads; i++) - threads[i] = new Thread(StringPrintf("Thread %d", i).c_str()); + threads[i] = new Thread(StringPrintf("Thread %d", i)); // Enable tracing. BeginTrace(); @@ -1665,15 +1713,21 @@ TEST_F(TraceEventTestFixture, TracingIsLazy) { TEST_F(TraceEventTestFixture, TraceEnableDisable) { TraceLog* trace_log = TraceLog::GetInstance(); CategoryFilter cf_inc_all("*"); - trace_log->SetEnabled(cf_inc_all, TraceLog::RECORD_UNTIL_FULL); + trace_log->SetEnabled(cf_inc_all, + base::debug::TraceLog::RECORDING_MODE, + TraceLog::RECORD_UNTIL_FULL); EXPECT_TRUE(trace_log->IsEnabled()); trace_log->SetDisabled(); EXPECT_FALSE(trace_log->IsEnabled()); - trace_log->SetEnabled(cf_inc_all, TraceLog::RECORD_UNTIL_FULL); + trace_log->SetEnabled(cf_inc_all, + base::debug::TraceLog::RECORDING_MODE, + TraceLog::RECORD_UNTIL_FULL); EXPECT_TRUE(trace_log->IsEnabled()); const std::vector<std::string> empty; - trace_log->SetEnabled(CategoryFilter(""), TraceLog::RECORD_UNTIL_FULL); + trace_log->SetEnabled(CategoryFilter(""), + base::debug::TraceLog::RECORDING_MODE, + TraceLog::RECORD_UNTIL_FULL); EXPECT_TRUE(trace_log->IsEnabled()); trace_log->SetDisabled(); EXPECT_FALSE(trace_log->IsEnabled()); @@ -1683,15 +1737,21 @@ TEST_F(TraceEventTestFixture, TraceEnableDisable) { TEST_F(TraceEventTestFixture, TraceCategoriesAfterNestedEnable) { TraceLog* trace_log = TraceLog::GetInstance(); - trace_log->SetEnabled(CategoryFilter("foo,bar"), TraceLog::RECORD_UNTIL_FULL); + trace_log->SetEnabled(CategoryFilter("foo,bar"), + base::debug::TraceLog::RECORDING_MODE, + TraceLog::RECORD_UNTIL_FULL); EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("foo")); EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("bar")); EXPECT_FALSE(*trace_log->GetCategoryGroupEnabled("baz")); - trace_log->SetEnabled(CategoryFilter("foo2"), TraceLog::RECORD_UNTIL_FULL); + trace_log->SetEnabled(CategoryFilter("foo2"), + base::debug::TraceLog::RECORDING_MODE, + TraceLog::RECORD_UNTIL_FULL); EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("foo2")); EXPECT_FALSE(*trace_log->GetCategoryGroupEnabled("baz")); // The "" becomes the default catergory set when applied. - trace_log->SetEnabled(CategoryFilter(""), TraceLog::RECORD_UNTIL_FULL); + trace_log->SetEnabled(CategoryFilter(""), + base::debug::TraceLog::RECORDING_MODE, + TraceLog::RECORD_UNTIL_FULL); EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("foo")); EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("baz")); EXPECT_STREQ("-*Debug,-*Test", @@ -1703,10 +1763,13 @@ TEST_F(TraceEventTestFixture, TraceCategoriesAfterNestedEnable) { EXPECT_FALSE(*trace_log->GetCategoryGroupEnabled("baz")); trace_log->SetEnabled(CategoryFilter("-foo,-bar"), + base::debug::TraceLog::RECORDING_MODE, TraceLog::RECORD_UNTIL_FULL); EXPECT_FALSE(*trace_log->GetCategoryGroupEnabled("foo")); EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("baz")); - trace_log->SetEnabled(CategoryFilter("moo"), TraceLog::RECORD_UNTIL_FULL); + trace_log->SetEnabled(CategoryFilter("moo"), + base::debug::TraceLog::RECORDING_MODE, + TraceLog::RECORD_UNTIL_FULL); EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("baz")); EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("moo")); EXPECT_FALSE(*trace_log->GetCategoryGroupEnabled("foo")); @@ -1717,9 +1780,11 @@ TEST_F(TraceEventTestFixture, TraceCategoriesAfterNestedEnable) { // Make sure disabled categories aren't cleared if we set in the second. trace_log->SetEnabled(CategoryFilter("disabled-by-default-cc,foo"), + base::debug::TraceLog::RECORDING_MODE, TraceLog::RECORD_UNTIL_FULL); EXPECT_FALSE(*trace_log->GetCategoryGroupEnabled("bar")); trace_log->SetEnabled(CategoryFilter("disabled-by-default-gpu"), + base::debug::TraceLog::RECORDING_MODE, TraceLog::RECORD_UNTIL_FULL); EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("disabled-by-default-cc")); EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("disabled-by-default-gpu")); @@ -1733,6 +1798,7 @@ TEST_F(TraceEventTestFixture, TraceCategoriesAfterNestedEnable) { TEST_F(TraceEventTestFixture, TraceSampling) { TraceLog::GetInstance()->SetEnabled( CategoryFilter("*"), + base::debug::TraceLog::RECORDING_MODE, TraceLog::Options(TraceLog::RECORD_UNTIL_FULL | TraceLog::ENABLE_SAMPLING)); @@ -1751,6 +1817,7 @@ TEST_F(TraceEventTestFixture, TraceSampling) { TEST_F(TraceEventTestFixture, TraceSamplingScope) { TraceLog::GetInstance()->SetEnabled( CategoryFilter("*"), + base::debug::TraceLog::RECORDING_MODE, TraceLog::Options(TraceLog::RECORD_UNTIL_FULL | TraceLog::ENABLE_SAMPLING)); @@ -1785,7 +1852,8 @@ TEST_F(TraceEventTestFixture, TraceSamplingScope) { TEST_F(TraceEventTestFixture, TraceContinuousSampling) { TraceLog::GetInstance()->SetEnabled( CategoryFilter("*"), - TraceLog::Options(TraceLog::MONITOR_SAMPLING)); + base::debug::TraceLog::MONITORING_MODE, + TraceLog::Options(TraceLog::ENABLE_SAMPLING)); TRACE_EVENT_SET_SAMPLING_STATE_FOR_BUCKET(1, "category", "AAA"); TraceLog::GetInstance()->WaitSamplingEventForTesting(); @@ -1843,6 +1911,7 @@ class MyData : public base::debug::ConvertableToTraceFormat { TEST_F(TraceEventTestFixture, ConvertableTypes) { TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"), + base::debug::TraceLog::RECORDING_MODE, TraceLog::RECORD_UNTIL_FULL); scoped_refptr<ConvertableToTraceFormat> data(new MyData()); @@ -1946,6 +2015,171 @@ TEST_F(TraceEventTestFixture, ConvertableTypes) { EXPECT_EQ(1, foo_val); } +TEST_F(TraceEventTestFixture, PrimitiveArgs) { + TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"), + base::debug::TraceLog::RECORDING_MODE, + TraceLog::RECORD_UNTIL_FULL); + + TRACE_EVENT1("foo", "event1", "int_one", 1); + TRACE_EVENT1("foo", "event2", "int_neg_ten", -10); + TRACE_EVENT1("foo", "event3", "float_one", 1.0f); + TRACE_EVENT1("foo", "event4", "float_half", .5f); + TRACE_EVENT1("foo", "event5", "float_neghalf", -.5f); + TRACE_EVENT1("foo", "event6", "float_infinity", + std::numeric_limits<float>::infinity()); + TRACE_EVENT1("foo", "event6b", "float_neg_infinity", + -std::numeric_limits<float>::infinity()); + TRACE_EVENT1("foo", "event7", "double_nan", + std::numeric_limits<double>::quiet_NaN()); + void* p = 0; + TRACE_EVENT1("foo", "event8", "pointer_null", p); + p = reinterpret_cast<void*>(0xbadf00d); + TRACE_EVENT1("foo", "event9", "pointer_badf00d", p); + TRACE_EVENT1("foo", "event10", "bool_true", true); + TRACE_EVENT1("foo", "event11", "bool_false", false); + TRACE_EVENT1("foo", "event12", "time_null", + base::Time()); + TRACE_EVENT1("foo", "event13", "time_one", + base::Time::FromInternalValue(1)); + TRACE_EVENT1("foo", "event14", "timeticks_null", + base::TimeTicks()); + TRACE_EVENT1("foo", "event15", "timeticks_one", + base::TimeTicks::FromInternalValue(1)); + EndTraceAndFlush(); + + const DictionaryValue* args_dict = NULL; + DictionaryValue* dict = NULL; + const Value* value = NULL; + std::string str_value; + int int_value; + double double_value; + bool bool_value; + + dict = FindNamePhase("event1", "X"); + ASSERT_TRUE(dict); + dict->GetDictionary("args", &args_dict); + ASSERT_TRUE(args_dict); + EXPECT_TRUE(args_dict->GetInteger("int_one", &int_value)); + EXPECT_EQ(1, int_value); + + dict = FindNamePhase("event2", "X"); + ASSERT_TRUE(dict); + dict->GetDictionary("args", &args_dict); + ASSERT_TRUE(args_dict); + EXPECT_TRUE(args_dict->GetInteger("int_neg_ten", &int_value)); + EXPECT_EQ(-10, int_value); + + // 1f must be serlized to JSON as "1.0" in order to be a double, not an int. + dict = FindNamePhase("event3", "X"); + ASSERT_TRUE(dict); + dict->GetDictionary("args", &args_dict); + ASSERT_TRUE(args_dict); + EXPECT_TRUE(args_dict->Get("float_one", &value)); + EXPECT_TRUE(value->IsType(Value::TYPE_DOUBLE)); + EXPECT_TRUE(value->GetAsDouble(&double_value)); + EXPECT_EQ(1, double_value); + + // .5f must be serlized to JSON as "0.5". + dict = FindNamePhase("event4", "X"); + ASSERT_TRUE(dict); + dict->GetDictionary("args", &args_dict); + ASSERT_TRUE(args_dict); + EXPECT_TRUE(args_dict->Get("float_half", &value)); + EXPECT_TRUE(value->IsType(Value::TYPE_DOUBLE)); + EXPECT_TRUE(value->GetAsDouble(&double_value)); + EXPECT_EQ(0.5, double_value); + + // -.5f must be serlized to JSON as "-0.5". + dict = FindNamePhase("event5", "X"); + ASSERT_TRUE(dict); + dict->GetDictionary("args", &args_dict); + ASSERT_TRUE(args_dict); + EXPECT_TRUE(args_dict->Get("float_neghalf", &value)); + EXPECT_TRUE(value->IsType(Value::TYPE_DOUBLE)); + EXPECT_TRUE(value->GetAsDouble(&double_value)); + EXPECT_EQ(-0.5, double_value); + + // Infinity is serialized to JSON as a string. + dict = FindNamePhase("event6", "X"); + ASSERT_TRUE(dict); + dict->GetDictionary("args", &args_dict); + ASSERT_TRUE(args_dict); + EXPECT_TRUE(args_dict->GetString("float_infinity", &str_value)); + EXPECT_STREQ("Infinity", str_value.c_str()); + dict = FindNamePhase("event6b", "X"); + ASSERT_TRUE(dict); + dict->GetDictionary("args", &args_dict); + ASSERT_TRUE(args_dict); + EXPECT_TRUE(args_dict->GetString("float_neg_infinity", &str_value)); + EXPECT_STREQ("-Infinity", str_value.c_str()); + + // NaN is serialized to JSON as a string. + dict = FindNamePhase("event7", "X"); + ASSERT_TRUE(dict); + dict->GetDictionary("args", &args_dict); + ASSERT_TRUE(args_dict); + EXPECT_TRUE(args_dict->GetString("double_nan", &str_value)); + EXPECT_STREQ("NaN", str_value.c_str()); + + // NULL pointers should be serialized as "0x0". + dict = FindNamePhase("event8", "X"); + ASSERT_TRUE(dict); + dict->GetDictionary("args", &args_dict); + ASSERT_TRUE(args_dict); + EXPECT_TRUE(args_dict->GetString("pointer_null", &str_value)); + EXPECT_STREQ("0x0", str_value.c_str()); + + // Other pointers should be serlized as a hex string. + dict = FindNamePhase("event9", "X"); + ASSERT_TRUE(dict); + dict->GetDictionary("args", &args_dict); + ASSERT_TRUE(args_dict); + EXPECT_TRUE(args_dict->GetString("pointer_badf00d", &str_value)); + EXPECT_STREQ("0xbadf00d", str_value.c_str()); + + dict = FindNamePhase("event10", "X"); + ASSERT_TRUE(dict); + dict->GetDictionary("args", &args_dict); + ASSERT_TRUE(args_dict); + EXPECT_TRUE(args_dict->GetBoolean("bool_true", &bool_value)); + EXPECT_TRUE(bool_value); + + dict = FindNamePhase("event11", "X"); + ASSERT_TRUE(dict); + dict->GetDictionary("args", &args_dict); + ASSERT_TRUE(args_dict); + EXPECT_TRUE(args_dict->GetBoolean("bool_false", &bool_value)); + EXPECT_FALSE(bool_value); + + dict = FindNamePhase("event12", "X"); + ASSERT_TRUE(dict); + dict->GetDictionary("args", &args_dict); + ASSERT_TRUE(args_dict); + EXPECT_TRUE(args_dict->GetInteger("time_null", &int_value)); + EXPECT_EQ(0, int_value); + + dict = FindNamePhase("event13", "X"); + ASSERT_TRUE(dict); + dict->GetDictionary("args", &args_dict); + ASSERT_TRUE(args_dict); + EXPECT_TRUE(args_dict->GetInteger("time_one", &int_value)); + EXPECT_EQ(1, int_value); + + dict = FindNamePhase("event14", "X"); + ASSERT_TRUE(dict); + dict->GetDictionary("args", &args_dict); + ASSERT_TRUE(args_dict); + EXPECT_TRUE(args_dict->GetInteger("timeticks_null", &int_value)); + EXPECT_EQ(0, int_value); + + dict = FindNamePhase("event15", "X"); + ASSERT_TRUE(dict); + dict->GetDictionary("args", &args_dict); + ASSERT_TRUE(args_dict); + EXPECT_TRUE(args_dict->GetInteger("timeticks_one", &int_value)); + EXPECT_EQ(1, int_value); +} + class TraceEventCallbackTest : public TraceEventTestFixture { public: virtual void SetUp() OVERRIDE { @@ -2046,6 +2280,7 @@ TEST_F(TraceEventCallbackTest, TraceEventCallback) { TEST_F(TraceEventCallbackTest, TraceEventCallbackWhileFull) { TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"), + base::debug::TraceLog::RECORDING_MODE, TraceLog::RECORD_UNTIL_FULL); do { TRACE_EVENT_INSTANT0("all", "badger badger", TRACE_EVENT_SCOPE_GLOBAL); @@ -2066,8 +2301,9 @@ TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecording1) { Callback); TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL); TRACE_EVENT_INSTANT0("callback", "yes", TRACE_EVENT_SCOPE_GLOBAL); - TraceLog::GetInstance()->SetEnabled( - CategoryFilter("recording"), TraceLog::RECORD_UNTIL_FULL); + TraceLog::GetInstance()->SetEnabled(CategoryFilter("recording"), + base::debug::TraceLog::RECORDING_MODE, + TraceLog::RECORD_UNTIL_FULL); TRACE_EVENT_INSTANT0("recording", "yes", TRACE_EVENT_SCOPE_GLOBAL); TRACE_EVENT_INSTANT0("callback", "yes", TRACE_EVENT_SCOPE_GLOBAL); TraceLog::GetInstance()->SetEventCallbackDisabled(); @@ -2077,6 +2313,7 @@ TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecording1) { TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL); TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL); + DropTracedMetadataRecords(); VerifyCallbackAndRecordedEvents(2, 2); } @@ -2088,8 +2325,9 @@ TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecording2) { Callback); TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL); TRACE_EVENT_INSTANT0("callback", "yes", TRACE_EVENT_SCOPE_GLOBAL); - TraceLog::GetInstance()->SetEnabled( - CategoryFilter("recording"), TraceLog::RECORD_UNTIL_FULL); + TraceLog::GetInstance()->SetEnabled(CategoryFilter("recording"), + base::debug::TraceLog::RECORDING_MODE, + TraceLog::RECORD_UNTIL_FULL); TRACE_EVENT_INSTANT0("recording", "yes", TRACE_EVENT_SCOPE_GLOBAL); TRACE_EVENT_INSTANT0("callback", "yes", TRACE_EVENT_SCOPE_GLOBAL); EndTraceAndFlush(); @@ -2099,6 +2337,7 @@ TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecording2) { TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL); TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL); + DropTracedMetadataRecords(); VerifyCallbackAndRecordedEvents(3, 1); } @@ -2106,8 +2345,9 @@ TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecording2) { TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecording3) { TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL); TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL); - TraceLog::GetInstance()->SetEnabled( - CategoryFilter("recording"), TraceLog::RECORD_UNTIL_FULL); + TraceLog::GetInstance()->SetEnabled(CategoryFilter("recording"), + base::debug::TraceLog::RECORDING_MODE, + TraceLog::RECORD_UNTIL_FULL); TRACE_EVENT_INSTANT0("recording", "yes", TRACE_EVENT_SCOPE_GLOBAL); TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL); TraceLog::GetInstance()->SetEventCallbackEnabled(CategoryFilter("callback"), @@ -2121,6 +2361,7 @@ TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecording3) { TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL); TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL); + DropTracedMetadataRecords(); VerifyCallbackAndRecordedEvents(1, 3); } @@ -2128,8 +2369,9 @@ TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecording3) { TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecording4) { TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL); TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL); - TraceLog::GetInstance()->SetEnabled( - CategoryFilter("recording"), TraceLog::RECORD_UNTIL_FULL); + TraceLog::GetInstance()->SetEnabled(CategoryFilter("recording"), + base::debug::TraceLog::RECORDING_MODE, + TraceLog::RECORD_UNTIL_FULL); TRACE_EVENT_INSTANT0("recording", "yes", TRACE_EVENT_SCOPE_GLOBAL); TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL); TraceLog::GetInstance()->SetEventCallbackEnabled(CategoryFilter("callback"), @@ -2143,6 +2385,7 @@ TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecording4) { TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL); TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL); + DropTracedMetadataRecords(); VerifyCallbackAndRecordedEvents(2, 2); } @@ -2151,8 +2394,9 @@ TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecordingDuration) { Callback); { TRACE_EVENT0("callback", "duration1"); - TraceLog::GetInstance()->SetEnabled( - CategoryFilter("*"), TraceLog::RECORD_UNTIL_FULL); + TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"), + base::debug::TraceLog::RECORDING_MODE, + TraceLog::RECORD_UNTIL_FULL); TRACE_EVENT0("callback", "duration2"); EndTraceAndFlush(); TRACE_EVENT0("callback", "duration3"); @@ -2170,6 +2414,7 @@ TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecordingDuration) { TEST_F(TraceEventTestFixture, TraceBufferRingBufferGetReturnChunk) { TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"), + base::debug::TraceLog::RECORDING_MODE, TraceLog::RECORD_CONTINUOUSLY); TraceBuffer* buffer = TraceLog::GetInstance()->trace_buffer(); size_t capacity = buffer->Capacity(); @@ -2229,6 +2474,7 @@ TEST_F(TraceEventTestFixture, TraceBufferRingBufferGetReturnChunk) { TEST_F(TraceEventTestFixture, TraceBufferRingBufferHalfIteration) { TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"), + base::debug::TraceLog::RECORDING_MODE, TraceLog::RECORD_CONTINUOUSLY); TraceBuffer* buffer = TraceLog::GetInstance()->trace_buffer(); size_t capacity = buffer->Capacity(); @@ -2256,6 +2502,7 @@ TEST_F(TraceEventTestFixture, TraceBufferRingBufferHalfIteration) { TEST_F(TraceEventTestFixture, TraceBufferRingBufferFullIteration) { TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"), + base::debug::TraceLog::RECORDING_MODE, TraceLog::RECORD_CONTINUOUSLY); TraceBuffer* buffer = TraceLog::GetInstance()->trace_buffer(); size_t capacity = buffer->Capacity(); @@ -2504,6 +2751,7 @@ TEST_F(TraceEventTestFixture, EchoToConsole) { logging::SetLogMessageHandler(MockLogMessageHandler); TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"), + base::debug::TraceLog::RECORDING_MODE, TraceLog::ECHO_TO_CONSOLE); TRACE_EVENT_BEGIN0("a", "begin_end"); { @@ -2539,6 +2787,7 @@ TEST_F(TraceEventTestFixture, EchoToConsoleTraceEventRecursion) { logging::SetLogMessageHandler(LogMessageHandlerWithTraceEvent); TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"), + base::debug::TraceLog::RECORDING_MODE, TraceLog::ECHO_TO_CONSOLE); { // This should not cause deadlock or infinite recursion. @@ -2567,6 +2816,7 @@ TEST_F(TraceEventTestFixture, TimeOffset) { TimeTicks::NowFromSystemTraceTime().ToInternalValue()); EndTraceAndFlush(); + DropTracedMetadataRecords(); double end_time = static_cast<double>( (TimeTicks::NowFromSystemTraceTime() - time_offset).ToInternalValue()); @@ -2582,5 +2832,48 @@ TEST_F(TraceEventTestFixture, TimeOffset) { } } +TEST_F(TraceEventTestFixture, ConfigureSyntheticDelays) { + BeginSpecificTrace("DELAY(test.Delay;0.05)"); + + base::TimeTicks start = base::TimeTicks::Now(); + { + TRACE_EVENT_SYNTHETIC_DELAY("test.Delay"); + } + base::TimeDelta duration = base::TimeTicks::Now() - start; + EXPECT_GE(duration.InMilliseconds(), 50); + + EndTraceAndFlush(); +} + +TEST_F(TraceEventTestFixture, BadSyntheticDelayConfigurations) { + const char* configs[] = { + "", + "DELAY(", + "DELAY(;", + "DELAY(;)", + "DELAY(test.Delay)", + "DELAY(test.Delay;)" + }; + for (size_t i = 0; i < arraysize(configs); i++) { + BeginSpecificTrace(configs[i]); + EndTraceAndFlush(); + CategoryFilter filter = TraceLog::GetInstance()->GetCurrentCategoryFilter(); + EXPECT_EQ(0u, filter.GetSyntheticDelayValues().size()); + } +} + +TEST_F(TraceEventTestFixture, SyntheticDelayConfigurationMerging) { + CategoryFilter filter1("DELAY(test.Delay1;16)"); + CategoryFilter filter2("DELAY(test.Delay2;32)"); + filter1.Merge(filter2); + EXPECT_EQ(2u, filter1.GetSyntheticDelayValues().size()); +} + +TEST_F(TraceEventTestFixture, SyntheticDelayConfigurationToString) { + const char config[] = "DELAY(test.Delay;16;oneshot)"; + CategoryFilter filter(config); + EXPECT_EQ(config, filter.ToString()); +} + } // namespace debug } // namespace base diff --git a/chromium/base/debug/tsan_suppressions.cc b/chromium/base/debug/tsan_suppressions.cc new file mode 100644 index 00000000000..70ce21ee563 --- /dev/null +++ b/chromium/base/debug/tsan_suppressions.cc @@ -0,0 +1,297 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file contains the default suppressions for ThreadSanitizer. +// You can also pass additional suppressions via TSAN_OPTIONS: +// TSAN_OPTIONS=suppressions=/path/to/suppressions. Please refer to +// http://dev.chromium.org/developers/testing/threadsanitizer-tsan-v2 +// for more info. + +#if defined(THREAD_SANITIZER) + +// Please make sure the code below declares a single string variable +// kTSanDefaultSuppressions contains TSan suppressions delimited by newlines. +// See http://dev.chromium.org/developers/testing/threadsanitizer-tsan-v2 +// for the instructions on writing suppressions. +char kTSanDefaultSuppressions[] = +// False positives in libflashplayer.so and libglib.so. Since we don't +// instrument them, we cannot reason about the synchronization in them. +"race:libflashplayer.so\n" +"race:libglib*.so\n" + +// Intentional race in ToolsSanityTest.DataRace in base_unittests. +"race:base/tools_sanity_unittest.cc\n" + +// Data race on WatchdogCounter [test-only]. +"race:base/threading/watchdog_unittest.cc\n" + +// Races in libevent, http://crbug.com/23244. +"race:libevent/event.c\n" + +// http://crbug.com/46840. +"race:base::HistogramSamples::IncreaseSum\n" +"race:base::Histogram::Add\n" +"race:base::HistogramSamples::Add\n" + +// http://crbug.com/84094. +"race:sqlite3StatusSet\n" +"race:pcache1EnforceMaxPage\n" +"race:pcache1AllocPage\n" + +// http://crbug.com/102327. +// Test-only race, won't fix. +"race:tracked_objects::ThreadData::ShutdownSingleThreadedCleanup\n" + +// http://crbug.com/115540 +"race:*GetCurrentThreadIdentifier\n" + +// http://crbug.com/120808 +"race:base/threading/watchdog.cc\n" + +// http://crbug.com/157586 +"race:third_party/libvpx/source/libvpx/vp8/decoder/threading.c\n" + +// http://crbug.com/158718 +"race:third_party/ffmpeg/libavcodec/pthread.c\n" +"race:third_party/ffmpeg/libavcodec/pthread_frame.c\n" +"race:third_party/ffmpeg/libavcodec/vp8.c\n" +"race:third_party/ffmpeg/libavutil/mem.c\n" +"race:*HashFrameForTesting\n" +"race:third_party/ffmpeg/libavcodec/h264pred.c\n" +"race:media::ReleaseData\n" + +// http://crbug.com/158922 +"race:third_party/libvpx/source/libvpx/vp8/encoder/*\n" + +// http://crbug.com/189177 +"race:thread_manager\n" +"race:v8::Locker::Initialize\n" + +// http://crbug.com/223352 +"race:uprv_malloc_46\n" +"race:uprv_realloc_46\n" + +// http://crbug.com/239359 +"race:media::TestInputCallback::OnData\n" + +// http://crbug.com/244368 +"race:skia::BeginPlatformPaint\n" + +// http://crbug.com/244385 +"race:unixTempFileDir\n" + +// http://crbug.com/244755 +"race:v8::internal::Zone::NewExpand\n" +"race:TooLateToEnableNow\n" +"race:adjust_segment_bytes_allocated\n" + +// http://crbug.com/244774 +"race:webrtc::RTPReceiver::ProcessBitrate\n" +"race:webrtc::RTPSender::ProcessBitrate\n" +"race:webrtc::VideoCodingModuleImpl::Decode\n" +"race:webrtc::RTPSender::SendOutgoingData\n" +"race:webrtc::VP8EncoderImpl::GetEncodedPartitions\n" +"race:webrtc::VP8EncoderImpl::Encode\n" +"race:webrtc::ViEEncoder::DeliverFrame\n" +"race:webrtc::vcm::VideoReceiver::Decode\n" +"race:webrtc::VCMReceiver::FrameForDecoding\n" +"race:*trace_event_unique_catstatic*\n" + +// http://crbug.com/244856 +"race:AutoPulseLock\n" + +// http://crbug.com/246968 +"race:webrtc::VideoCodingModuleImpl::RegisterPacketRequestCallback\n" + +// http://crbug.com/246970 +"race:webrtc::EventPosix::StartTimer\n" + +// http://crbug.com/246974 +"race:content::GpuWatchdogThread::CheckArmed\n" + +// http://crbug.com/257396 +"race:base::debug::TraceEventTestFixture_TraceSamplingScope_Test::TestBody\n" + +// http://crbug.com/258479 +"race:SamplingStateScope\n" +"race:g_trace_state\n" + +// http://crbug.com/258499 +"race:third_party/skia/include/core/SkRefCnt.h\n" + +// http://crbug.com/268924 +"race:base::g_power_monitor\n" +"race:base::PowerMonitor::PowerMonitor\n" +"race:base::PowerMonitor::AddObserver\n" + +// http://crbug.com/268941 +"race:tracked_objects::ThreadData::tls_index_\n" + +// http://crbug.com/270037 +"race:gLibCleanupFunctions\n" + +// http://crbug.com/272095 +"race:base::g_top_manager\n" + +// http://crbug.com/272987 +"race:webrtc::MediaStreamTrack<webrtc::AudioTrackInterface>::set_enabled\n" + +// http://crbug.com/273047 +"race:base::*::g_lazy_tls_ptr\n" +"race:IPC::SyncChannel::ReceivedSyncMsgQueue::lazy_tls_ptr_\n" + +// http://crbug.com/280466 +"race:content::WebRtcAudioCapturer::SetCapturerSource\n" + +// http://crbug.com/285242 +"race:media::PulseAudioOutputStream::SetVolume\n" + +// http://crbug.com/290964 +"race:PostponeInterruptsScope\n" +"race:v8::internal::StackGuard::RequestInstallCode\n" + +// http://crbug.com/296883 +"race:net::URLFetcherCore::Stop\n" + +// http://crbug.com/308590 +"race:CustomThreadWatcher::~CustomThreadWatcher\n" + +// http://crbug.com/310851 +"race:net::ProxyResolverV8Tracing::Job::~Job\n" + +// http://crbug.com/313726 +"race:CallbackWasCalled\n" + +// http://crbug.com/327330 +"race:PrepareTextureMailbox\n" +"race:cc::LayerTreeHost::PaintLayerContents\n" + +// http://crbug.com/328804 +"race:v8::internal::Heap::SetStackLimits\n" +"race:ScavengePointer\n" + +// http://crbug.com/328826 +"race:gLCDOrder\n" +"race:gLCDOrientation\n" + +// http://crbug.com/328868 +"race:PR_Lock\n" + +// http://crbug.com/329225 +"race:blink::currentTimeFunction\n" + +// http://crbug.com/329460 +"race:extensions::InfoMap::AddExtension\n" + +// http://crbug.com/330528 +"race:v8::internal::MarkCompactCollector::SweepInParallel\n" + +// http://crbug.com/333244 +"race:content::" + "VideoCaptureImplTest::MockVideoCaptureImpl::~MockVideoCaptureImpl\n" + +// http://crbug.com/333871 +"race:v8::internal::Interface::NewValue()::value_interface\n" +"race:v8::internal::IsMinusZero(double)::minus_zero\n" +"race:v8::internal::FastCloneShallowObjectStub::InitializeInterfaceDescriptor\n" +"race:v8::internal::KeyedLoadStubCompiler::registers\n" +"race:v8::internal::KeyedStoreStubCompiler::registers()::registers\n" +"race:v8::internal::KeyedLoadFastElementStub::InitializeInterfaceDescriptor\n" +"race:v8::internal::KeyedStoreFastElementStub::InitializeInterfaceDescriptor\n" +"race:v8::internal::LoadStubCompiler::registers\n" +"race:v8::internal::StoreStubCompiler::registers\n" +"race:v8::internal::HValue::LoopWeight\n" + +// http://crbug.com/334140 +"race:CommandLine::HasSwitch\n" +"race:CommandLine::current_process_commandline_\n" +"race:CommandLine::GetSwitchValueASCII\n" + +// http://crbug.com/338675 +"race:blink::s_platform\n" +"race:content::" + "RendererWebKitPlatformSupportImpl::~RendererWebKitPlatformSupportImpl\n" + +// http://crbug.com/345240 +"race:WTF::s_shutdown\n" + +// http://crbug.com/345245 +"race:jingle_glue::JingleThreadWrapper::~JingleThreadWrapper\n" +"race:webrtc::voe::Channel::UpdatePacketDelay\n" +"race:webrtc::voe::Channel::GetDelayEstimate\n" +"race:webrtc::VCMCodecDataBase::DeregisterReceiveCodec\n" +"race:webrtc::GainControlImpl::set_stream_analog_level\n" + +// http://crbug.com/345618 +"race:WebCore::AudioDestinationNode::render\n" + +// http://crbug.com/345624 +"race:media::DataSource::set_host\n" + +// http://crbug.com/347534 +"race:v8::internal::V8::TearDown\n" + +// http://crbug.com/347538 +"race:sctp_timer_start\n" + +// http://crbug.com/347548 +"race:cricket::WebRtcVideoMediaChannel::MaybeResetVieSendCodec\n" +"race:cricket::WebRtcVideoMediaChannel::SetSendCodec\n" + +// http://crbug.com/347553 +"race:blink::WebString::reset\n" + +// http://crbug.com/348511 +"race:webrtc::acm1::AudioCodingModuleImpl::PlayoutData10Ms\n" + +// http://crbug.com/348982 +"race:cricket::P2PTransportChannel::OnConnectionDestroyed\n" +"race:cricket::P2PTransportChannel::AddConnection\n" + +// http://crbug.com/348984 +"race:sctp_express_handle_sack\n" + +// http://crbug.com/350982 +"race:libvpx/vp9/decoder/vp9_thread.c\n" + +// http://crbug.com/363999 +"race:v8::internal::EnterDebugger::*EnterDebugger\n" + +// http://crbug.com/364006 +"race:gfx::ImageFamily::~ImageFamily\n" + +// http://crbug.com/364014 +"race:WTF::Latin1Encoding()::globalLatin1Encoding\n" + +// https://code.google.com/p/v8/issues/detail?id=3143 +"race:v8::internal::FLAG_track_double_fields\n" + +// https://crbug.com/369257 +// TODO(mtklein): annotate properly and remove suppressions. +"race:SandboxIPCHandler::HandleFontMatchRequest\n" +"race:SkFontConfigInterfaceDirect::matchFamilyName\n" +"race:SkFontConfigInterface::GetSingletonDirectInterface\n" +"race:FcStrStaticName\n" + +// http://crbug.com/372807 +"deadlock:net::X509Certificate::CreateCertificateListFromBytes\n" +"deadlock:net::X509Certificate::CreateFromBytes\n" +"deadlock:net::SSLClientSocketNSS::Core::DoHandshakeLoop\n" + +// http://crbug.com/374135 +"race:media::AlsaWrapper::PcmWritei\n" + +// False positive in libc's tzset_internal, http://crbug.com/379738. +"race:tzset_internal\n" + +// http://crbug.com/380554 +"deadlock:g_type_add_interface_static\n" + +// http:://crbug.com/386385 +"race:appcache::AppCacheStorageImpl::DatabaseTask::CallRunCompleted\n" + +// End of suppressions. +; // Please keep this semicolon. + +#endif // THREAD_SANITIZER diff --git a/chromium/base/file_descriptor_posix.h b/chromium/base/file_descriptor_posix.h index abc07893faf..c730be65f74 100644 --- a/chromium/base/file_descriptor_posix.h +++ b/chromium/base/file_descriptor_posix.h @@ -5,6 +5,8 @@ #ifndef BASE_FILE_DESCRIPTOR_POSIX_H_ #define BASE_FILE_DESCRIPTOR_POSIX_H_ +#include "base/files/file.h" + namespace base { // ----------------------------------------------------------------------------- @@ -16,18 +18,21 @@ namespace base { // above the template specialisation for this structure. // ----------------------------------------------------------------------------- struct FileDescriptor { - FileDescriptor() - : fd(-1), - auto_close(false) { } + FileDescriptor() : fd(-1), auto_close(false) {} + + FileDescriptor(int ifd, bool iauto_close) : fd(ifd), auto_close(iauto_close) { + } - FileDescriptor(int ifd, bool iauto_close) - : fd(ifd), - auto_close(iauto_close) { } + FileDescriptor(File file) : fd(file.TakePlatformFile()), auto_close(true) {} bool operator==(const FileDescriptor& other) const { return (fd == other.fd && auto_close == other.auto_close); } + bool operator!=(const FileDescriptor& other) const { + return !operator==(other); + } + // A comparison operator so that we can use these as keys in a std::map. bool operator<(const FileDescriptor& other) const { return other.fd < fd; diff --git a/chromium/base/file_util.cc b/chromium/base/file_util.cc index 1575a079d6b..d11cd15a2e0 100644 --- a/chromium/base/file_util.cc +++ b/chromium/base/file_util.cc @@ -10,6 +10,7 @@ #include <stdio.h> #include <fstream> +#include <limits> #include "base/files/file_enumerator.h" #include "base/files/file_path.h" @@ -126,7 +127,11 @@ bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2) { return true; } -bool ReadFileToString(const FilePath& path, std::string* contents) { +bool ReadFileToString(const FilePath& path, + std::string* contents, + size_t max_size) { + if (contents) + contents->clear(); if (path.ReferencesParent()) return false; FILE* file = OpenFile(path, "rb"); @@ -136,13 +141,30 @@ bool ReadFileToString(const FilePath& path, std::string* contents) { char buf[1 << 16]; size_t len; + size_t size = 0; + bool read_status = true; + + // Many files supplied in |path| have incorrect size (proc files etc). + // Hence, the file is read sequentially as opposed to a one-shot read. while ((len = fread(buf, 1, sizeof(buf), file)) > 0) { if (contents) - contents->append(buf, len); + contents->append(buf, std::min(len, max_size - size)); + + if ((max_size - size) < len) { + read_status = false; + break; + } + + size += len; } + read_status = read_status && !ferror(file); CloseFile(file); - return true; + return read_status; +} + +bool ReadFileToString(const FilePath& path, std::string* contents) { + return ReadFileToString(path, contents, std::numeric_limits<size_t>::max()); } bool IsDirectoryEmpty(const FilePath& dir_path) { @@ -166,7 +188,7 @@ bool CreateDirectory(const FilePath& full_path) { } bool GetFileSize(const FilePath& file_path, int64* file_size) { - PlatformFileInfo info; + File::Info info; if (!GetFileInfo(file_path, &info)) return false; *file_size = info.size; @@ -176,22 +198,19 @@ bool GetFileSize(const FilePath& file_path, int64* file_size) { bool TouchFile(const FilePath& path, const Time& last_accessed, const Time& last_modified) { - int flags = PLATFORM_FILE_OPEN | PLATFORM_FILE_WRITE_ATTRIBUTES; + int flags = File::FLAG_OPEN | File::FLAG_WRITE_ATTRIBUTES; #if defined(OS_WIN) // On Windows, FILE_FLAG_BACKUP_SEMANTICS is needed to open a directory. if (DirectoryExists(path)) - flags |= PLATFORM_FILE_BACKUP_SEMANTICS; + flags |= File::FLAG_BACKUP_SEMANTICS; #endif // OS_WIN - const PlatformFile file = CreatePlatformFile(path, flags, NULL, NULL); - if (file != kInvalidPlatformFileValue) { - bool result = TouchPlatformFile(file, last_accessed, last_modified); - ClosePlatformFile(file); - return result; - } + File file(path, flags); + if (!file.IsValid()) + return false; - return false; + return file.SetTimes(last_accessed, last_modified); } bool CloseFile(FILE* file) { @@ -218,18 +237,8 @@ bool TruncateFile(FILE* file) { return true; } -} // namespace base - -// ----------------------------------------------------------------------------- - -namespace file_util { - -using base::FilePath; -using base::kMaxUniqueFiles; - -int GetUniquePathNumber( - const FilePath& path, - const FilePath::StringType& suffix) { +int GetUniquePathNumber(const FilePath& path, + const FilePath::StringType& suffix) { bool have_suffix = !suffix.empty(); if (!PathExists(path) && (!have_suffix || !PathExists(FilePath(path.value() + suffix)))) { @@ -238,8 +247,7 @@ int GetUniquePathNumber( FilePath new_path; for (int count = 1; count <= kMaxUniqueFiles; ++count) { - new_path = - path.InsertBeforeExtensionASCII(base::StringPrintf(" (%d)", count)); + new_path = path.InsertBeforeExtensionASCII(StringPrintf(" (%d)", count)); if (!PathExists(new_path) && (!have_suffix || !PathExists(FilePath(new_path.value() + suffix)))) { return count; @@ -249,4 +257,4 @@ int GetUniquePathNumber( return -1; } -} // namespace file_util +} // namespace base diff --git a/chromium/base/file_util.h b/chromium/base/file_util.h index 3f892e3ad14..bb04e629771 100644 --- a/chromium/base/file_util.h +++ b/chromium/base/file_util.h @@ -25,9 +25,9 @@ #include "base/base_export.h" #include "base/basictypes.h" +#include "base/files/file.h" #include "base/files/file_path.h" #include "base/memory/scoped_ptr.h" -#include "base/platform_file.h" #include "base/strings/string16.h" #if defined(OS_POSIX) @@ -93,10 +93,13 @@ BASE_EXPORT bool Move(const FilePath& from_path, const FilePath& to_path); // Returns false on failure and sets *error appropriately, if it is non-NULL. BASE_EXPORT bool ReplaceFile(const FilePath& from_path, const FilePath& to_path, - PlatformFileError* error); + File::Error* error); // Copies a single file. Use CopyDirectory to copy directories. // This function fails if either path contains traversal components ('..'). +// +// This function keeps the metadata on Windows. The read only bit on Windows is +// not kept. BASE_EXPORT bool CopyFile(const FilePath& from_path, const FilePath& to_path); // Copies the given path, and optionally all subdirectories and their contents @@ -105,6 +108,9 @@ BASE_EXPORT bool CopyFile(const FilePath& from_path, const FilePath& to_path); // If there are files existing under to_path, always overwrite. Returns true // if successful, false otherwise. Wildcards on the names are not supported. // +// This function calls into CopyFile() so the same behavior w.r.t. metadata +// applies. +// // If you only need to copy a file use CopyFile, it's faster. BASE_EXPORT bool CopyDirectory(const FilePath& from_path, const FilePath& to_path, @@ -130,13 +136,28 @@ BASE_EXPORT bool ContentsEqual(const FilePath& filename1, BASE_EXPORT bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2); -// Read the file at |path| into |contents|, returning true on success. -// This function fails if the |path| contains path traversal components ('..'). -// |contents| may be NULL, in which case this function is useful for its -// side effect of priming the disk cache. -// Useful for unit tests. +// Reads the file at |path| into |contents| and returns true on success and +// false on error. For security reasons, a |path| containing path traversal +// components ('..') is treated as a read error and |contents| is set to empty. +// In case of I/O error, |contents| holds the data that could be read from the +// file before the error occurred. +// |contents| may be NULL, in which case this function is useful for its side +// effect of priming the disk cache (could be used for unit tests). BASE_EXPORT bool ReadFileToString(const FilePath& path, std::string* contents); +// Reads the file at |path| into |contents| and returns true on success and +// false on error. For security reasons, a |path| containing path traversal +// components ('..') is treated as a read error and |contents| is set to empty. +// In case of I/O error, |contents| holds the data that could be read from the +// file before the error occurred. When the file size exceeds |max_size|, the +// function returns false with |contents| holding the file truncated to +// |max_size|. +// |contents| may be NULL, in which case this function is useful for its side +// effect of priming the disk cache (could be used for unit tests). +BASE_EXPORT bool ReadFileToString(const FilePath& path, + std::string* contents, + size_t max_size); + #if defined(OS_POSIX) // Read exactly |bytes| bytes from file descriptor |fd|, storing the result @@ -153,7 +174,7 @@ BASE_EXPORT bool CreateSymbolicLink(const FilePath& target, // Returns false upon failure. BASE_EXPORT bool ReadSymbolicLink(const FilePath& symlink, FilePath* target); -// Bits ans masks of the file permission. +// Bits and masks of the file permission. enum FilePermissionBits { FILE_PERMISSION_MASK = S_IRWXU | S_IRWXG | S_IRWXO, FILE_PERMISSION_USER_MASK = S_IRWXU, @@ -192,25 +213,13 @@ BASE_EXPORT bool IsDirectoryEmpty(const FilePath& dir_path); // they're open (which can lead to security issues). BASE_EXPORT bool GetTempDir(FilePath* path); -// Get a temporary directory for shared memory files. The directory may depend -// on whether the destination is intended for executable files, which in turn -// depends on how /dev/shmem was mounted. As a result, you must supply whether -// you intend to create executable shmem segments so this function can find -// an appropriate location. -// -// Only useful on POSIX; redirects to GetTempDir() on Windows. -BASE_EXPORT bool GetShmemTempDir(bool executable, FilePath* path); - -#if defined(OS_POSIX) -// Get the home directory. This is more complicated than just getenv("HOME") +// Get the home directory. This is more complicated than just getenv("HOME") // as it knows to fall back on getpwent() etc. // -// This function is not currently implemented on Windows or Mac because we -// don't use it. Generally you would use one of PathService's APP_DATA -// directories on those platforms. If we need it, this could be implemented -// there to return the appropriate directory. +// You should not generally call this directly. Instead use DIR_HOME with the +// path service which will use this function but cache the value. +// Path service may also override DIR_HOME. BASE_EXPORT FilePath GetHomeDir(); -#endif // OS_POSIX // Creates a temporary file. The full path is placed in |path|, and the // function returns true if was successful in creating the file. The file will @@ -226,12 +235,6 @@ BASE_EXPORT bool CreateTemporaryFileInDir(const FilePath& dir, // Returns a handle to the opened file or NULL if an error occurred. BASE_EXPORT FILE* CreateAndOpenTemporaryFile(FilePath* path); -// Like above but for shmem files. Only useful for POSIX. -// The executable flag says the file needs to support using -// mprotect with PROT_EXEC after mapping. -BASE_EXPORT FILE* CreateAndOpenTemporaryShmemFile(FilePath* path, - bool executable); - // Similar to CreateAndOpenTemporaryFile, but the file is created in |dir|. BASE_EXPORT FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path); @@ -256,7 +259,7 @@ BASE_EXPORT bool CreateTemporaryDirInDir(const FilePath& base_dir, // Returns true on success, leaving *error unchanged. // Returns false on failure and sets *error appropriately, if it is non-NULL. BASE_EXPORT bool CreateDirectoryAndGetError(const FilePath& full_path, - PlatformFileError* error); + File::Error* error); // Backward-compatible convenience method for the above. BASE_EXPORT bool CreateDirectory(const FilePath& full_path); @@ -292,7 +295,7 @@ BASE_EXPORT bool NormalizeToNativeFilePath(const FilePath& path, BASE_EXPORT bool IsLink(const FilePath& file_path); // Returns information about the given file path. -BASE_EXPORT bool GetFileInfo(const FilePath& file_path, PlatformFileInfo* info); +BASE_EXPORT bool GetFileInfo(const FilePath& file_path, File::Info* info); // Sets the time of the last access and the time of the last modification. BASE_EXPORT bool TouchFile(const FilePath& path, @@ -305,52 +308,45 @@ BASE_EXPORT FILE* OpenFile(const FilePath& filename, const char* mode); // Closes file opened by OpenFile. Returns true on success. BASE_EXPORT bool CloseFile(FILE* file); +// Associates a standard FILE stream with an existing File. Note that this +// functions take ownership of the existing File. +BASE_EXPORT FILE* FileToFILE(File file, const char* mode); + // Truncates an open file to end at the location of the current file pointer. // This is a cross-platform analog to Windows' SetEndOfFile() function. BASE_EXPORT bool TruncateFile(FILE* file); -// Reads the given number of bytes from the file into the buffer. Returns -// the number of read bytes, or -1 on error. -BASE_EXPORT int ReadFile(const base::FilePath& filename, char* data, int size); - -} // namespace base - -// ----------------------------------------------------------------------------- - -namespace file_util { +// Reads at most the given number of bytes from the file into the buffer. +// Returns the number of read bytes, or -1 on error. +BASE_EXPORT int ReadFile(const FilePath& filename, char* data, int max_size); // Writes the given buffer into the file, overwriting any data that was // previously there. Returns the number of bytes written, or -1 on error. -BASE_EXPORT int WriteFile(const base::FilePath& filename, const char* data, +BASE_EXPORT int WriteFile(const FilePath& filename, const char* data, int size); + #if defined(OS_POSIX) // Append the data to |fd|. Does not close |fd| when done. BASE_EXPORT int WriteFileDescriptor(const int fd, const char* data, int size); #endif + // Append the given buffer into the file. Returns the number of bytes written, // or -1 on error. -BASE_EXPORT int AppendToFile(const base::FilePath& filename, +BASE_EXPORT int AppendToFile(const FilePath& filename, const char* data, int size); // Gets the current working directory for the process. -BASE_EXPORT bool GetCurrentDirectory(base::FilePath* path); +BASE_EXPORT bool GetCurrentDirectory(FilePath* path); // Sets the current working directory for the process. -BASE_EXPORT bool SetCurrentDirectory(const base::FilePath& path); +BASE_EXPORT bool SetCurrentDirectory(const FilePath& path); // Attempts to find a number that can be appended to the |path| to make it // unique. If |path| does not exist, 0 is returned. If it fails to find such // a number, -1 is returned. If |suffix| is not empty, also checks the // existence of it with the given suffix. -BASE_EXPORT int GetUniquePathNumber(const base::FilePath& path, - const base::FilePath::StringType& suffix); - -#if defined(OS_POSIX) -// Creates a directory with a guaranteed unique name based on |path|, returning -// the pathname if successful, or an empty path if there was an error creating -// the directory. Does not create parent directories. -BASE_EXPORT base::FilePath MakeUniqueDirectory(const base::FilePath& path); -#endif +BASE_EXPORT int GetUniquePathNumber(const FilePath& path, + const FilePath::StringType& suffix); #if defined(OS_POSIX) // Test that |path| can only be changed by a given user and members of @@ -385,33 +381,6 @@ BASE_EXPORT bool VerifyPathControlledByAdmin(const base::FilePath& path); // the directory |path|, in the number of FilePath::CharType, or -1 on failure. BASE_EXPORT int GetMaximumPathComponentLength(const base::FilePath& path); -// A class to handle auto-closing of FILE*'s. -class ScopedFILEClose { - public: - inline void operator()(FILE* x) const { - if (x) { - fclose(x); - } - } -}; - -typedef scoped_ptr_malloc<FILE, ScopedFILEClose> ScopedFILE; - -#if defined(OS_POSIX) -// A class to handle auto-closing of FDs. -class ScopedFDClose { - public: - inline void operator()(int* x) const { - if (x && *x >= 0) { - if (IGNORE_EINTR(close(*x)) < 0) - DPLOG(ERROR) << "close"; - } - } -}; - -typedef scoped_ptr_malloc<int, ScopedFDClose> ScopedFD; -#endif // OS_POSIX - #if defined(OS_LINUX) // Broad categories of file systems as returned by statfs() on Linux. enum FileSystemType { @@ -429,10 +398,35 @@ enum FileSystemType { // Attempts determine the FileSystemType for |path|. // Returns false if |path| doesn't exist. -BASE_EXPORT bool GetFileSystemType(const base::FilePath& path, - FileSystemType* type); +BASE_EXPORT bool GetFileSystemType(const FilePath& path, FileSystemType* type); #endif +#if defined(OS_POSIX) +// Get a temporary directory for shared memory files. The directory may depend +// on whether the destination is intended for executable files, which in turn +// depends on how /dev/shmem was mounted. As a result, you must supply whether +// you intend to create executable shmem segments so this function can find +// an appropriate location. +BASE_EXPORT bool GetShmemTempDir(bool executable, FilePath* path); +#endif + +} // namespace base + +// ----------------------------------------------------------------------------- + +namespace file_util { + +// Functor for |ScopedFILE| (below). +struct ScopedFILEClose { + inline void operator()(FILE* x) const { + if (x) + fclose(x); + } +}; + +// Automatically closes |FILE*|s. +typedef scoped_ptr<FILE, ScopedFILEClose> ScopedFILE; + } // namespace file_util // Internal -------------------------------------------------------------------- diff --git a/chromium/base/file_util_linux.cc b/chromium/base/file_util_linux.cc index e4f0e28b129..2910c9cb4ec 100644 --- a/chromium/base/file_util_linux.cc +++ b/chromium/base/file_util_linux.cc @@ -4,14 +4,30 @@ #include "base/file_util.h" -#include "base/files/file_path.h" - #include <errno.h> +#include <linux/magic.h> #include <sys/vfs.h> -namespace file_util { +#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 base::FilePath& path, FileSystemType* type) { +bool GetFileSystemType(const FilePath& path, FileSystemType* type) { struct statfs statfs_buf; if (statfs(path.value().c_str(), &statfs_buf) < 0) { if (errno == ENOENT) @@ -20,37 +36,37 @@ bool GetFileSystemType(const base::FilePath& path, FileSystemType* type) { return true; } - // While you would think the possible values of f_type would be available - // in a header somewhere, it appears that is not the case. These values - // are copied from the statfs man page. + // Not all possible |statfs_buf.f_type| values are in linux/magic.h. + // Missing values are copied from the statfs man page. switch (statfs_buf.f_type) { case 0: *type = FILE_SYSTEM_0; break; - case 0xEF53: // ext2, ext3. - case 0x4D44: // dos - case 0x5346544E: // NFTS - case 0x52654973: // reiser + case EXT2_SUPER_MAGIC: // Also ext3 and ext4 + case MSDOS_SUPER_MAGIC: + case REISERFS_SUPER_MAGIC: + case BTRFS_SUPER_MAGIC: + case 0x5346544E: // NTFS case 0x58465342: // XFS - case 0x9123683E: // btrfs case 0x3153464A: // JFS *type = FILE_SYSTEM_ORDINARY; break; - case 0x6969: // NFS + case NFS_SUPER_MAGIC: *type = FILE_SYSTEM_NFS; break; + case SMB_SUPER_MAGIC: case 0xFF534D42: // CIFS - case 0x517B: // SMB *type = FILE_SYSTEM_SMB; break; - case 0x73757245: // Coda + case CODA_SUPER_MAGIC: *type = FILE_SYSTEM_CODA; break; - case 0x858458f6: // ramfs - case 0x01021994: // tmpfs + case HUGETLBFS_MAGIC: + case RAMFS_MAGIC: + case TMPFS_MAGIC: *type = FILE_SYSTEM_MEMORY; break; - case 0x27e0eb: // CGROUP + case CGROUP_SUPER_MAGIC: *type = FILE_SYSTEM_CGROUP; break; default: @@ -59,4 +75,4 @@ bool GetFileSystemType(const base::FilePath& path, FileSystemType* type) { return true; } -} // namespace +} // namespace base diff --git a/chromium/base/file_util_mac.mm b/chromium/base/file_util_mac.mm index f8a6b6301f5..4aa6d552a76 100644 --- a/chromium/base/file_util_mac.mm +++ b/chromium/base/file_util_mac.mm @@ -19,7 +19,7 @@ namespace internal { bool CopyFileUnsafe(const FilePath& from_path, const FilePath& to_path) { ThreadRestrictions::AssertIOAllowed(); return (copyfile(from_path.value().c_str(), - to_path.value().c_str(), NULL, COPYFILE_ALL) == 0); + to_path.value().c_str(), NULL, COPYFILE_DATA) == 0); } } // namespace internal @@ -32,8 +32,21 @@ bool GetTempDir(base::FilePath* path) { return true; } -bool GetShmemTempDir(bool executable, base::FilePath* path) { - return GetTempDir(path); +FilePath GetHomeDir() { + NSString* tmp = NSHomeDirectory(); + if (tmp != nil) { + FilePath mac_home_dir = base::mac::NSStringToFilePath(tmp); + if (!mac_home_dir.empty()) + return mac_home_dir; + } + + // Fall back on temp dir if no home directory is defined. + FilePath rv; + if (GetTempDir(&rv)) + return rv; + + // Last resort. + return FilePath("/tmp"); } } // namespace base diff --git a/chromium/base/file_util_posix.cc b/chromium/base/file_util_posix.cc index ddcd5ddb912..92e8cad1605 100644 --- a/chromium/base/file_util_posix.cc +++ b/chromium/base/file_util_posix.cc @@ -33,6 +33,7 @@ #include "base/basictypes.h" #include "base/files/file_enumerator.h" #include "base/files/file_path.h" +#include "base/files/scoped_file.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/memory/singleton.h" @@ -60,8 +61,7 @@ namespace base { namespace { -#if defined(OS_BSD) || defined(OS_MACOSX) -typedef struct stat stat_wrapper_t; +#if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) static int CallStat(const char *path, stat_wrapper_t *sb) { ThreadRestrictions::AssertIOAllowed(); return stat(path, sb); @@ -70,8 +70,7 @@ static int CallLstat(const char *path, stat_wrapper_t *sb) { ThreadRestrictions::AssertIOAllowed(); return lstat(path, sb); } -#else -typedef struct stat64 stat_wrapper_t; +#else // defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) static int CallStat(const char *path, stat_wrapper_t *sb) { ThreadRestrictions::AssertIOAllowed(); return stat64(path, sb); @@ -80,13 +79,7 @@ static int CallLstat(const char *path, stat_wrapper_t *sb) { ThreadRestrictions::AssertIOAllowed(); return lstat64(path, sb); } -#if defined(OS_ANDROID) -static int CallFstat(int fd, stat_wrapper_t *sb) { - ThreadRestrictions::AssertIOAllowed(); - return fstat64(fd, sb); -} -#endif -#endif +#endif // !(defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL)) // Helper for NormalizeFilePath(), defined below. bool RealPath(const FilePath& path, FilePath* real_path) { @@ -172,15 +165,15 @@ int CreateAndOpenFdForTemporaryFile(FilePath directory, FilePath* path) { bool DetermineDevShmExecutable() { bool result = false; FilePath path; - int fd = CreateAndOpenFdForTemporaryFile(FilePath("/dev/shm"), &path); - if (fd >= 0) { - file_util::ScopedFD shm_fd_closer(&fd); + + ScopedFD fd(CreateAndOpenFdForTemporaryFile(FilePath("/dev/shm"), &path)); + if (fd.is_valid()) { DeleteFile(path, false); long sysconf_result = sysconf(_SC_PAGESIZE); CHECK_GE(sysconf_result, 0); size_t pagesize = static_cast<size_t>(sysconf_result); CHECK_GE(sizeof(pagesize), sizeof(sysconf_result)); - void *mapping = mmap(NULL, pagesize, PROT_READ, MAP_SHARED, fd, 0); + void* mapping = mmap(NULL, pagesize, PROT_READ, MAP_SHARED, fd.get(), 0); if (mapping != MAP_FAILED) { if (mprotect(mapping, pagesize, PROT_READ | PROT_EXEC) == 0) result = true; @@ -244,12 +237,12 @@ bool DeleteFile(const FilePath& path, bool recursive) { bool ReplaceFile(const FilePath& from_path, const FilePath& to_path, - PlatformFileError* error) { + File::Error* error) { ThreadRestrictions::AssertIOAllowed(); if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0) return true; if (error) - *error = ErrnoToPlatformFileError(errno); + *error = File::OSErrorToFileError(errno); return false; } @@ -260,13 +253,10 @@ bool CopyDirectory(const FilePath& from_path, // Some old callers of CopyDirectory want it to support wildcards. // After some discussion, we decided to fix those callers. // Break loudly here if anyone tries to do this. - // TODO(evanm): remove this once we're sure it's ok. DCHECK(to_path.value().find('*') == std::string::npos); DCHECK(from_path.value().find('*') == std::string::npos); - char top_dir[PATH_MAX]; - if (strlcpy(top_dir, from_path.value().c_str(), - arraysize(top_dir)) >= arraysize(top_dir)) { + if (from_path.value().size() >= PATH_MAX) { return false; } @@ -286,10 +276,10 @@ bool CopyDirectory(const FilePath& from_path, return false; if (real_to_path.value().size() >= real_from_path.value().size() && real_to_path.value().compare(0, real_from_path.value().size(), - real_from_path.value()) == 0) + real_from_path.value()) == 0) { return false; + } - bool success = true; int traverse_type = FileEnumerator::FILES | FileEnumerator::SHOW_SYM_LINKS; if (recursive) traverse_type |= FileEnumerator::DIRECTORIES; @@ -302,7 +292,7 @@ bool CopyDirectory(const FilePath& from_path, if (stat(from_path.value().c_str(), &from_stat) < 0) { DLOG(ERROR) << "CopyDirectory() couldn't stat source directory: " << from_path.value() << " errno = " << errno; - success = false; + return false; } struct stat to_path_stat; FilePath from_path_base = from_path; @@ -315,8 +305,10 @@ bool CopyDirectory(const FilePath& from_path, // The Windows version of this function assumes that non-recursive calls // will always have a directory for from_path. + // TODO(maruel): This is not necessary anymore. DCHECK(recursive || S_ISDIR(from_stat.st_mode)); + bool success = true; while (success && !current.empty()) { // current is the source path, including from_path, so append // the suffix after from_path to to_path to create the target_path. @@ -463,30 +455,14 @@ bool GetTempDir(FilePath* path) { } #endif // !defined(OS_MACOSX) -#if !defined(OS_MACOSX) && !defined(OS_ANDROID) -// This is implemented in file_util_mac.mm and file_util_android.cc for those -// platforms. -bool GetShmemTempDir(bool executable, FilePath* path) { -#if defined(OS_LINUX) - bool use_dev_shm = true; - if (executable) { - static const bool s_dev_shm_executable = DetermineDevShmExecutable(); - use_dev_shm = s_dev_shm_executable; - } - if (use_dev_shm) { - *path = FilePath("/dev/shm"); - return true; - } -#endif - return GetTempDir(path); -} -#endif // !defined(OS_MACOSX) && !defined(OS_ANDROID) - -#if !defined(OS_MACOSX) +#if !defined(OS_MACOSX) // Mac implementation is in file_util_mac.mm. FilePath GetHomeDir() { #if defined(OS_CHROMEOS) - if (SysInfo::IsRunningOnChromeOS()) - return FilePath("/home/chronos/user"); + if (SysInfo::IsRunningOnChromeOS()) { + // On Chrome OS chrome::DIR_USER_DATA is overridden with a primary user + // homedir once it becomes available. Return / as the safe option. + return FilePath("/"); + } #endif const char* home_dir = getenv("HOME"); @@ -496,9 +472,12 @@ FilePath GetHomeDir() { #if defined(OS_ANDROID) DLOG(WARNING) << "OS_ANDROID: Home directory lookup not yet implemented."; #elif defined(USE_GLIB) && !defined(OS_CHROMEOS) - // g_get_home_dir calls getpwent, which can fall through to LDAP calls. - ThreadRestrictions::AssertIOAllowed(); - + // g_get_home_dir calls getpwent, which can fall through to LDAP calls so + // this may do I/O. However, it should be rare that $HOME is not defined and + // this is typically called from the path service which has no threading + // restrictions. The path service will cache the result which limits the + // badness of blocking on I/O. As a result, we don't have a thread + // restriction here. home_dir = g_get_home_dir(); if (home_dir && home_dir[0]) return FilePath(home_dir); @@ -525,14 +504,6 @@ bool CreateTemporaryFile(FilePath* path) { return true; } -FILE* CreateAndOpenTemporaryShmemFile(FilePath* path, bool executable) { - FilePath directory; - if (!GetShmemTempDir(executable, &directory)) - return NULL; - - return CreateAndOpenTemporaryFileInDir(directory, path); -} - FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) { int fd = CreateAndOpenFdForTemporaryFile(dir, path); if (fd < 0) @@ -589,7 +560,7 @@ bool CreateNewTempDirectory(const FilePath::StringType& prefix, } bool CreateDirectoryAndGetError(const FilePath& full_path, - PlatformFileError* error) { + File::Error* error) { ThreadRestrictions::AssertIOAllowed(); // For call to mkdir(). std::vector<FilePath> subpaths; @@ -616,7 +587,7 @@ bool CreateDirectoryAndGetError(const FilePath& full_path, int saved_errno = errno; if (!DirectoryExists(*i)) { if (error) - *error = ErrnoToPlatformFileError(saved_errno); + *error = File::OSErrorToFileError(saved_errno); return false; } } @@ -654,16 +625,14 @@ bool IsLink(const FilePath& file_path) { return false; } -bool GetFileInfo(const FilePath& file_path, PlatformFileInfo* results) { +bool GetFileInfo(const FilePath& file_path, File::Info* results) { stat_wrapper_t file_info; #if defined(OS_ANDROID) if (file_path.IsContentUri()) { - int fd = OpenContentUriForRead(file_path); - if (fd < 0) - return false; - file_util::ScopedFD scoped_fd(&fd); - if (CallFstat(fd, &file_info) != 0) + File file = OpenContentUriForRead(file_path); + if (!file.IsValid()) return false; + return file.GetInfo(results); } else { #endif // defined(OS_ANDROID) if (CallStat(file_path.value().c_str(), &file_info) != 0) @@ -671,21 +640,8 @@ bool GetFileInfo(const FilePath& file_path, PlatformFileInfo* results) { #if defined(OS_ANDROID) } #endif // defined(OS_ANDROID) - results->is_directory = S_ISDIR(file_info.st_mode); - results->size = file_info.st_size; -#if defined(OS_MACOSX) - results->last_modified = Time::FromTimeSpec(file_info.st_mtimespec); - results->last_accessed = Time::FromTimeSpec(file_info.st_atimespec); - results->creation_time = Time::FromTimeSpec(file_info.st_ctimespec); -#elif defined(OS_ANDROID) - results->last_modified = Time::FromTimeT(file_info.st_mtime); - results->last_accessed = Time::FromTimeT(file_info.st_atime); - results->creation_time = Time::FromTimeT(file_info.st_ctime); -#else - results->last_modified = Time::FromTimeSpec(file_info.st_mtim); - results->last_accessed = Time::FromTimeSpec(file_info.st_atim); - results->creation_time = Time::FromTimeSpec(file_info.st_ctim); -#endif + + results->FromStat(file_info); return true; } @@ -698,65 +654,37 @@ FILE* OpenFile(const FilePath& filename, const char* mode) { return result; } -int ReadFile(const FilePath& filename, char* data, int size) { +// NaCl doesn't implement system calls to open files directly. +#if !defined(OS_NACL) +FILE* FileToFILE(File file, const char* mode) { + FILE* stream = fdopen(file.GetPlatformFile(), mode); + if (stream) + file.TakePlatformFile(); + return stream; +} +#endif // !defined(OS_NACL) + +int ReadFile(const FilePath& filename, char* data, int max_size) { ThreadRestrictions::AssertIOAllowed(); int fd = HANDLE_EINTR(open(filename.value().c_str(), O_RDONLY)); if (fd < 0) return -1; - ssize_t bytes_read = HANDLE_EINTR(read(fd, data, size)); - if (int ret = IGNORE_EINTR(close(fd)) < 0) - return ret; + ssize_t bytes_read = HANDLE_EINTR(read(fd, data, max_size)); + if (IGNORE_EINTR(close(fd)) < 0) + return -1; return bytes_read; } -} // namespace base - -// ----------------------------------------------------------------------------- - -namespace file_util { - -using base::stat_wrapper_t; -using base::CallStat; -using base::CallLstat; -using base::CreateAndOpenFdForTemporaryFile; -using base::DirectoryExists; -using base::FileEnumerator; -using base::FilePath; -using base::MakeAbsoluteFilePath; -using base::VerifySpecificPathControlledByUser; - -base::FilePath MakeUniqueDirectory(const base::FilePath& path) { - const int kMaxAttempts = 20; - for (int attempts = 0; attempts < kMaxAttempts; attempts++) { - int uniquifier = - GetUniquePathNumber(path, base::FilePath::StringType()); - if (uniquifier < 0) - break; - base::FilePath test_path = (uniquifier == 0) ? path : - path.InsertBeforeExtensionASCII( - base::StringPrintf(" (%d)", uniquifier)); - if (mkdir(test_path.value().c_str(), 0777) == 0) - return test_path; - else if (errno != EEXIST) - break; - } - return base::FilePath(); -} - -FILE* OpenFile(const std::string& filename, const char* mode) { - return OpenFile(FilePath(filename), mode); -} - int WriteFile(const FilePath& filename, const char* data, int size) { - base::ThreadRestrictions::AssertIOAllowed(); - int fd = HANDLE_EINTR(creat(filename.value().c_str(), 0666)); + ThreadRestrictions::AssertIOAllowed(); + int fd = HANDLE_EINTR(creat(filename.value().c_str(), 0640)); if (fd < 0) return -1; int bytes_written = WriteFileDescriptor(fd, data, size); - if (int ret = IGNORE_EINTR(close(fd)) < 0) - return ret; + if (IGNORE_EINTR(close(fd)) < 0) + return -1; return bytes_written; } @@ -776,21 +704,21 @@ int WriteFileDescriptor(const int fd, const char* data, int size) { } int AppendToFile(const FilePath& filename, const char* data, int size) { - base::ThreadRestrictions::AssertIOAllowed(); + ThreadRestrictions::AssertIOAllowed(); int fd = HANDLE_EINTR(open(filename.value().c_str(), O_WRONLY | O_APPEND)); if (fd < 0) return -1; int bytes_written = WriteFileDescriptor(fd, data, size); - if (int ret = IGNORE_EINTR(close(fd)) < 0) - return ret; + if (IGNORE_EINTR(close(fd)) < 0) + return -1; return bytes_written; } // Gets the current working directory for the process. bool GetCurrentDirectory(FilePath* dir) { // getcwd can return ENOENT, which implies it checks against the disk. - base::ThreadRestrictions::AssertIOAllowed(); + ThreadRestrictions::AssertIOAllowed(); char system_buffer[PATH_MAX] = ""; if (!getcwd(system_buffer, sizeof(system_buffer))) { @@ -803,7 +731,7 @@ bool GetCurrentDirectory(FilePath* dir) { // Sets the current working directory for the process. bool SetCurrentDirectory(const FilePath& path) { - base::ThreadRestrictions::AssertIOAllowed(); + ThreadRestrictions::AssertIOAllowed(); int ret = chdir(path.value().c_str()); return !ret; } @@ -859,7 +787,7 @@ bool VerifyPathControlledByAdmin(const FilePath& path) { }; // Reading the groups database may touch the file system. - base::ThreadRestrictions::AssertIOAllowed(); + ThreadRestrictions::AssertIOAllowed(); std::set<gid_t> allowed_group_ids; for (int i = 0, ie = arraysize(kAdminGroupNames); i < ie; ++i) { @@ -879,13 +807,30 @@ bool VerifyPathControlledByAdmin(const FilePath& path) { #endif // defined(OS_MACOSX) && !defined(OS_IOS) int GetMaximumPathComponentLength(const FilePath& path) { - base::ThreadRestrictions::AssertIOAllowed(); + ThreadRestrictions::AssertIOAllowed(); return pathconf(path.value().c_str(), _PC_NAME_MAX); } -} // namespace file_util +#if !defined(OS_ANDROID) +// This is implemented in file_util_android.cc for that platform. +bool GetShmemTempDir(bool executable, FilePath* path) { +#if defined(OS_LINUX) + bool use_dev_shm = true; + if (executable) { + static const bool s_dev_shm_executable = DetermineDevShmExecutable(); + use_dev_shm = s_dev_shm_executable; + } + if (use_dev_shm) { + *path = FilePath("/dev/shm"); + return true; + } +#endif + return GetTempDir(path); +} +#endif // !defined(OS_ANDROID) + +// ----------------------------------------------------------------------------- -namespace base { namespace internal { bool MoveUnsafe(const FilePath& from_path, const FilePath& to_path) { diff --git a/chromium/base/file_util_unittest.cc b/chromium/base/file_util_unittest.cc index b65e171719f..239563d304e 100644 --- a/chromium/base/file_util_unittest.cc +++ b/chromium/base/file_util_unittest.cc @@ -12,14 +12,22 @@ #include <winioctl.h> #endif +#if defined(OS_POSIX) +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#endif + #include <algorithm> #include <fstream> #include <set> +#include <vector> #include "base/base_paths.h" #include "base/file_util.h" #include "base/files/file_enumerator.h" #include "base/files/file_path.h" +#include "base/files/scoped_file.h" #include "base/files/scoped_temp_dir.h" #include "base/path_service.h" #include "base/strings/utf_string_conversions.h" @@ -485,25 +493,6 @@ TEST_F(FileUtilTest, DevicePathToDriveLetter) { &win32_path)); } -TEST_F(FileUtilTest, GetPlatformFileInfoForDirectory) { - FilePath empty_dir = temp_dir_.path().Append(FPL("gpfi_test")); - ASSERT_TRUE(CreateDirectory(empty_dir)); - win::ScopedHandle dir( - ::CreateFile(empty_dir.value().c_str(), - FILE_ALL_ACCESS, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, - OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS, // Needed to open a directory. - NULL)); - ASSERT_TRUE(dir.IsValid()); - PlatformFileInfo info; - EXPECT_TRUE(GetPlatformFileInfo(dir.Get(), &info)); - EXPECT_TRUE(info.is_directory); - EXPECT_FALSE(info.is_symbolic_link); - EXPECT_EQ(0, info.size); -} - TEST_F(FileUtilTest, CreateTemporaryFileInDirLongPathTest) { // Test that CreateTemporaryFileInDir() creates a path and returns a long path // if it is available. This test requires that: @@ -643,6 +632,17 @@ TEST_F(FileUtilTest, DeleteNonExistent) { ASSERT_FALSE(PathExists(non_existent)); } +TEST_F(FileUtilTest, DeleteNonExistentWithNonExistentParent) { + FilePath non_existent = temp_dir_.path().AppendASCII("bogus_topdir"); + non_existent = non_existent.AppendASCII("bogus_subdir"); + ASSERT_FALSE(PathExists(non_existent)); + + EXPECT_TRUE(DeleteFile(non_existent, false)); + ASSERT_FALSE(PathExists(non_existent)); + EXPECT_TRUE(DeleteFile(non_existent, true)); + ASSERT_FALSE(PathExists(non_existent)); +} + TEST_F(FileUtilTest, DeleteFile) { // Create a file FilePath file_name = temp_dir_.path().Append(FPL("Test DeleteFile 1.txt")); @@ -716,7 +716,7 @@ TEST_F(FileUtilTest, ChangeFilePermissionsAndRead) { // Write file. EXPECT_EQ(static_cast<int>(kData.length()), - file_util::WriteFile(file_name, kData.data(), kData.length())); + WriteFile(file_name, kData.data(), kData.length())); EXPECT_TRUE(PathExists(file_name)); // Make sure the file is readable. @@ -755,7 +755,7 @@ TEST_F(FileUtilTest, ChangeFilePermissionsAndWrite) { // Write file. EXPECT_EQ(static_cast<int>(kData.length()), - file_util::WriteFile(file_name, kData.data(), kData.length())); + WriteFile(file_name, kData.data(), kData.length())); EXPECT_TRUE(PathExists(file_name)); // Make sure the file is writable. @@ -769,8 +769,7 @@ TEST_F(FileUtilTest, ChangeFilePermissionsAndWrite) { EXPECT_TRUE(GetPosixFilePermissions(file_name, &mode)); EXPECT_FALSE(mode & FILE_PERMISSION_WRITE_BY_USER); // Make sure the file can't be write. - EXPECT_EQ(-1, - file_util::WriteFile(file_name, kData.data(), kData.length())); + EXPECT_EQ(-1, WriteFile(file_name, kData.data(), kData.length())); EXPECT_FALSE(PathIsWritable(file_name)); // Give read permission. @@ -780,7 +779,7 @@ TEST_F(FileUtilTest, ChangeFilePermissionsAndWrite) { EXPECT_TRUE(mode & FILE_PERMISSION_WRITE_BY_USER); // Make sure the file can be write. EXPECT_EQ(static_cast<int>(kData.length()), - file_util::WriteFile(file_name, kData.data(), kData.length())); + WriteFile(file_name, kData.data(), kData.length())); EXPECT_TRUE(PathIsWritable(file_name)); // Delete the file. @@ -800,7 +799,7 @@ TEST_F(FileUtilTest, ChangeDirectoryPermissionsAndEnumerate) { EXPECT_FALSE(PathExists(file_name)); const std::string kData("hello"); EXPECT_EQ(static_cast<int>(kData.length()), - file_util::WriteFile(file_name, kData.data(), kData.length())); + WriteFile(file_name, kData.data(), kData.length())); EXPECT_TRUE(PathExists(file_name)); // Make sure the directory has the all permissions. @@ -817,7 +816,7 @@ TEST_F(FileUtilTest, ChangeDirectoryPermissionsAndEnumerate) { FileEnumerator f1(subdir_path, true, FileEnumerator::FILES); EXPECT_TRUE(PathExists(subdir_path)); FindResultCollector c1(f1); - EXPECT_EQ(c1.size(), 0); + EXPECT_EQ(0, c1.size()); EXPECT_FALSE(GetPosixFilePermissions(file_name, &mode)); // Give the permissions to the directory. @@ -829,7 +828,7 @@ TEST_F(FileUtilTest, ChangeDirectoryPermissionsAndEnumerate) { FileEnumerator f2(subdir_path, true, FileEnumerator::FILES); FindResultCollector c2(f2); EXPECT_TRUE(c2.HasFile(file_name)); - EXPECT_EQ(c2.size(), 1); + EXPECT_EQ(1, c2.size()); // Delete the file. EXPECT_TRUE(DeleteFile(subdir_path, true)); @@ -1379,6 +1378,60 @@ TEST_F(FileUtilTest, CopyDirectoryWithTrailingSeparators) { EXPECT_TRUE(PathExists(file_name_to)); } +// Sets the source file to read-only. +void SetReadOnly(const FilePath& path) { +#if defined(OS_WIN) + // On Windows, it involves setting a bit. + DWORD attrs = GetFileAttributes(path.value().c_str()); + ASSERT_NE(INVALID_FILE_ATTRIBUTES, attrs); + ASSERT_TRUE(SetFileAttributes( + path.value().c_str(), attrs | FILE_ATTRIBUTE_READONLY)); + attrs = GetFileAttributes(path.value().c_str()); + // 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 = FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_READONLY; + if (win::GetVersion() >= win::VERSION_VISTA) + expected |= FILE_ATTRIBUTE_NOT_CONTENT_INDEXED; + ASSERT_EQ(expected, attrs); +#else + // On all other platforms, it involves removing the write bit. + EXPECT_TRUE(SetPosixFilePermissions(path, S_IRUSR)); +#endif +} + +bool IsReadOnly(const FilePath& path) { +#if defined(OS_WIN) + DWORD attrs = GetFileAttributes(path.value().c_str()); + EXPECT_NE(INVALID_FILE_ATTRIBUTES, attrs); + return attrs & FILE_ATTRIBUTE_READONLY; +#else + int mode = 0; + EXPECT_TRUE(GetPosixFilePermissions(path, &mode)); + return !(mode & S_IWUSR); +#endif +} + +TEST_F(FileUtilTest, CopyDirectoryACL) { + // Create a directory. + FilePath src = temp_dir_.path().Append(FILE_PATH_LITERAL("src")); + CreateDirectory(src); + ASSERT_TRUE(PathExists(src)); + + // Create a file under the directory. + FilePath src_file = src.Append(FILE_PATH_LITERAL("src.txt")); + CreateTextFile(src_file, L"Gooooooooooooooooooooogle"); + SetReadOnly(src_file); + ASSERT_TRUE(IsReadOnly(src_file)); + + // Copy the directory recursively. + FilePath dst = temp_dir_.path().Append(FILE_PATH_LITERAL("dst")); + FilePath dst_file = dst.Append(FILE_PATH_LITERAL("src.txt")); + EXPECT_TRUE(CopyDirectory(src, dst, true)); + + ASSERT_FALSE(IsReadOnly(dst_file)); +} + TEST_F(FileUtilTest, CopyFile) { // Create a directory FilePath dir_name_from = @@ -1417,6 +1470,27 @@ TEST_F(FileUtilTest, CopyFile) { EXPECT_TRUE(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(). + FilePath src = temp_dir_.path().Append(FILE_PATH_LITERAL("src.txt")); + const std::wstring file_contents(L"Gooooooooooooooooooooogle"); + CreateTextFile(src, file_contents); + + // Set the source file to read-only. + ASSERT_FALSE(IsReadOnly(src)); + SetReadOnly(src); + ASSERT_TRUE(IsReadOnly(src)); + + // Copy the file. + FilePath dst = temp_dir_.path().Append(FILE_PATH_LITERAL("dst.txt")); + ASSERT_TRUE(CopyFile(src, dst)); + EXPECT_EQ(file_contents, ReadTextFile(dst)); + + ASSERT_FALSE(IsReadOnly(dst)); +} + // file_util winds up using autoreleased objects on the Mac, so this needs // to be a PlatformTest. typedef PlatformTest ReadOnlyFileUtilTest; @@ -1611,6 +1685,21 @@ TEST_F(FileUtilTest, CreateAndOpenTemporaryFileTest) { } } +TEST_F(FileUtilTest, FileToFILE) { + File file; + FILE* stream = FileToFILE(file.Pass(), "w"); + EXPECT_FALSE(stream); + + FilePath file_name = temp_dir_.path().Append(FPL("The file.txt")); + file = File(file_name, File::FLAG_CREATE | File::FLAG_WRITE); + EXPECT_TRUE(file.IsValid()); + + stream = FileToFILE(file.Pass(), "w"); + EXPECT_TRUE(stream); + EXPECT_FALSE(file.IsValid()); + EXPECT_TRUE(CloseFile(stream)); +} + TEST_F(FileUtilTest, CreateNewTempDirectoryTest) { FilePath temp_dir; ASSERT_TRUE(CreateNewTempDirectory(FilePath::StringType(), &temp_dir)); @@ -1629,11 +1718,24 @@ TEST_F(FileUtilTest, CreateNewTemporaryDirInDirTest) { EXPECT_TRUE(DeleteFile(new_dir, false)); } +#if defined(OS_POSIX) TEST_F(FileUtilTest, GetShmemTempDirTest) { FilePath dir; EXPECT_TRUE(GetShmemTempDir(false, &dir)); EXPECT_TRUE(DirectoryExists(dir)); } +#endif + +TEST_F(FileUtilTest, GetHomeDirTest) { +#if !defined(OS_ANDROID) // Not implemented on Android. + // We don't actually know what the home directory is supposed to be without + // calling some OS functions which would just duplicate the implementation. + // So here we just test that it returns something "reasonable". + FilePath home = GetHomeDir(); + ASSERT_FALSE(home.empty()); + ASSERT_TRUE(home.IsAbsolute()); +#endif +} TEST_F(FileUtilTest, CreateDirectoryTest) { FilePath test_root = @@ -1712,8 +1814,8 @@ TEST_F(FileUtilTest, DetectDirectoryTest) { TEST_F(FileUtilTest, FileEnumeratorTest) { // Test an empty directory. FileEnumerator f0(temp_dir_.path(), true, FILES_AND_DIRECTORIES); - EXPECT_EQ(f0.Next().value(), FPL("")); - EXPECT_EQ(f0.Next().value(), FPL("")); + EXPECT_EQ(FPL(""), f0.Next().value()); + EXPECT_EQ(FPL(""), f0.Next().value()); // Test an empty directory, non-recursively, including "..". FileEnumerator f0_dotdot(temp_dir_.path(), false, @@ -1749,7 +1851,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) { EXPECT_TRUE(c1.HasFile(file2_abs)); EXPECT_TRUE(c1.HasFile(dir2file)); EXPECT_TRUE(c1.HasFile(dir2innerfile)); - EXPECT_EQ(c1.size(), 4); + EXPECT_EQ(4, c1.size()); // Only enumerate directories. FileEnumerator f2(temp_dir_.path(), true, FileEnumerator::DIRECTORIES); @@ -1757,7 +1859,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) { EXPECT_TRUE(c2.HasFile(dir1)); EXPECT_TRUE(c2.HasFile(dir2)); EXPECT_TRUE(c2.HasFile(dir2inner)); - EXPECT_EQ(c2.size(), 3); + EXPECT_EQ(3, c2.size()); // Only enumerate directories non-recursively. FileEnumerator f2_non_recursive( @@ -1765,7 +1867,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) { FindResultCollector c2_non_recursive(f2_non_recursive); EXPECT_TRUE(c2_non_recursive.HasFile(dir1)); EXPECT_TRUE(c2_non_recursive.HasFile(dir2)); - EXPECT_EQ(c2_non_recursive.size(), 2); + EXPECT_EQ(2, c2_non_recursive.size()); // Only enumerate directories, non-recursively, including "..". FileEnumerator f2_dotdot(temp_dir_.path(), false, @@ -1775,7 +1877,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) { EXPECT_TRUE(c2_dotdot.HasFile(dir1)); EXPECT_TRUE(c2_dotdot.HasFile(dir2)); EXPECT_TRUE(c2_dotdot.HasFile(temp_dir_.path().Append(FPL("..")))); - EXPECT_EQ(c2_dotdot.size(), 3); + EXPECT_EQ(3, c2_dotdot.size()); // Enumerate files and directories. FileEnumerator f3(temp_dir_.path(), true, FILES_AND_DIRECTORIES); @@ -1787,7 +1889,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) { EXPECT_TRUE(c3.HasFile(dir2file)); EXPECT_TRUE(c3.HasFile(dir2inner)); EXPECT_TRUE(c3.HasFile(dir2innerfile)); - EXPECT_EQ(c3.size(), 7); + EXPECT_EQ(7, c3.size()); // Non-recursive operation. FileEnumerator f4(temp_dir_.path(), false, FILES_AND_DIRECTORIES); @@ -1796,7 +1898,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) { EXPECT_TRUE(c4.HasFile(dir2)); EXPECT_TRUE(c4.HasFile(file1)); EXPECT_TRUE(c4.HasFile(file2_abs)); - EXPECT_EQ(c4.size(), 4); + EXPECT_EQ(4, c4.size()); // Enumerate with a pattern. FileEnumerator f5(temp_dir_.path(), true, FILES_AND_DIRECTORIES, FPL("dir*")); @@ -1806,7 +1908,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) { EXPECT_TRUE(c5.HasFile(dir2file)); EXPECT_TRUE(c5.HasFile(dir2inner)); EXPECT_TRUE(c5.HasFile(dir2innerfile)); - EXPECT_EQ(c5.size(), 5); + EXPECT_EQ(5, c5.size()); #if defined(OS_WIN) { @@ -1824,7 +1926,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) { EXPECT_TRUE(c6.HasFile(inner2)); EXPECT_TRUE(c6.HasFile(inner2.Append(FPL("innerfile.txt")))); EXPECT_TRUE(c6.HasFile(dir1.Append(FPL("dir2file.txt")))); - EXPECT_EQ(c6.size(), 3); + EXPECT_EQ(3, c6.size()); } // No changes for non recursive operation. @@ -1834,7 +1936,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) { EXPECT_TRUE(c7.HasFile(dir2)); EXPECT_TRUE(c7.HasFile(file1)); EXPECT_TRUE(c7.HasFile(file2_abs)); - EXPECT_EQ(c7.size(), 4); + EXPECT_EQ(4, c7.size()); // Should not enumerate inside dir1 when using recursion. FileEnumerator f8(temp_dir_.path(), true, FILES_AND_DIRECTORIES); @@ -1846,7 +1948,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) { EXPECT_TRUE(c8.HasFile(dir2file)); EXPECT_TRUE(c8.HasFile(dir2inner)); EXPECT_TRUE(c8.HasFile(dir2innerfile)); - EXPECT_EQ(c8.size(), 7); + EXPECT_EQ(7, c8.size()); } #endif @@ -1875,16 +1977,117 @@ TEST_F(FileUtilTest, AppendToFile) { FilePath foobar(data_dir.Append(FILE_PATH_LITERAL("foobar.txt"))); std::string data("hello"); - EXPECT_EQ(-1, file_util::AppendToFile(foobar, data.c_str(), data.length())); + EXPECT_EQ(-1, AppendToFile(foobar, data.c_str(), data.length())); EXPECT_EQ(static_cast<int>(data.length()), - file_util::WriteFile(foobar, data.c_str(), data.length())); + WriteFile(foobar, data.c_str(), data.length())); EXPECT_EQ(static_cast<int>(data.length()), - file_util::AppendToFile(foobar, data.c_str(), data.length())); + AppendToFile(foobar, data.c_str(), data.length())); const std::wstring read_content = ReadTextFile(foobar); EXPECT_EQ(L"hellohello", read_content); } +TEST_F(FileUtilTest, ReadFile) { + // Create a test file to be read. + const std::string kTestData("The quick brown fox jumps over the lazy dog."); + FilePath file_path = + temp_dir_.path().Append(FILE_PATH_LITERAL("ReadFileTest")); + + ASSERT_EQ(static_cast<int>(kTestData.size()), + WriteFile(file_path, kTestData.data(), kTestData.size())); + + // Make buffers with various size. + std::vector<char> small_buffer(kTestData.size() / 2); + std::vector<char> exact_buffer(kTestData.size()); + std::vector<char> large_buffer(kTestData.size() * 2); + + // Read the file with smaller buffer. + int bytes_read_small = ReadFile( + file_path, &small_buffer[0], static_cast<int>(small_buffer.size())); + EXPECT_EQ(static_cast<int>(small_buffer.size()), bytes_read_small); + EXPECT_EQ( + std::string(kTestData.begin(), kTestData.begin() + small_buffer.size()), + std::string(small_buffer.begin(), small_buffer.end())); + + // Read the file with buffer which have exactly same size. + int bytes_read_exact = ReadFile( + file_path, &exact_buffer[0], static_cast<int>(exact_buffer.size())); + EXPECT_EQ(static_cast<int>(kTestData.size()), bytes_read_exact); + EXPECT_EQ(kTestData, std::string(exact_buffer.begin(), exact_buffer.end())); + + // Read the file with larger buffer. + int bytes_read_large = ReadFile( + file_path, &large_buffer[0], static_cast<int>(large_buffer.size())); + EXPECT_EQ(static_cast<int>(kTestData.size()), bytes_read_large); + EXPECT_EQ(kTestData, std::string(large_buffer.begin(), + large_buffer.begin() + kTestData.size())); + + // Make sure the return value is -1 if the file doesn't exist. + FilePath file_path_not_exist = + temp_dir_.path().Append(FILE_PATH_LITERAL("ReadFileNotExistTest")); + EXPECT_EQ(-1, + ReadFile(file_path_not_exist, + &exact_buffer[0], + static_cast<int>(exact_buffer.size()))); +} + +TEST_F(FileUtilTest, ReadFileToString) { + const char kTestData[] = "0123"; + std::string data; + + FilePath file_path = + temp_dir_.path().Append(FILE_PATH_LITERAL("ReadFileToStringTest")); + FilePath file_path_dangerous = + temp_dir_.path().Append(FILE_PATH_LITERAL("..")). + Append(temp_dir_.path().BaseName()). + Append(FILE_PATH_LITERAL("ReadFileToStringTest")); + + // Create test file. + ASSERT_EQ(4, WriteFile(file_path, kTestData, 4)); + + EXPECT_TRUE(ReadFileToString(file_path, &data)); + EXPECT_EQ(kTestData, data); + + data = "temp"; + EXPECT_FALSE(ReadFileToString(file_path, &data, 0)); + EXPECT_EQ(0u, data.length()); + + data = "temp"; + EXPECT_FALSE(ReadFileToString(file_path, &data, 2)); + EXPECT_EQ("01", data); + + data.clear(); + EXPECT_FALSE(ReadFileToString(file_path, &data, 3)); + EXPECT_EQ("012", data); + + data.clear(); + EXPECT_TRUE(ReadFileToString(file_path, &data, 4)); + EXPECT_EQ("0123", data); + + data.clear(); + EXPECT_TRUE(ReadFileToString(file_path, &data, 6)); + EXPECT_EQ("0123", data); + + EXPECT_TRUE(ReadFileToString(file_path, NULL, 6)); + + EXPECT_TRUE(ReadFileToString(file_path, NULL)); + + data = "temp"; + EXPECT_FALSE(ReadFileToString(file_path_dangerous, &data)); + EXPECT_EQ(0u, data.length()); + + // Delete test file. + EXPECT_TRUE(base::DeleteFile(file_path, false)); + + data = "temp"; + EXPECT_FALSE(ReadFileToString(file_path, &data)); + EXPECT_EQ(0u, data.length()); + + data = "temp"; + EXPECT_FALSE(ReadFileToString(file_path, &data, 6)); + EXPECT_EQ(0u, data.length()); +} + TEST_F(FileUtilTest, TouchFile) { FilePath data_dir = temp_dir_.path().Append(FILE_PATH_LITERAL("FilePathTest")); @@ -1897,13 +2100,13 @@ TEST_F(FileUtilTest, TouchFile) { FilePath foobar(data_dir.Append(FILE_PATH_LITERAL("foobar.txt"))); std::string data("hello"); - ASSERT_TRUE(file_util::WriteFile(foobar, data.c_str(), data.length())); + ASSERT_TRUE(WriteFile(foobar, data.c_str(), data.length())); Time access_time; // This timestamp is divisible by one day (in local timezone), // to make it work on FAT too. ASSERT_TRUE(Time::FromString("Wed, 16 Nov 1994, 00:00:00", - &access_time)); + &access_time)); Time modification_time; // Note that this timestamp is divisible by two (seconds) - FAT stores @@ -1912,12 +2115,12 @@ TEST_F(FileUtilTest, TouchFile) { &modification_time)); ASSERT_TRUE(TouchFile(foobar, access_time, modification_time)); - PlatformFileInfo file_info; + File::Info file_info; ASSERT_TRUE(GetFileInfo(foobar, &file_info)); - EXPECT_EQ(file_info.last_accessed.ToInternalValue(), - access_time.ToInternalValue()); - EXPECT_EQ(file_info.last_modified.ToInternalValue(), - modification_time.ToInternalValue()); + EXPECT_EQ(access_time.ToInternalValue(), + file_info.last_accessed.ToInternalValue()); + EXPECT_EQ(modification_time.ToInternalValue(), + file_info.last_modified.ToInternalValue()); } TEST_F(FileUtilTest, IsDirectoryEmpty) { @@ -1931,7 +2134,7 @@ TEST_F(FileUtilTest, IsDirectoryEmpty) { FilePath foo(empty_dir.Append(FILE_PATH_LITERAL("foo.txt"))); std::string bar("baz"); - ASSERT_TRUE(file_util::WriteFile(foo, bar.c_str(), bar.length())); + ASSERT_TRUE(WriteFile(foo, bar.c_str(), bar.length())); EXPECT_FALSE(IsDirectoryEmpty(empty_dir)); } @@ -2008,23 +2211,23 @@ TEST_F(VerifyPathControlledByUserTest, BadPaths) { .AppendASCII("not") .AppendASCII("exist"); EXPECT_FALSE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( base_dir_, does_not_exist, uid_, ok_gids_)); // |base| not a subpath of |path|. EXPECT_FALSE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( sub_dir_, base_dir_, uid_, ok_gids_)); // An empty base path will fail to be a prefix for any path. FilePath empty; EXPECT_FALSE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( empty, base_dir_, uid_, ok_gids_)); // Finding that a bad call fails proves nothing unless a good call succeeds. EXPECT_TRUE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( base_dir_, sub_dir_, uid_, ok_gids_)); } @@ -2037,10 +2240,10 @@ TEST_F(VerifyPathControlledByUserTest, Symlinks) { << "Failed to create symlink."; EXPECT_FALSE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( base_dir_, file_link, uid_, ok_gids_)); EXPECT_FALSE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( file_link, file_link, uid_, ok_gids_)); // Symlink from one directory to another within the path. @@ -2052,16 +2255,16 @@ TEST_F(VerifyPathControlledByUserTest, Symlinks) { ASSERT_TRUE(PathExists(file_path_with_link)); EXPECT_FALSE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( base_dir_, file_path_with_link, uid_, ok_gids_)); EXPECT_FALSE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( link_to_sub_dir, file_path_with_link, uid_, ok_gids_)); // Symlinks in parents of base path are allowed. EXPECT_TRUE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( file_path_with_link, file_path_with_link, uid_, ok_gids_)); } @@ -2079,35 +2282,35 @@ TEST_F(VerifyPathControlledByUserTest, OwnershipChecks) { // We control these paths. EXPECT_TRUE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( base_dir_, sub_dir_, uid_, ok_gids_)); EXPECT_TRUE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( base_dir_, text_file_, uid_, ok_gids_)); EXPECT_TRUE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( sub_dir_, text_file_, uid_, ok_gids_)); // Another user does not control these paths. EXPECT_FALSE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( base_dir_, sub_dir_, bad_uid, ok_gids_)); EXPECT_FALSE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( base_dir_, text_file_, bad_uid, ok_gids_)); EXPECT_FALSE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( sub_dir_, text_file_, bad_uid, ok_gids_)); // Another group does not control the paths. EXPECT_FALSE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( base_dir_, sub_dir_, uid_, bad_gids_)); EXPECT_FALSE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( base_dir_, text_file_, uid_, bad_gids_)); EXPECT_FALSE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( sub_dir_, text_file_, uid_, bad_gids_)); } @@ -2122,36 +2325,36 @@ TEST_F(VerifyPathControlledByUserTest, GroupWriteTest) { // Any group is okay because the path is not group-writable. EXPECT_TRUE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( base_dir_, sub_dir_, uid_, ok_gids_)); EXPECT_TRUE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( base_dir_, text_file_, uid_, ok_gids_)); EXPECT_TRUE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( sub_dir_, text_file_, uid_, ok_gids_)); EXPECT_TRUE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( base_dir_, sub_dir_, uid_, bad_gids_)); EXPECT_TRUE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( base_dir_, text_file_, uid_, bad_gids_)); EXPECT_TRUE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( sub_dir_, text_file_, uid_, bad_gids_)); // No group is okay, because we don't check the group // if no group can write. std::set<gid_t> no_gids; // Empty set of gids. EXPECT_TRUE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( base_dir_, sub_dir_, uid_, no_gids)); EXPECT_TRUE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( base_dir_, text_file_, uid_, no_gids)); EXPECT_TRUE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( sub_dir_, text_file_, uid_, no_gids)); @@ -2165,23 +2368,23 @@ TEST_F(VerifyPathControlledByUserTest, GroupWriteTest) { // Now |ok_gids_| works, but |bad_gids_| fails. EXPECT_TRUE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( base_dir_, sub_dir_, uid_, ok_gids_)); EXPECT_TRUE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( base_dir_, text_file_, uid_, ok_gids_)); EXPECT_TRUE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( sub_dir_, text_file_, uid_, ok_gids_)); EXPECT_FALSE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( base_dir_, sub_dir_, uid_, bad_gids_)); EXPECT_FALSE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( base_dir_, text_file_, uid_, bad_gids_)); EXPECT_FALSE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( sub_dir_, text_file_, uid_, bad_gids_)); // Because any group in the group set is allowed, @@ -2194,13 +2397,13 @@ TEST_F(VerifyPathControlledByUserTest, GroupWriteTest) { std::inserter(multiple_gids, multiple_gids.begin())); EXPECT_TRUE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( base_dir_, sub_dir_, uid_, multiple_gids)); EXPECT_TRUE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( base_dir_, text_file_, uid_, multiple_gids)); EXPECT_TRUE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( sub_dir_, text_file_, uid_, multiple_gids)); } @@ -2215,78 +2418,78 @@ TEST_F(VerifyPathControlledByUserTest, WriteBitChecks) { // Initialy, we control all parts of the path. EXPECT_TRUE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( base_dir_, sub_dir_, uid_, ok_gids_)); EXPECT_TRUE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( base_dir_, text_file_, uid_, ok_gids_)); EXPECT_TRUE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( sub_dir_, text_file_, uid_, ok_gids_)); // Make base_dir_ world-writable. ASSERT_NO_FATAL_FAILURE( ChangePosixFilePermissions(base_dir_, S_IWOTH, 0u)); EXPECT_FALSE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( base_dir_, sub_dir_, uid_, ok_gids_)); EXPECT_FALSE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( base_dir_, text_file_, uid_, ok_gids_)); EXPECT_TRUE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( sub_dir_, text_file_, uid_, ok_gids_)); // Make sub_dir_ world writable. ASSERT_NO_FATAL_FAILURE( ChangePosixFilePermissions(sub_dir_, S_IWOTH, 0u)); EXPECT_FALSE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( base_dir_, sub_dir_, uid_, ok_gids_)); EXPECT_FALSE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( base_dir_, text_file_, uid_, ok_gids_)); EXPECT_FALSE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( sub_dir_, text_file_, uid_, ok_gids_)); // Make text_file_ world writable. ASSERT_NO_FATAL_FAILURE( ChangePosixFilePermissions(text_file_, S_IWOTH, 0u)); EXPECT_FALSE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( base_dir_, sub_dir_, uid_, ok_gids_)); EXPECT_FALSE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( base_dir_, text_file_, uid_, ok_gids_)); EXPECT_FALSE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( sub_dir_, text_file_, uid_, ok_gids_)); // Make sub_dir_ non-world writable. ASSERT_NO_FATAL_FAILURE( ChangePosixFilePermissions(sub_dir_, 0u, S_IWOTH)); EXPECT_FALSE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( base_dir_, sub_dir_, uid_, ok_gids_)); EXPECT_FALSE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( base_dir_, text_file_, uid_, ok_gids_)); EXPECT_FALSE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( sub_dir_, text_file_, uid_, ok_gids_)); // Make base_dir_ non-world-writable. ASSERT_NO_FATAL_FAILURE( ChangePosixFilePermissions(base_dir_, 0u, S_IWOTH)); EXPECT_TRUE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( base_dir_, sub_dir_, uid_, ok_gids_)); EXPECT_FALSE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( base_dir_, text_file_, uid_, ok_gids_)); EXPECT_FALSE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( sub_dir_, text_file_, uid_, ok_gids_)); // Back to the initial state: Nothing is writable, so every path @@ -2294,13 +2497,13 @@ TEST_F(VerifyPathControlledByUserTest, WriteBitChecks) { ASSERT_NO_FATAL_FAILURE( ChangePosixFilePermissions(text_file_, 0u, S_IWOTH)); EXPECT_TRUE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( base_dir_, sub_dir_, uid_, ok_gids_)); EXPECT_TRUE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( base_dir_, text_file_, uid_, ok_gids_)); EXPECT_TRUE( - file_util::VerifyPathControlledByUser( + base::VerifyPathControlledByUser( sub_dir_, text_file_, uid_, ok_gids_)); } @@ -2329,9 +2532,9 @@ TEST_F(FileUtilTest, ValidContentUriTest) { // We should be able to read the file. char* buffer = new char[image_size]; - int fd = OpenContentUriForRead(path); - EXPECT_LT(0, fd); - EXPECT_TRUE(ReadFromFD(fd, buffer, image_size)); + File file = OpenContentUriForRead(path); + EXPECT_TRUE(file.IsValid()); + EXPECT_TRUE(file.ReadAtCurrentPos(buffer, image_size)); delete[] buffer; } @@ -2344,11 +2547,48 @@ TEST_F(FileUtilTest, NonExistentContentUriTest) { EXPECT_FALSE(GetFileSize(path, &size)); // We should not be able to read the file. - int fd = OpenContentUriForRead(path); - EXPECT_EQ(-1, fd); + File file = OpenContentUriForRead(path); + EXPECT_FALSE(file.IsValid()); } #endif +TEST(ScopedFD, ScopedFDDoesClose) { + int fds[2]; + char c = 0; + ASSERT_EQ(0, pipe(fds)); + const int write_end = fds[1]; + base::ScopedFD read_end_closer(fds[0]); + { + base::ScopedFD write_end_closer(fds[1]); + } + // This is the only thread. This file descriptor should no longer be valid. + int ret = close(write_end); + EXPECT_EQ(-1, ret); + EXPECT_EQ(EBADF, errno); + // Make sure read(2) won't block. + ASSERT_EQ(0, fcntl(fds[0], F_SETFL, O_NONBLOCK)); + // Reading the pipe should EOF. + EXPECT_EQ(0, read(fds[0], &c, 1)); +} + +#if defined(GTEST_HAS_DEATH_TEST) +void CloseWithScopedFD(int fd) { + base::ScopedFD fd_closer(fd); +} +#endif + +TEST(ScopedFD, ScopedFDCrashesOnCloseFailure) { + int fds[2]; + ASSERT_EQ(0, pipe(fds)); + base::ScopedFD read_end_closer(fds[0]); + EXPECT_EQ(0, IGNORE_EINTR(close(fds[1]))); +#if defined(GTEST_HAS_DEATH_TEST) + // This is the only thread. This file descriptor should no longer be valid. + // Trying to close it should crash. This is important for security. + EXPECT_DEATH(CloseWithScopedFD(fds[1]), ""); +#endif +} + #endif // defined(OS_POSIX) } // namespace diff --git a/chromium/base/file_util_win.cc b/chromium/base/file_util_win.cc index 44c1205aa65..e3cd1f83ac2 100644 --- a/chromium/base/file_util_win.cc +++ b/chromium/base/file_util_win.cc @@ -5,6 +5,7 @@ #include "base/file_util.h" #include <windows.h> +#include <io.h> #include <psapi.h> #include <shellapi.h> #include <shlobj.h> @@ -14,6 +15,7 @@ #include <limits> #include <string> +#include "base/files/file_enumerator.h" #include "base/files/file_path.h" #include "base/logging.h" #include "base/metrics/histogram.h" @@ -34,44 +36,6 @@ namespace { const DWORD kFileShareAll = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; -bool ShellCopy(const FilePath& from_path, - const FilePath& to_path, - bool recursive) { - // WinXP SHFileOperation doesn't like trailing separators. - FilePath stripped_from = from_path.StripTrailingSeparators(); - FilePath stripped_to = to_path.StripTrailingSeparators(); - - ThreadRestrictions::AssertIOAllowed(); - - // NOTE: I suspect we could support longer paths, but that would involve - // analyzing all our usage of files. - if (stripped_from.value().length() >= MAX_PATH || - stripped_to.value().length() >= MAX_PATH) { - return false; - } - - // SHFILEOPSTRUCT wants the path to be terminated with two NULLs, - // so we have to use wcscpy because wcscpy_s writes non-NULLs - // into the rest of the buffer. - wchar_t double_terminated_path_from[MAX_PATH + 1] = {0}; - wchar_t double_terminated_path_to[MAX_PATH + 1] = {0}; -#pragma warning(suppress:4996) // don't complain about wcscpy deprecation - wcscpy(double_terminated_path_from, stripped_from.value().c_str()); -#pragma warning(suppress:4996) // don't complain about wcscpy deprecation - wcscpy(double_terminated_path_to, stripped_to.value().c_str()); - - SHFILEOPSTRUCT file_operation = {0}; - file_operation.wFunc = FO_COPY; - file_operation.pFrom = double_terminated_path_from; - file_operation.pTo = double_terminated_path_to; - file_operation.fFlags = FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION | - FOF_NOCONFIRMMKDIR; - if (!recursive) - file_operation.fFlags |= FOF_NORECURSION | FOF_FILESONLY; - - return (SHFileOperation(&file_operation) == 0); -} - } // namespace FilePath MakeAbsoluteFilePath(const FilePath& input) { @@ -88,10 +52,15 @@ bool DeleteFile(const FilePath& path, bool recursive) { if (path.value().length() >= MAX_PATH) return false; + // On XP SHFileOperation will return ERROR_ACCESS_DENIED instead of + // ERROR_FILE_NOT_FOUND, so just shortcut this here. + if (path.empty()) + return true; + if (!recursive) { // If not recursing, then first check to see if |path| is a directory. // If it is, then remove it with RemoveDirectory. - PlatformFileInfo file_info; + File::Info file_info; if (GetFileInfo(path, &file_info) && file_info.is_directory) return RemoveDirectory(path.value().c_str()) != 0; @@ -126,8 +95,10 @@ bool DeleteFile(const FilePath& path, bool recursive) { // Some versions of Windows return ERROR_FILE_NOT_FOUND (0x2) when deleting // an empty directory and some return 0x402 when they should be returning - // ERROR_FILE_NOT_FOUND. MSDN says Vista and up won't return 0x402. - return (err == 0 || err == ERROR_FILE_NOT_FOUND || err == 0x402); + // ERROR_FILE_NOT_FOUND. MSDN says Vista and up won't return 0x402. Windows 7 + // can return DE_INVALIDFILES (0x7C) for nonexistent directories. + return (err == 0 || err == ERROR_FILE_NOT_FOUND || err == 0x402 || + err == 0x7C); } bool DeleteFileAfterReboot(const FilePath& path) { @@ -143,7 +114,7 @@ bool DeleteFileAfterReboot(const FilePath& path) { bool ReplaceFile(const FilePath& from_path, const FilePath& to_path, - PlatformFileError* error) { + File::Error* error) { ThreadRestrictions::AssertIOAllowed(); // Try a simple move first. It will only succeed when |to_path| doesn't // already exist. @@ -158,33 +129,99 @@ bool ReplaceFile(const FilePath& from_path, return true; } if (error) - *error = LastErrorToPlatformFileError(GetLastError()); + *error = File::OSErrorToFileError(GetLastError()); return false; } bool CopyDirectory(const FilePath& from_path, const FilePath& to_path, bool recursive) { + // NOTE(maruel): Previous version of this function used to call + // SHFileOperation(). This used to copy the file attributes and extended + // attributes, OLE structured storage, NTFS file system alternate data + // streams, SECURITY_DESCRIPTOR. In practice, this is not what we want, we + // want the containing directory to propagate its SECURITY_DESCRIPTOR. 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; + } + + // This function does not properly handle destinations within the source. + FilePath real_to_path = to_path; + if (PathExists(real_to_path)) { + real_to_path = MakeAbsoluteFilePath(real_to_path); + if (real_to_path.empty()) + return false; + } else { + real_to_path = MakeAbsoluteFilePath(real_to_path.DirName()); + if (real_to_path.empty()) + return false; + } + FilePath real_from_path = MakeAbsoluteFilePath(from_path); + if (real_from_path.empty()) + return false; + if (real_to_path.value().size() >= real_from_path.value().size() && + real_to_path.value().compare(0, real_from_path.value().size(), + real_from_path.value()) == 0) { + return false; + } + + int traverse_type = FileEnumerator::FILES; if (recursive) - return ShellCopy(from_path, to_path, true); + traverse_type |= FileEnumerator::DIRECTORIES; + FileEnumerator traversal(from_path, recursive, traverse_type); + + if (!PathExists(from_path)) { + DLOG(ERROR) << "CopyDirectory() couldn't stat source directory: " + << from_path.value().c_str(); + return false; + } + // TODO(maruel): This is not necessary anymore. + DCHECK(recursive || DirectoryExists(from_path)); + + FilePath current = from_path; + bool from_is_dir = DirectoryExists(from_path); + bool success = true; + FilePath from_path_base = from_path; + if (recursive && DirectoryExists(to_path)) { + // If the destination already exists and is a directory, then the + // top level of source needs to be copied. + from_path_base = from_path.DirName(); + } - // The following code assumes that from path is a directory. - DCHECK(DirectoryExists(from_path)); + while (success && !current.empty()) { + // current is the source path, including from_path, so append + // the suffix after from_path to to_path to create the target_path. + FilePath target_path(to_path); + if (from_path_base != current) { + if (!from_path_base.AppendRelativePath(current, &target_path)) { + success = false; + break; + } + } + + if (from_is_dir) { + if (!DirectoryExists(target_path) && + !::CreateDirectory(target_path.value().c_str(), NULL)) { + DLOG(ERROR) << "CopyDirectory() couldn't create directory: " + << target_path.value().c_str(); + success = false; + } + } else if (!internal::CopyFileUnsafe(current, target_path)) { + DLOG(ERROR) << "CopyDirectory() couldn't create file: " + << target_path.value().c_str(); + success = false; + } - // Instead of creating a new directory, we copy the old one to include the - // security information of the folder as part of the copy. - if (!PathExists(to_path)) { - // Except that Vista fails to do that, and instead do a recursive copy if - // the target directory doesn't exist. - if (base::win::GetVersion() >= base::win::VERSION_VISTA) - CreateDirectory(to_path); - else - ShellCopy(from_path, to_path, false); + current = traversal.Next(); + if (!current.empty()) + from_is_dir = traversal.GetInfo().IsDirectory(); } - FilePath directory = from_path.Append(L"*.*"); - return ShellCopy(directory, to_path, false); + return success; } bool PathExists(const FilePath& path) { @@ -225,8 +262,21 @@ bool GetTempDir(FilePath* path) { return true; } -bool GetShmemTempDir(bool executable, FilePath* path) { - return GetTempDir(path); +FilePath GetHomeDir() { + char16 result[MAX_PATH]; + if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT, + result)) && + result[0]) { + return FilePath(result); + } + + // Fall back to the temporary directory on failure. + FilePath temp; + if (GetTempDir(&temp)) + return temp; + + // Last resort. + return FilePath(L"C:\\"); } bool CreateTemporaryFile(FilePath* path) { @@ -245,11 +295,6 @@ bool CreateTemporaryFile(FilePath* path) { return false; } -FILE* CreateAndOpenTemporaryShmemFile(FilePath* path, bool executable) { - ThreadRestrictions::AssertIOAllowed(); - return CreateAndOpenTemporaryFile(path); -} - // On POSIX we have semantics to create and open a temporary file // atomically. // TODO(jrg): is there equivalent call to use on Windows instead of @@ -328,7 +373,7 @@ bool CreateNewTempDirectory(const FilePath::StringType& prefix, } bool CreateDirectoryAndGetError(const FilePath& full_path, - PlatformFileError* error) { + File::Error* error) { ThreadRestrictions::AssertIOAllowed(); // If the path exists, we've succeeded if it's a directory, failed otherwise. @@ -343,7 +388,7 @@ bool CreateDirectoryAndGetError(const FilePath& full_path, DLOG(WARNING) << "CreateDirectory(" << full_path_str << "), " << "conflicts with existing file."; if (error) { - *error = PLATFORM_FILE_ERROR_NOT_A_DIRECTORY; + *error = File::FILE_ERROR_NOT_A_DIRECTORY; } return false; } @@ -356,14 +401,14 @@ bool CreateDirectoryAndGetError(const FilePath& full_path, FilePath parent_path(full_path.DirName()); if (parent_path.value() == full_path.value()) { if (error) { - *error = PLATFORM_FILE_ERROR_NOT_FOUND; + *error = File::FILE_ERROR_NOT_FOUND; } return false; } if (!CreateDirectoryAndGetError(parent_path, error)) { DLOG(WARNING) << "Failed to create one of the parent directories."; if (error) { - DCHECK(*error != PLATFORM_FILE_OK); + DCHECK(*error != File::FILE_OK); } return false; } @@ -378,7 +423,7 @@ bool CreateDirectoryAndGetError(const FilePath& full_path, return true; } else { if (error) - *error = LastErrorToPlatformFileError(error_code); + *error = File::OSErrorToFileError(error_code); DLOG(WARNING) << "Failed to create directory " << full_path_str << ", last error is " << error_code << "."; return false; @@ -505,7 +550,7 @@ bool IsLink(const FilePath& file_path) { return false; } -bool GetFileInfo(const FilePath& file_path, PlatformFileInfo* results) { +bool GetFileInfo(const FilePath& file_path, File::Info* results) { ThreadRestrictions::AssertIOAllowed(); WIN32_FILE_ATTRIBUTE_DATA attr; @@ -534,7 +579,21 @@ FILE* OpenFile(const FilePath& filename, const char* mode) { return _wfsopen(filename.value().c_str(), w_mode.c_str(), _SH_DENYNO); } -int ReadFile(const FilePath& filename, char* data, int size) { +FILE* FileToFILE(File file, const char* mode) { + if (!file.IsValid()) + return NULL; + int fd = + _open_osfhandle(reinterpret_cast<intptr_t>(file.GetPlatformFile()), 0); + if (fd < 0) + return NULL; + file.TakePlatformFile(); + FILE* stream = _fdopen(fd, mode); + if (!stream) + _close(fd); + return stream; +} + +int ReadFile(const FilePath& filename, char* data, int max_size) { ThreadRestrictions::AssertIOAllowed(); base::win::ScopedHandle file(CreateFile(filename.value().c_str(), GENERIC_READ, @@ -547,29 +606,14 @@ int ReadFile(const FilePath& filename, char* data, int size) { return -1; DWORD read; - if (::ReadFile(file, data, size, &read, NULL) && - static_cast<int>(read) == size) + if (::ReadFile(file, data, max_size, &read, NULL)) return read; - return -1; -} - -} // namespace base - -// ----------------------------------------------------------------------------- - -namespace file_util { - -using base::DirectoryExists; -using base::FilePath; -using base::kFileShareAll; -FILE* OpenFile(const std::string& filename, const char* mode) { - base::ThreadRestrictions::AssertIOAllowed(); - return _fsopen(filename.c_str(), mode, _SH_DENYNO); + return -1; } int WriteFile(const FilePath& filename, const char* data, int size) { - base::ThreadRestrictions::AssertIOAllowed(); + ThreadRestrictions::AssertIOAllowed(); base::win::ScopedHandle file(CreateFile(filename.value().c_str(), GENERIC_WRITE, 0, @@ -578,8 +622,8 @@ int WriteFile(const FilePath& filename, const char* data, int size) { 0, NULL)); if (!file) { - DLOG_GETLASTERROR(WARNING) << "CreateFile failed for path " - << filename.value(); + DPLOG(WARNING) << "CreateFile failed for path " + << UTF16ToUTF8(filename.value()); return -1; } @@ -590,18 +634,18 @@ int WriteFile(const FilePath& filename, const char* data, int size) { if (!result) { // WriteFile failed. - DLOG_GETLASTERROR(WARNING) << "writing file " << filename.value() - << " failed"; + DPLOG(WARNING) << "writing file " << UTF16ToUTF8(filename.value()) + << " failed"; } else { // Didn't write all the bytes. DLOG(WARNING) << "wrote" << written << " bytes to " - << filename.value() << " expected " << size; + << UTF16ToUTF8(filename.value()) << " expected " << size; } return -1; } int AppendToFile(const FilePath& filename, const char* data, int size) { - base::ThreadRestrictions::AssertIOAllowed(); + ThreadRestrictions::AssertIOAllowed(); base::win::ScopedHandle file(CreateFile(filename.value().c_str(), FILE_APPEND_DATA, 0, @@ -610,8 +654,8 @@ int AppendToFile(const FilePath& filename, const char* data, int size) { 0, NULL)); if (!file) { - DLOG_GETLASTERROR(WARNING) << "CreateFile failed for path " - << filename.value(); + DPLOG(WARNING) << "CreateFile failed for path " + << UTF16ToUTF8(filename.value()); return -1; } @@ -622,19 +666,19 @@ int AppendToFile(const FilePath& filename, const char* data, int size) { if (!result) { // WriteFile failed. - DLOG_GETLASTERROR(WARNING) << "writing file " << filename.value() - << " failed"; + DPLOG(WARNING) << "writing file " << UTF16ToUTF8(filename.value()) + << " failed"; } else { // Didn't write all the bytes. DLOG(WARNING) << "wrote" << written << " bytes to " - << filename.value() << " expected " << size; + << UTF16ToUTF8(filename.value()) << " expected " << size; } return -1; } // Gets the current working directory for the process. bool GetCurrentDirectory(FilePath* dir) { - base::ThreadRestrictions::AssertIOAllowed(); + ThreadRestrictions::AssertIOAllowed(); wchar_t system_buffer[MAX_PATH]; system_buffer[0] = 0; @@ -651,13 +695,13 @@ bool GetCurrentDirectory(FilePath* dir) { // Sets the current working directory for the process. bool SetCurrentDirectory(const FilePath& directory) { - base::ThreadRestrictions::AssertIOAllowed(); + ThreadRestrictions::AssertIOAllowed(); BOOL ret = ::SetCurrentDirectory(directory.value().c_str()); return ret != 0; } int GetMaximumPathComponentLength(const FilePath& path) { - base::ThreadRestrictions::AssertIOAllowed(); + ThreadRestrictions::AssertIOAllowed(); wchar_t volume_path[MAX_PATH]; if (!GetVolumePathNameW(path.NormalizePathSeparators().value().c_str(), @@ -680,9 +724,8 @@ int GetMaximumPathComponentLength(const FilePath& path) { return std::min(whole_path_limit, static_cast<int>(max_length)); } -} // namespace file_util +// ----------------------------------------------------------------------------- -namespace base { namespace internal { bool MoveUnsafe(const FilePath& from_path, const FilePath& to_path) { @@ -728,8 +771,24 @@ bool CopyFileUnsafe(const FilePath& from_path, const FilePath& to_path) { to_path.value().length() >= MAX_PATH) { return false; } - return (::CopyFile(from_path.value().c_str(), to_path.value().c_str(), - false) != 0); + + // 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, diff --git a/chromium/base/file_version_info_mac.h b/chromium/base/file_version_info_mac.h index f488cce4e58..f0edb3aea9f 100644 --- a/chromium/base/file_version_info_mac.h +++ b/chromium/base/file_version_info_mac.h @@ -23,27 +23,27 @@ class FileVersionInfoMac : public FileVersionInfo { // Accessors to the different version properties. // Returns an empty string if the property is not found. - virtual string16 company_name() OVERRIDE; - virtual string16 company_short_name() OVERRIDE; - virtual string16 product_name() OVERRIDE; - virtual string16 product_short_name() OVERRIDE; - virtual string16 internal_name() OVERRIDE; - virtual string16 product_version() OVERRIDE; - virtual string16 private_build() OVERRIDE; - virtual string16 special_build() OVERRIDE; - virtual string16 comments() OVERRIDE; - virtual string16 original_filename() OVERRIDE; - virtual string16 file_description() OVERRIDE; - virtual string16 file_version() OVERRIDE; - virtual string16 legal_copyright() OVERRIDE; - virtual string16 legal_trademarks() OVERRIDE; - virtual string16 last_change() OVERRIDE; + 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; private: - // Returns a string16 value for a property name. + // Returns a base::string16 value for a property name. // Returns the empty string if the property does not exist. - string16 GetString16Value(CFStringRef name); + base::string16 GetString16Value(CFStringRef name); base::scoped_nsobject<NSBundle> bundle_; diff --git a/chromium/base/file_version_info_mac.mm b/chromium/base/file_version_info_mac.mm index 0c6f8d4d3ca..3b5a8ba9bf8 100644 --- a/chromium/base/file_version_info_mac.mm +++ b/chromium/base/file_version_info_mac.mm @@ -31,35 +31,35 @@ FileVersionInfo* FileVersionInfo::CreateFileVersionInfo( return new FileVersionInfoMac(bundle); } -string16 FileVersionInfoMac::company_name() { - return string16(); +base::string16 FileVersionInfoMac::company_name() { + return base::string16(); } -string16 FileVersionInfoMac::company_short_name() { - return string16(); +base::string16 FileVersionInfoMac::company_short_name() { + return base::string16(); } -string16 FileVersionInfoMac::internal_name() { - return string16(); +base::string16 FileVersionInfoMac::internal_name() { + return base::string16(); } -string16 FileVersionInfoMac::product_name() { +base::string16 FileVersionInfoMac::product_name() { return GetString16Value(kCFBundleNameKey); } -string16 FileVersionInfoMac::product_short_name() { +base::string16 FileVersionInfoMac::product_short_name() { return GetString16Value(kCFBundleNameKey); } -string16 FileVersionInfoMac::comments() { - return string16(); +base::string16 FileVersionInfoMac::comments() { + return base::string16(); } -string16 FileVersionInfoMac::legal_copyright() { +base::string16 FileVersionInfoMac::legal_copyright() { return GetString16Value(CFSTR("CFBundleGetInfoString")); } -string16 FileVersionInfoMac::product_version() { +base::string16 FileVersionInfoMac::product_version() { // On OS X, CFBundleVersion is used by LaunchServices, and must follow // specific formatting rules, so the four-part Chrome version is in // CFBundleShortVersionString. On iOS, however, CFBundleVersion can be the @@ -72,31 +72,31 @@ string16 FileVersionInfoMac::product_version() { #endif // defined(OS_IOS) } -string16 FileVersionInfoMac::file_description() { - return string16(); +base::string16 FileVersionInfoMac::file_description() { + return base::string16(); } -string16 FileVersionInfoMac::legal_trademarks() { - return string16(); +base::string16 FileVersionInfoMac::legal_trademarks() { + return base::string16(); } -string16 FileVersionInfoMac::private_build() { - return string16(); +base::string16 FileVersionInfoMac::private_build() { + return base::string16(); } -string16 FileVersionInfoMac::file_version() { +base::string16 FileVersionInfoMac::file_version() { return product_version(); } -string16 FileVersionInfoMac::original_filename() { +base::string16 FileVersionInfoMac::original_filename() { return GetString16Value(kCFBundleNameKey); } -string16 FileVersionInfoMac::special_build() { - return string16(); +base::string16 FileVersionInfoMac::special_build() { + return base::string16(); } -string16 FileVersionInfoMac::last_change() { +base::string16 FileVersionInfoMac::last_change() { return GetString16Value(CFSTR("SCMRevision")); } @@ -108,7 +108,7 @@ bool FileVersionInfoMac::is_official_build() { #endif } -string16 FileVersionInfoMac::GetString16Value(CFStringRef name) { +base::string16 FileVersionInfoMac::GetString16Value(CFStringRef name) { if (bundle_) { NSString *ns_name = base::mac::CFToNSCast(name); NSString* value = [bundle_ objectForInfoDictionaryKey:ns_name]; @@ -116,5 +116,5 @@ string16 FileVersionInfoMac::GetString16Value(CFStringRef name) { return base::SysNSStringToUTF16(value); } } - return string16(); + return base::string16(); } diff --git a/chromium/base/file_version_info_win.cc b/chromium/base/file_version_info_win.cc index 80dbaeaadbf..5f33d1470f1 100644 --- a/chromium/base/file_version_info_win.cc +++ b/chromium/base/file_version_info_win.cc @@ -81,63 +81,63 @@ FileVersionInfo* FileVersionInfo::CreateFileVersionInfo( } } -string16 FileVersionInfoWin::company_name() { +base::string16 FileVersionInfoWin::company_name() { return GetStringValue(L"CompanyName"); } -string16 FileVersionInfoWin::company_short_name() { +base::string16 FileVersionInfoWin::company_short_name() { return GetStringValue(L"CompanyShortName"); } -string16 FileVersionInfoWin::internal_name() { +base::string16 FileVersionInfoWin::internal_name() { return GetStringValue(L"InternalName"); } -string16 FileVersionInfoWin::product_name() { +base::string16 FileVersionInfoWin::product_name() { return GetStringValue(L"ProductName"); } -string16 FileVersionInfoWin::product_short_name() { +base::string16 FileVersionInfoWin::product_short_name() { return GetStringValue(L"ProductShortName"); } -string16 FileVersionInfoWin::comments() { +base::string16 FileVersionInfoWin::comments() { return GetStringValue(L"Comments"); } -string16 FileVersionInfoWin::legal_copyright() { +base::string16 FileVersionInfoWin::legal_copyright() { return GetStringValue(L"LegalCopyright"); } -string16 FileVersionInfoWin::product_version() { +base::string16 FileVersionInfoWin::product_version() { return GetStringValue(L"ProductVersion"); } -string16 FileVersionInfoWin::file_description() { +base::string16 FileVersionInfoWin::file_description() { return GetStringValue(L"FileDescription"); } -string16 FileVersionInfoWin::legal_trademarks() { +base::string16 FileVersionInfoWin::legal_trademarks() { return GetStringValue(L"LegalTrademarks"); } -string16 FileVersionInfoWin::private_build() { +base::string16 FileVersionInfoWin::private_build() { return GetStringValue(L"PrivateBuild"); } -string16 FileVersionInfoWin::file_version() { +base::string16 FileVersionInfoWin::file_version() { return GetStringValue(L"FileVersion"); } -string16 FileVersionInfoWin::original_filename() { +base::string16 FileVersionInfoWin::original_filename() { return GetStringValue(L"OriginalFilename"); } -string16 FileVersionInfoWin::special_build() { +base::string16 FileVersionInfoWin::special_build() { return GetStringValue(L"SpecialBuild"); } -string16 FileVersionInfoWin::last_change() { +base::string16 FileVersionInfoWin::last_change() { return GetStringValue(L"LastChange"); } diff --git a/chromium/base/file_version_info_win.h b/chromium/base/file_version_info_win.h index a37857783a9..d623c454261 100644 --- a/chromium/base/file_version_info_win.h +++ b/chromium/base/file_version_info_win.h @@ -22,21 +22,21 @@ class FileVersionInfoWin : public FileVersionInfo { // Accessors to the different version properties. // Returns an empty string if the property is not found. - virtual string16 company_name() OVERRIDE; - virtual string16 company_short_name() OVERRIDE; - virtual string16 product_name() OVERRIDE; - virtual string16 product_short_name() OVERRIDE; - virtual string16 internal_name() OVERRIDE; - virtual string16 product_version() OVERRIDE; - virtual string16 private_build() OVERRIDE; - virtual string16 special_build() OVERRIDE; - virtual string16 comments() OVERRIDE; - virtual string16 original_filename() OVERRIDE; - virtual string16 file_description() OVERRIDE; - virtual string16 file_version() OVERRIDE; - virtual string16 legal_copyright() OVERRIDE; - virtual string16 legal_trademarks() OVERRIDE; - virtual string16 last_change() OVERRIDE; + 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; // Lets you access other properties not covered above. @@ -50,7 +50,7 @@ class FileVersionInfoWin : public FileVersionInfo { VS_FIXEDFILEINFO* fixed_file_info() { return fixed_file_info_; } private: - scoped_ptr_malloc<char> data_; + scoped_ptr<char, base::FreeDeleter> data_; int language_; int code_page_; // This is a pointer into the data_ if it exists. Otherwise NULL. diff --git a/chromium/base/files/OWNERS b/chromium/base/files/OWNERS index 7260260bb41..b99e8a2fc7a 100644 --- a/chromium/base/files/OWNERS +++ b/chromium/base/files/OWNERS @@ -1,2 +1,3 @@ -# for file_path_watcher* -mnissler@chromium.org +rvargas@chromium.org + +per-file file_path_watcher*=mnissler@chromium.org diff --git a/chromium/base/files/file.cc b/chromium/base/files/file.cc index 4902f15a2ff..bfe87bdcc4d 100644 --- a/chromium/base/files/file.cc +++ b/chromium/base/files/file.cc @@ -20,7 +20,7 @@ File::Info::~Info() { File::File() : file_(kInvalidPlatformFileValue), - error_(FILE_OK), + error_details_(FILE_ERROR_FAILED), created_(false), async_(false) { } @@ -28,25 +28,39 @@ File::File() #if !defined(OS_NACL) File::File(const FilePath& name, uint32 flags) : file_(kInvalidPlatformFileValue), - error_(FILE_OK), + error_details_(FILE_OK), created_(false), async_(false) { - if (name.ReferencesParent()) { - error_ = FILE_ERROR_ACCESS_DENIED; - return; - } - CreateBaseFileUnsafe(name, flags); + Initialize(name, flags); } #endif +File::File(PlatformFile platform_file) + : file_(platform_file), + error_details_(FILE_OK), + created_(false), + async_(false) { +#if defined(OS_POSIX) + DCHECK_GE(platform_file, -1); +#endif +} + +File::File(Error error_details) + : file_(kInvalidPlatformFileValue), + error_details_(error_details), + created_(false), + async_(false) { +} + File::File(RValue other) : file_(other.object->TakePlatformFile()), - error_(other.object->error()), + error_details_(other.object->error_details()), created_(other.object->created()), async_(other.object->async_) { } File::~File() { + // Go through the AssertIOAllowed logic. Close(); } @@ -54,11 +68,65 @@ File& File::operator=(RValue other) { if (this != other.object) { Close(); SetPlatformFile(other.object->TakePlatformFile()); - error_ = other.object->error(); + error_details_ = other.object->error_details(); created_ = other.object->created(); async_ = other.object->async_; } return *this; } +#if !defined(OS_NACL) +void File::Initialize(const FilePath& name, uint32 flags) { + if (name.ReferencesParent()) { + error_details_ = FILE_ERROR_ACCESS_DENIED; + return; + } + InitializeUnsafe(name, flags); +} +#endif + +std::string File::ErrorToString(Error error) { + switch (error) { + case FILE_OK: + return "FILE_OK"; + case FILE_ERROR_FAILED: + return "FILE_ERROR_FAILED"; + case FILE_ERROR_IN_USE: + return "FILE_ERROR_IN_USE"; + case FILE_ERROR_EXISTS: + return "FILE_ERROR_EXISTS"; + case FILE_ERROR_NOT_FOUND: + return "FILE_ERROR_NOT_FOUND"; + case FILE_ERROR_ACCESS_DENIED: + return "FILE_ERROR_ACCESS_DENIED"; + case FILE_ERROR_TOO_MANY_OPENED: + return "FILE_ERROR_TOO_MANY_OPENED"; + case FILE_ERROR_NO_MEMORY: + return "FILE_ERROR_NO_MEMORY"; + case FILE_ERROR_NO_SPACE: + return "FILE_ERROR_NO_SPACE"; + case FILE_ERROR_NOT_A_DIRECTORY: + return "FILE_ERROR_NOT_A_DIRECTORY"; + case FILE_ERROR_INVALID_OPERATION: + return "FILE_ERROR_INVALID_OPERATION"; + case FILE_ERROR_SECURITY: + return "FILE_ERROR_SECURITY"; + case FILE_ERROR_ABORT: + return "FILE_ERROR_ABORT"; + case FILE_ERROR_NOT_A_FILE: + return "FILE_ERROR_NOT_A_FILE"; + case FILE_ERROR_NOT_EMPTY: + return "FILE_ERROR_NOT_EMPTY"; + case FILE_ERROR_INVALID_URL: + return "FILE_ERROR_INVALID_URL"; + case FILE_ERROR_IO: + return "FILE_ERROR_IO"; + case FILE_ERROR_MAX: + break; + } + + NOTREACHED(); + return ""; +} + } // namespace base diff --git a/chromium/base/files/file.h b/chromium/base/files/file.h index d1e0e8ca587..1913bc7f6c4 100644 --- a/chromium/base/files/file.h +++ b/chromium/base/files/file.h @@ -10,11 +10,15 @@ #include <windows.h> #endif +#if defined(OS_POSIX) +#include <sys/stat.h> +#endif + #include <string> #include "base/base_export.h" #include "base/basictypes.h" -#include "base/files/file_path.h" +#include "base/files/scoped_file.h" #include "base/move.h" #include "base/time/time.h" @@ -24,12 +28,19 @@ namespace base { +class FilePath; + #if defined(OS_WIN) typedef HANDLE PlatformFile; #elif defined(OS_POSIX) typedef int PlatformFile; -#endif +#if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) +typedef struct stat stat_wrapper_t; +#else +typedef struct stat64 stat_wrapper_t; +#endif +#endif // defined(OS_POSIX) // Thin wrapper around an OS-level file. // Note that this class does not provide any support for asynchronous IO, other @@ -120,6 +131,10 @@ class BASE_EXPORT File { struct BASE_EXPORT Info { Info(); ~Info(); +#if defined(OS_POSIX) + // Fills this struct with values from |stat_info|. + void FromStat(const stat_wrapper_t& stat_info); +#endif // The size of the file in bytes. Undefined when is_directory is true. int64 size; @@ -149,6 +164,9 @@ class BASE_EXPORT File { // Takes ownership of |platform_file|. explicit File(PlatformFile platform_file); + // Creates an object with a specific error_details code. + explicit File(Error error_details); + // Move constructor for C++03 move emulation of this type. File(RValue other); @@ -157,9 +175,12 @@ class BASE_EXPORT File { // Move operator= for C++03 move emulation of this type. 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 CreateBaseFileUnsafe(const FilePath& name, uint32 flags); + void InitializeUnsafe(const FilePath& name, uint32 flags); bool IsValid() const; @@ -168,10 +189,14 @@ class BASE_EXPORT File { // FLAG_CREATE_ALWAYS), and false otherwise. bool created() const { return created_; } - // Returns the OS result of opening this file. - Error error() const { return error_; } + // 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); + // if (!file.IsValid()) + // return; + Error error_details() const { return error_details_; } - PlatformFile GetPlatformFile() const { return file_; } + PlatformFile GetPlatformFile() const; PlatformFile TakePlatformFile(); // Destroying this object closes the file automatically. @@ -216,10 +241,13 @@ class BASE_EXPORT File { // platforms. Returns the number of bytes written, or -1 on error. int WriteAtCurrentPosNoBestEffort(const char* data, int size); + // Returns the current size of this file, or a negative number on failure. + int64 GetLength(); + // Truncates the file to the given length. If |length| is greater than the // current size of the file, the file is extended with zeros. If the file // doesn't exist, |false| is returned. - bool Truncate(int64 length); + bool SetLength(int64 length); // Flushes the buffers. bool Flush(); @@ -255,22 +283,27 @@ class BASE_EXPORT File { // Unlock a file previously locked. Error Unlock(); + bool async() const { return async_; } + #if defined(OS_WIN) static Error OSErrorToFileError(DWORD last_error); #elif defined(OS_POSIX) static Error OSErrorToFileError(int saved_errno); #endif + // Converts an error value to a human-readable form. Used for logging. + static std::string ErrorToString(Error error); + private: void SetPlatformFile(PlatformFile file); #if defined(OS_WIN) win::ScopedHandle file_; #elif defined(OS_POSIX) - PlatformFile file_; + ScopedFD file_; #endif - Error error_; + Error error_details_; bool created_; bool async_; }; diff --git a/chromium/base/files/file_path.cc b/chromium/base/files/file_path.cc index 3ea5856a0d5..a8b27139988 100644 --- a/chromium/base/files/file_path.cc +++ b/chromium/base/files/file_path.cc @@ -521,7 +521,7 @@ FilePath FilePath::Append(const FilePath& component) const { } FilePath FilePath::AppendASCII(const StringPiece& component) const { - DCHECK(IsStringASCII(component)); + DCHECK(base::IsStringASCII(component)); #if defined(OS_WIN) return Append(ASCIIToUTF16(component.as_string())); #elif defined(OS_POSIX) @@ -587,7 +587,7 @@ string16 FilePath::LossyDisplayName() const { } std::string FilePath::MaybeAsASCII() const { - if (IsStringASCII(path_)) + if (base::IsStringASCII(path_)) return path_; return std::string(); } @@ -632,9 +632,9 @@ string16 FilePath::LossyDisplayName() const { } std::string FilePath::MaybeAsASCII() const { - if (IsStringASCII(path_)) - return WideToASCII(path_); - return ""; + if (base::IsStringASCII(path_)) + return UTF16ToASCII(path_); + return std::string(); } std::string FilePath::AsUTF8Unsafe() const { @@ -1292,10 +1292,16 @@ void FilePath::StripTrailingSeparatorsInternal() { } FilePath FilePath::NormalizePathSeparators() const { + return NormalizePathSeparatorsTo(kSeparators[0]); +} + +FilePath FilePath::NormalizePathSeparatorsTo(CharType separator) const { #if defined(FILE_PATH_USES_WIN_SEPARATORS) + DCHECK_NE(kSeparators + kSeparatorsLength, + std::find(kSeparators, kSeparators + kSeparatorsLength, separator)); StringType copy = path_; - for (size_t i = 1; i < kSeparatorsLength; ++i) { - std::replace(copy.begin(), copy.end(), kSeparators[i], kSeparators[0]); + for (size_t i = 0; i < kSeparatorsLength; ++i) { + std::replace(copy.begin(), copy.end(), kSeparators[i], separator); } return FilePath(copy); #else diff --git a/chromium/base/files/file_path.h b/chromium/base/files/file_path.h index f4b8ff8ade8..008b9f5afc6 100644 --- a/chromium/base/files/file_path.h +++ b/chromium/base/files/file_path.h @@ -189,6 +189,13 @@ class BASE_EXPORT FilePath { // Returns a vector of all of the components of the provided path. It is // equivalent to calling DirName().value() on the path's root component, // and BaseName().value() on each child component. + // + // To make sure this is lossless so we can differentiate absolute and + // relative paths, the root slash will be included even though no other + // slashes will be. The precise behavior is: + // + // Posix: "/foo/bar" -> [ "/", "foo", "bar" ] + // Windows: "C:\foo\bar" -> [ "C:", "\\", "foo", "bar" ] void GetComponents(std::vector<FilePath::StringType>* components) const; // Returns true if this FilePath is a strict parent of the |child|. Absolute @@ -239,7 +246,7 @@ class BASE_EXPORT FilePath { // TODO(davidben): Check all our extension-sensitive code to see if // 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 file_util::GetUniquePathNumber(). + // long "extensions" to logic like base::GetUniquePathNumber(). StringType FinalExtension() const; // Returns "C:\pics\jojo" for path "C:\pics\jojo.jpg" @@ -314,8 +321,8 @@ class BASE_EXPORT FilePath { // separator. FilePath StripTrailingSeparators() const WARN_UNUSED_RESULT; - // Returns true if this FilePath contains any attempt to reference a parent - // directory (i.e. has a path component that is ".." + // Returns true if this FilePath contains an attempt to reference a parent + // directory (e.g. has a path component that is ".."). bool ReferencesParent() const; // Return a Unicode human-readable version of this path. @@ -367,6 +374,10 @@ class BASE_EXPORT FilePath { // (if FILE_PATH_USES_WIN_SEPARATORS is true), or do nothing on POSIX systems. FilePath NormalizePathSeparators() const; + // Normalize all path separattors to given type on Windows + // (if FILE_PATH_USES_WIN_SEPARATORS is true), or do nothing on POSIX systems. + FilePath NormalizePathSeparatorsTo(CharType separator) const; + // Compare two strings in the same way the file system does. // Note that these always ignore case, even on file systems that are case- // sensitive. If case-sensitive comparison is ever needed, add corresponding diff --git a/chromium/base/files/file_path_watcher.cc b/chromium/base/files/file_path_watcher.cc index 49e0a237f69..b17354197d4 100644 --- a/chromium/base/files/file_path_watcher.cc +++ b/chromium/base/files/file_path_watcher.cc @@ -10,6 +10,10 @@ #include "base/logging.h" #include "base/message_loop/message_loop.h" +#if defined(OS_MACOSX) && !defined(OS_IOS) +#include "base/mac/mac_util.h" +#endif + namespace base { FilePathWatcher::~FilePathWatcher() { @@ -22,6 +26,19 @@ void FilePathWatcher::CancelWatch( delegate->CancelOnMessageLoopThread(); } +// static +bool FilePathWatcher::RecursiveWatchAvailable() { +#if defined(OS_MACOSX) && !defined(OS_IOS) + // 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) + return true; +#else + return false; +#endif +} + FilePathWatcher::PlatformDelegate::PlatformDelegate(): cancelled_(false) { } diff --git a/chromium/base/files/file_path_watcher.h b/chromium/base/files/file_path_watcher.h index 3c1941f012b..b90efa1800f 100644 --- a/chromium/base/files/file_path_watcher.h +++ b/chromium/base/files/file_path_watcher.h @@ -88,12 +88,15 @@ class BASE_EXPORT FilePathWatcher { // shutdown. static void CancelWatch(const scoped_refptr<PlatformDelegate>& delegate); + // Returns true if the platform and OS version support recursive watches. + static bool RecursiveWatchAvailable(); + // Invokes |callback| whenever updates to |path| are detected. This should be // called at most once, and from a MessageLoop of TYPE_IO. Set |recursive| to // true, to watch |path| and its children. The callback will be invoked on // the same loop. Returns true on success. // - // NOTE: Recursive watch is not supported on all platforms and file systems. + // Recursive watch is not supported on all platforms and file systems. // Watch() will return false in the case of failure. bool Watch(const FilePath& path, bool recursive, const Callback& callback); diff --git a/chromium/base/files/file_path_watcher_browsertest.cc b/chromium/base/files/file_path_watcher_browsertest.cc index aed409c7871..8f57cadda90 100644 --- a/chromium/base/files/file_path_watcher_browsertest.cc +++ b/chromium/base/files/file_path_watcher_browsertest.cc @@ -172,8 +172,7 @@ class FilePathWatcherTest : public testing::Test { // Write |content| to |file|. Returns true on success. bool WriteFile(const FilePath& file, const std::string& content) { - int write_size = file_util::WriteFile(file, content.c_str(), - content.length()); + int write_size = ::base::WriteFile(file, content.c_str(), content.length()); return write_size == static_cast<int>(content.length()); } @@ -206,9 +205,8 @@ bool FilePathWatcherTest::SetupWatch(const FilePath& target, bool result; file_thread_.message_loop_proxy()->PostTask( FROM_HERE, - base::Bind(SetupWatchCallback, - target, watcher, delegate, recursive_watch, &result, - &completion)); + base::Bind(SetupWatchCallback, target, watcher, delegate, recursive_watch, + &result, &completion)); completion.Wait(); return result; } @@ -484,12 +482,17 @@ TEST_F(FilePathWatcherTest, MoveParent) { DeleteDelegateOnFileThread(subdir_delegate.release()); } -#if defined(OS_WIN) TEST_F(FilePathWatcherTest, RecursiveWatch) { FilePathWatcher watcher; FilePath dir(temp_dir_.path().AppendASCII("dir")); scoped_ptr<TestDelegate> delegate(new TestDelegate(collector())); - ASSERT_TRUE(SetupWatch(dir, &watcher, delegate.get(), true)); + bool setup_result = SetupWatch(dir, &watcher, delegate.get(), true); + if (!FilePathWatcher::RecursiveWatchAvailable()) { + ASSERT_FALSE(setup_result); + DeleteDelegateOnFileThread(delegate.release()); + return; + } + ASSERT_TRUE(setup_result); // Main directory("dir") creation. ASSERT_TRUE(base::CreateDirectory(dir)); @@ -537,16 +540,48 @@ TEST_F(FilePathWatcherTest, RecursiveWatch) { ASSERT_TRUE(WaitForEvents()); DeleteDelegateOnFileThread(delegate.release()); } -#else -TEST_F(FilePathWatcherTest, RecursiveWatch) { + +#if defined(OS_POSIX) +TEST_F(FilePathWatcherTest, RecursiveWithSymLink) { + if (!FilePathWatcher::RecursiveWatchAvailable()) + return; + FilePathWatcher watcher; - FilePath dir(temp_dir_.path().AppendASCII("dir")); + FilePath test_dir(temp_dir_.path().AppendASCII("test_dir")); + ASSERT_TRUE(base::CreateDirectory(test_dir)); + FilePath symlink(test_dir.AppendASCII("symlink")); scoped_ptr<TestDelegate> delegate(new TestDelegate(collector())); - // Non-Windows implementaion does not support recursive watching. - ASSERT_FALSE(SetupWatch(dir, &watcher, delegate.get(), true)); + ASSERT_TRUE(SetupWatch(symlink, &watcher, delegate.get(), true)); + + // Link creation. + FilePath target1(temp_dir_.path().AppendASCII("target1")); + ASSERT_TRUE(base::CreateSymbolicLink(target1, symlink)); + ASSERT_TRUE(WaitForEvents()); + + // Target1 creation. + ASSERT_TRUE(base::CreateDirectory(target1)); + ASSERT_TRUE(WaitForEvents()); + + // Create a file in target1. + FilePath target1_file(target1.AppendASCII("file")); + ASSERT_TRUE(WriteFile(target1_file, "content")); + ASSERT_TRUE(WaitForEvents()); + + // Link change. + FilePath target2(temp_dir_.path().AppendASCII("target2")); + ASSERT_TRUE(base::CreateDirectory(target2)); + ASSERT_TRUE(base::DeleteFile(symlink, false)); + ASSERT_TRUE(base::CreateSymbolicLink(target2, symlink)); + ASSERT_TRUE(WaitForEvents()); + + // Create a file in target2. + FilePath target2_file(target2.AppendASCII("file")); + ASSERT_TRUE(WriteFile(target2_file, "content")); + ASSERT_TRUE(WaitForEvents()); + DeleteDelegateOnFileThread(delegate.release()); } -#endif +#endif // OS_POSIX TEST_F(FilePathWatcherTest, MoveChild) { FilePathWatcher file_watcher; @@ -575,10 +610,6 @@ TEST_F(FilePathWatcherTest, MoveChild) { DeleteDelegateOnFileThread(subdir_delegate.release()); } -#if !defined(OS_LINUX) -// Linux implementation of FilePathWatcher doesn't catch attribute changes. -// http://crbug.com/78043 - // Verify that changing attributes on a file is caught TEST_F(FilePathWatcherTest, FileAttributesChanged) { ASSERT_TRUE(WriteFile(test_file(), "content")); @@ -592,8 +623,6 @@ TEST_F(FilePathWatcherTest, FileAttributesChanged) { DeleteDelegateOnFileThread(delegate.release()); } -#endif // !OS_LINUX - #if defined(OS_LINUX) // Verify that creating a symlink is caught. diff --git a/chromium/base/files/file_path_watcher_fsevents.cc b/chromium/base/files/file_path_watcher_fsevents.cc new file mode 100644 index 00000000000..edf4d239465 --- /dev/null +++ b/chromium/base/files/file_path_watcher_fsevents.cc @@ -0,0 +1,263 @@ +// 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/files/file_path_watcher_fsevents.h" + +#include <list> + +#include "base/bind.h" +#include "base/file_util.h" +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/mac/libdispatch_task_runner.h" +#include "base/mac/scoped_cftyperef.h" +#include "base/message_loop/message_loop.h" + +namespace base { + +namespace { + +// The latency parameter passed to FSEventsStreamCreate(). +const CFAbsoluteTime kEventLatencySeconds = 0.3; + +class FSEventsTaskRunner : public mac::LibDispatchTaskRunner { + public: + FSEventsTaskRunner() + : mac::LibDispatchTaskRunner("org.chromium.FilePathWatcherFSEvents") { + } + + protected: + virtual ~FSEventsTaskRunner() {} +}; + +static LazyInstance<FSEventsTaskRunner>::Leaky g_task_runner = + LAZY_INSTANCE_INITIALIZER; + +// Resolve any symlinks in the path. +FilePath ResolvePath(const FilePath& path) { + const unsigned kMaxLinksToResolve = 255; + + std::vector<FilePath::StringType> component_vector; + path.GetComponents(&component_vector); + std::list<FilePath::StringType> + components(component_vector.begin(), component_vector.end()); + + FilePath result; + unsigned resolve_count = 0; + while (resolve_count < kMaxLinksToResolve && !components.empty()) { + FilePath component(*components.begin()); + components.pop_front(); + + FilePath current; + if (component.IsAbsolute()) { + current = component; + } else { + current = result.Append(component); + } + + FilePath target; + if (ReadSymbolicLink(current, &target)) { + if (target.IsAbsolute()) + result.clear(); + std::vector<FilePath::StringType> target_components; + target.GetComponents(&target_components); + components.insert(components.begin(), target_components.begin(), + target_components.end()); + resolve_count++; + } else { + result = current; + } + } + + if (resolve_count >= kMaxLinksToResolve) + result.clear(); + 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[]) { + FilePathWatcherFSEvents* watcher = + reinterpret_cast<FilePathWatcherFSEvents*>(event_watcher); + DCHECK(g_task_runner.Get().RunsTasksOnCurrentThread()); + + bool root_changed = watcher->ResolveTargetPath(); + std::vector<FilePath> paths; + FSEventStreamEventId root_change_at = FSEventStreamGetLatestEventId(stream); + for (size_t i = 0; i < num_events; i++) { + if (flags[i] & kFSEventStreamEventFlagRootChanged) + root_changed = true; + if (event_ids[i]) + root_change_at = std::min(root_change_at, event_ids[i]); + paths.push_back(FilePath( + reinterpret_cast<char**>(event_paths)[i]).StripTrailingSeparators()); + } + + // Reinitialize the event stream if we find changes to the root. This is + // necessary since FSEvents doesn't report any events for the subtree after + // the directory to be watched gets created. + if (root_changed) { + // Resetting the event stream from within the callback fails (FSEvents spews + // bad file descriptor errors), so post a task to do the reset. + g_task_runner.Get().PostTask( + FROM_HERE, + Bind(&FilePathWatcherFSEvents::UpdateEventStream, watcher, + root_change_at)); + } + + watcher->OnFilePathsChanged(paths); +} + +} // namespace + +FilePathWatcherFSEvents::FilePathWatcherFSEvents() : fsevent_stream_(NULL) { +} + +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; + } + } +} + +bool FilePathWatcherFSEvents::Watch(const FilePath& path, + bool recursive, + const FilePathWatcher::Callback& callback) { + DCHECK(resolved_target_.empty()); + DCHECK(MessageLoopForIO::current()); + DCHECK(!callback.is_null()); + + // 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(); + 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(); + } +} + +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. + DCHECK(g_task_runner.Get().RunsTasksOnCurrentThread()); + + set_cancelled(); + if (fsevent_stream_) { + DestroyEventStream(); + callback_.Reset(); + target_.clear(); + resolved_target_.clear(); + } +} + +void FilePathWatcherFSEvents::UpdateEventStream( + FSEventStreamEventId start_event) { + DCHECK(g_task_runner.Get().RunsTasksOnCurrentThread()); + + // 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()) + return; + + if (fsevent_stream_) + DestroyEventStream(); + + ScopedCFTypeRef<CFStringRef> cf_path(CFStringCreateWithCString( + NULL, resolved_target_.value().c_str(), kCFStringEncodingMacHFS)); + ScopedCFTypeRef<CFStringRef> cf_dir_path(CFStringCreateWithCString( + NULL, resolved_target_.DirName().value().c_str(), + kCFStringEncodingMacHFS)); + CFStringRef paths_array[] = { cf_path.get(), cf_dir_path.get() }; + ScopedCFTypeRef<CFArrayRef> watched_paths(CFArrayCreate( + NULL, reinterpret_cast<const void**>(paths_array), arraysize(paths_array), + &kCFTypeArrayCallBacks)); + + FSEventStreamContext context; + context.version = 0; + context.info = this; + context.retain = NULL; + context.release = NULL; + context.copyDescription = NULL; + + fsevent_stream_ = FSEventStreamCreate(NULL, &FSEventsCallback, &context, + watched_paths, + start_event, + kEventLatencySeconds, + kFSEventStreamCreateFlagWatchRoot); + FSEventStreamSetDispatchQueue(fsevent_stream_, + g_task_runner.Get().GetDispatchQueue()); + + if (!FSEventStreamStart(fsevent_stream_)) + message_loop()->PostTask(FROM_HERE, Bind(callback_, target_, true)); +} + +bool FilePathWatcherFSEvents::ResolveTargetPath() { + DCHECK(g_task_runner.Get().RunsTasksOnCurrentThread()); + 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)); + return changed; +} + +void FilePathWatcherFSEvents::DestroyEventStream() { + FSEventStreamStop(fsevent_stream_); + FSEventStreamInvalidate(fsevent_stream_); + FSEventStreamRelease(fsevent_stream_); + fsevent_stream_ = NULL; +} + +void FilePathWatcherFSEvents::StartEventStream( + FSEventStreamEventId start_event) { + DCHECK(g_task_runner.Get().RunsTasksOnCurrentThread()); + 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 new file mode 100644 index 00000000000..5640b4d5497 --- /dev/null +++ b/chromium/base/files/file_path_watcher_fsevents.h @@ -0,0 +1,73 @@ +// 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_FILES_FILE_PATH_WATCHER_FSEVENTS_H_ +#define BASE_FILES_FILE_PATH_WATCHER_FSEVENTS_H_ + +#include <CoreServices/CoreServices.h> + +#include <vector> + +#include "base/files/file_path.h" +#include "base/files/file_path_watcher.h" + +namespace base { + +// Mac-specific file watcher implementation based on FSEvents. +// There are trade-offs between the FSEvents implementation and a kqueue +// implementation. The biggest issues are that FSEvents on 10.6 sometimes drops +// events and kqueue does not trigger for modifications to a file in a watched +// directory. See file_path_watcher_mac.cc for the code that decides when to +// use which one. +class FilePathWatcherFSEvents : public FilePathWatcher::PlatformDelegate { + public: + FilePathWatcherFSEvents(); + + // Called from the FSEvents callback whenever there is a change to the paths. + void OnFilePathsChanged(const std::vector<FilePath>& paths); + + // (Re-)Initialize the event stream to start reporting events from + // |start_event|. + void UpdateEventStream(FSEventStreamEventId start_event); + + // Returns true if resolving the target path got a different result than + // last time it was done. + bool ResolveTargetPath(); + + // FilePathWatcher::PlatformDelegate overrides. + virtual bool Watch(const FilePath& path, + bool recursive, + const FilePathWatcher::Callback& callback) OVERRIDE; + virtual void Cancel() OVERRIDE; + + private: + virtual ~FilePathWatcherFSEvents(); + + // Destroy the event stream. + void DestroyEventStream(); + + // Start watching the FSEventStream. + void StartEventStream(FSEventStreamEventId start_event); + + // Cleans up and stops the event stream. + virtual void CancelOnMessageLoopThread() OVERRIDE; + + // Callback to notify upon changes. + FilePathWatcher::Callback callback_; + + // Target path to watch (passed to callback). + FilePath target_; + + // Target path with all symbolic links resolved. + FilePath resolved_target_; + + // Backend stream we receive event callbacks from (strong reference). + FSEventStreamRef fsevent_stream_; + + DISALLOW_COPY_AND_ASSIGN(FilePathWatcherFSEvents); +}; + +} // namespace base + +#endif // BASE_FILES_FILE_PATH_WATCHER_FSEVENTS_H_ diff --git a/chromium/base/files/file_path_watcher_kqueue.cc b/chromium/base/files/file_path_watcher_kqueue.cc index e035f22caa5..c38e3448e73 100644 --- a/chromium/base/files/file_path_watcher_kqueue.cc +++ b/chromium/base/files/file_path_watcher_kqueue.cc @@ -2,19 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/files/file_path_watcher.h" +#include "base/files/file_path_watcher_kqueue.h" #include <fcntl.h> -#include <sys/event.h> #include <sys/param.h> -#include <vector> - #include "base/bind.h" #include "base/file_util.h" #include "base/logging.h" -#include "base/message_loop/message_loop.h" -#include "base/message_loop/message_loop_proxy.h" #include "base/strings/stringprintf.h" // On some platforms these are not defined. @@ -27,136 +22,18 @@ namespace base { -namespace { - -// Mac-specific file watcher implementation based on kqueue. -// Originally it was based on FSEvents so that the semantics were equivalent -// on Linux, OSX and Windows where it was able to detect: -// - file creation/deletion/modification in a watched directory -// - file creation/deletion/modification for a watched file -// - modifications to the paths to a watched object that would affect the -// object such as renaming/attibute changes etc. -// The FSEvents version did all of the above except handling attribute changes -// to path components. Unfortunately FSEvents appears to have an issue where the -// current implementation (Mac OS X 10.6.7) sometimes drops events and doesn't -// send notifications. See -// http://code.google.com/p/chromium/issues/detail?id=54822#c31 for source that -// will reproduce the problem. FSEvents also required having a CFRunLoop -// backing the thread that it was running on, that caused added complexity -// in the interfaces. -// The kqueue implementation will handle all of the items in the list above -// except for detecting modifications to files in a watched directory. It will -// detect the creation and deletion of files, just not the modification of -// files. It does however detect the attribute changes that the FSEvents impl -// would miss. -class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate, - public MessageLoopForIO::Watcher, - public MessageLoop::DestructionObserver { - public: - FilePathWatcherImpl() : kqueue_(-1) {} - - // MessageLoopForIO::Watcher overrides. - virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE; - virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE; - - // MessageLoop::DestructionObserver overrides. - virtual void WillDestroyCurrentMessageLoop() OVERRIDE; - - // FilePathWatcher::PlatformDelegate overrides. - virtual bool Watch(const FilePath& path, - bool recursive, - const FilePathWatcher::Callback& callback) OVERRIDE; - virtual void Cancel() OVERRIDE; - - protected: - virtual ~FilePathWatcherImpl() {} - - private: - class EventData { - public: - EventData(const FilePath& path, const FilePath::StringType& subdir) - : path_(path), subdir_(subdir) { } - FilePath path_; // Full path to this item. - FilePath::StringType subdir_; // Path to any sub item. - }; - typedef std::vector<struct kevent> EventVector; - - // Can only be called on |io_message_loop_|'s thread. - virtual void CancelOnMessageLoopThread() OVERRIDE; - - // Returns true if the kevent values are error free. - bool AreKeventValuesValid(struct kevent* kevents, int count); - - // Respond to a change of attributes of the path component represented by - // |event|. Sets |target_file_affected| to true if |target_| is affected. - // Sets |update_watches| to true if |events_| need to be updated. - void HandleAttributesChange(const EventVector::iterator& event, - bool* target_file_affected, - bool* update_watches); - - // Respond to a move or deletion of the path component represented by - // |event|. Sets |target_file_affected| to true if |target_| is affected. - // Sets |update_watches| to true if |events_| need to be updated. - void HandleDeleteOrMoveChange(const EventVector::iterator& event, - bool* target_file_affected, - bool* update_watches); - - // Respond to a creation of an item in the path component represented by - // |event|. Sets |target_file_affected| to true if |target_| is affected. - // Sets |update_watches| to true if |events_| need to be updated. - void HandleCreateItemChange(const EventVector::iterator& event, - bool* target_file_affected, - bool* update_watches); - - // Update |events_| with the current status of the system. - // Sets |target_file_affected| to true if |target_| is affected. - // Returns false if an error occurs. - bool UpdateWatches(bool* target_file_affected); - - // Fills |events| with one kevent per component in |path|. - // Returns the number of valid events created where a valid event is - // defined as one that has a ident (file descriptor) field != -1. - static int EventsForPath(FilePath path, EventVector *events); - - // Release a kevent generated by EventsForPath. - static void ReleaseEvent(struct kevent& event); - - // Returns a file descriptor that will not block the system from deleting - // the file it references. - static uintptr_t FileDescriptorForPath(const FilePath& path); - - static const uintptr_t kNoFileDescriptor = static_cast<uintptr_t>(-1); - - // Closes |*fd| and sets |*fd| to -1. - static void CloseFileDescriptor(uintptr_t* fd); - - // Returns true if kevent has open file descriptor. - static bool IsKeventFileDescriptorOpen(const struct kevent& event) { - return event.ident != kNoFileDescriptor; - } - - static EventData* EventDataForKevent(const struct kevent& event) { - return reinterpret_cast<EventData*>(event.udata); - } - - EventVector events_; - scoped_refptr<base::MessageLoopProxy> io_message_loop_; - MessageLoopForIO::FileDescriptorWatcher kqueue_watcher_; - FilePathWatcher::Callback callback_; - FilePath target_; - int kqueue_; +FilePathWatcherKQueue::FilePathWatcherKQueue() : kqueue_(-1) {} - DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl); -}; +FilePathWatcherKQueue::~FilePathWatcherKQueue() {} -void FilePathWatcherImpl::ReleaseEvent(struct kevent& event) { +void FilePathWatcherKQueue::ReleaseEvent(struct kevent& event) { CloseFileDescriptor(&event.ident); EventData* entry = EventDataForKevent(event); delete entry; event.udata = NULL; } -int FilePathWatcherImpl::EventsForPath(FilePath path, EventVector* events) { +int FilePathWatcherKQueue::EventsForPath(FilePath path, EventVector* events) { DCHECK(MessageLoopForIO::current()); // Make sure that we are working with a clean slate. DCHECK(events->empty()); @@ -198,14 +75,14 @@ int FilePathWatcherImpl::EventsForPath(FilePath path, EventVector* events) { return last_existing_entry; } -uintptr_t FilePathWatcherImpl::FileDescriptorForPath(const FilePath& path) { +uintptr_t FilePathWatcherKQueue::FileDescriptorForPath(const FilePath& path) { int fd = HANDLE_EINTR(open(path.value().c_str(), O_EVTONLY)); if (fd == -1) return kNoFileDescriptor; return fd; } -void FilePathWatcherImpl::CloseFileDescriptor(uintptr_t* fd) { +void FilePathWatcherKQueue::CloseFileDescriptor(uintptr_t* fd) { if (*fd == kNoFileDescriptor) { return; } @@ -216,7 +93,7 @@ void FilePathWatcherImpl::CloseFileDescriptor(uintptr_t* fd) { *fd = kNoFileDescriptor; } -bool FilePathWatcherImpl::AreKeventValuesValid(struct kevent* kevents, +bool FilePathWatcherKQueue::AreKeventValuesValid(struct kevent* kevents, int count) { if (count < 0) { DPLOG(ERROR) << "kevent"; @@ -250,7 +127,7 @@ bool FilePathWatcherImpl::AreKeventValuesValid(struct kevent* kevents, return valid; } -void FilePathWatcherImpl::HandleAttributesChange( +void FilePathWatcherKQueue::HandleAttributesChange( const EventVector::iterator& event, bool* target_file_affected, bool* update_watches) { @@ -274,7 +151,7 @@ void FilePathWatcherImpl::HandleAttributesChange( } } -void FilePathWatcherImpl::HandleDeleteOrMoveChange( +void FilePathWatcherKQueue::HandleDeleteOrMoveChange( const EventVector::iterator& event, bool* target_file_affected, bool* update_watches) { @@ -290,7 +167,7 @@ void FilePathWatcherImpl::HandleDeleteOrMoveChange( } } -void FilePathWatcherImpl::HandleCreateItemChange( +void FilePathWatcherKQueue::HandleCreateItemChange( const EventVector::iterator& event, bool* target_file_affected, bool* update_watches) { @@ -310,7 +187,7 @@ void FilePathWatcherImpl::HandleCreateItemChange( } } -bool FilePathWatcherImpl::UpdateWatches(bool* target_file_affected) { +bool FilePathWatcherKQueue::UpdateWatches(bool* target_file_affected) { // Iterate over events adding kevents for items that exist to the kqueue. // Then check to see if new components in the path have been created. // Repeat until no new components in the path are detected. @@ -351,7 +228,7 @@ bool FilePathWatcherImpl::UpdateWatches(bool* target_file_affected) { return true; } -void FilePathWatcherImpl::OnFileCanReadWithoutBlocking(int fd) { +void FilePathWatcherKQueue::OnFileCanReadWithoutBlocking(int fd) { DCHECK(MessageLoopForIO::current()); DCHECK_EQ(fd, kqueue_); DCHECK(events_.size()); @@ -424,24 +301,24 @@ void FilePathWatcherImpl::OnFileCanReadWithoutBlocking(int fd) { } } -void FilePathWatcherImpl::OnFileCanWriteWithoutBlocking(int fd) { +void FilePathWatcherKQueue::OnFileCanWriteWithoutBlocking(int fd) { NOTREACHED(); } -void FilePathWatcherImpl::WillDestroyCurrentMessageLoop() { +void FilePathWatcherKQueue::WillDestroyCurrentMessageLoop() { CancelOnMessageLoopThread(); } -bool FilePathWatcherImpl::Watch(const FilePath& path, - bool recursive, - const FilePathWatcher::Callback& callback) { +bool FilePathWatcherKQueue::Watch(const FilePath& path, + bool recursive, + const FilePathWatcher::Callback& callback) { DCHECK(MessageLoopForIO::current()); DCHECK(target_.value().empty()); // Can only watch one path. DCHECK(!callback.is_null()); DCHECK_EQ(kqueue_, -1); if (recursive) { - // Recursive watch is not supported on this platform. + // Recursive watch is not supported using kqueue. NOTIMPLEMENTED(); return false; } @@ -478,7 +355,7 @@ bool FilePathWatcherImpl::Watch(const FilePath& path, kqueue_, true, MessageLoopForIO::WATCH_READ, &kqueue_watcher_, this); } -void FilePathWatcherImpl::Cancel() { +void FilePathWatcherKQueue::Cancel() { base::MessageLoopProxy* proxy = io_message_loop_.get(); if (!proxy) { set_cancelled(); @@ -486,13 +363,13 @@ void FilePathWatcherImpl::Cancel() { } if (!proxy->BelongsToCurrentThread()) { proxy->PostTask(FROM_HERE, - base::Bind(&FilePathWatcherImpl::Cancel, this)); + base::Bind(&FilePathWatcherKQueue::Cancel, this)); return; } CancelOnMessageLoopThread(); } -void FilePathWatcherImpl::CancelOnMessageLoopThread() { +void FilePathWatcherKQueue::CancelOnMessageLoopThread() { DCHECK(MessageLoopForIO::current()); if (!is_cancelled()) { set_cancelled(); @@ -509,10 +386,4 @@ void FilePathWatcherImpl::CancelOnMessageLoopThread() { } } -} // namespace - -FilePathWatcher::FilePathWatcher() { - impl_ = new FilePathWatcherImpl(); -} - } // namespace base diff --git a/chromium/base/files/file_path_watcher_kqueue.h b/chromium/base/files/file_path_watcher_kqueue.h new file mode 100644 index 00000000000..703fda67c4b --- /dev/null +++ b/chromium/base/files/file_path_watcher_kqueue.h @@ -0,0 +1,132 @@ +// 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_FILES_FILE_PATH_WATCHER_KQUEUE_H_ +#define BASE_FILES_FILE_PATH_WATCHER_KQUEUE_H_ + +#include <sys/event.h> +#include <vector> + +#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" + +namespace base { + +// Mac-specific file watcher implementation based on kqueue. +// The Linux and Windows versions are able to detect: +// - file creation/deletion/modification in a watched directory +// - file creation/deletion/modification for a watched file +// - modifications to the paths to a watched object that would affect the +// object such as renaming/attibute changes etc. +// The kqueue implementation will handle all of the items in the list above +// except for detecting modifications to files in a watched directory. It will +// detect the creation and deletion of files, just not the modification of +// files. It does however detect the attribute changes that the FSEvents impl +// would miss. +class FilePathWatcherKQueue : public FilePathWatcher::PlatformDelegate, + public MessageLoopForIO::Watcher, + public MessageLoop::DestructionObserver { + public: + FilePathWatcherKQueue(); + + // MessageLoopForIO::Watcher overrides. + virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE; + virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE; + + // MessageLoop::DestructionObserver overrides. + virtual void WillDestroyCurrentMessageLoop() OVERRIDE; + + // FilePathWatcher::PlatformDelegate overrides. + virtual bool Watch(const FilePath& path, + bool recursive, + const FilePathWatcher::Callback& callback) OVERRIDE; + virtual void Cancel() OVERRIDE; + + protected: + virtual ~FilePathWatcherKQueue(); + + private: + class EventData { + public: + EventData(const FilePath& path, const FilePath::StringType& subdir) + : path_(path), subdir_(subdir) { } + FilePath path_; // Full path to this item. + FilePath::StringType subdir_; // Path to any sub item. + }; + + typedef std::vector<struct kevent> EventVector; + + // Can only be called on |io_message_loop_|'s thread. + virtual void CancelOnMessageLoopThread() OVERRIDE; + + // Returns true if the kevent values are error free. + bool AreKeventValuesValid(struct kevent* kevents, int count); + + // Respond to a change of attributes of the path component represented by + // |event|. Sets |target_file_affected| to true if |target_| is affected. + // Sets |update_watches| to true if |events_| need to be updated. + void HandleAttributesChange(const EventVector::iterator& event, + bool* target_file_affected, + bool* update_watches); + + // Respond to a move or deletion of the path component represented by + // |event|. Sets |target_file_affected| to true if |target_| is affected. + // Sets |update_watches| to true if |events_| need to be updated. + void HandleDeleteOrMoveChange(const EventVector::iterator& event, + bool* target_file_affected, + bool* update_watches); + + // Respond to a creation of an item in the path component represented by + // |event|. Sets |target_file_affected| to true if |target_| is affected. + // Sets |update_watches| to true if |events_| need to be updated. + void HandleCreateItemChange(const EventVector::iterator& event, + bool* target_file_affected, + bool* update_watches); + + // Update |events_| with the current status of the system. + // Sets |target_file_affected| to true if |target_| is affected. + // Returns false if an error occurs. + bool UpdateWatches(bool* target_file_affected); + + // Fills |events| with one kevent per component in |path|. + // Returns the number of valid events created where a valid event is + // defined as one that has a ident (file descriptor) field != -1. + static int EventsForPath(FilePath path, EventVector *events); + + // Release a kevent generated by EventsForPath. + static void ReleaseEvent(struct kevent& event); + + // Returns a file descriptor that will not block the system from deleting + // the file it references. + static uintptr_t FileDescriptorForPath(const FilePath& path); + + static const uintptr_t kNoFileDescriptor = static_cast<uintptr_t>(-1); + + // Closes |*fd| and sets |*fd| to -1. + static void CloseFileDescriptor(uintptr_t* fd); + + // Returns true if kevent has open file descriptor. + static bool IsKeventFileDescriptorOpen(const struct kevent& event) { + return event.ident != kNoFileDescriptor; + } + + static EventData* EventDataForKevent(const struct kevent& event) { + return reinterpret_cast<EventData*>(event.udata); + } + + EventVector events_; + scoped_refptr<base::MessageLoopProxy> io_message_loop_; + MessageLoopForIO::FileDescriptorWatcher kqueue_watcher_; + FilePathWatcher::Callback callback_; + FilePath target_; + int kqueue_; + + DISALLOW_COPY_AND_ASSIGN(FilePathWatcherKQueue); +}; + +} // namespace base + +#endif // BASE_FILES_FILE_PATH_WATCHER_KQUEUE_H_ diff --git a/chromium/base/files/file_path_watcher_linux.cc b/chromium/base/files/file_path_watcher_linux.cc index d5052e2e512..915ad50abb2 100644 --- a/chromium/base/files/file_path_watcher_linux.cc +++ b/chromium/base/files/file_path_watcher_linux.cc @@ -12,6 +12,7 @@ #include <unistd.h> #include <algorithm> +#include <map> #include <set> #include <utility> #include <vector> @@ -20,6 +21,7 @@ #include "base/containers/hash_tables.h" #include "base/debug/trace_event.h" #include "base/file_util.h" +#include "base/files/file_enumerator.h" #include "base/files/file_path.h" #include "base/lazy_instance.h" #include "base/location.h" @@ -49,8 +51,8 @@ class InotifyReader { // change. Returns kInvalidWatch on failure. Watch AddWatch(const FilePath& path, FilePathWatcherImpl* watcher); - // Remove |watch|. Returns true on success. - bool RemoveWatch(Watch watch, FilePathWatcherImpl* watcher); + // Remove |watch| if it's valid. + void RemoveWatch(Watch watch, FilePathWatcherImpl* watcher); // Callback for InotifyReaderTask. void OnInotifyEvent(const inotify_event* event); @@ -91,12 +93,21 @@ class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate, // Called for each event coming from the watch. |fired_watch| identifies the // watch that fired, |child| indicates what has changed, and is relative to - // the currently watched path for |fired_watch|. The flag |created| is true if - // the object appears. + // the currently watched path for |fired_watch|. + // + // |created| is true if the object appears. + // |deleted| is true if the object disappears. + // |is_dir| is true if the object is a directory. void OnFilePathChanged(InotifyReader::Watch fired_watch, const FilePath::StringType& child, - bool created); + bool created, + bool deleted, + bool is_dir); + protected: + virtual ~FilePathWatcherImpl() {} + + private: // Start watching |path| for changes and notify |delegate| on each change. // Returns true if watch for |path| has been added successfully. virtual bool Watch(const FilePath& path, @@ -106,36 +117,58 @@ class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate, // Cancel the watch. This unregisters the instance with InotifyReader. virtual void Cancel() OVERRIDE; + // Cleans up and stops observing the message_loop() thread. + virtual void CancelOnMessageLoopThread() 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; - protected: - virtual ~FilePathWatcherImpl() {} - - private: - // Cleans up and stops observing the |message_loop_| thread. - virtual void CancelOnMessageLoopThread() 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. struct WatchEntry { - WatchEntry(InotifyReader::Watch watch, const FilePath::StringType& subdir) - : watch_(watch), - subdir_(subdir) {} + explicit WatchEntry(const FilePath::StringType& dirname) + : watch(InotifyReader::kInvalidWatch), + subdir(dirname) {} - InotifyReader::Watch watch_; - FilePath::StringType subdir_; - FilePath::StringType linkname_; + InotifyReader::Watch watch; + FilePath::StringType subdir; + FilePath::StringType linkname; }; typedef std::vector<WatchEntry> WatchVector; // Reconfigure to watch for the most specific parent directory of |target_| - // that exists. Updates |watched_path_|. Returns true on success. - bool UpdateWatches() WARN_UNUSED_RESULT; + // that exists. Also calls UpdateRecursiveWatches() below. + void UpdateWatches(); + + // Reconfigure to recursively watch |target_| and all its sub-directories. + // - This is a no-op if the watch is not recursive. + // - If |target_| does not exist, then clear all the recursive watches. + // - Assuming |target_| exists, passing kInvalidWatch as |fired_watch| forces + // addition of recursive watches for |target_|. + // - Otherwise, only the directory associated with |fired_watch| and its + // sub-directories will be reconfigured. + void UpdateRecursiveWatches(InotifyReader::Watch fired_watch, bool is_dir); + + // Enumerate recursively through |path| and add / update watches. + void UpdateRecursiveWatchesForPath(const FilePath& path); + + // Do internal bookkeeping to update mappings between |watch| and its + // associated full path |path|. + void TrackWatchForRecursion(InotifyReader::Watch watch, const FilePath& path); + + // Remove all the recursive watches. + void RemoveRecursiveWatches(); + + // |path| is a symlink to a non-existent target. Attempt to add a watch to + // the link target's parent directory. Returns true and update |watch_entry| + // on success. + bool AddWatchForBrokenSymlink(const FilePath& path, WatchEntry* watch_entry); + + bool HasValidWatchVector() const; // Callback to notify upon changes. FilePathWatcher::Callback callback_; @@ -143,11 +176,16 @@ class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate, // The file or directory we're supposed to watch. FilePath target_; + bool recursive_; + // The vector of watches and next component names for all path components, // starting at the root directory. The last entry corresponds to the watch for - // |target_| and always stores an empty next component name in |subdir_|. + // |target_| and always stores an empty next component name in |subdir|. WatchVector watches_; + hash_map<InotifyReader::Watch, FilePath> recursive_paths_by_watch_; + std::map<FilePath, InotifyReader::Watch> recursive_watches_by_path_; + DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl); }; @@ -224,8 +262,8 @@ InotifyReader::InotifyReader() shutdown_pipe_[1] = -1; if (inotify_fd_ >= 0 && pipe(shutdown_pipe_) == 0 && thread_.Start()) { thread_.message_loop()->PostTask( - FROM_HERE, Bind(&InotifyReaderCallback, this, inotify_fd_, - shutdown_pipe_[0])); + FROM_HERE, + Bind(&InotifyReaderCallback, this, inotify_fd_, shutdown_pipe_[0])); valid_ = true; } } @@ -255,7 +293,7 @@ InotifyReader::Watch InotifyReader::AddWatch( AutoLock auto_lock(lock_); Watch watch = inotify_add_watch(inotify_fd_, path.value().c_str(), - IN_CREATE | IN_DELETE | + IN_ATTRIB | IN_CREATE | IN_DELETE | IN_CLOSE_WRITE | IN_MOVE | IN_ONLYDIR); @@ -267,10 +305,9 @@ InotifyReader::Watch InotifyReader::AddWatch( return watch; } -bool InotifyReader::RemoveWatch(Watch watch, - FilePathWatcherImpl* watcher) { - if (!valid_) - return false; +void InotifyReader::RemoveWatch(Watch watch, FilePathWatcherImpl* watcher) { + if (!valid_ || (watch == kInvalidWatch)) + return; AutoLock auto_lock(lock_); @@ -278,10 +315,8 @@ bool InotifyReader::RemoveWatch(Watch watch, if (watchers_[watch].empty()) { watchers_.erase(watch); - return (inotify_rm_watch(inotify_fd_, watch) == 0); + inotify_rm_watch(inotify_fd_, watch); } - - return true; } void InotifyReader::OnInotifyEvent(const inotify_event* event) { @@ -296,73 +331,96 @@ void InotifyReader::OnInotifyEvent(const inotify_event* event) { ++watcher) { (*watcher)->OnFilePathChanged(event->wd, child, - event->mask & (IN_CREATE | IN_MOVED_TO)); + event->mask & (IN_CREATE | IN_MOVED_TO), + event->mask & (IN_DELETE | IN_MOVED_FROM), + event->mask & IN_ISDIR); } } -FilePathWatcherImpl::FilePathWatcherImpl() { +FilePathWatcherImpl::FilePathWatcherImpl() + : recursive_(false) { } void FilePathWatcherImpl::OnFilePathChanged(InotifyReader::Watch fired_watch, const FilePath::StringType& child, - bool created) { + 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)); + // Switch to message_loop() to access |watches_| safely. + message_loop()->PostTask( + FROM_HERE, + Bind(&FilePathWatcherImpl::OnFilePathChanged, this, + fired_watch, child, created, deleted, is_dir)); + return; + } + + // Check to see if CancelOnMessageLoopThread() has already been called. + // May happen when code flow reaches here from the PostTask() above. + if (watches_.empty()) { + DCHECK(target_.empty()); return; } DCHECK(MessageLoopForIO::current()); + DCHECK(HasValidWatchVector()); + + // Used below to avoid multiple recursive updates. + bool did_update = false; // Find the entry in |watches_| that corresponds to |fired_watch|. - WatchVector::const_iterator watch_entry(watches_.begin()); - for ( ; watch_entry != watches_.end(); ++watch_entry) { - if (fired_watch == watch_entry->watch_) { - // Check whether a path component of |target_| changed. - bool change_on_target_path = child.empty() || - ((child == watch_entry->subdir_) && watch_entry->linkname_.empty()) || - (child == watch_entry->linkname_); - - // Check whether the change references |target_| or a direct child. - DCHECK(watch_entry->subdir_.empty() || - (watch_entry + 1) != watches_.end()); - bool target_changed = - (watch_entry->subdir_.empty() && (child == watch_entry->linkname_)) || - (watch_entry->subdir_.empty() && watch_entry->linkname_.empty()) || - (watch_entry->subdir_ == child && (watch_entry + 1)->subdir_.empty()); - - // Update watches if a directory component of the |target_| path - // (dis)appears. Note that we don't add the additional restriction - // of checking the event mask to see if it is for a directory here - // as changes to symlinks on the target path will not have - // IN_ISDIR set in the event masks. As a result we may sometimes - // call UpdateWatches() unnecessarily. - if (change_on_target_path && !UpdateWatches()) { - callback_.Run(target_, true /* error */); - return; - } + for (size_t i = 0; i < watches_.size(); ++i) { + const WatchEntry& watch_entry = watches_[i]; + if (fired_watch != watch_entry.watch) + continue; + + // Check whether a path component of |target_| changed. + bool change_on_target_path = + child.empty() || + (child == watch_entry.linkname) || + (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()); + + // Update watches if a directory component of the |target_| path + // (dis)appears. Note that we don't add the additional restriction of + // checking the event mask to see if it is for a directory here as changes + // to symlinks on the target path will not have IN_ISDIR set in the event + // masks. As a result we may sometimes call UpdateWatches() unnecessarily. + if (change_on_target_path && (created || deleted) && !did_update) { + UpdateWatches(); + did_update = true; + } - // Report the following events: - // - The target or a direct child of the target got changed (in case the - // watched path refers to a directory). - // - One of the parent directories got moved or deleted, since the target - // disappears in this case. - // - One of the parent directories appears. The event corresponding to - // the target appearing might have been missed in this case, so - // recheck. - if (target_changed || - (change_on_target_path && !created) || - (change_on_target_path && PathExists(target_))) { - callback_.Run(target_, false); - return; + // Report the following events: + // - The target or a direct child of the target got changed (in case the + // watched path refers to a directory). + // - One of the parent directories got moved or deleted, since the target + // disappears in this case. + // - One of the parent directories appears. The event corresponding to + // the target appearing might have been missed in this case, so recheck. + if (target_changed || + (change_on_target_path && deleted) || + (change_on_target_path && created && PathExists(target_))) { + if (!did_update) { + UpdateRecursiveWatches(fired_watch, is_dir); + did_update = true; } + callback_.Run(target_, false /* error */); + return; } } + + if (ContainsKey(recursive_paths_by_watch_, fired_watch)) { + if (!did_update) + UpdateRecursiveWatches(fired_watch, is_dir); + callback_.Run(target_, false /* error */); + } } bool FilePathWatcherImpl::Watch(const FilePath& path, @@ -370,120 +428,238 @@ bool FilePathWatcherImpl::Watch(const FilePath& path, const FilePathWatcher::Callback& callback) { DCHECK(target_.empty()); DCHECK(MessageLoopForIO::current()); - if (recursive) { - // Recursive watch is not supported on this platform. - NOTIMPLEMENTED(); - return false; - } set_message_loop(MessageLoopProxy::current().get()); callback_ = callback; target_ = path; + recursive_ = recursive; MessageLoop::current()->AddDestructionObserver(this); std::vector<FilePath::StringType> comps; target_.GetComponents(&comps); DCHECK(!comps.empty()); - std::vector<FilePath::StringType>::const_iterator comp = comps.begin(); - for (++comp; comp != comps.end(); ++comp) - watches_.push_back(WatchEntry(InotifyReader::kInvalidWatch, *comp)); - - watches_.push_back(WatchEntry(InotifyReader::kInvalidWatch, - FilePath::StringType())); - return UpdateWatches(); + for (size_t i = 1; i < comps.size(); ++i) + watches_.push_back(WatchEntry(comps[i])); + watches_.push_back(WatchEntry(FilePath::StringType())); + UpdateWatches(); + return true; } void FilePathWatcherImpl::Cancel() { if (callback_.is_null()) { - // Watch was never called, or the |message_loop_| thread is already gone. + // Watch was never called, or the message_loop() thread is already gone. set_cancelled(); return; } - // Switch to the message_loop_ if necessary so we can access |watches_|. + // 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))); + make_scoped_refptr(this))); } else { CancelOnMessageLoopThread(); } } void FilePathWatcherImpl::CancelOnMessageLoopThread() { - if (!is_cancelled()) - set_cancelled(); + DCHECK(message_loop()->BelongsToCurrentThread()); + set_cancelled(); if (!callback_.is_null()) { MessageLoop::current()->RemoveDestructionObserver(this); callback_.Reset(); } - for (WatchVector::iterator watch_entry(watches_.begin()); - watch_entry != watches_.end(); ++watch_entry) { - if (watch_entry->watch_ != InotifyReader::kInvalidWatch) - g_inotify_reader.Get().RemoveWatch(watch_entry->watch_, this); - } + for (size_t i = 0; i < watches_.size(); ++i) + g_inotify_reader.Get().RemoveWatch(watches_[i].watch, this); watches_.clear(); target_.clear(); + + if (recursive_) + RemoveRecursiveWatches(); } void FilePathWatcherImpl::WillDestroyCurrentMessageLoop() { CancelOnMessageLoopThread(); } -bool FilePathWatcherImpl::UpdateWatches() { - // Ensure this runs on the |message_loop_| exclusively in order to avoid +void FilePathWatcherImpl::UpdateWatches() { + // Ensure this runs on the message_loop() exclusively in order to avoid // concurrency issues. DCHECK(message_loop()->BelongsToCurrentThread()); + DCHECK(HasValidWatchVector()); // Walk the list of watches and update them as we go. FilePath path(FILE_PATH_LITERAL("/")); bool path_valid = true; - for (WatchVector::iterator watch_entry(watches_.begin()); - watch_entry != watches_.end(); ++watch_entry) { - InotifyReader::Watch old_watch = watch_entry->watch_; + for (size_t i = 0; i < watches_.size(); ++i) { + WatchEntry& watch_entry = watches_[i]; + InotifyReader::Watch old_watch = watch_entry.watch; + watch_entry.watch = InotifyReader::kInvalidWatch; + watch_entry.linkname.clear(); if (path_valid) { - watch_entry->watch_ = g_inotify_reader.Get().AddWatch(path, this); - if ((watch_entry->watch_ == InotifyReader::kInvalidWatch) && - base::IsLink(path)) { - FilePath link; - if (ReadSymbolicLink(path, &link)) { - if (!link.IsAbsolute()) - link = path.DirName().Append(link); - // Try watching symlink target directory. If the link target is "/", - // then we shouldn't get here in normal situations and if we do, we'd - // watch "/" for changes to a component "/" which is harmless so no - // special treatment of this case is required. - watch_entry->watch_ = - g_inotify_reader.Get().AddWatch(link.DirName(), this); - if (watch_entry->watch_ != InotifyReader::kInvalidWatch) { - watch_entry->linkname_ = link.BaseName().value(); - } else { - DPLOG(WARNING) << "Watch failed for " << link.DirName().value(); - // TODO(craig) Symlinks only work if the parent directory - // for the target exist. Ideally we should make sure we've - // watched all the components of the symlink path for - // changes. See crbug.com/91561 for details. - } + watch_entry.watch = g_inotify_reader.Get().AddWatch(path, this); + if (watch_entry.watch == InotifyReader::kInvalidWatch) { + if (IsLink(path)) { + path_valid = AddWatchForBrokenSymlink(path, &watch_entry); + } else { + path_valid = false; } } - if (watch_entry->watch_ == InotifyReader::kInvalidWatch) { - path_valid = false; - } - } else { - watch_entry->watch_ = InotifyReader::kInvalidWatch; } - if (old_watch != InotifyReader::kInvalidWatch && - old_watch != watch_entry->watch_) { + if (old_watch != watch_entry.watch) g_inotify_reader.Get().RemoveWatch(old_watch, this); + path = path.Append(watch_entry.subdir); + } + + UpdateRecursiveWatches(InotifyReader::kInvalidWatch, + false /* is directory? */); +} + +void FilePathWatcherImpl::UpdateRecursiveWatches( + InotifyReader::Watch fired_watch, + bool is_dir) { + if (!recursive_) + return; + + if (!DirectoryExists(target_)) { + RemoveRecursiveWatches(); + return; + } + + // Check to see if this is a forced update or if some component of |target_| + // has changed. For these cases, redo the watches for |target_| and below. + if (!ContainsKey(recursive_paths_by_watch_, fired_watch)) { + UpdateRecursiveWatchesForPath(target_); + return; + } + + // Underneath |target_|, only directory changes trigger watch updates. + if (!is_dir) + return; + + const FilePath& changed_dir = recursive_paths_by_watch_[fired_watch]; + + std::map<FilePath, InotifyReader::Watch>::iterator start_it = + recursive_watches_by_path_.lower_bound(changed_dir); + std::map<FilePath, InotifyReader::Watch>::iterator end_it = start_it; + for (; end_it != recursive_watches_by_path_.end(); ++end_it) { + const FilePath& cur_path = end_it->first; + if (!changed_dir.IsParent(cur_path)) + break; + if (!DirectoryExists(cur_path)) + g_inotify_reader.Get().RemoveWatch(end_it->second, this); + } + recursive_watches_by_path_.erase(start_it, end_it); + UpdateRecursiveWatchesForPath(changed_dir); +} + +void FilePathWatcherImpl::UpdateRecursiveWatchesForPath(const FilePath& path) { + DCHECK(recursive_); + DCHECK(!path.empty()); + DCHECK(DirectoryExists(path)); + + // Note: SHOW_SYM_LINKS exposes symlinks as symlinks, so they are ignored + // rather than followed. Following symlinks can easily lead to the undesirable + // situation where the entire file system is being watched. + FileEnumerator enumerator( + path, + true /* recursive enumeration */, + FileEnumerator::DIRECTORIES | FileEnumerator::SHOW_SYM_LINKS); + for (FilePath current = enumerator.Next(); + !current.empty(); + current = enumerator.Next()) { + DCHECK(enumerator.GetInfo().IsDirectory()); + + if (!ContainsKey(recursive_watches_by_path_, current)) { + // Add new watches. + InotifyReader::Watch watch = + g_inotify_reader.Get().AddWatch(current, this); + TrackWatchForRecursion(watch, current); + } else { + // Update existing watches. + InotifyReader::Watch old_watch = recursive_watches_by_path_[current]; + DCHECK_NE(InotifyReader::kInvalidWatch, old_watch); + InotifyReader::Watch watch = + g_inotify_reader.Get().AddWatch(current, this); + if (watch != old_watch) { + g_inotify_reader.Get().RemoveWatch(old_watch, this); + recursive_paths_by_watch_.erase(old_watch); + recursive_watches_by_path_.erase(current); + TrackWatchForRecursion(watch, current); + } } - path = path.Append(watch_entry->subdir_); } +} + +void FilePathWatcherImpl::TrackWatchForRecursion(InotifyReader::Watch watch, + const FilePath& path) { + DCHECK(recursive_); + DCHECK(!path.empty()); + DCHECK(target_.IsParent(path)); + + if (watch == InotifyReader::kInvalidWatch) + return; + + DCHECK(!ContainsKey(recursive_paths_by_watch_, watch)); + DCHECK(!ContainsKey(recursive_watches_by_path_, path)); + recursive_paths_by_watch_[watch] = path; + recursive_watches_by_path_[path] = watch; +} + +void FilePathWatcherImpl::RemoveRecursiveWatches() { + if (!recursive_) + return; + + for (hash_map<InotifyReader::Watch, FilePath>::const_iterator it = + recursive_paths_by_watch_.begin(); + it != recursive_paths_by_watch_.end(); + ++it) { + g_inotify_reader.Get().RemoveWatch(it->first, this); + } + recursive_paths_by_watch_.clear(); + recursive_watches_by_path_.clear(); +} +bool FilePathWatcherImpl::AddWatchForBrokenSymlink(const FilePath& path, + WatchEntry* watch_entry) { + DCHECK_EQ(InotifyReader::kInvalidWatch, watch_entry->watch); + FilePath link; + if (!ReadSymbolicLink(path, &link)) + return false; + + if (!link.IsAbsolute()) + link = path.DirName().Append(link); + + // Try watching symlink target directory. If the link target is "/", then we + // shouldn't get here in normal situations and if we do, we'd watch "/" for + // changes to a component "/" which is harmless so no special treatment of + // this case is required. + InotifyReader::Watch watch = + g_inotify_reader.Get().AddWatch(link.DirName(), this); + if (watch == InotifyReader::kInvalidWatch) { + // TODO(craig) Symlinks only work if the parent directory for the target + // exist. Ideally we should make sure we've watched all the components of + // the symlink path for changes. See crbug.com/91561 for details. + DPLOG(WARNING) << "Watch failed for " << link.DirName().value(); + return false; + } + watch_entry->watch = watch; + watch_entry->linkname = link.BaseName().value(); return true; } +bool FilePathWatcherImpl::HasValidWatchVector() const { + if (watches_.empty()) + return false; + for (size_t i = 0; i < watches_.size() - 1; ++i) { + if (watches_[i].subdir.empty()) + return false; + } + return watches_[watches_.size() - 1].subdir.empty(); +} + } // namespace FilePathWatcher::FilePathWatcher() { diff --git a/chromium/base/files/file_path_watcher_mac.cc b/chromium/base/files/file_path_watcher_mac.cc new file mode 100644 index 00000000000..54ca46d12bb --- /dev/null +++ b/chromium/base/files/file_path_watcher_mac.cc @@ -0,0 +1,60 @@ +// 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/files/file_path_watcher.h" +#include "base/files/file_path_watcher_kqueue.h" + +#if !defined(OS_IOS) +#include "base/files/file_path_watcher_fsevents.h" +#endif + +namespace base { + +namespace { + +class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate { + public: + virtual bool Watch(const FilePath& path, + bool recursive, + const FilePathWatcher::Callback& callback) OVERRIDE { + // Use kqueue for non-recursive watches and FSEvents for recursive ones. + DCHECK(!impl_.get()); + if (recursive) { + if (!FilePathWatcher::RecursiveWatchAvailable()) + return false; +#if !defined(OS_IOS) + impl_ = new FilePathWatcherFSEvents(); +#endif // OS_IOS + } else { + impl_ = new FilePathWatcherKQueue(); + } + DCHECK(impl_.get()); + return impl_->Watch(path, recursive, callback); + } + + virtual void Cancel() OVERRIDE { + if (impl_) + impl_->Cancel(); + set_cancelled(); + } + + virtual void CancelOnMessageLoopThread() OVERRIDE { + if (impl_) + impl_->Cancel(); + set_cancelled(); + } + + protected: + virtual ~FilePathWatcherImpl() {} + + scoped_refptr<PlatformDelegate> impl_; +}; + +} // namespace + +FilePathWatcher::FilePathWatcher() { + impl_ = new FilePathWatcherImpl(); +} + +} // namespace base diff --git a/chromium/base/files/file_path_watcher_win.cc b/chromium/base/files/file_path_watcher_win.cc index 2abbceacd29..6c10c12d313 100644 --- a/chromium/base/files/file_path_watcher_win.cc +++ b/chromium/base/files/file_path_watcher_win.cc @@ -6,6 +6,7 @@ #include "base/bind.h" #include "base/file_util.h" +#include "base/files/file.h" #include "base/files/file_path.h" #include "base/logging.h" #include "base/memory/ref_counted.h" @@ -96,6 +97,12 @@ bool FilePathWatcherImpl::Watch(const FilePath& path, recursive_watch_ = recursive; MessageLoop::current()->AddDestructionObserver(this); + File::Info file_info; + if (GetFileInfo(target_, &file_info)) { + last_modified_ = file_info.last_modified; + first_notification_ = Time::Now(); + } + if (!UpdateWatch()) return false; @@ -122,6 +129,7 @@ void FilePathWatcherImpl::Cancel() { } void FilePathWatcherImpl::CancelOnMessageLoopThread() { + DCHECK(message_loop()->BelongsToCurrentThread()); set_cancelled(); if (handle_ != INVALID_HANDLE_VALUE) @@ -148,14 +156,23 @@ void FilePathWatcherImpl::OnObjectSignaled(HANDLE object) { } // Check whether the event applies to |target_| and notify the callback. - PlatformFileInfo file_info; + File::Info file_info; bool file_exists = GetFileInfo(target_, &file_info); - if (file_exists && (last_modified_.is_null() || - last_modified_ != file_info.last_modified)) { + if (recursive_watch_) { + // Only the mtime of |target_| is tracked but in a recursive watch, + // some other file or directory may have changed so all notifications + // are passed through. It is possible to figure out which file changed + // using ReadDirectoryChangesW() instead of FindFirstChangeNotification(), + // but that function is quite complicated: + // http://qualapps.blogspot.com/2010/05/understanding-readdirectorychangesw.html + callback_.Run(target_, false); + } else if (file_exists && (last_modified_.is_null() || + last_modified_ != file_info.last_modified)) { last_modified_ = file_info.last_modified; first_notification_ = Time::Now(); callback_.Run(target_, false); - } else if (file_exists && !first_notification_.is_null()) { + } else if (file_exists && last_modified_ == file_info.last_modified && + !first_notification_.is_null()) { // The target's last modification time is equal to what's on record. This // means that either an unrelated event occurred, or the target changed // again (file modification times only have a resolution of 1s). Comparing @@ -229,12 +246,6 @@ bool FilePathWatcherImpl::UpdateWatch() { if (handle_ != INVALID_HANDLE_VALUE) DestroyWatch(); - PlatformFileInfo file_info; - if (GetFileInfo(target_, &file_info)) { - last_modified_ = file_info.last_modified; - first_notification_ = Time::Now(); - } - // Start at the target and walk up the directory chain until we succesfully // create a watch handle in |handle_|. |child_dirs| keeps a stack of child // directories stripped from target, in reverse order. diff --git a/chromium/base/files/file_posix.cc b/chromium/base/files/file_posix.cc index 9d97c336aa6..0764ee98660 100644 --- a/chromium/base/files/file_posix.cc +++ b/chromium/base/files/file_posix.cc @@ -32,13 +32,11 @@ COMPILE_ASSERT(File::FROM_BEGIN == SEEK_SET && namespace { #if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) -typedef struct stat stat_wrapper_t; static int CallFstat(int fd, stat_wrapper_t *sb) { base::ThreadRestrictions::AssertIOAllowed(); return fstat(fd, sb); } #else -typedef struct stat64 stat_wrapper_t; static int CallFstat(int fd, stat_wrapper_t *sb) { base::ThreadRestrictions::AssertIOAllowed(); return fstat64(fd, sb); @@ -119,13 +117,63 @@ static File::Error CallFctnlFlock(PlatformFile file, bool do_lock) { } // namespace +void File::Info::FromStat(const stat_wrapper_t& stat_info) { + is_directory = S_ISDIR(stat_info.st_mode); + is_symbolic_link = S_ISLNK(stat_info.st_mode); + size = stat_info.st_size; + +#if defined(OS_LINUX) + time_t last_modified_sec = stat_info.st_mtim.tv_sec; + int64 last_modified_nsec = stat_info.st_mtim.tv_nsec; + time_t last_accessed_sec = stat_info.st_atim.tv_sec; + int64 last_accessed_nsec = stat_info.st_atim.tv_nsec; + time_t creation_time_sec = stat_info.st_ctim.tv_sec; + int64 creation_time_nsec = stat_info.st_ctim.tv_nsec; +#elif defined(OS_ANDROID) + time_t last_modified_sec = stat_info.st_mtime; + int64 last_modified_nsec = stat_info.st_mtime_nsec; + time_t last_accessed_sec = stat_info.st_atime; + int64 last_accessed_nsec = stat_info.st_atime_nsec; + time_t creation_time_sec = stat_info.st_ctime; + int64 creation_time_nsec = stat_info.st_ctime_nsec; +#elif defined(OS_MACOSX) || defined(OS_IOS) || defined(OS_BSD) + time_t last_modified_sec = stat_info.st_mtimespec.tv_sec; + int64 last_modified_nsec = stat_info.st_mtimespec.tv_nsec; + time_t last_accessed_sec = stat_info.st_atimespec.tv_sec; + int64 last_accessed_nsec = stat_info.st_atimespec.tv_nsec; + time_t creation_time_sec = stat_info.st_ctimespec.tv_sec; + int64 creation_time_nsec = stat_info.st_ctimespec.tv_nsec; +#else + time_t last_modified_sec = stat_info.st_mtime; + int64 last_modified_nsec = 0; + time_t last_accessed_sec = stat_info.st_atime; + int64 last_accessed_nsec = 0; + time_t creation_time_sec = stat_info.st_ctime; + int64 creation_time_nsec = 0; +#endif + + last_modified = + Time::FromTimeT(last_modified_sec) + + TimeDelta::FromMicroseconds(last_modified_nsec / + Time::kNanosecondsPerMicrosecond); + + last_accessed = + Time::FromTimeT(last_accessed_sec) + + TimeDelta::FromMicroseconds(last_accessed_nsec / + Time::kNanosecondsPerMicrosecond); + + creation_time = + Time::FromTimeT(creation_time_sec) + + TimeDelta::FromMicroseconds(creation_time_nsec / + 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::CreateBaseFileUnsafe(const FilePath& name, uint32 flags) { +void File::InitializeUnsafe(const FilePath& name, uint32 flags) { base::ThreadRestrictions::AssertIOAllowed(); DCHECK(!IsValid()); - DCHECK(!(flags & FLAG_ASYNC)); int open_flags = 0; if (flags & FLAG_CREATE) @@ -135,6 +183,7 @@ void File::CreateBaseFileUnsafe(const FilePath& name, uint32 flags) { if (flags & FLAG_CREATE_ALWAYS) { DCHECK(!open_flags); + DCHECK(flags & FLAG_WRITE); open_flags = O_CREAT | O_TRUNC; } @@ -147,7 +196,7 @@ void File::CreateBaseFileUnsafe(const FilePath& name, uint32 flags) { if (!open_flags && !(flags & FLAG_OPEN) && !(flags & FLAG_OPEN_ALWAYS)) { NOTREACHED(); errno = EOPNOTSUPP; - error_ = FILE_ERROR_FAILED; + error_details_ = FILE_ERROR_FAILED; return; } @@ -191,47 +240,51 @@ void File::CreateBaseFileUnsafe(const FilePath& name, uint32 flags) { } } - if (descriptor >= 0 && (flags & (FLAG_CREATE_ALWAYS | FLAG_CREATE))) + if (descriptor < 0) { + error_details_ = File::OSErrorToFileError(errno); + return; + } + + if (flags & (FLAG_CREATE_ALWAYS | FLAG_CREATE)) created_ = true; - if ((descriptor >= 0) && (flags & FLAG_DELETE_ON_CLOSE)) + if (flags & FLAG_DELETE_ON_CLOSE) unlink(name.value().c_str()); - if (descriptor >= 0) - error_ = FILE_OK; - else - error_ = File::OSErrorToFileError(errno); - - file_ = descriptor; + async_ = ((flags & FLAG_ASYNC) == FLAG_ASYNC); + error_details_ = FILE_OK; + file_.reset(descriptor); } #endif // !defined(OS_NACL) bool File::IsValid() const { - return file_ >= 0; + return file_.is_valid(); +} + +PlatformFile File::GetPlatformFile() const { + return file_.get(); } PlatformFile File::TakePlatformFile() { - PlatformFile file = file_; - file_ = kInvalidPlatformFileValue; - return file; + return file_.release(); } void File::Close() { - base::ThreadRestrictions::AssertIOAllowed(); if (!IsValid()) return; - if (!IGNORE_EINTR(close(file_))) - file_ = kInvalidPlatformFileValue; + base::ThreadRestrictions::AssertIOAllowed(); + file_.reset(); } int64 File::Seek(Whence whence, int64 offset) { base::ThreadRestrictions::AssertIOAllowed(); DCHECK(IsValid()); - if (file_ < 0 || offset < 0) + if (offset < 0) return -1; - return lseek(file_, static_cast<off_t>(offset), static_cast<int>(whence)); + return lseek(file_.get(), static_cast<off_t>(offset), + static_cast<int>(whence)); } int File::Read(int64 offset, char* data, int size) { @@ -243,7 +296,7 @@ int File::Read(int64 offset, char* data, int size) { int bytes_read = 0; int rv; do { - rv = HANDLE_EINTR(pread(file_, data + bytes_read, + rv = HANDLE_EINTR(pread(file_.get(), data + bytes_read, size - bytes_read, offset + bytes_read)); if (rv <= 0) break; @@ -263,7 +316,7 @@ int File::ReadAtCurrentPos(char* data, int size) { int bytes_read = 0; int rv; do { - rv = HANDLE_EINTR(read(file_, data, size)); + rv = HANDLE_EINTR(read(file_.get(), data + bytes_read, size - bytes_read)); if (rv <= 0) break; @@ -277,7 +330,7 @@ int File::ReadNoBestEffort(int64 offset, char* data, int size) { base::ThreadRestrictions::AssertIOAllowed(); DCHECK(IsValid()); - return HANDLE_EINTR(pread(file_, data, size, offset)); + return HANDLE_EINTR(pread(file_.get(), data, size, offset)); } int File::ReadAtCurrentPosNoBestEffort(char* data, int size) { @@ -286,13 +339,13 @@ int File::ReadAtCurrentPosNoBestEffort(char* data, int size) { if (size < 0) return -1; - return HANDLE_EINTR(read(file_, data, size)); + return HANDLE_EINTR(read(file_.get(), data, size)); } int File::Write(int64 offset, const char* data, int size) { base::ThreadRestrictions::AssertIOAllowed(); - if (IsOpenAppend(file_)) + if (IsOpenAppend(file_.get())) return WriteAtCurrentPos(data, size); DCHECK(IsValid()); @@ -302,7 +355,7 @@ int File::Write(int64 offset, const char* data, int size) { int bytes_written = 0; int rv; do { - rv = HANDLE_EINTR(pwrite(file_, data + bytes_written, + rv = HANDLE_EINTR(pwrite(file_.get(), data + bytes_written, size - bytes_written, offset + bytes_written)); if (rv <= 0) break; @@ -322,7 +375,8 @@ int File::WriteAtCurrentPos(const char* data, int size) { int bytes_written = 0; int rv; do { - rv = HANDLE_EINTR(write(file_, data, size)); + rv = HANDLE_EINTR(write(file_.get(), data + bytes_written, + size - bytes_written)); if (rv <= 0) break; @@ -338,19 +392,29 @@ int File::WriteAtCurrentPosNoBestEffort(const char* data, int size) { if (size < 0) return -1; - return HANDLE_EINTR(write(file_, data, size)); + return HANDLE_EINTR(write(file_.get(), data, size)); } -bool File::Truncate(int64 length) { +int64 File::GetLength() { + DCHECK(IsValid()); + + stat_wrapper_t file_info; + if (CallFstat(file_.get(), &file_info)) + return false; + + return file_info.st_size; +} + +bool File::SetLength(int64 length) { base::ThreadRestrictions::AssertIOAllowed(); DCHECK(IsValid()); - return !CallFtruncate(file_, length); + return !CallFtruncate(file_.get(), length); } bool File::Flush() { base::ThreadRestrictions::AssertIOAllowed(); DCHECK(IsValid()); - return !CallFsync(file_); + return !CallFsync(file_.get()); } bool File::SetTimes(Time last_access_time, Time last_modified_time) { @@ -361,72 +425,26 @@ bool File::SetTimes(Time last_access_time, Time last_modified_time) { times[0] = last_access_time.ToTimeVal(); times[1] = last_modified_time.ToTimeVal(); - return !CallFutimes(file_, times); + return !CallFutimes(file_.get(), times); } bool File::GetInfo(Info* info) { DCHECK(IsValid()); stat_wrapper_t file_info; - if (CallFstat(file_, &file_info)) + if (CallFstat(file_.get(), &file_info)) return false; - info->is_directory = S_ISDIR(file_info.st_mode); - info->is_symbolic_link = S_ISLNK(file_info.st_mode); - info->size = file_info.st_size; - -#if defined(OS_LINUX) - const time_t last_modified_sec = file_info.st_mtim.tv_sec; - const int64 last_modified_nsec = file_info.st_mtim.tv_nsec; - const time_t last_accessed_sec = file_info.st_atim.tv_sec; - const int64 last_accessed_nsec = file_info.st_atim.tv_nsec; - const time_t creation_time_sec = file_info.st_ctim.tv_sec; - const int64 creation_time_nsec = file_info.st_ctim.tv_nsec; -#elif defined(OS_ANDROID) - const time_t last_modified_sec = file_info.st_mtime; - const int64 last_modified_nsec = file_info.st_mtime_nsec; - const time_t last_accessed_sec = file_info.st_atime; - const int64 last_accessed_nsec = file_info.st_atime_nsec; - const time_t creation_time_sec = file_info.st_ctime; - const int64 creation_time_nsec = file_info.st_ctime_nsec; -#elif defined(OS_MACOSX) || defined(OS_IOS) || defined(OS_BSD) - const time_t last_modified_sec = file_info.st_mtimespec.tv_sec; - const int64 last_modified_nsec = file_info.st_mtimespec.tv_nsec; - const time_t last_accessed_sec = file_info.st_atimespec.tv_sec; - const int64 last_accessed_nsec = file_info.st_atimespec.tv_nsec; - const time_t creation_time_sec = file_info.st_ctimespec.tv_sec; - const int64 creation_time_nsec = file_info.st_ctimespec.tv_nsec; -#else - // TODO(gavinp): Investigate a good high resolution option for OS_NACL. - const time_t last_modified_sec = file_info.st_mtime; - const int64 last_modified_nsec = 0; - const time_t last_accessed_sec = file_info.st_atime; - const int64 last_accessed_nsec = 0; - const time_t creation_time_sec = file_info.st_ctime; - const int64 creation_time_nsec = 0; -#endif - - info->last_modified = - base::Time::FromTimeT(last_modified_sec) + - base::TimeDelta::FromMicroseconds(last_modified_nsec / - base::Time::kNanosecondsPerMicrosecond); - info->last_accessed = - base::Time::FromTimeT(last_accessed_sec) + - base::TimeDelta::FromMicroseconds(last_accessed_nsec / - base::Time::kNanosecondsPerMicrosecond); - info->creation_time = - base::Time::FromTimeT(creation_time_sec) + - base::TimeDelta::FromMicroseconds(creation_time_nsec / - base::Time::kNanosecondsPerMicrosecond); + info->FromStat(file_info); return true; } File::Error File::Lock() { - return CallFctnlFlock(file_, true); + return CallFctnlFlock(file_.get(), true); } File::Error File::Unlock() { - return CallFctnlFlock(file_, false); + return CallFctnlFlock(file_.get(), false); } // Static. @@ -463,8 +481,8 @@ File::Error File::OSErrorToFileError(int saved_errno) { } void File::SetPlatformFile(PlatformFile file) { - DCHECK_EQ(file_, kInvalidPlatformFileValue); - file_ = file; + DCHECK(!file_.is_valid()); + file_.reset(file); } } // namespace base diff --git a/chromium/base/files/file_proxy.cc b/chromium/base/files/file_proxy.cc new file mode 100644 index 00000000000..291b98d1117 --- /dev/null +++ b/chromium/base/files/file_proxy.cc @@ -0,0 +1,359 @@ +// 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/files/file_proxy.h" + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/file_util.h" +#include "base/files/file.h" +#include "base/location.h" +#include "base/message_loop/message_loop_proxy.h" +#include "base/task_runner.h" +#include "base/task_runner_util.h" + +namespace { + +void FileDeleter(base::File file) { +} + +} // namespace + +namespace base { + +class FileHelper { + public: + FileHelper(FileProxy* proxy, File file) + : file_(file.Pass()), + error_(File::FILE_ERROR_FAILED), + task_runner_(proxy->task_runner()), + proxy_(AsWeakPtr(proxy)) { + } + + void PassFile() { + if (proxy_) + proxy_->SetFile(file_.Pass()); + else if (file_.IsValid()) + task_runner_->PostTask(FROM_HERE, Bind(&FileDeleter, Passed(&file_))); + } + + protected: + File file_; + File::Error error_; + + private: + scoped_refptr<TaskRunner> task_runner_; + WeakPtr<FileProxy> proxy_; + DISALLOW_COPY_AND_ASSIGN(FileHelper); +}; + +namespace { + +class GenericFileHelper : public FileHelper { + public: + GenericFileHelper(FileProxy* proxy, File file) + : FileHelper(proxy, file.Pass()) { + } + + void Close() { + file_.Close(); + error_ = File::FILE_OK; + } + + void SetTimes(Time last_access_time, Time last_modified_time) { + bool rv = file_.SetTimes(last_access_time, last_modified_time); + error_ = rv ? File::FILE_OK : File::FILE_ERROR_FAILED; + } + + void SetLength(int64 length) { + if (file_.SetLength(length)) + error_ = File::FILE_OK; + } + + void Flush() { + if (file_.Flush()) + error_ = File::FILE_OK; + } + + void Reply(const FileProxy::StatusCallback& callback) { + PassFile(); + if (!callback.is_null()) + callback.Run(error_); + } + + private: + DISALLOW_COPY_AND_ASSIGN(GenericFileHelper); +}; + +class CreateOrOpenHelper : public FileHelper { + public: + CreateOrOpenHelper(FileProxy* proxy, File file) + : FileHelper(proxy, file.Pass()) { + } + + void RunWork(const FilePath& file_path, int file_flags) { + file_.Initialize(file_path, file_flags); + error_ = file_.IsValid() ? File::FILE_OK : file_.error_details(); + } + + void Reply(const FileProxy::StatusCallback& callback) { + DCHECK(!callback.is_null()); + PassFile(); + callback.Run(error_); + } + + private: + DISALLOW_COPY_AND_ASSIGN(CreateOrOpenHelper); +}; + +class CreateTemporaryHelper : public FileHelper { + public: + CreateTemporaryHelper(FileProxy* proxy, File file) + : FileHelper(proxy, file.Pass()) { + } + + void RunWork(uint32 additional_file_flags) { + // TODO(darin): file_util should have a variant of CreateTemporaryFile + // that returns a FilePath and a File. + if (!CreateTemporaryFile(&file_path_)) { + // TODO(davidben): base::CreateTemporaryFile should preserve the error + // code. + error_ = File::FILE_ERROR_FAILED; + return; + } + + uint32 file_flags = File::FLAG_WRITE | + File::FLAG_TEMPORARY | + File::FLAG_CREATE_ALWAYS | + additional_file_flags; + + file_.Initialize(file_path_, file_flags); + if (file_.IsValid()) { + error_ = File::FILE_OK; + } else { + error_ = file_.error_details(); + DeleteFile(file_path_, false); + file_path_.clear(); + } + } + + void Reply(const FileProxy::CreateTemporaryCallback& callback) { + DCHECK(!callback.is_null()); + PassFile(); + callback.Run(error_, file_path_); + } + + private: + FilePath file_path_; + DISALLOW_COPY_AND_ASSIGN(CreateTemporaryHelper); +}; + +class GetInfoHelper : public FileHelper { + public: + GetInfoHelper(FileProxy* proxy, File file) + : FileHelper(proxy, file.Pass()) { + } + + void RunWork() { + if (file_.GetInfo(&file_info_)) + error_ = File::FILE_OK; + } + + void Reply(const FileProxy::GetFileInfoCallback& callback) { + PassFile(); + DCHECK(!callback.is_null()); + callback.Run(error_, file_info_); + } + + private: + File::Info file_info_; + DISALLOW_COPY_AND_ASSIGN(GetInfoHelper); +}; + +class ReadHelper : public FileHelper { + public: + ReadHelper(FileProxy* proxy, File file, int bytes_to_read) + : FileHelper(proxy, file.Pass()), + buffer_(new char[bytes_to_read]), + bytes_to_read_(bytes_to_read), + bytes_read_(0) { + } + + void RunWork(int64 offset) { + bytes_read_ = file_.Read(offset, buffer_.get(), bytes_to_read_); + error_ = (bytes_read_ < 0) ? File::FILE_ERROR_FAILED : File::FILE_OK; + } + + void Reply(const FileProxy::ReadCallback& callback) { + PassFile(); + DCHECK(!callback.is_null()); + callback.Run(error_, buffer_.get(), bytes_read_); + } + + private: + scoped_ptr<char[]> buffer_; + int bytes_to_read_; + int bytes_read_; + DISALLOW_COPY_AND_ASSIGN(ReadHelper); +}; + +class WriteHelper : public FileHelper { + public: + WriteHelper(FileProxy* proxy, + File file, + const char* buffer, int bytes_to_write) + : FileHelper(proxy, file.Pass()), + buffer_(new char[bytes_to_write]), + bytes_to_write_(bytes_to_write), + bytes_written_(0) { + memcpy(buffer_.get(), buffer, bytes_to_write); + } + + void RunWork(int64 offset) { + bytes_written_ = file_.Write(offset, buffer_.get(), bytes_to_write_); + error_ = (bytes_written_ < 0) ? File::FILE_ERROR_FAILED : File::FILE_OK; + } + + void Reply(const FileProxy::WriteCallback& callback) { + PassFile(); + if (!callback.is_null()) + callback.Run(error_, bytes_written_); + } + + private: + scoped_ptr<char[]> buffer_; + int bytes_to_write_; + int bytes_written_; + DISALLOW_COPY_AND_ASSIGN(WriteHelper); +}; + +} // namespace + +FileProxy::FileProxy(TaskRunner* task_runner) : task_runner_(task_runner) { +} + +FileProxy::~FileProxy() { + if (file_.IsValid()) + task_runner_->PostTask(FROM_HERE, Bind(&FileDeleter, Passed(&file_))); +} + +bool FileProxy::CreateOrOpen(const FilePath& file_path, + uint32 file_flags, + const StatusCallback& callback) { + DCHECK(!file_.IsValid()); + CreateOrOpenHelper* helper = new CreateOrOpenHelper(this, File()); + return task_runner_->PostTaskAndReply( + FROM_HERE, + Bind(&CreateOrOpenHelper::RunWork, Unretained(helper), file_path, + file_flags), + Bind(&CreateOrOpenHelper::Reply, Owned(helper), callback)); +} + +bool FileProxy::CreateTemporary(uint32 additional_file_flags, + const CreateTemporaryCallback& callback) { + DCHECK(!file_.IsValid()); + CreateTemporaryHelper* helper = new CreateTemporaryHelper(this, File()); + return task_runner_->PostTaskAndReply( + FROM_HERE, + Bind(&CreateTemporaryHelper::RunWork, Unretained(helper), + additional_file_flags), + Bind(&CreateTemporaryHelper::Reply, Owned(helper), callback)); +} + +bool FileProxy::IsValid() const { + return file_.IsValid(); +} + +void FileProxy::SetFile(File file) { + DCHECK(!file_.IsValid()); + file_ = file.Pass(); +} + +File FileProxy::TakeFile() { + return file_.Pass(); +} + +PlatformFile FileProxy::GetPlatformFile() const { + return file_.GetPlatformFile(); +} + +bool FileProxy::Close(const StatusCallback& callback) { + DCHECK(file_.IsValid()); + GenericFileHelper* helper = new GenericFileHelper(this, file_.Pass()); + return task_runner_->PostTaskAndReply( + FROM_HERE, + Bind(&GenericFileHelper::Close, Unretained(helper)), + Bind(&GenericFileHelper::Reply, Owned(helper), callback)); +} + +bool FileProxy::GetInfo(const GetFileInfoCallback& callback) { + DCHECK(file_.IsValid()); + GetInfoHelper* helper = new GetInfoHelper(this, file_.Pass()); + return task_runner_->PostTaskAndReply( + FROM_HERE, + Bind(&GetInfoHelper::RunWork, Unretained(helper)), + Bind(&GetInfoHelper::Reply, Owned(helper), callback)); +} + +bool FileProxy::Read(int64 offset, + int bytes_to_read, + const ReadCallback& callback) { + DCHECK(file_.IsValid()); + if (bytes_to_read < 0) + return false; + + ReadHelper* helper = new ReadHelper(this, file_.Pass(), bytes_to_read); + return task_runner_->PostTaskAndReply( + FROM_HERE, + Bind(&ReadHelper::RunWork, Unretained(helper), offset), + Bind(&ReadHelper::Reply, Owned(helper), callback)); +} + +bool FileProxy::Write(int64 offset, + const char* buffer, + int bytes_to_write, + const WriteCallback& callback) { + DCHECK(file_.IsValid()); + if (bytes_to_write <= 0 || buffer == NULL) + return false; + + WriteHelper* helper = + new WriteHelper(this, file_.Pass(), buffer, bytes_to_write); + return task_runner_->PostTaskAndReply( + FROM_HERE, + Bind(&WriteHelper::RunWork, Unretained(helper), offset), + Bind(&WriteHelper::Reply, Owned(helper), callback)); +} + +bool FileProxy::SetTimes(Time last_access_time, + Time last_modified_time, + const StatusCallback& callback) { + DCHECK(file_.IsValid()); + GenericFileHelper* helper = new GenericFileHelper(this, file_.Pass()); + return task_runner_->PostTaskAndReply( + FROM_HERE, + Bind(&GenericFileHelper::SetTimes, Unretained(helper), last_access_time, + last_modified_time), + Bind(&GenericFileHelper::Reply, Owned(helper), callback)); +} + +bool FileProxy::SetLength(int64 length, const StatusCallback& callback) { + DCHECK(file_.IsValid()); + GenericFileHelper* helper = new GenericFileHelper(this, file_.Pass()); + return task_runner_->PostTaskAndReply( + FROM_HERE, + Bind(&GenericFileHelper::SetLength, Unretained(helper), length), + Bind(&GenericFileHelper::Reply, Owned(helper), callback)); +} + +bool FileProxy::Flush(const StatusCallback& callback) { + DCHECK(file_.IsValid()); + GenericFileHelper* helper = new GenericFileHelper(this, file_.Pass()); + return task_runner_->PostTaskAndReply( + FROM_HERE, + Bind(&GenericFileHelper::Flush, Unretained(helper)), + Bind(&GenericFileHelper::Reply, Owned(helper), callback)); +} + +} // namespace base diff --git a/chromium/base/files/file_proxy.h b/chromium/base/files/file_proxy.h new file mode 100644 index 00000000000..f990d044d37 --- /dev/null +++ b/chromium/base/files/file_proxy.h @@ -0,0 +1,142 @@ +// 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_FILES_FILE_PROXY_H_ +#define BASE_FILES_FILE_PROXY_H_ + +#include "base/base_export.h" +#include "base/callback_forward.h" +#include "base/files/file.h" +#include "base/files/file_path.h" +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" + +namespace tracked_objects { +class Location; +}; + +namespace base { + +class TaskRunner; +class Time; + +// This class provides asynchronous access to a File. All methods follow the +// same rules of the equivalent File method, as they are implemented by bouncing +// the operation to File using a TaskRunner. +// +// This class performs automatic proxying to close the underlying file at +// destruction. +// +// The TaskRunner is in charge of any sequencing of the operations, but a single +// operation can be proxied at a time, regardless of the use of a callback. +// In other words, having a sequence like +// +// proxy.Write(...); +// proxy.Write(...); +// +// means the second Write will always fail. +class BASE_EXPORT FileProxy : public SupportsWeakPtr<FileProxy> { + public: + // This callback is used by methods that report only an error code. It is + // valid to pass a null callback to some functions that takes a + // StatusCallback, in which case the operation will complete silently. + typedef Callback<void(File::Error)> StatusCallback; + + typedef Callback<void(File::Error, + const FilePath&)> CreateTemporaryCallback; + typedef Callback<void(File::Error, + const File::Info&)> GetFileInfoCallback; + typedef Callback<void(File::Error, + const char* data, + int bytes_read)> ReadCallback; + typedef Callback<void(File::Error, + int bytes_written)> WriteCallback; + + FileProxy(); + explicit FileProxy(TaskRunner* task_runner); + ~FileProxy(); + + // Creates or opens a file with the given flags. It is invalid to pass a null + // callback. If File::FLAG_CREATE is set in |file_flags| it always tries to + // create a new file at the given |file_path| and fails if the file already + // exists. + // + // This returns false if task posting to |task_runner| has failed. + bool CreateOrOpen(const FilePath& file_path, + uint32 file_flags, + const StatusCallback& callback); + + // Creates a temporary file for writing. The path and an open file are + // returned. It is invalid to pass a null callback. The additional file flags + // will be added on top of the default file flags which are: + // File::FLAG_CREATE_ALWAYS + // File::FLAG_WRITE + // File::FLAG_TEMPORARY. + // + // This returns false if task posting to |task_runner| has failed. + bool CreateTemporary(uint32 additional_file_flags, + const CreateTemporaryCallback& callback); + + // Returns true if the underlying |file_| is valid. + bool IsValid() const; + + // Returns true if a new file was created (or an old one truncated to zero + // length to simulate a new file), and false otherwise. + bool created() const { return file_.created(); } + + // Claims ownership of |file|. It is an error to call this method when + // IsValid() returns true. + void SetFile(File file); + + File TakeFile(); + + PlatformFile GetPlatformFile() const; + + // Proxies File::Close. The callback can be null. + // This returns false if task posting to |task_runner| has failed. + bool Close(const StatusCallback& callback); + + // Proxies File::GetInfo. The callback can't be null. + // This returns false if task posting to |task_runner| has failed. + bool GetInfo(const GetFileInfoCallback& callback); + + // Proxies File::Read. The callback can't be null. + // This returns false if |bytes_to_read| is less than zero, or + // if task posting to |task_runner| has failed. + bool Read(int64 offset, int bytes_to_read, const ReadCallback& callback); + + // Proxies File::Write. The callback can be null. + // This returns false if |bytes_to_write| is less than or equal to zero, + // if |buffer| is NULL, or if task posting to |task_runner| has failed. + bool Write(int64 offset, + const char* buffer, + int bytes_to_write, + const WriteCallback& callback); + + // Proxies File::SetTimes. The callback can be null. + // This returns false if task posting to |task_runner| has failed. + bool SetTimes(Time last_access_time, + Time last_modified_time, + const StatusCallback& callback); + + // Proxies File::SetLength. The callback can be null. + // This returns false if task posting to |task_runner| has failed. + bool SetLength(int64 length, const StatusCallback& callback); + + // Proxies File::Flush. The callback can be null. + // This returns false if task posting to |task_runner| has failed. + bool Flush(const StatusCallback& callback); + + private: + friend class FileHelper; + TaskRunner* task_runner() { return task_runner_.get(); } + + scoped_refptr<TaskRunner> task_runner_; + File file_; + DISALLOW_COPY_AND_ASSIGN(FileProxy); +}; + +} // namespace base + +#endif // BASE_FILES_FILE_PROXY_H_ diff --git a/chromium/base/files/file_proxy_unittest.cc b/chromium/base/files/file_proxy_unittest.cc new file mode 100644 index 00000000000..9be8abf67d2 --- /dev/null +++ b/chromium/base/files/file_proxy_unittest.cc @@ -0,0 +1,370 @@ +// 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/files/file_proxy.h" + +#include "base/bind.h" +#include "base/file_util.h" +#include "base/files/file.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" + +namespace base { + +class FileProxyTest : public testing::Test { + public: + FileProxyTest() + : file_thread_("FileProxyTestFileThread"), + error_(File::FILE_OK), + bytes_written_(-1), + weak_factory_(this) {} + + virtual void SetUp() OVERRIDE { + ASSERT_TRUE(dir_.CreateUniqueTempDir()); + ASSERT_TRUE(file_thread_.Start()); + } + + void DidFinish(File::Error error) { + error_ = error; + MessageLoop::current()->QuitWhenIdle(); + } + + void DidCreateOrOpen(File::Error error) { + error_ = error; + MessageLoop::current()->QuitWhenIdle(); + } + + void DidCreateTemporary(File::Error error, + const FilePath& path) { + error_ = error; + path_ = path; + MessageLoop::current()->QuitWhenIdle(); + } + + void DidGetFileInfo(File::Error error, + const File::Info& file_info) { + error_ = error; + file_info_ = file_info; + MessageLoop::current()->QuitWhenIdle(); + } + + void DidRead(File::Error error, + const char* data, + int bytes_read) { + error_ = error; + buffer_.resize(bytes_read); + memcpy(&buffer_[0], data, bytes_read); + MessageLoop::current()->QuitWhenIdle(); + } + + void DidWrite(File::Error error, + int bytes_written) { + error_ = error; + bytes_written_ = bytes_written; + MessageLoop::current()->QuitWhenIdle(); + } + + protected: + void CreateProxy(uint32 flags, FileProxy* proxy) { + proxy->CreateOrOpen( + test_path(), flags, + Bind(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr())); + MessageLoop::current()->Run(); + EXPECT_TRUE(proxy->IsValid()); + } + + TaskRunner* file_task_runner() const { + return file_thread_.message_loop_proxy().get(); + } + const FilePath& test_dir_path() const { return dir_.path(); } + const FilePath test_path() const { return dir_.path().AppendASCII("test"); } + + MessageLoopForIO message_loop_; + Thread file_thread_; + + ScopedTempDir dir_; + File::Error error_; + FilePath path_; + File::Info file_info_; + std::vector<char> buffer_; + int bytes_written_; + WeakPtrFactory<FileProxyTest> weak_factory_; +}; + +TEST_F(FileProxyTest, CreateOrOpen_Create) { + FileProxy proxy(file_task_runner()); + proxy.CreateOrOpen( + test_path(), + File::FLAG_CREATE | File::FLAG_READ, + Bind(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr())); + MessageLoop::current()->Run(); + + EXPECT_EQ(File::FILE_OK, error_); + EXPECT_TRUE(proxy.IsValid()); + EXPECT_TRUE(proxy.created()); + EXPECT_TRUE(PathExists(test_path())); +} + +TEST_F(FileProxyTest, CreateOrOpen_Open) { + // Creates a file. + base::WriteFile(test_path(), NULL, 0); + ASSERT_TRUE(PathExists(test_path())); + + // Opens the created file. + FileProxy proxy(file_task_runner()); + proxy.CreateOrOpen( + test_path(), + File::FLAG_OPEN | File::FLAG_READ, + Bind(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr())); + MessageLoop::current()->Run(); + + EXPECT_EQ(File::FILE_OK, error_); + EXPECT_TRUE(proxy.IsValid()); + EXPECT_FALSE(proxy.created()); +} + +TEST_F(FileProxyTest, CreateOrOpen_OpenNonExistent) { + FileProxy proxy(file_task_runner()); + proxy.CreateOrOpen( + test_path(), + File::FLAG_OPEN | File::FLAG_READ, + Bind(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr())); + MessageLoop::current()->Run(); + EXPECT_EQ(File::FILE_ERROR_NOT_FOUND, error_); + EXPECT_FALSE(proxy.IsValid()); + EXPECT_FALSE(proxy.created()); + EXPECT_FALSE(PathExists(test_path())); +} + +TEST_F(FileProxyTest, CreateOrOpen_AbandonedCreate) { + bool prev = ThreadRestrictions::SetIOAllowed(false); + { + FileProxy proxy(file_task_runner()); + proxy.CreateOrOpen( + test_path(), + File::FLAG_CREATE | File::FLAG_READ, + Bind(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr())); + } + MessageLoop::current()->Run(); + ThreadRestrictions::SetIOAllowed(prev); + + EXPECT_TRUE(PathExists(test_path())); +} + +TEST_F(FileProxyTest, Close) { + // Creates a file. + FileProxy proxy(file_task_runner()); + CreateProxy(File::FLAG_CREATE | File::FLAG_WRITE, &proxy); + +#if defined(OS_WIN) + // This fails on Windows if the file is not closed. + EXPECT_FALSE(base::Move(test_path(), test_dir_path().AppendASCII("new"))); +#endif + + proxy.Close(Bind(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr())); + MessageLoop::current()->Run(); + EXPECT_EQ(File::FILE_OK, error_); + EXPECT_FALSE(proxy.IsValid()); + + // Now it should pass on all platforms. + EXPECT_TRUE(base::Move(test_path(), test_dir_path().AppendASCII("new"))); +} + +TEST_F(FileProxyTest, CreateTemporary) { + { + FileProxy proxy(file_task_runner()); + proxy.CreateTemporary( + 0 /* additional_file_flags */, + Bind(&FileProxyTest::DidCreateTemporary, weak_factory_.GetWeakPtr())); + MessageLoop::current()->Run(); + + EXPECT_TRUE(proxy.IsValid()); + EXPECT_EQ(File::FILE_OK, error_); + EXPECT_TRUE(PathExists(path_)); + + // The file should be writable. + proxy.Write(0, "test", 4, + Bind(&FileProxyTest::DidWrite, weak_factory_.GetWeakPtr())); + MessageLoop::current()->Run(); + EXPECT_EQ(File::FILE_OK, error_); + EXPECT_EQ(4, bytes_written_); + } + + // Make sure the written data can be read from the returned path. + std::string data; + EXPECT_TRUE(ReadFileToString(path_, &data)); + EXPECT_EQ("test", data); + + // Make sure we can & do delete the created file to prevent leaks on the bots. + EXPECT_TRUE(base::DeleteFile(path_, false)); +} + +TEST_F(FileProxyTest, SetAndTake) { + File file(test_path(), File::FLAG_CREATE | File::FLAG_READ); + ASSERT_TRUE(file.IsValid()); + FileProxy proxy(file_task_runner()); + EXPECT_FALSE(proxy.IsValid()); + proxy.SetFile(file.Pass()); + EXPECT_TRUE(proxy.IsValid()); + EXPECT_FALSE(file.IsValid()); + + file = proxy.TakeFile(); + EXPECT_FALSE(proxy.IsValid()); + EXPECT_TRUE(file.IsValid()); +} + +TEST_F(FileProxyTest, GetInfo) { + // Setup. + ASSERT_EQ(4, base::WriteFile(test_path(), "test", 4)); + File::Info expected_info; + GetFileInfo(test_path(), &expected_info); + + // Run. + FileProxy proxy(file_task_runner()); + CreateProxy(File::FLAG_OPEN | File::FLAG_READ, &proxy); + proxy.GetInfo( + Bind(&FileProxyTest::DidGetFileInfo, weak_factory_.GetWeakPtr())); + MessageLoop::current()->Run(); + + // Verify. + EXPECT_EQ(File::FILE_OK, error_); + EXPECT_EQ(expected_info.size, file_info_.size); + EXPECT_EQ(expected_info.is_directory, file_info_.is_directory); + EXPECT_EQ(expected_info.is_symbolic_link, file_info_.is_symbolic_link); + EXPECT_EQ(expected_info.last_modified, file_info_.last_modified); + EXPECT_EQ(expected_info.creation_time, file_info_.creation_time); +} + +TEST_F(FileProxyTest, Read) { + // Setup. + const char expected_data[] = "bleh"; + int expected_bytes = arraysize(expected_data); + ASSERT_EQ(expected_bytes, + base::WriteFile(test_path(), expected_data, expected_bytes)); + + // Run. + FileProxy proxy(file_task_runner()); + CreateProxy(File::FLAG_OPEN | File::FLAG_READ, &proxy); + + proxy.Read(0, 128, Bind(&FileProxyTest::DidRead, weak_factory_.GetWeakPtr())); + MessageLoop::current()->Run(); + + // Verify. + EXPECT_EQ(File::FILE_OK, error_); + EXPECT_EQ(expected_bytes, static_cast<int>(buffer_.size())); + for (size_t i = 0; i < buffer_.size(); ++i) { + EXPECT_EQ(expected_data[i], buffer_[i]); + } +} + +TEST_F(FileProxyTest, WriteAndFlush) { + FileProxy proxy(file_task_runner()); + CreateProxy(File::FLAG_CREATE | File::FLAG_WRITE, &proxy); + + const char data[] = "foo!"; + int data_bytes = ARRAYSIZE_UNSAFE(data); + proxy.Write(0, data, data_bytes, + Bind(&FileProxyTest::DidWrite, weak_factory_.GetWeakPtr())); + MessageLoop::current()->Run(); + EXPECT_EQ(File::FILE_OK, error_); + EXPECT_EQ(data_bytes, bytes_written_); + + // Flush the written data. (So that the following read should always + // succeed. On some platforms it may work with or without this flush.) + proxy.Flush(Bind(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr())); + MessageLoop::current()->Run(); + EXPECT_EQ(File::FILE_OK, error_); + + // Verify the written data. + char buffer[10]; + EXPECT_EQ(data_bytes, base::ReadFile(test_path(), buffer, data_bytes)); + for (int i = 0; i < data_bytes; ++i) { + EXPECT_EQ(data[i], buffer[i]); + } +} + +TEST_F(FileProxyTest, SetTimes) { + FileProxy proxy(file_task_runner()); + CreateProxy( + File::FLAG_CREATE | File::FLAG_WRITE | File::FLAG_WRITE_ATTRIBUTES, + &proxy); + + Time last_accessed_time = Time::Now() - TimeDelta::FromDays(12345); + Time last_modified_time = Time::Now() - TimeDelta::FromHours(98765); + + proxy.SetTimes(last_accessed_time, last_modified_time, + Bind(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr())); + MessageLoop::current()->Run(); + EXPECT_EQ(File::FILE_OK, error_); + + File::Info info; + GetFileInfo(test_path(), &info); + + // The returned values may only have the seconds precision, so we cast + // the double values to int here. + EXPECT_EQ(static_cast<int>(last_modified_time.ToDoubleT()), + static_cast<int>(info.last_modified.ToDoubleT())); + EXPECT_EQ(static_cast<int>(last_accessed_time.ToDoubleT()), + static_cast<int>(info.last_accessed.ToDoubleT())); +} + +TEST_F(FileProxyTest, SetLength_Shrink) { + // Setup. + const char kTestData[] = "0123456789"; + ASSERT_EQ(10, base::WriteFile(test_path(), kTestData, 10)); + File::Info info; + GetFileInfo(test_path(), &info); + ASSERT_EQ(10, info.size); + + // Run. + FileProxy proxy(file_task_runner()); + CreateProxy(File::FLAG_OPEN | File::FLAG_WRITE, &proxy); + proxy.SetLength(7, + Bind(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr())); + MessageLoop::current()->Run(); + + // Verify. + GetFileInfo(test_path(), &info); + ASSERT_EQ(7, info.size); + + char buffer[7]; + EXPECT_EQ(7, base::ReadFile(test_path(), buffer, 7)); + int i = 0; + for (; i < 7; ++i) + EXPECT_EQ(kTestData[i], buffer[i]); +} + +TEST_F(FileProxyTest, SetLength_Expand) { + // Setup. + const char kTestData[] = "9876543210"; + ASSERT_EQ(10, base::WriteFile(test_path(), kTestData, 10)); + File::Info info; + GetFileInfo(test_path(), &info); + ASSERT_EQ(10, info.size); + + // Run. + FileProxy proxy(file_task_runner()); + CreateProxy(File::FLAG_OPEN | File::FLAG_WRITE, &proxy); + proxy.SetLength(53, + Bind(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr())); + MessageLoop::current()->Run(); + + // Verify. + GetFileInfo(test_path(), &info); + ASSERT_EQ(53, info.size); + + char buffer[53]; + EXPECT_EQ(53, base::ReadFile(test_path(), buffer, 53)); + int i = 0; + for (; i < 10; ++i) + EXPECT_EQ(kTestData[i], buffer[i]); + for (; i < 53; ++i) + EXPECT_EQ(0, buffer[i]); +} + +} // namespace base diff --git a/chromium/base/files/file_unittest.cc b/chromium/base/files/file_unittest.cc index b2e855da1a0..cba043c332f 100644 --- a/chromium/base/files/file_unittest.cc +++ b/chromium/base/files/file_unittest.cc @@ -11,16 +11,27 @@ using base::File; using base::FilePath; -TEST(File, Create) { +TEST(FileTest, Create) { base::ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); FilePath file_path = temp_dir.path().AppendASCII("create_file_1"); { + // Don't create a File at all. + File file; + EXPECT_FALSE(file.IsValid()); + EXPECT_EQ(base::File::FILE_ERROR_FAILED, file.error_details()); + + File file2(base::File::FILE_ERROR_TOO_MANY_OPENED); + EXPECT_FALSE(file2.IsValid()); + EXPECT_EQ(base::File::FILE_ERROR_TOO_MANY_OPENED, file2.error_details()); + } + + { // Open a file that doesn't exist. File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ); EXPECT_FALSE(file.IsValid()); - EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, file.error()); + EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, file.error_details()); } { @@ -28,7 +39,7 @@ TEST(File, Create) { File file(file_path, base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_READ); EXPECT_TRUE(file.IsValid()); EXPECT_TRUE(file.created()); - EXPECT_EQ(base::File::FILE_OK, file.error()); + EXPECT_EQ(base::File::FILE_OK, file.error_details()); } { @@ -36,7 +47,20 @@ TEST(File, Create) { File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ); EXPECT_TRUE(file.IsValid()); EXPECT_FALSE(file.created()); - EXPECT_EQ(base::File::FILE_OK, file.error()); + EXPECT_EQ(base::File::FILE_OK, file.error_details()); + + // This time verify closing the file. + file.Close(); + EXPECT_FALSE(file.IsValid()); + } + + { + // Open an existing file through Initialize + File file; + file.Initialize(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ); + EXPECT_TRUE(file.IsValid()); + EXPECT_FALSE(file.created()); + EXPECT_EQ(base::File::FILE_OK, file.error_details()); // This time verify closing the file. file.Close(); @@ -48,16 +72,16 @@ TEST(File, Create) { File file(file_path, base::File::FLAG_CREATE | base::File::FLAG_READ); EXPECT_FALSE(file.IsValid()); EXPECT_FALSE(file.created()); - EXPECT_EQ(base::File::FILE_ERROR_EXISTS, file.error()); + EXPECT_EQ(base::File::FILE_ERROR_EXISTS, file.error_details()); } { // Create or overwrite a file. File file(file_path, - base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_READ); + base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); EXPECT_TRUE(file.IsValid()); EXPECT_TRUE(file.created()); - EXPECT_EQ(base::File::FILE_OK, file.error()); + EXPECT_EQ(base::File::FILE_OK, file.error_details()); } { @@ -68,13 +92,31 @@ TEST(File, Create) { base::File::FLAG_DELETE_ON_CLOSE); EXPECT_TRUE(file.IsValid()); EXPECT_TRUE(file.created()); - EXPECT_EQ(base::File::FILE_OK, file.error()); + EXPECT_EQ(base::File::FILE_OK, file.error_details()); } EXPECT_FALSE(base::PathExists(file_path)); } -TEST(File, DeleteOpenFile) { +TEST(FileTest, Async) { + base::ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + FilePath file_path = temp_dir.path().AppendASCII("create_file"); + + { + File file(file_path, base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_ASYNC); + EXPECT_TRUE(file.IsValid()); + EXPECT_TRUE(file.async()); + } + + { + File file(file_path, base::File::FLAG_OPEN_ALWAYS); + EXPECT_TRUE(file.IsValid()); + EXPECT_FALSE(file.async()); + } +} + +TEST(FileTest, DeleteOpenFile) { base::ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); FilePath file_path = temp_dir.path().AppendASCII("create_file_1"); @@ -85,7 +127,7 @@ TEST(File, DeleteOpenFile) { base::File::FLAG_SHARE_DELETE); EXPECT_TRUE(file.IsValid()); EXPECT_TRUE(file.created()); - EXPECT_EQ(base::File::FILE_OK, file.error()); + EXPECT_EQ(base::File::FILE_OK, file.error_details()); // Open an existing file and mark it as delete on close. File same_file(file_path, @@ -93,7 +135,7 @@ TEST(File, DeleteOpenFile) { base::File::FLAG_READ); EXPECT_TRUE(file.IsValid()); EXPECT_FALSE(same_file.created()); - EXPECT_EQ(base::File::FILE_OK, same_file.error()); + EXPECT_EQ(base::File::FILE_OK, same_file.error_details()); // Close both handles and check that the file is gone. file.Close(); @@ -101,7 +143,7 @@ TEST(File, DeleteOpenFile) { EXPECT_FALSE(base::PathExists(file_path)); } -TEST(File, ReadWrite) { +TEST(FileTest, ReadWrite) { base::ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); FilePath file_path = temp_dir.path().AppendASCII("read_write_file"); @@ -173,7 +215,7 @@ TEST(File, ReadWrite) { EXPECT_EQ(data_to_write[i - kOffsetBeyondEndOfFile], data_read_2[i]); } -TEST(File, Append) { +TEST(FileTest, Append) { base::ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); FilePath file_path = temp_dir.path().AppendASCII("append_file"); @@ -221,7 +263,7 @@ TEST(File, Append) { } -TEST(File, Truncate) { +TEST(FileTest, Length) { base::ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); FilePath file_path = temp_dir.path().AppendASCII("truncate_file"); @@ -229,6 +271,7 @@ TEST(File, Truncate) { base::File::FLAG_CREATE | base::File::FLAG_READ | base::File::FLAG_WRITE); ASSERT_TRUE(file.IsValid()); + EXPECT_EQ(0, file.GetLength()); // Write "test" to the file. char data_to_write[] = "test"; @@ -239,7 +282,8 @@ TEST(File, Truncate) { // Extend the file. const int kExtendedFileLength = 10; int64 file_size = 0; - EXPECT_TRUE(file.Truncate(kExtendedFileLength)); + EXPECT_TRUE(file.SetLength(kExtendedFileLength)); + EXPECT_EQ(kExtendedFileLength, file.GetLength()); EXPECT_TRUE(GetFileSize(file_path, &file_size)); EXPECT_EQ(kExtendedFileLength, file_size); @@ -254,7 +298,8 @@ TEST(File, Truncate) { // Truncate the file. const int kTruncatedFileLength = 2; - EXPECT_TRUE(file.Truncate(kTruncatedFileLength)); + EXPECT_TRUE(file.SetLength(kTruncatedFileLength)); + EXPECT_EQ(kTruncatedFileLength, file.GetLength()); EXPECT_TRUE(GetFileSize(file_path, &file_size)); EXPECT_EQ(kTruncatedFileLength, file_size); @@ -267,9 +312,9 @@ TEST(File, Truncate) { // Flakily fails: http://crbug.com/86494 #if defined(OS_ANDROID) -TEST(File, TouchGetInfo) { +TEST(FileTest, TouchGetInfo) { #else -TEST(File, DISABLED_TouchGetInfo) { +TEST(FileTest, DISABLED_TouchGetInfo) { #endif base::ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); @@ -333,18 +378,17 @@ TEST(File, DISABLED_TouchGetInfo) { creation_time.ToInternalValue()); } -TEST(File, ReadFileAtCurrentPosition) { +TEST(FileTest, ReadAtCurrentPosition) { base::ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - FilePath file_path = - temp_dir.path().AppendASCII("read_file_at_current_position"); + FilePath file_path = temp_dir.path().AppendASCII("read_at_current_position"); File file(file_path, base::File::FLAG_CREATE | base::File::FLAG_READ | base::File::FLAG_WRITE); EXPECT_TRUE(file.IsValid()); const char kData[] = "test"; - const int kDataSize = arraysize(kData) - 1; + const int kDataSize = sizeof(kData) - 1; EXPECT_EQ(kDataSize, file.Write(0, kData, kDataSize)); EXPECT_EQ(0, file.Seek(base::File::FROM_BEGIN, 0)); @@ -355,6 +399,53 @@ TEST(File, ReadFileAtCurrentPosition) { EXPECT_EQ(kDataSize - first_chunk_size, file.ReadAtCurrentPos(buffer + first_chunk_size, kDataSize - first_chunk_size)); - EXPECT_EQ(std::string(buffer, buffer + kDataSize), - std::string(kData)); + EXPECT_EQ(std::string(buffer, buffer + kDataSize), std::string(kData)); +} + +TEST(FileTest, WriteAtCurrentPosition) { + base::ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + FilePath file_path = temp_dir.path().AppendASCII("write_at_current_position"); + File file(file_path, + base::File::FLAG_CREATE | base::File::FLAG_READ | + base::File::FLAG_WRITE); + EXPECT_TRUE(file.IsValid()); + + const char kData[] = "test"; + const int kDataSize = sizeof(kData) - 1; + + int first_chunk_size = kDataSize / 2; + EXPECT_EQ(first_chunk_size, file.WriteAtCurrentPos(kData, first_chunk_size)); + EXPECT_EQ(kDataSize - first_chunk_size, + file.WriteAtCurrentPos(kData + first_chunk_size, + kDataSize - first_chunk_size)); + + char buffer[kDataSize]; + EXPECT_EQ(kDataSize, file.Read(0, buffer, kDataSize)); + EXPECT_EQ(std::string(buffer, buffer + kDataSize), std::string(kData)); +} + +#if defined(OS_WIN) +TEST(FileTest, GetInfoForDirectory) { + base::ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + FilePath empty_dir = temp_dir.path().Append(FILE_PATH_LITERAL("gpfi_test")); + ASSERT_TRUE(CreateDirectory(empty_dir)); + + base::File dir( + ::CreateFile(empty_dir.value().c_str(), + FILE_ALL_ACCESS, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, // Needed to open a directory. + NULL)); + ASSERT_TRUE(dir.IsValid()); + + base::File::Info info; + EXPECT_TRUE(dir.GetInfo(&info)); + EXPECT_TRUE(info.is_directory); + EXPECT_FALSE(info.is_symbolic_link); + EXPECT_EQ(0, info.size); } +#endif // defined(OS_WIN) diff --git a/chromium/base/files/file_util_proxy.cc b/chromium/base/files/file_util_proxy.cc index 40cac112820..72d9436fa1f 100644 --- a/chromium/base/files/file_util_proxy.cc +++ b/chromium/base/files/file_util_proxy.cc @@ -8,7 +8,6 @@ #include "base/bind_helpers.h" #include "base/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" @@ -19,105 +18,21 @@ namespace { void CallWithTranslatedParameter(const FileUtilProxy::StatusCallback& callback, bool value) { DCHECK(!callback.is_null()); - callback.Run(value ? PLATFORM_FILE_OK : PLATFORM_FILE_ERROR_FAILED); + callback.Run(value ? File::FILE_OK : File::FILE_ERROR_FAILED); } -// Helper classes or routines for individual methods. -class CreateOrOpenHelper { - public: - CreateOrOpenHelper(TaskRunner* task_runner, - const FileUtilProxy::CloseTask& close_task) - : task_runner_(task_runner), - close_task_(close_task), - file_handle_(kInvalidPlatformFileValue), - created_(false), - error_(PLATFORM_FILE_OK) {} - - ~CreateOrOpenHelper() { - if (file_handle_ != kInvalidPlatformFileValue) { - task_runner_->PostTask( - FROM_HERE, - base::Bind(base::IgnoreResult(close_task_), file_handle_)); - } - } - - void RunWork(const FileUtilProxy::CreateOrOpenTask& task) { - error_ = task.Run(&file_handle_, &created_); - } - - void Reply(const FileUtilProxy::CreateOrOpenCallback& callback) { - DCHECK(!callback.is_null()); - callback.Run(error_, PassPlatformFile(&file_handle_), created_); - } - - private: - scoped_refptr<TaskRunner> task_runner_; - FileUtilProxy::CloseTask close_task_; - PlatformFile file_handle_; - bool created_; - PlatformFileError error_; - DISALLOW_COPY_AND_ASSIGN(CreateOrOpenHelper); -}; - -class CreateTemporaryHelper { - public: - explicit CreateTemporaryHelper(TaskRunner* task_runner) - : task_runner_(task_runner), - file_handle_(kInvalidPlatformFileValue), - error_(PLATFORM_FILE_OK) {} - - ~CreateTemporaryHelper() { - if (file_handle_ != kInvalidPlatformFileValue) { - FileUtilProxy::Close( - task_runner_.get(), file_handle_, FileUtilProxy::StatusCallback()); - } - } - - void RunWork(int additional_file_flags) { - // TODO(darin): file_util should have a variant of CreateTemporaryFile - // that returns a FilePath and a PlatformFile. - base::CreateTemporaryFile(&file_path_); - - int file_flags = - PLATFORM_FILE_WRITE | - PLATFORM_FILE_TEMPORARY | - PLATFORM_FILE_CREATE_ALWAYS | - additional_file_flags; - - error_ = PLATFORM_FILE_OK; - file_handle_ = CreatePlatformFile(file_path_, file_flags, NULL, &error_); - } - - void Reply(const FileUtilProxy::CreateTemporaryCallback& callback) { - DCHECK(!callback.is_null()); - callback.Run(error_, PassPlatformFile(&file_handle_), file_path_); - } - - private: - scoped_refptr<TaskRunner> task_runner_; - PlatformFile file_handle_; - FilePath file_path_; - PlatformFileError error_; - DISALLOW_COPY_AND_ASSIGN(CreateTemporaryHelper); -}; - class GetFileInfoHelper { public: GetFileInfoHelper() - : error_(PLATFORM_FILE_OK) {} + : error_(File::FILE_OK) {} void RunWorkForFilePath(const FilePath& file_path) { if (!PathExists(file_path)) { - error_ = PLATFORM_FILE_ERROR_NOT_FOUND; + error_ = File::FILE_ERROR_NOT_FOUND; return; } if (!GetFileInfo(file_path, &file_info_)) - error_ = PLATFORM_FILE_ERROR_FAILED; - } - - void RunWorkForPlatformFile(PlatformFile file) { - if (!GetPlatformFileInfo(file, &file_info_)) - error_ = PLATFORM_FILE_ERROR_FAILED; + error_ = File::FILE_ERROR_FAILED; } void Reply(const FileUtilProxy::GetFileInfoCallback& callback) { @@ -127,138 +42,26 @@ class GetFileInfoHelper { } private: - PlatformFileError error_; - PlatformFileInfo file_info_; + File::Error error_; + File::Info file_info_; DISALLOW_COPY_AND_ASSIGN(GetFileInfoHelper); }; -class ReadHelper { - public: - explicit ReadHelper(int bytes_to_read) - : buffer_(new char[bytes_to_read]), - bytes_to_read_(bytes_to_read), - bytes_read_(0) {} - - void RunWork(PlatformFile file, int64 offset) { - bytes_read_ = ReadPlatformFile(file, offset, buffer_.get(), bytes_to_read_); - } - - void Reply(const FileUtilProxy::ReadCallback& callback) { - if (!callback.is_null()) { - PlatformFileError error = - (bytes_read_ < 0) ? PLATFORM_FILE_ERROR_FAILED : PLATFORM_FILE_OK; - callback.Run(error, buffer_.get(), bytes_read_); - } - } - - private: - scoped_ptr<char[]> buffer_; - int bytes_to_read_; - int bytes_read_; - DISALLOW_COPY_AND_ASSIGN(ReadHelper); -}; - -class WriteHelper { - public: - WriteHelper(const char* buffer, int bytes_to_write) - : buffer_(new char[bytes_to_write]), - bytes_to_write_(bytes_to_write), - bytes_written_(0) { - memcpy(buffer_.get(), buffer, bytes_to_write); - } - - void RunWork(PlatformFile file, int64 offset) { - bytes_written_ = WritePlatformFile(file, offset, buffer_.get(), - bytes_to_write_); - } - - void Reply(const FileUtilProxy::WriteCallback& callback) { - if (!callback.is_null()) { - PlatformFileError error = - (bytes_written_ < 0) ? PLATFORM_FILE_ERROR_FAILED : PLATFORM_FILE_OK; - callback.Run(error, bytes_written_); - } - } - - private: - scoped_ptr<char[]> buffer_; - int bytes_to_write_; - int bytes_written_; - DISALLOW_COPY_AND_ASSIGN(WriteHelper); -}; - -PlatformFileError CreateOrOpenAdapter( - const FilePath& file_path, int file_flags, - PlatformFile* file_handle, bool* created) { - DCHECK(file_handle); - DCHECK(created); - if (!DirectoryExists(file_path.DirName())) { - // If its parent does not exist, should return NOT_FOUND error. - return PLATFORM_FILE_ERROR_NOT_FOUND; - } - PlatformFileError error = PLATFORM_FILE_OK; - *file_handle = CreatePlatformFile(file_path, file_flags, created, &error); - return error; -} - -PlatformFileError CloseAdapter(PlatformFile file_handle) { - if (!ClosePlatformFile(file_handle)) { - return PLATFORM_FILE_ERROR_FAILED; - } - return PLATFORM_FILE_OK; -} - -PlatformFileError DeleteAdapter(const FilePath& file_path, bool recursive) { +File::Error DeleteAdapter(const FilePath& file_path, bool recursive) { if (!PathExists(file_path)) { - return PLATFORM_FILE_ERROR_NOT_FOUND; + return File::FILE_ERROR_NOT_FOUND; } if (!base::DeleteFile(file_path, recursive)) { if (!recursive && !base::IsDirectoryEmpty(file_path)) { - return PLATFORM_FILE_ERROR_NOT_EMPTY; + return File::FILE_ERROR_NOT_EMPTY; } - return PLATFORM_FILE_ERROR_FAILED; + return File::FILE_ERROR_FAILED; } - return PLATFORM_FILE_OK; + return File::FILE_OK; } } // namespace -// static -bool FileUtilProxy::CreateOrOpen( - TaskRunner* task_runner, - const FilePath& file_path, int file_flags, - const CreateOrOpenCallback& callback) { - return RelayCreateOrOpen( - task_runner, - base::Bind(&CreateOrOpenAdapter, file_path, file_flags), - base::Bind(&CloseAdapter), - callback); -} - -// static -bool FileUtilProxy::CreateTemporary( - TaskRunner* task_runner, - int additional_file_flags, - const CreateTemporaryCallback& callback) { - CreateTemporaryHelper* helper = new CreateTemporaryHelper(task_runner); - return task_runner->PostTaskAndReply( - FROM_HERE, - Bind(&CreateTemporaryHelper::RunWork, Unretained(helper), - additional_file_flags), - Bind(&CreateTemporaryHelper::Reply, Owned(helper), callback)); -} - -// static -bool FileUtilProxy::Close( - TaskRunner* task_runner, - base::PlatformFile file_handle, - const StatusCallback& callback) { - return RelayClose( - task_runner, - base::Bind(&CloseAdapter), - file_handle, callback); -} - // Retrieves the information about a file. It is invalid to pass NULL for the // callback. bool FileUtilProxy::GetFileInfo( @@ -274,19 +77,6 @@ bool FileUtilProxy::GetFileInfo( } // static -bool FileUtilProxy::GetFileInfoFromPlatformFile( - TaskRunner* task_runner, - PlatformFile file, - const GetFileInfoCallback& callback) { - GetFileInfoHelper* helper = new GetFileInfoHelper; - return task_runner->PostTaskAndReply( - FROM_HERE, - Bind(&GetFileInfoHelper::RunWorkForPlatformFile, - Unretained(helper), file), - Bind(&GetFileInfoHelper::Reply, Owned(helper), callback)); -} - -// static bool FileUtilProxy::DeleteFile(TaskRunner* task_runner, const FilePath& file_path, bool recursive, @@ -298,56 +88,6 @@ bool FileUtilProxy::DeleteFile(TaskRunner* task_runner, } // static -bool FileUtilProxy::Read( - TaskRunner* task_runner, - PlatformFile file, - int64 offset, - int bytes_to_read, - const ReadCallback& callback) { - if (bytes_to_read < 0) { - return false; - } - ReadHelper* helper = new ReadHelper(bytes_to_read); - return task_runner->PostTaskAndReply( - FROM_HERE, - Bind(&ReadHelper::RunWork, Unretained(helper), file, offset), - Bind(&ReadHelper::Reply, Owned(helper), callback)); -} - -// static -bool FileUtilProxy::Write( - TaskRunner* task_runner, - PlatformFile file, - int64 offset, - const char* buffer, - int bytes_to_write, - const WriteCallback& callback) { - if (bytes_to_write <= 0 || buffer == NULL) { - return false; - } - WriteHelper* helper = new WriteHelper(buffer, bytes_to_write); - return task_runner->PostTaskAndReply( - FROM_HERE, - Bind(&WriteHelper::RunWork, Unretained(helper), file, offset), - Bind(&WriteHelper::Reply, Owned(helper), callback)); -} - -// static -bool FileUtilProxy::Touch( - TaskRunner* task_runner, - PlatformFile file, - const Time& last_access_time, - const Time& last_modified_time, - const StatusCallback& callback) { - return base::PostTaskAndReplyWithResult( - task_runner, - FROM_HERE, - Bind(&TouchPlatformFile, file, - last_access_time, last_modified_time), - Bind(&CallWithTranslatedParameter, callback)); -} - -// static bool FileUtilProxy::Touch( TaskRunner* task_runner, const FilePath& file_path, @@ -361,53 +101,4 @@ bool FileUtilProxy::Touch( Bind(&CallWithTranslatedParameter, callback)); } -// static -bool FileUtilProxy::Truncate( - TaskRunner* task_runner, - PlatformFile file, - int64 length, - const StatusCallback& callback) { - return base::PostTaskAndReplyWithResult( - task_runner, - FROM_HERE, - Bind(&TruncatePlatformFile, file, length), - Bind(&CallWithTranslatedParameter, callback)); -} - -// static -bool FileUtilProxy::Flush( - TaskRunner* task_runner, - PlatformFile file, - const StatusCallback& callback) { - return base::PostTaskAndReplyWithResult( - task_runner, - FROM_HERE, - Bind(&FlushPlatformFile, file), - Bind(&CallWithTranslatedParameter, callback)); -} - -// static -bool FileUtilProxy::RelayCreateOrOpen( - TaskRunner* task_runner, - const CreateOrOpenTask& open_task, - const CloseTask& close_task, - const CreateOrOpenCallback& callback) { - CreateOrOpenHelper* helper = new CreateOrOpenHelper( - task_runner, close_task); - return task_runner->PostTaskAndReply( - FROM_HERE, - Bind(&CreateOrOpenHelper::RunWork, Unretained(helper), open_task), - Bind(&CreateOrOpenHelper::Reply, Owned(helper), callback)); -} - -// static -bool FileUtilProxy::RelayClose( - TaskRunner* task_runner, - const CloseTask& close_task, - PlatformFile file_handle, - const StatusCallback& callback) { - return base::PostTaskAndReplyWithResult( - task_runner, FROM_HERE, Bind(close_task, file_handle), callback); -} - } // namespace base diff --git a/chromium/base/files/file_util_proxy.h b/chromium/base/files/file_util_proxy.h index bded1612005..80688cfbb48 100644 --- a/chromium/base/files/file_util_proxy.h +++ b/chromium/base/files/file_util_proxy.h @@ -7,13 +7,8 @@ #include "base/base_export.h" #include "base/callback_forward.h" +#include "base/files/file.h" #include "base/files/file_path.h" -#include "base/memory/ref_counted.h" -#include "base/platform_file.h" - -namespace tracked_objects { -class Location; -}; namespace base { @@ -26,57 +21,10 @@ class BASE_EXPORT FileUtilProxy { // This callback is used by methods that report only an error code. It is // valid to pass a null callback to any function that takes a StatusCallback, // in which case the operation will complete silently. - typedef Callback<void(PlatformFileError)> StatusCallback; - - typedef Callback<void(PlatformFileError, - PassPlatformFile, - bool /* created */)> CreateOrOpenCallback; - typedef Callback<void(PlatformFileError, - PassPlatformFile, - const FilePath&)> CreateTemporaryCallback; - typedef Callback<void(PlatformFileError, - const PlatformFileInfo&)> GetFileInfoCallback; - typedef Callback<void(PlatformFileError, - const char* /* data */, - int /* bytes read */)> ReadCallback; - typedef Callback<void(PlatformFileError, - int /* bytes written */)> WriteCallback; + typedef Callback<void(File::Error)> StatusCallback; - typedef Callback<PlatformFileError(PlatformFile*, bool*)> CreateOrOpenTask; - typedef Callback<PlatformFileError(PlatformFile)> CloseTask; - typedef Callback<PlatformFileError(void)> FileTask; - - // Creates or opens a file with the given flags. It is invalid to pass a null - // callback. If PLATFORM_FILE_CREATE is set in |file_flags| it always tries to - // create a new file at the given |file_path| and calls back with - // PLATFORM_FILE_ERROR_FILE_EXISTS if the |file_path| already exists. - // - // This returns false if task posting to |task_runner| has failed. - static bool CreateOrOpen(TaskRunner* task_runner, - const FilePath& file_path, - int file_flags, - const CreateOrOpenCallback& callback); - - // Creates a temporary file for writing. The path and an open file handle are - // returned. It is invalid to pass a null callback. The additional file flags - // will be added on top of the default file flags which are: - // base::PLATFORM_FILE_CREATE_ALWAYS - // base::PLATFORM_FILE_WRITE - // base::PLATFORM_FILE_TEMPORARY. - // Set |additional_file_flags| to 0 for synchronous writes and set to - // base::PLATFORM_FILE_ASYNC to support asynchronous file operations. - // - // This returns false if task posting to |task_runner| has failed. - static bool CreateTemporary( - TaskRunner* task_runner, - int additional_file_flags, - const CreateTemporaryCallback& callback); - - // Close the given file handle. - // This returns false if task posting to |task_runner| has failed. - static bool Close(TaskRunner* task_runner, - PlatformFile, - const StatusCallback& callback); + typedef Callback<void(File::Error, + const File::Info&)> GetFileInfoCallback; // Retrieves the information about a file. It is invalid to pass a null // callback. @@ -86,13 +34,6 @@ class BASE_EXPORT FileUtilProxy { const FilePath& file_path, const GetFileInfoCallback& callback); - // Does the same as GetFileInfo but takes PlatformFile instead of FilePath. - // This returns false if task posting to |task_runner| has failed. - static bool GetFileInfoFromPlatformFile( - TaskRunner* task_runner, - PlatformFile file, - const GetFileInfoCallback& callback); - // Deletes a file or a directory. // It is an error to delete a non-empty directory with recursive=false. // This returns false if task posting to |task_runner| has failed. @@ -101,42 +42,6 @@ class BASE_EXPORT FileUtilProxy { bool recursive, const StatusCallback& callback); - // Reads from a file. On success, the file pointer is moved to position - // |offset + bytes_to_read| in the file. The callback can be null. - // - // This returns false if |bytes_to_read| is less than zero, or - // if task posting to |task_runner| has failed. - static bool Read( - TaskRunner* task_runner, - PlatformFile file, - int64 offset, - int bytes_to_read, - const ReadCallback& callback); - - // Writes to a file. If |offset| is greater than the length of the file, - // |false| is returned. On success, the file pointer is moved to position - // |offset + bytes_to_write| in the file. The callback can be null. - // |bytes_to_write| must be greater than zero. - // - // This returns false if |bytes_to_write| is less than or equal to zero, - // if |buffer| is NULL, or if task posting to |task_runner| has failed. - static bool Write( - TaskRunner* task_runner, - PlatformFile file, - int64 offset, - const char* buffer, - int bytes_to_write, - const WriteCallback& callback); - - // Touches a file. The callback can be null. - // This returns false if task posting to |task_runner| has failed. - static bool Touch( - TaskRunner* task_runner, - PlatformFile file, - const Time& last_access_time, - const Time& last_modified_time, - const StatusCallback& callback); - // Touches a file. The callback can be null. // This returns false if task posting to |task_runner| has failed. static bool Touch( @@ -146,46 +51,6 @@ class BASE_EXPORT FileUtilProxy { const Time& last_modified_time, const StatusCallback& callback); - // Truncates a file to the given length. If |length| is greater than the - // current length of the file, the file will be extended with zeroes. - // The callback can be null. - // This returns false if task posting to |task_runner| has failed. - static bool Truncate( - TaskRunner* task_runner, - PlatformFile file, - int64 length, - const StatusCallback& callback); - - // Truncates a file to the given length. If |length| is greater than the - // current length of the file, the file will be extended with zeroes. - // The callback can be null. - // This returns false if task posting to |task_runner| has failed. - static bool Truncate( - TaskRunner* task_runner, - const FilePath& path, - int64 length, - const StatusCallback& callback); - - // Flushes a file. The callback can be null. - // This returns false if task posting to |task_runner| has failed. - static bool Flush( - TaskRunner* task_runner, - PlatformFile file, - const StatusCallback& callback); - - // Relay helpers. - // They return false if posting a given task to |task_runner| has failed. - static bool RelayCreateOrOpen( - TaskRunner* task_runner, - const CreateOrOpenTask& open_task, - const CloseTask& close_task, - const CreateOrOpenCallback& callback); - static bool RelayClose( - TaskRunner* task_runner, - const CloseTask& close_task, - PlatformFile, - const StatusCallback& callback); - private: DISALLOW_IMPLICIT_CONSTRUCTORS(FileUtilProxy); }; diff --git a/chromium/base/files/file_util_proxy_unittest.cc b/chromium/base/files/file_util_proxy_unittest.cc index 17c7a3f7cf5..52073ae3b50 100644 --- a/chromium/base/files/file_util_proxy_unittest.cc +++ b/chromium/base/files/file_util_proxy_unittest.cc @@ -4,15 +4,11 @@ #include "base/files/file_util_proxy.h" -#include <map> - #include "base/bind.h" #include "base/file_util.h" #include "base/files/scoped_temp_dir.h" -#include "base/logging.h" #include "base/memory/weak_ptr.h" #include "base/message_loop/message_loop.h" -#include "base/platform_file.h" #include "base/threading/thread.h" #include "testing/gtest/include/gtest/gtest.h" @@ -21,11 +17,9 @@ namespace base { class FileUtilProxyTest : public testing::Test { public: FileUtilProxyTest() - : message_loop_(MessageLoop::TYPE_IO), - file_thread_("FileUtilProxyTestFileThread"), - error_(PLATFORM_FILE_OK), + : file_thread_("FileUtilProxyTestFileThread"), + error_(File::FILE_OK), created_(false), - file_(kInvalidPlatformFileValue), bytes_written_(-1), weak_factory_(this) {} @@ -34,198 +28,43 @@ class FileUtilProxyTest : public testing::Test { ASSERT_TRUE(file_thread_.Start()); } - virtual void TearDown() OVERRIDE { - if (file_ != kInvalidPlatformFileValue) - ClosePlatformFile(file_); - } - - void DidFinish(PlatformFileError error) { - error_ = error; - MessageLoop::current()->QuitWhenIdle(); - } - - void DidCreateOrOpen(PlatformFileError error, - PassPlatformFile file, - bool created) { + void DidFinish(File::Error error) { error_ = error; - file_ = file.ReleaseValue(); - created_ = created; MessageLoop::current()->QuitWhenIdle(); } - void DidCreateTemporary(PlatformFileError error, - PassPlatformFile file, - const FilePath& path) { - error_ = error; - file_ = file.ReleaseValue(); - path_ = path; - MessageLoop::current()->QuitWhenIdle(); - } - - void DidGetFileInfo(PlatformFileError error, - const PlatformFileInfo& file_info) { + void DidGetFileInfo(File::Error error, + const File::Info& file_info) { error_ = error; file_info_ = file_info; MessageLoop::current()->QuitWhenIdle(); } - void DidRead(PlatformFileError error, - const char* data, - int bytes_read) { - error_ = error; - buffer_.resize(bytes_read); - memcpy(&buffer_[0], data, bytes_read); - MessageLoop::current()->QuitWhenIdle(); - } - - void DidWrite(PlatformFileError error, - int bytes_written) { - error_ = error; - bytes_written_ = bytes_written; - MessageLoop::current()->QuitWhenIdle(); - } - protected: - PlatformFile GetTestPlatformFile(int flags) { - if (file_ != kInvalidPlatformFileValue) - return file_; - bool created; - PlatformFileError error; - file_ = CreatePlatformFile(test_path(), flags, &created, &error); - EXPECT_EQ(PLATFORM_FILE_OK, error); - EXPECT_NE(kInvalidPlatformFileValue, file_); - return file_; - } - TaskRunner* file_task_runner() const { return file_thread_.message_loop_proxy().get(); } const FilePath& test_dir_path() const { return dir_.path(); } const FilePath test_path() const { return dir_.path().AppendASCII("test"); } - MessageLoop message_loop_; + MessageLoopForIO message_loop_; Thread file_thread_; ScopedTempDir dir_; - PlatformFileError error_; + File::Error error_; bool created_; - PlatformFile file_; FilePath path_; - PlatformFileInfo file_info_; + File::Info file_info_; std::vector<char> buffer_; int bytes_written_; WeakPtrFactory<FileUtilProxyTest> weak_factory_; }; -TEST_F(FileUtilProxyTest, CreateOrOpen_Create) { - FileUtilProxy::CreateOrOpen( - file_task_runner(), - test_path(), - PLATFORM_FILE_CREATE | PLATFORM_FILE_READ, - Bind(&FileUtilProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr())); - MessageLoop::current()->Run(); - - EXPECT_EQ(PLATFORM_FILE_OK, error_); - EXPECT_TRUE(created_); - EXPECT_NE(kInvalidPlatformFileValue, file_); - EXPECT_TRUE(PathExists(test_path())); -} - -TEST_F(FileUtilProxyTest, CreateOrOpen_Open) { - // Creates a file. - file_util::WriteFile(test_path(), NULL, 0); - ASSERT_TRUE(PathExists(test_path())); - - // Opens the created file. - FileUtilProxy::CreateOrOpen( - file_task_runner(), - test_path(), - PLATFORM_FILE_OPEN | PLATFORM_FILE_READ, - Bind(&FileUtilProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr())); - MessageLoop::current()->Run(); - - EXPECT_EQ(PLATFORM_FILE_OK, error_); - EXPECT_FALSE(created_); - EXPECT_NE(kInvalidPlatformFileValue, file_); -} - -TEST_F(FileUtilProxyTest, CreateOrOpen_OpenNonExistent) { - FileUtilProxy::CreateOrOpen( - file_task_runner(), - test_path(), - PLATFORM_FILE_OPEN | PLATFORM_FILE_READ, - Bind(&FileUtilProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr())); - MessageLoop::current()->Run(); - EXPECT_EQ(PLATFORM_FILE_ERROR_NOT_FOUND, error_); - EXPECT_FALSE(created_); - EXPECT_EQ(kInvalidPlatformFileValue, file_); - EXPECT_FALSE(PathExists(test_path())); -} - -TEST_F(FileUtilProxyTest, Close) { - // Creates a file. - PlatformFile file = GetTestPlatformFile( - PLATFORM_FILE_CREATE | PLATFORM_FILE_WRITE); - -#if defined(OS_WIN) - // This fails on Windows if the file is not closed. - EXPECT_FALSE(base::Move(test_path(), - test_dir_path().AppendASCII("new"))); -#endif - - FileUtilProxy::Close( - file_task_runner(), - file, - Bind(&FileUtilProxyTest::DidFinish, weak_factory_.GetWeakPtr())); - MessageLoop::current()->Run(); - EXPECT_EQ(PLATFORM_FILE_OK, error_); - - // Now it should pass on all platforms. - EXPECT_TRUE(base::Move(test_path(), test_dir_path().AppendASCII("new"))); -} - -TEST_F(FileUtilProxyTest, CreateTemporary) { - FileUtilProxy::CreateTemporary( - file_task_runner(), 0 /* additional_file_flags */, - Bind(&FileUtilProxyTest::DidCreateTemporary, weak_factory_.GetWeakPtr())); - MessageLoop::current()->Run(); - EXPECT_EQ(PLATFORM_FILE_OK, error_); - EXPECT_TRUE(PathExists(path_)); - EXPECT_NE(kInvalidPlatformFileValue, file_); - - // The file should be writable. -#if defined(OS_WIN) - HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - OVERLAPPED overlapped = {0}; - overlapped.hEvent = hEvent; - DWORD bytes_written; - if (!::WriteFile(file_, "test", 4, &bytes_written, &overlapped)) { - // Temporary file is created with ASYNC flag, so WriteFile may return 0 - // with ERROR_IO_PENDING. - EXPECT_EQ(ERROR_IO_PENDING, GetLastError()); - GetOverlappedResult(file_, &overlapped, &bytes_written, TRUE); - } - EXPECT_EQ(4, bytes_written); -#else - // On POSIX ASYNC flag does not affect synchronous read/write behavior. - EXPECT_EQ(4, WritePlatformFile(file_, 0, "test", 4)); -#endif - EXPECT_TRUE(ClosePlatformFile(file_)); - file_ = kInvalidPlatformFileValue; - - // Make sure the written data can be read from the returned path. - std::string data; - EXPECT_TRUE(ReadFileToString(path_, &data)); - EXPECT_EQ("test", data); - - // Make sure we can & do delete the created file to prevent leaks on the bots. - EXPECT_TRUE(base::DeleteFile(path_, false)); -} TEST_F(FileUtilProxyTest, GetFileInfo_File) { // Setup. - ASSERT_EQ(4, file_util::WriteFile(test_path(), "test", 4)); - PlatformFileInfo expected_info; + ASSERT_EQ(4, WriteFile(test_path(), "test", 4)); + File::Info expected_info; GetFileInfo(test_path(), &expected_info); // Run. @@ -236,7 +75,7 @@ TEST_F(FileUtilProxyTest, GetFileInfo_File) { MessageLoop::current()->Run(); // Verify. - EXPECT_EQ(PLATFORM_FILE_OK, error_); + EXPECT_EQ(File::FILE_OK, error_); EXPECT_EQ(expected_info.size, file_info_.size); EXPECT_EQ(expected_info.is_directory, file_info_.is_directory); EXPECT_EQ(expected_info.is_symbolic_link, file_info_.is_symbolic_link); @@ -248,7 +87,7 @@ TEST_F(FileUtilProxyTest, GetFileInfo_File) { TEST_F(FileUtilProxyTest, GetFileInfo_Directory) { // Setup. ASSERT_TRUE(base::CreateDirectory(test_path())); - PlatformFileInfo expected_info; + File::Info expected_info; GetFileInfo(test_path(), &expected_info); // Run. @@ -259,7 +98,7 @@ TEST_F(FileUtilProxyTest, GetFileInfo_Directory) { MessageLoop::current()->Run(); // Verify. - EXPECT_EQ(PLATFORM_FILE_OK, error_); + EXPECT_EQ(File::FILE_OK, error_); EXPECT_EQ(expected_info.size, file_info_.size); EXPECT_EQ(expected_info.is_directory, file_info_.is_directory); EXPECT_EQ(expected_info.is_symbolic_link, file_info_.is_symbolic_link); @@ -268,80 +107,21 @@ TEST_F(FileUtilProxyTest, GetFileInfo_Directory) { EXPECT_EQ(expected_info.creation_time, file_info_.creation_time); } -TEST_F(FileUtilProxyTest, Read) { - // Setup. - const char expected_data[] = "bleh"; - int expected_bytes = arraysize(expected_data); - ASSERT_EQ(expected_bytes, - file_util::WriteFile(test_path(), expected_data, expected_bytes)); - - // Run. - FileUtilProxy::Read( - file_task_runner(), - GetTestPlatformFile(PLATFORM_FILE_OPEN | PLATFORM_FILE_READ), - 0, // offset - 128, - Bind(&FileUtilProxyTest::DidRead, weak_factory_.GetWeakPtr())); - MessageLoop::current()->Run(); - - // Verify. - EXPECT_EQ(PLATFORM_FILE_OK, error_); - EXPECT_EQ(expected_bytes, static_cast<int>(buffer_.size())); - for (size_t i = 0; i < buffer_.size(); ++i) { - EXPECT_EQ(expected_data[i], buffer_[i]); - } -} - -TEST_F(FileUtilProxyTest, WriteAndFlush) { - const char data[] = "foo!"; - int data_bytes = ARRAYSIZE_UNSAFE(data); - PlatformFile file = GetTestPlatformFile( - PLATFORM_FILE_CREATE | PLATFORM_FILE_WRITE); - - FileUtilProxy::Write( - file_task_runner(), - file, - 0, // offset - data, - data_bytes, - Bind(&FileUtilProxyTest::DidWrite, weak_factory_.GetWeakPtr())); - MessageLoop::current()->Run(); - EXPECT_EQ(PLATFORM_FILE_OK, error_); - EXPECT_EQ(data_bytes, bytes_written_); - - // Flush the written data. (So that the following read should always - // succeed. On some platforms it may work with or without this flush.) - FileUtilProxy::Flush( - file_task_runner(), - file, - Bind(&FileUtilProxyTest::DidFinish, weak_factory_.GetWeakPtr())); - MessageLoop::current()->Run(); - EXPECT_EQ(PLATFORM_FILE_OK, error_); - - // Verify the written data. - char buffer[10]; - EXPECT_EQ(data_bytes, base::ReadFile(test_path(), buffer, data_bytes)); - for (int i = 0; i < data_bytes; ++i) { - EXPECT_EQ(data[i], buffer[i]); - } -} - TEST_F(FileUtilProxyTest, Touch) { + ASSERT_EQ(4, WriteFile(test_path(), "test", 4)); Time last_accessed_time = Time::Now() - TimeDelta::FromDays(12345); Time last_modified_time = Time::Now() - TimeDelta::FromHours(98765); FileUtilProxy::Touch( file_task_runner(), - GetTestPlatformFile(PLATFORM_FILE_CREATE | - PLATFORM_FILE_WRITE | - PLATFORM_FILE_WRITE_ATTRIBUTES), + test_path(), last_accessed_time, last_modified_time, Bind(&FileUtilProxyTest::DidFinish, weak_factory_.GetWeakPtr())); MessageLoop::current()->Run(); - EXPECT_EQ(PLATFORM_FILE_OK, error_); + EXPECT_EQ(File::FILE_OK, error_); - PlatformFileInfo info; + File::Info info; GetFileInfo(test_path(), &info); // The returned values may only have the seconds precision, so we cast @@ -352,60 +132,4 @@ TEST_F(FileUtilProxyTest, Touch) { static_cast<int>(info.last_accessed.ToDoubleT())); } -TEST_F(FileUtilProxyTest, Truncate_Shrink) { - // Setup. - const char kTestData[] = "0123456789"; - ASSERT_EQ(10, file_util::WriteFile(test_path(), kTestData, 10)); - PlatformFileInfo info; - GetFileInfo(test_path(), &info); - ASSERT_EQ(10, info.size); - - // Run. - FileUtilProxy::Truncate( - file_task_runner(), - GetTestPlatformFile(PLATFORM_FILE_OPEN | PLATFORM_FILE_WRITE), - 7, - Bind(&FileUtilProxyTest::DidFinish, weak_factory_.GetWeakPtr())); - MessageLoop::current()->Run(); - - // Verify. - GetFileInfo(test_path(), &info); - ASSERT_EQ(7, info.size); - - char buffer[7]; - EXPECT_EQ(7, base::ReadFile(test_path(), buffer, 7)); - int i = 0; - for (; i < 7; ++i) - EXPECT_EQ(kTestData[i], buffer[i]); -} - -TEST_F(FileUtilProxyTest, Truncate_Expand) { - // Setup. - const char kTestData[] = "9876543210"; - ASSERT_EQ(10, file_util::WriteFile(test_path(), kTestData, 10)); - PlatformFileInfo info; - GetFileInfo(test_path(), &info); - ASSERT_EQ(10, info.size); - - // Run. - FileUtilProxy::Truncate( - file_task_runner(), - GetTestPlatformFile(PLATFORM_FILE_OPEN | PLATFORM_FILE_WRITE), - 53, - Bind(&FileUtilProxyTest::DidFinish, weak_factory_.GetWeakPtr())); - MessageLoop::current()->Run(); - - // Verify. - GetFileInfo(test_path(), &info); - ASSERT_EQ(53, info.size); - - char buffer[53]; - EXPECT_EQ(53, base::ReadFile(test_path(), buffer, 53)); - int i = 0; - for (; i < 10; ++i) - EXPECT_EQ(kTestData[i], buffer[i]); - for (; i < 53; ++i) - EXPECT_EQ(0, buffer[i]); -} - } // namespace base diff --git a/chromium/base/files/file_win.cc b/chromium/base/files/file_win.cc index 94f4d7f59c9..9e18bda70b1 100644 --- a/chromium/base/files/file_win.cc +++ b/chromium/base/files/file_win.cc @@ -13,7 +13,7 @@ namespace base { -void File::CreateBaseFileUnsafe(const FilePath& name, uint32 flags) { +void File::InitializeUnsafe(const FilePath& name, uint32 flags) { base::ThreadRestrictions::AssertIOAllowed(); DCHECK(!IsValid()); @@ -34,6 +34,7 @@ void File::CreateBaseFileUnsafe(const FilePath& name, uint32 flags) { if (flags & FLAG_CREATE_ALWAYS) { DCHECK(!disposition); + DCHECK(flags & FLAG_WRITE); disposition = CREATE_ALWAYS; } @@ -84,7 +85,7 @@ void File::CreateBaseFileUnsafe(const FilePath& name, uint32 flags) { disposition, create_flags, NULL)); if (file_.IsValid()) { - error_ = FILE_OK; + error_details_ = FILE_OK; async_ = ((flags & FLAG_ASYNC) == FLAG_ASYNC); if (flags & (FLAG_OPEN_ALWAYS)) @@ -92,20 +93,27 @@ void File::CreateBaseFileUnsafe(const FilePath& name, uint32 flags) { else if (flags & (FLAG_CREATE_ALWAYS | FLAG_CREATE)) created_ = true; } else { - error_ = OSErrorToFileError(GetLastError()); + error_details_ = OSErrorToFileError(GetLastError()); } } bool File::IsValid() const { return file_.IsValid(); } + +PlatformFile File::GetPlatformFile() const { + return file_; +} + PlatformFile File::TakePlatformFile() { return file_.Take(); } void File::Close() { - base::ThreadRestrictions::AssertIOAllowed(); - file_.Close(); + if (file_.IsValid()) { + base::ThreadRestrictions::AssertIOAllowed(); + file_.Close(); + } } int64 File::Seek(Whence whence, int64 offset) { @@ -137,7 +145,7 @@ int File::Read(int64 offset, char* data, int size) { overlapped.OffsetHigh = offset_li.HighPart; DWORD bytes_read; - if (::ReadFile(file_, data, size, &bytes_read, &overlapped) != 0) + if (::ReadFile(file_, data, size, &bytes_read, &overlapped)) return bytes_read; if (ERROR_HANDLE_EOF == GetLastError()) return 0; @@ -153,7 +161,7 @@ int File::ReadAtCurrentPos(char* data, int size) { return -1; DWORD bytes_read; - if (::ReadFile(file_, data, size, &bytes_read, NULL) != 0) + if (::ReadFile(file_, data, size, &bytes_read, NULL)) return bytes_read; if (ERROR_HANDLE_EOF == GetLastError()) return 0; @@ -182,14 +190,23 @@ int File::Write(int64 offset, const char* data, int size) { overlapped.OffsetHigh = offset_li.HighPart; DWORD bytes_written; - if (::WriteFile(file_, data, size, &bytes_written, &overlapped) != 0) + if (::WriteFile(file_, data, size, &bytes_written, &overlapped)) return bytes_written; return -1; } int File::WriteAtCurrentPos(const char* data, int size) { - NOTREACHED(); + base::ThreadRestrictions::AssertIOAllowed(); + DCHECK(IsValid()); + DCHECK(!async_); + if (size < 0) + return -1; + + DWORD bytes_written; + if (::WriteFile(file_, data, size, &bytes_written, NULL)) + return bytes_written; + return -1; } @@ -197,7 +214,17 @@ int File::WriteAtCurrentPosNoBestEffort(const char* data, int size) { return WriteAtCurrentPos(data, size); } -bool File::Truncate(int64 length) { +int64 File::GetLength() { + base::ThreadRestrictions::AssertIOAllowed(); + DCHECK(IsValid()); + LARGE_INTEGER size; + if (!::GetFileSizeEx(file_.Get(), &size)) + return -1; + + return static_cast<int64>(size.QuadPart); +} + +bool File::SetLength(int64 length) { base::ThreadRestrictions::AssertIOAllowed(); DCHECK(IsValid()); @@ -205,7 +232,7 @@ bool File::Truncate(int64 length) { LARGE_INTEGER file_pointer; LARGE_INTEGER zero; zero.QuadPart = 0; - if (::SetFilePointerEx(file_, zero, &file_pointer, FILE_CURRENT) == 0) + if (!::SetFilePointerEx(file_, zero, &file_pointer, FILE_CURRENT)) return false; LARGE_INTEGER length_li; @@ -218,8 +245,11 @@ bool File::Truncate(int64 length) { // Set the new file length and move the file pointer to its old position. // This is consistent with ftruncate()'s behavior, even when the file // pointer points to a location beyond the end of the file. - return ((::SetEndOfFile(file_) != 0) && - (::SetFilePointerEx(file_, file_pointer, NULL, FILE_BEGIN) != 0)); + // TODO(rvargas): Emulating ftruncate details seem suspicious and it is not + // promised by the interface (nor was promised by PlatformFile). See if this + // implementation detail can be removed. + return ((::SetEndOfFile(file_) != FALSE) && + (::SetFilePointerEx(file_, file_pointer, NULL, FILE_BEGIN) != FALSE)); } bool File::Flush() { @@ -235,7 +265,7 @@ bool File::SetTimes(Time last_access_time, Time last_modified_time) { FILETIME last_access_filetime = last_access_time.ToFileTime(); FILETIME last_modified_filetime = last_modified_time.ToFileTime(); return (::SetFileTime(file_, NULL, &last_access_filetime, - &last_modified_filetime) != 0); + &last_modified_filetime) != FALSE); } bool File::GetInfo(Info* info) { @@ -243,7 +273,7 @@ bool File::GetInfo(Info* info) { DCHECK(IsValid()); BY_HANDLE_FILE_INFORMATION file_info; - if (GetFileInformationByHandle(file_, &file_info) == 0) + if (!GetFileInformationByHandle(file_, &file_info)) return false; LARGE_INTEGER size; diff --git a/chromium/base/files/important_file_writer.cc b/chromium/base/files/important_file_writer.cc index 261c98772e5..bf4e0033e3a 100644 --- a/chromium/base/files/important_file_writer.cc +++ b/chromium/base/files/important_file_writer.cc @@ -2,12 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#if defined _MSC_VER && _MSC_VER == 1800 -// TODO(scottmg): Internal errors on VS2013 RC in LTCG. This should be removed -// after RTM. http://crbug.com/288948 -#pragma optimize("", off) -#endif - #include "base/files/important_file_writer.h" #include <stdio.h> @@ -17,11 +11,13 @@ #include "base/bind.h" #include "base/critical_closure.h" #include "base/file_util.h" +#include "base/files/file.h" #include "base/files/file_path.h" #include "base/logging.h" #include "base/metrics/histogram.h" #include "base/strings/string_number_conversions.h" #include "base/task_runner.h" +#include "base/task_runner_util.h" #include "base/threading/thread.h" #include "base/time/time.h" @@ -63,25 +59,18 @@ bool ImportantFileWriter::WriteFileAtomically(const FilePath& path, return false; } - int flags = PLATFORM_FILE_OPEN | PLATFORM_FILE_WRITE; - PlatformFile tmp_file = - CreatePlatformFile(tmp_file_path, flags, NULL, NULL); - if (tmp_file == kInvalidPlatformFileValue) { + File tmp_file(tmp_file_path, File::FLAG_OPEN | File::FLAG_WRITE); + if (!tmp_file.IsValid()) { LogFailure(path, FAILED_OPENING, "could not open temporary file"); return false; } // If this happens in the wild something really bad is going on. CHECK_LE(data.length(), static_cast<size_t>(kint32max)); - int bytes_written = WritePlatformFile( - tmp_file, 0, data.data(), static_cast<int>(data.length())); - FlushPlatformFile(tmp_file); // Ignore return value. - - if (!ClosePlatformFile(tmp_file)) { - LogFailure(path, FAILED_CLOSING, "failed to close temporary file"); - base::DeleteFile(tmp_file_path, false); - return false; - } + int bytes_written = tmp_file.Write(0, data.data(), + static_cast<int>(data.length())); + tmp_file.Flush(); // Ignore return value. + tmp_file.Close(); if (bytes_written < static_cast<int>(data.length())) { LogFailure(path, FAILED_WRITING, "error writing, bytes_written=" + @@ -99,13 +88,13 @@ bool ImportantFileWriter::WriteFileAtomically(const FilePath& path, return true; } -ImportantFileWriter::ImportantFileWriter( - const FilePath& path, base::SequencedTaskRunner* task_runner) - : path_(path), - task_runner_(task_runner), - serializer_(NULL), - commit_interval_(TimeDelta::FromMilliseconds( - kDefaultCommitIntervalMs)) { +ImportantFileWriter::ImportantFileWriter(const FilePath& path, + base::SequencedTaskRunner* task_runner) + : path_(path), + task_runner_(task_runner), + serializer_(NULL), + commit_interval_(TimeDelta::FromMilliseconds(kDefaultCommitIntervalMs)), + weak_factory_(this) { DCHECK(CalledOnValidThread()); DCHECK(task_runner_.get()); } @@ -132,11 +121,7 @@ void ImportantFileWriter::WriteNow(const std::string& data) { if (HasPendingWrite()) timer_.Stop(); - if (!task_runner_->PostTask( - FROM_HERE, - MakeCriticalClosure( - Bind(IgnoreResult(&ImportantFileWriter::WriteFileAtomically), - path_, data)))) { + if (!PostWriteTask(data)) { // 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. @@ -170,4 +155,40 @@ void ImportantFileWriter::DoScheduledWrite() { serializer_ = NULL; } +void ImportantFileWriter::RegisterOnNextSuccessfulWriteCallback( + const base::Closure& on_next_successful_write) { + DCHECK(on_next_successful_write_.is_null()); + on_next_successful_write_ = on_next_successful_write; +} + +bool ImportantFileWriter::PostWriteTask(const std::string& data) { + // 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 + // suppressing all of those is unrealistic hence we avoid most of them by + // using PostTask() in the typical scenario below. + if (!on_next_successful_write_.is_null()) { + return base::PostTaskAndReplyWithResult( + task_runner_, + FROM_HERE, + MakeCriticalClosure( + Bind(&ImportantFileWriter::WriteFileAtomically, path_, data)), + Bind(&ImportantFileWriter::ForwardSuccessfulWrite, + weak_factory_.GetWeakPtr())); + } + return task_runner_->PostTask( + FROM_HERE, + MakeCriticalClosure( + Bind(IgnoreResult(&ImportantFileWriter::WriteFileAtomically), + path_, data))); +} + +void ImportantFileWriter::ForwardSuccessfulWrite(bool result) { + DCHECK(CalledOnValidThread()); + if (result && !on_next_successful_write_.is_null()) { + on_next_successful_write_.Run(); + on_next_successful_write_.Reset(); + } +} + } // namespace base diff --git a/chromium/base/files/important_file_writer.h b/chromium/base/files/important_file_writer.h index ba1c745a4ef..61a53b16503 100644 --- a/chromium/base/files/important_file_writer.h +++ b/chromium/base/files/important_file_writer.h @@ -9,6 +9,7 @@ #include "base/base_export.h" #include "base/basictypes.h" +#include "base/callback.h" #include "base/files/file_path.h" #include "base/memory/ref_counted.h" #include "base/threading/non_thread_safe.h" @@ -89,6 +90,11 @@ class BASE_EXPORT ImportantFileWriter : public NonThreadSafe { // Serialize data pending to be saved and execute write on backend thread. void DoScheduledWrite(); + // Registers |on_next_successful_write| to be called once, on the next + // successful write event. Only one callback can be set at once. + void RegisterOnNextSuccessfulWriteCallback( + const base::Closure& on_next_successful_write); + TimeDelta commit_interval() const { return commit_interval_; } @@ -98,6 +104,16 @@ class BASE_EXPORT ImportantFileWriter : public NonThreadSafe { } private: + // Helper method for WriteNow(). + bool PostWriteTask(const std::string& data); + + // If |result| is true and |on_next_successful_write_| is set, invokes + // |on_successful_write_| and then resets it; no-ops otherwise. + void ForwardSuccessfulWrite(bool result); + + // Invoked once and then reset on the next successful write event. + base::Closure on_next_successful_write_; + // Path being written to. const FilePath path_; @@ -113,6 +129,8 @@ class BASE_EXPORT ImportantFileWriter : public NonThreadSafe { // Time delta after which scheduled data will be written to disk. TimeDelta commit_interval_; + WeakPtrFactory<ImportantFileWriter> weak_factory_; + DISALLOW_COPY_AND_ASSIGN(ImportantFileWriter); }; diff --git a/chromium/base/files/important_file_writer_unittest.cc b/chromium/base/files/important_file_writer_unittest.cc index 02a5f76b2b6..3f62fe4953b 100644 --- a/chromium/base/files/important_file_writer_unittest.cc +++ b/chromium/base/files/important_file_writer_unittest.cc @@ -4,6 +4,7 @@ #include "base/files/important_file_writer.h" +#include "base/bind.h" #include "base/compiler_specific.h" #include "base/file_util.h" #include "base/files/file_path.h" @@ -41,6 +42,41 @@ class DataSerializer : public ImportantFileWriter::DataSerializer { const std::string data_; }; +class SuccessfulWriteObserver { + public: + SuccessfulWriteObserver() : successful_write_observed_(false) {} + + // Register on_successful_write() to be called on the next successful write + // of |writer|. + void ObserveNextSuccessfulWrite(ImportantFileWriter* writer); + + // Returns true if a successful write was observed via on_successful_write() + // and resets the observation state to false regardless. + bool GetAndResetObservationState(); + + private: + void on_successful_write() { + EXPECT_FALSE(successful_write_observed_); + successful_write_observed_ = true; + } + + bool successful_write_observed_; + + DISALLOW_COPY_AND_ASSIGN(SuccessfulWriteObserver); +}; + +void SuccessfulWriteObserver::ObserveNextSuccessfulWrite( + ImportantFileWriter* writer) { + writer->RegisterOnNextSuccessfulWriteCallback(base::Bind( + &SuccessfulWriteObserver::on_successful_write, base::Unretained(this))); +} + +bool SuccessfulWriteObserver::GetAndResetObservationState() { + bool was_successful_write_observed = successful_write_observed_; + successful_write_observed_ = false; + return was_successful_write_observed; +} + } // namespace class ImportantFileWriterTest : public testing::Test { @@ -52,6 +88,7 @@ class ImportantFileWriterTest : public testing::Test { } protected: + SuccessfulWriteObserver successful_write_observer_; FilePath file_; MessageLoop loop_; @@ -62,11 +99,47 @@ class ImportantFileWriterTest : public testing::Test { TEST_F(ImportantFileWriterTest, Basic) { ImportantFileWriter writer(file_, MessageLoopProxy::current().get()); EXPECT_FALSE(PathExists(writer.path())); + EXPECT_FALSE(successful_write_observer_.GetAndResetObservationState()); + writer.WriteNow("foo"); + RunLoop().RunUntilIdle(); + + EXPECT_FALSE(successful_write_observer_.GetAndResetObservationState()); + ASSERT_TRUE(PathExists(writer.path())); + EXPECT_EQ("foo", GetFileContent(writer.path())); +} + +TEST_F(ImportantFileWriterTest, BasicWithSuccessfulWriteObserver) { + ImportantFileWriter writer(file_, MessageLoopProxy::current().get()); + EXPECT_FALSE(PathExists(writer.path())); + EXPECT_FALSE(successful_write_observer_.GetAndResetObservationState()); + successful_write_observer_.ObserveNextSuccessfulWrite(&writer); writer.WriteNow("foo"); RunLoop().RunUntilIdle(); + // Confirm that the observer is invoked. + EXPECT_TRUE(successful_write_observer_.GetAndResetObservationState()); ASSERT_TRUE(PathExists(writer.path())); EXPECT_EQ("foo", GetFileContent(writer.path())); + + // Confirm that re-installing the observer works for another write. + EXPECT_FALSE(successful_write_observer_.GetAndResetObservationState()); + successful_write_observer_.ObserveNextSuccessfulWrite(&writer); + writer.WriteNow("bar"); + RunLoop().RunUntilIdle(); + + EXPECT_TRUE(successful_write_observer_.GetAndResetObservationState()); + ASSERT_TRUE(PathExists(writer.path())); + EXPECT_EQ("bar", GetFileContent(writer.path())); + + // Confirm that writing again without re-installing the observer doesn't + // result in a notification. + EXPECT_FALSE(successful_write_observer_.GetAndResetObservationState()); + writer.WriteNow("baz"); + RunLoop().RunUntilIdle(); + + EXPECT_FALSE(successful_write_observer_.GetAndResetObservationState()); + ASSERT_TRUE(PathExists(writer.path())); + EXPECT_EQ("baz", GetFileContent(writer.path())); } TEST_F(ImportantFileWriterTest, ScheduleWrite) { diff --git a/chromium/base/files/memory_mapped_file.cc b/chromium/base/files/memory_mapped_file.cc index a48ec0ceb2a..ace4e112628 100644 --- a/chromium/base/files/memory_mapped_file.cc +++ b/chromium/base/files/memory_mapped_file.cc @@ -17,7 +17,14 @@ bool MemoryMappedFile::Initialize(const FilePath& file_name) { if (IsValid()) return false; - if (!MapFileToMemory(file_name)) { + file_.Initialize(file_name, File::FLAG_OPEN | File::FLAG_READ); + + if (!file_.IsValid()) { + DLOG(ERROR) << "Couldn't open " << file_name.AsUTF8Unsafe(); + return false; + } + + if (!MapFileToMemory()) { CloseHandles(); return false; } @@ -25,13 +32,13 @@ bool MemoryMappedFile::Initialize(const FilePath& file_name) { return true; } -bool MemoryMappedFile::Initialize(PlatformFile file) { +bool MemoryMappedFile::Initialize(File file) { if (IsValid()) return false; - file_ = file; + file_ = file.Pass(); - if (!MapFileToMemoryInternal()) { + if (!MapFileToMemory()) { CloseHandles(); return false; } @@ -43,16 +50,4 @@ bool MemoryMappedFile::IsValid() const { return data_ != NULL; } -bool MemoryMappedFile::MapFileToMemory(const FilePath& file_name) { - file_ = CreatePlatformFile(file_name, PLATFORM_FILE_OPEN | PLATFORM_FILE_READ, - NULL, NULL); - - if (file_ == kInvalidPlatformFileValue) { - DLOG(ERROR) << "Couldn't open " << file_name.AsUTF8Unsafe(); - return false; - } - - return MapFileToMemoryInternal(); -} - } // namespace base diff --git a/chromium/base/files/memory_mapped_file.h b/chromium/base/files/memory_mapped_file.h index 6df1bad6359..b02d8cfbdae 100644 --- a/chromium/base/files/memory_mapped_file.h +++ b/chromium/base/files/memory_mapped_file.h @@ -7,7 +7,7 @@ #include "base/base_export.h" #include "base/basictypes.h" -#include "base/platform_file.h" +#include "base/files/file.h" #include "build/build_config.h" #if defined(OS_WIN) @@ -30,9 +30,10 @@ class BASE_EXPORT MemoryMappedFile { // the file does not exist, or the memory mapping fails, it will return false. // Later we may want to allow the user to specify access. bool Initialize(const FilePath& file_name); - // As above, but works with an already-opened file. MemoryMappedFile will take - // ownership of |file| and close it when done. - bool Initialize(PlatformFile file); + + // As above, but works with an already-opened file. MemoryMappedFile takes + // ownership of |file| and closes it when done. + bool Initialize(File file); #if defined(OS_WIN) // Opens an existing file and maps it as an image section. Please refer to @@ -47,27 +48,22 @@ class BASE_EXPORT MemoryMappedFile { bool IsValid() const; private: - // Open the given file and pass it to MapFileToMemoryInternal(). - bool MapFileToMemory(const FilePath& file_name); - // Map the file to memory, set data_ to that memory address. Return true on // success, false on any kind of failure. This is a helper for Initialize(). - bool MapFileToMemoryInternal(); + bool MapFileToMemory(); - // Closes all open handles. Later we may want to make this public. + // Closes all open handles. void CloseHandles(); -#if defined(OS_WIN) - // MapFileToMemoryInternal calls this function. It provides the ability to - // pass in flags which control the mapped section. - bool MapFileToMemoryInternalEx(int flags); - - HANDLE file_mapping_; -#endif - PlatformFile file_; + File file_; uint8* data_; size_t length_; +#if defined(OS_WIN) + win::ScopedHandle file_mapping_; + bool image_; // Map as an image. +#endif + DISALLOW_COPY_AND_ASSIGN(MemoryMappedFile); }; diff --git a/chromium/base/files/memory_mapped_file_posix.cc b/chromium/base/files/memory_mapped_file_posix.cc index c4c477a3fe7..5d7e0079992 100644 --- a/chromium/base/files/memory_mapped_file_posix.cc +++ b/chromium/base/files/memory_mapped_file_posix.cc @@ -13,26 +13,23 @@ namespace base { -MemoryMappedFile::MemoryMappedFile() - : file_(kInvalidPlatformFileValue), - data_(NULL), - length_(0) { +MemoryMappedFile::MemoryMappedFile() : data_(NULL), length_(0) { } -bool MemoryMappedFile::MapFileToMemoryInternal() { +bool MemoryMappedFile::MapFileToMemory() { ThreadRestrictions::AssertIOAllowed(); struct stat file_stat; - if (fstat(file_, &file_stat) == kInvalidPlatformFileValue) { - DPLOG(ERROR) << "fstat " << file_; + if (fstat(file_.GetPlatformFile(), &file_stat) == -1 ) { + DPLOG(ERROR) << "fstat " << file_.GetPlatformFile(); return false; } length_ = file_stat.st_size; data_ = static_cast<uint8*>( - mmap(NULL, length_, PROT_READ, MAP_SHARED, file_, 0)); + mmap(NULL, length_, PROT_READ, MAP_SHARED, file_.GetPlatformFile(), 0)); if (data_ == MAP_FAILED) - DPLOG(ERROR) << "mmap " << file_; + DPLOG(ERROR) << "mmap " << file_.GetPlatformFile(); return data_ != MAP_FAILED; } @@ -42,12 +39,10 @@ void MemoryMappedFile::CloseHandles() { if (data_ != NULL) munmap(data_, length_); - if (file_ != kInvalidPlatformFileValue) - close(file_); + file_.Close(); data_ = NULL; length_ = 0; - file_ = kInvalidPlatformFileValue; } } // namespace base diff --git a/chromium/base/files/memory_mapped_file_win.cc b/chromium/base/files/memory_mapped_file_win.cc index 694212950de..f3822873bfd 100644 --- a/chromium/base/files/memory_mapped_file_win.cc +++ b/chromium/base/files/memory_mapped_file_win.cc @@ -5,83 +5,52 @@ #include "base/files/memory_mapped_file.h" #include "base/files/file_path.h" -#include "base/logging.h" -#include "base/metrics/histogram.h" #include "base/strings/string16.h" #include "base/threading/thread_restrictions.h" namespace base { -MemoryMappedFile::MemoryMappedFile() - : file_(INVALID_HANDLE_VALUE), - file_mapping_(INVALID_HANDLE_VALUE), - data_(NULL), - length_(INVALID_FILE_SIZE) { +MemoryMappedFile::MemoryMappedFile() : data_(NULL), length_(0), image_(false) { } bool MemoryMappedFile::InitializeAsImageSection(const FilePath& file_name) { - if (IsValid()) - return false; - file_ = CreatePlatformFile(file_name, PLATFORM_FILE_OPEN | PLATFORM_FILE_READ, - NULL, NULL); - - if (file_ == kInvalidPlatformFileValue) { - DLOG(ERROR) << "Couldn't open " << file_name.AsUTF8Unsafe(); - return false; - } - - if (!MapFileToMemoryInternalEx(SEC_IMAGE)) { - CloseHandles(); - return false; - } - - return true; -} - -bool MemoryMappedFile::MapFileToMemoryInternal() { - return MapFileToMemoryInternalEx(0); + image_ = true; + return Initialize(file_name); } -bool MemoryMappedFile::MapFileToMemoryInternalEx(int flags) { +bool MemoryMappedFile::MapFileToMemory() { ThreadRestrictions::AssertIOAllowed(); - if (file_ == INVALID_HANDLE_VALUE) + if (!file_.IsValid()) return false; - length_ = ::GetFileSize(file_, NULL); - if (length_ == INVALID_FILE_SIZE) + int64 len = file_.GetLength(); + if (len <= 0 || len > kint32max) return false; + length_ = static_cast<size_t>(len); + + int flags = image_ ? SEC_IMAGE | PAGE_READONLY : PAGE_READONLY; - file_mapping_ = ::CreateFileMapping(file_, NULL, PAGE_READONLY | flags, - 0, 0, NULL); - if (!file_mapping_) { - // According to msdn, system error codes are only reserved up to 15999. - // http://msdn.microsoft.com/en-us/library/ms681381(v=VS.85).aspx. - UMA_HISTOGRAM_ENUMERATION("MemoryMappedFile.CreateFileMapping", - logging::GetLastSystemErrorCode(), 16000); + file_mapping_.Set(::CreateFileMapping(file_.GetPlatformFile(), NULL, + flags, 0, 0, NULL)); + if (!file_mapping_.IsValid()) return false; - } - data_ = static_cast<uint8*>( - ::MapViewOfFile(file_mapping_, FILE_MAP_READ, 0, 0, 0)); - if (!data_) { - UMA_HISTOGRAM_ENUMERATION("MemoryMappedFile.MapViewOfFile", - logging::GetLastSystemErrorCode(), 16000); - } + data_ = static_cast<uint8*>(::MapViewOfFile(file_mapping_.Get(), + FILE_MAP_READ, 0, 0, 0)); return data_ != NULL; } void MemoryMappedFile::CloseHandles() { if (data_) ::UnmapViewOfFile(data_); - if (file_mapping_ != INVALID_HANDLE_VALUE) - ::CloseHandle(file_mapping_); - if (file_ != INVALID_HANDLE_VALUE) - ::CloseHandle(file_); + if (file_mapping_.IsValid()) + file_mapping_.Close(); + if (file_.IsValid()) + file_.Close(); data_ = NULL; - file_mapping_ = file_ = INVALID_HANDLE_VALUE; - length_ = INVALID_FILE_SIZE; + length_ = 0; } } // namespace base diff --git a/chromium/base/files/scoped_file.cc b/chromium/base/files/scoped_file.cc new file mode 100644 index 00000000000..39f064de1c4 --- /dev/null +++ b/chromium/base/files/scoped_file.cc @@ -0,0 +1,35 @@ +// 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/files/scoped_file.h" + +#include "base/logging.h" + +#if defined(OS_POSIX) +#include <unistd.h> + +#include "base/posix/eintr_wrapper.h" +#endif + +namespace base { +namespace internal { + +#if defined(OS_POSIX) + +// static +void ScopedFDCloseTraits::Free(int fd) { + // It's important to crash here. + // There are security implications to not closing a file descriptor + // properly. As file descriptors are "capabilities", keeping them open + // would make the current process keep access to a resource. Much of + // Chrome relies on being able to "drop" such access. + // It's especially problematic on Linux with the setuid sandbox, where + // a single open directory would bypass the entire security model. + PCHECK(0 == IGNORE_EINTR(close(fd))); +} + +#endif // OS_POSIX + +} // namespace internal +} // namespace base diff --git a/chromium/base/files/scoped_file.h b/chromium/base/files/scoped_file.h new file mode 100644 index 00000000000..106f6ad94bf --- /dev/null +++ b/chromium/base/files/scoped_file.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_FILES_SCOPED_FILE_H_ +#define BASE_FILES_SCOPED_FILE_H_ + +#include <stdio.h> + +#include "base/base_export.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/scoped_generic.h" +#include "build/build_config.h" + +namespace base { + +namespace internal { + +#if defined(OS_POSIX) +struct BASE_EXPORT ScopedFDCloseTraits { + static int InvalidValue() { + return -1; + } + static void Free(int fd); +}; +#endif + +// Functor for |ScopedFILE| (below). +struct ScopedFILECloser { + inline void operator()(FILE* x) const { + if (x) + fclose(x); + } +}; + +} // namespace internal + +// ----------------------------------------------------------------------------- + +#if defined(OS_POSIX) +// A low-level Posix file descriptor closer class. Use this when writing +// platform-specific code, especially that does non-file-like things with the +// FD (like sockets). +// +// If you're writing low-level Windows code, see base/win/scoped_handle.h +// which provides some additional functionality. +// +// If you're writing cross-platform code that deals with actual files, you +// should generally use base::File instead which can be constructed with a +// handle, and in addition to handling ownership, has convenient cross-platform +// file manipulation functions on it. +typedef ScopedGeneric<int, internal::ScopedFDCloseTraits> ScopedFD; +#endif + +// Automatically closes |FILE*|s. +typedef scoped_ptr<FILE, internal::ScopedFILECloser> ScopedFILE; + +} // namespace base + +#endif // BASE_FILES_SCOPED_FILE_H_ diff --git a/chromium/base/files/scoped_temp_dir_unittest.cc b/chromium/base/files/scoped_temp_dir_unittest.cc index fe243ce2ee5..da222304a09 100644 --- a/chromium/base/files/scoped_temp_dir_unittest.cc +++ b/chromium/base/files/scoped_temp_dir_unittest.cc @@ -5,8 +5,8 @@ #include <string> #include "base/file_util.h" +#include "base/files/file.h" #include "base/files/scoped_temp_dir.h" -#include "base/platform_file.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { @@ -98,17 +98,13 @@ TEST(ScopedTempDir, MultipleInvocations) { TEST(ScopedTempDir, LockedTempDir) { ScopedTempDir dir; EXPECT_TRUE(dir.CreateUniqueTempDir()); - int file_flags = base::PLATFORM_FILE_CREATE_ALWAYS | - base::PLATFORM_FILE_WRITE; - base::PlatformFileError error_code = base::PLATFORM_FILE_OK; - FilePath file_path(dir.path().Append(FILE_PATH_LITERAL("temp"))); - base::PlatformFile file = base::CreatePlatformFile(file_path, file_flags, - NULL, &error_code); - EXPECT_NE(base::kInvalidPlatformFileValue, file); - EXPECT_EQ(base::PLATFORM_FILE_OK, error_code); + base::File file(dir.path().Append(FILE_PATH_LITERAL("temp")), + base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); + EXPECT_TRUE(file.IsValid()); + EXPECT_EQ(base::File::FILE_OK, file.error_details()); EXPECT_FALSE(dir.Delete()); // We should not be able to delete. EXPECT_FALSE(dir.path().empty()); // We should still have a valid path. - EXPECT_TRUE(base::ClosePlatformFile(file)); + file.Close(); // Now, we should be able to delete. EXPECT_TRUE(dir.Delete()); } diff --git a/chromium/base/format_macros.h b/chromium/base/format_macros.h index 466d79be731..4d90c593a61 100644 --- a/chromium/base/format_macros.h +++ b/chromium/base/format_macros.h @@ -46,6 +46,34 @@ #define PRIuS "zu" #endif +// The size of NSInteger and NSUInteger varies between 32-bit and 64-bit +// architectures and Apple does not provides standard format macros and +// recommends casting. This has many drawbacks, so instead define macros +// for formatting those types. +#if defined(OS_MACOSX) +#if defined(ARCH_CPU_64_BITS) +#if !defined(PRIdNS) +#define PRIdNS "ld" +#endif +#if !defined(PRIuNS) +#define PRIuNS "lu" +#endif +#if !defined(PRIxNS) +#define PRIxNS "lx" +#endif +#else // defined(ARCH_CPU_64_BITS) +#if !defined(PRIdNS) +#define PRIdNS "d" +#endif +#if !defined(PRIuNS) +#define PRIuNS "u" +#endif +#if !defined(PRIxNS) +#define PRIxNS "x" +#endif +#endif +#endif // defined(OS_MACOSX) + #else // OS_WIN #if !defined(PRId64) diff --git a/chromium/base/hash.cc b/chromium/base/hash.cc index 2c87065045b..a7db64a919c 100644 --- a/chromium/base/hash.cc +++ b/chromium/base/hash.cc @@ -1,73 +1,18 @@ -// From http://www.azillionmonkeys.com/qed/hash.html +// 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/hash.h" -typedef uint32 uint32_t; -typedef uint16 uint16_t; +// Definition in base/third_party/superfasthash/superfasthash.c. (Third-party +// code did not come with its own header file, so declaring the function here.) +// Note: This algorithm is also in Blink under Source/wtf/StringHasher.h. +extern "C" uint32_t SuperFastHash(const char* data, int len); namespace base { -#undef get16bits -#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ - || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) -#define get16bits(d) (*((const uint16_t *) (d))) -#endif - -#if !defined (get16bits) -#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\ - +(uint32_t)(((const uint8_t *)(d))[0]) ) -#endif - -uint32 SuperFastHash(const char * data, int len) { - uint32_t hash = len, tmp; - int rem; - - if (len <= 0 || data == NULL) - return 0; - - rem = len & 3; - len >>= 2; - - /* Main loop */ - for (; len > 0; len--) { - hash += get16bits(data); - tmp = (get16bits(data + 2) << 11) ^ hash; - hash = (hash << 16) ^ tmp; - data += 2 * sizeof(uint16_t); - hash += hash >> 11; - } - - /* Handle end cases */ - switch (rem) { - case 3: - hash += get16bits(data); - hash ^= hash << 16; - - // Treat the final character as signed. This ensures all platforms behave - // consistently with the original x86 code. - hash ^= static_cast<signed char>(data[sizeof(uint16_t)]) << 18; - hash += hash >> 11; - break; - case 2: - hash += get16bits(data); - hash ^= hash << 11; - hash += hash >> 17; - break; - case 1: - hash += static_cast<signed char>(*data); - hash ^= hash << 10; - hash += hash >> 1; - } - - /* Force "avalanching" of final 127 bits */ - hash ^= hash << 3; - hash += hash >> 5; - hash ^= hash << 4; - hash += hash >> 17; - hash ^= hash << 25; - hash += hash >> 6; - - return hash; +uint32 SuperFastHash(const char* data, int len) { + return ::SuperFastHash(data, len); } } // namespace base diff --git a/chromium/base/hash.h b/chromium/base/hash.h index cf8ea3a26e4..e46f6ac2484 100644 --- a/chromium/base/hash.h +++ b/chromium/base/hash.h @@ -5,25 +5,32 @@ #ifndef BASE_HASH_H_ #define BASE_HASH_H_ +#include <limits> #include <string> #include "base/base_export.h" #include "base/basictypes.h" +#include "base/logging.h" namespace base { -// From http://www.azillionmonkeys.com/qed/hash.html -// This is the hash used on WebCore/platform/stringhash -BASE_EXPORT uint32 SuperFastHash(const char * data, int len); +// WARNING: This hash function should not be used for any cryptographic purpose. +BASE_EXPORT uint32 SuperFastHash(const char* data, int len); -inline uint32 Hash(const char* key, size_t length) { - return SuperFastHash(key, static_cast<int>(length)); +// Computes a hash of a memory buffer |data| of a given |length|. +// WARNING: This hash function should not be used for any cryptographic purpose. +inline uint32 Hash(const char* data, size_t length) { + if (length > static_cast<size_t>(std::numeric_limits<int>::max())) { + NOTREACHED(); + return 0; + } + return SuperFastHash(data, static_cast<int>(length)); } -inline uint32 Hash(const std::string& key) { - if (key.empty()) - return 0; - return SuperFastHash(key.data(), static_cast<int>(key.size())); +// Computes a hash of a string |str|. +// WARNING: This hash function should not be used for any cryptographic purpose. +inline uint32 Hash(const std::string& str) { + return Hash(str.data(), str.size()); } } // namespace base diff --git a/chromium/base/hash_unittest.cc b/chromium/base/hash_unittest.cc new file mode 100644 index 00000000000..fc8a7519ee9 --- /dev/null +++ b/chromium/base/hash_unittest.cc @@ -0,0 +1,82 @@ +// 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/hash.h" + +#include <string> +#include <vector> + +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { + +TEST(HashTest, String) { + std::string str; + // Empty string (should hash to 0). + str = ""; + EXPECT_EQ(0u, Hash(str)); + + // Simple test. + str = "hello world"; + EXPECT_EQ(2794219650u, Hash(str)); + + // Change one bit. + str = "helmo world"; + EXPECT_EQ(1006697176u, Hash(str)); + + // Insert a null byte. + str = "hello world"; + str[5] = '\0'; + EXPECT_EQ(2319902537u, Hash(str)); + + // Test that the bytes after the null contribute to the hash. + str = "hello worle"; + str[5] = '\0'; + EXPECT_EQ(553904462u, Hash(str)); + + // Extremely long string. + // Also tests strings with high bit set, and null byte. + std::vector<char> long_string_buffer; + for (int i = 0; i < 4096; ++i) + long_string_buffer.push_back((i % 256) - 128); + str.assign(&long_string_buffer.front(), long_string_buffer.size()); + EXPECT_EQ(2797962408u, Hash(str)); + + // All possible lengths (mod 4). Tests separate code paths. Also test with + // final byte high bit set (regression test for http://crbug.com/90659). + // Note that the 1 and 3 cases have a weird bug where the final byte is + // treated as a signed char. It was decided on the above bug discussion to + // enshrine that behaviour as "correct" to avoid invalidating existing hashes. + + // Length mod 4 == 0. + str = "hello w\xab"; + EXPECT_EQ(615571198u, Hash(str)); + // Length mod 4 == 1. + str = "hello wo\xab"; + EXPECT_EQ(623474296u, Hash(str)); + // Length mod 4 == 2. + str = "hello wor\xab"; + EXPECT_EQ(4278562408u, Hash(str)); + // Length mod 4 == 3. + str = "hello worl\xab"; + EXPECT_EQ(3224633008u, Hash(str)); +} + +TEST(HashTest, CString) { + const char* str; + // Empty string (should hash to 0). + str = ""; + EXPECT_EQ(0u, Hash(str, strlen(str))); + + // Simple test. + str = "hello world"; + EXPECT_EQ(2794219650u, Hash(str, strlen(str))); + + // Ensure that it stops reading after the given length, and does not expect a + // null byte. + str = "hello world; don't read this part"; + EXPECT_EQ(2794219650u, Hash(str, strlen("hello world"))); +} + +} // namespace base diff --git a/chromium/base/i18n/break_iterator.cc b/chromium/base/i18n/break_iterator.cc index 2c4d4669732..fe26a03a0b8 100644 --- a/chromium/base/i18n/break_iterator.cc +++ b/chromium/base/i18n/break_iterator.cc @@ -22,6 +22,15 @@ BreakIterator::BreakIterator(const string16& str, BreakType break_type) pos_(0) { } +BreakIterator::BreakIterator(const string16& str, const string16& rules) + : iter_(NULL), + string_(str), + rules_(rules), + break_type_(RULE_BASED), + prev_(npos), + pos_(0) { +} + BreakIterator::~BreakIterator() { if (iter_) ubrk_close(static_cast<UBreakIterator*>(iter_)); @@ -29,6 +38,7 @@ BreakIterator::~BreakIterator() { bool BreakIterator::Init() { UErrorCode status = U_ZERO_ERROR; + UParseError parse_error; UBreakIteratorType break_type; switch (break_type_) { case BREAK_CHARACTER: @@ -39,19 +49,39 @@ bool BreakIterator::Init() { break; case BREAK_LINE: case BREAK_NEWLINE: + case RULE_BASED: // (Keep compiler happy, break_type not used in this case) break_type = UBRK_LINE; break; default: NOTREACHED() << "invalid break_type_"; return false; } - iter_ = ubrk_open(break_type, NULL, - string_.data(), static_cast<int32_t>(string_.size()), - &status); + if (break_type_ == RULE_BASED) { + iter_ = ubrk_openRules(rules_.c_str(), + static_cast<int32_t>(rules_.length()), + string_.data(), + static_cast<int32_t>(string_.size()), + &parse_error, + &status); + if (U_FAILURE(status)) { + NOTREACHED() << "ubrk_openRules failed to parse rule string at line " + << parse_error.line << ", offset " << parse_error.offset; + } + } else { + iter_ = ubrk_open(break_type, + NULL, + string_.data(), + static_cast<int32_t>(string_.size()), + &status); + if (U_FAILURE(status)) { + NOTREACHED() << "ubrk_open failed"; + } + } + if (U_FAILURE(status)) { - NOTREACHED() << "ubrk_open failed"; return false; } + // Move the iterator to the beginning of the string. ubrk_first(static_cast<UBreakIterator*>(iter_)); return true; @@ -65,6 +95,7 @@ bool BreakIterator::Advance() { case BREAK_CHARACTER: case BREAK_WORD: case BREAK_LINE: + case RULE_BASED: pos = ubrk_next(static_cast<UBreakIterator*>(iter_)); if (pos == UBRK_DONE) { pos_ = npos; @@ -91,14 +122,29 @@ bool BreakIterator::Advance() { } } +bool BreakIterator::SetText(const base::char16* text, const size_t length) { + UErrorCode status = U_ZERO_ERROR; + ubrk_setText(static_cast<UBreakIterator*>(iter_), + text, length, &status); + pos_ = 0; // implicit when ubrk_setText is done + prev_ = npos; + if (U_FAILURE(status)) { + NOTREACHED() << "ubrk_setText failed"; + return false; + } + return true; +} + bool BreakIterator::IsWord() const { int32_t status = ubrk_getRuleStatus(static_cast<UBreakIterator*>(iter_)); - return (break_type_ == BREAK_WORD && status != UBRK_WORD_NONE); + if (break_type_ != BREAK_WORD && break_type_ != RULE_BASED) + return false; + return status != UBRK_WORD_NONE; } bool BreakIterator::IsEndOfWord(size_t position) const { - if (break_type_ != BREAK_WORD) - return false; + if (break_type_ != BREAK_WORD && break_type_ != RULE_BASED) + return false; UBreakIterator* iter = static_cast<UBreakIterator*>(iter_); UBool boundary = ubrk_isBoundary(iter, static_cast<int32_t>(position)); @@ -107,8 +153,8 @@ bool BreakIterator::IsEndOfWord(size_t position) const { } bool BreakIterator::IsStartOfWord(size_t position) const { - if (break_type_ != BREAK_WORD) - return false; + if (break_type_ != BREAK_WORD && break_type_ != RULE_BASED) + return false; UBreakIterator* iter = static_cast<UBreakIterator*>(iter_); UBool boundary = ubrk_isBoundary(iter, static_cast<int32_t>(position)); diff --git a/chromium/base/i18n/break_iterator.h b/chromium/base/i18n/break_iterator.h index 618a320924e..b34c6770d10 100644 --- a/chromium/base/i18n/break_iterator.h +++ b/chromium/base/i18n/break_iterator.h @@ -66,10 +66,17 @@ class BASE_I18N_EXPORT BreakIterator { BREAK_SPACE = BREAK_LINE, BREAK_NEWLINE, BREAK_CHARACTER, + // But don't remove this one! + RULE_BASED, }; // Requires |str| to live as long as the BreakIterator does. BreakIterator(const string16& str, BreakType break_type); + // Make a rule-based iterator. BreakType == RULE_BASED is implied. + // TODO(andrewhayden): This signature could easily be misinterpreted as + // "(const string16& str, const string16& locale)". We should do something + // better. + BreakIterator(const string16& str, const string16& rules); ~BreakIterator(); // Init() must be called before any of the iterators are valid. @@ -82,6 +89,11 @@ class BASE_I18N_EXPORT BreakIterator { // last time Advance() returns true.) bool Advance(); + // Updates the text used by the iterator, resetting the iterator as if + // if Init() had been called again. Any old state is lost. Returns true + // unless there is an error setting the text. + bool SetText(const base::char16* text, const size_t length); + // Under BREAK_WORD mode, returns true if the break we just hit is the // end of a word. (Otherwise, the break iterator just skipped over e.g. // whitespace or punctuation.) Under BREAK_LINE and BREAK_NEWLINE modes, @@ -113,10 +125,13 @@ class BASE_I18N_EXPORT BreakIterator { // callers from needing access to the ICU public headers directory. void* iter_; - // The string we're iterating over. + // The string we're iterating over. Can be changed with SetText(...) const string16& string_; - // The breaking style (word/space/newline). + // Rules for our iterator. Mutually exclusive with break_type_. + const string16 rules_; + + // The breaking style (word/space/newline). Mutually exclusive with rules_ BreakType break_type_; // Previous and current iterator positions. diff --git a/chromium/base/i18n/build_utf8_validator_tables.cc b/chromium/base/i18n/build_utf8_validator_tables.cc new file mode 100644 index 00000000000..d37a75172a7 --- /dev/null +++ b/chromium/base/i18n/build_utf8_validator_tables.cc @@ -0,0 +1,466 @@ +// 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. + +// Create a state machine for validating UTF-8. The algorithm in brief: +// 1. Convert the complete unicode range of code points, except for the +// surrogate code points, to an ordered array of sequences of bytes in +// UTF-8. +// 2. Convert individual bytes to ranges, starting from the right of each byte +// sequence. For each range, ensure the bytes on the left and the ranges +// on the right are the identical. +// 3. Convert the resulting list of ranges into a state machine, collapsing +// identical states. +// 4. Convert the state machine to an array of bytes. +// 5. Output as a C++ file. +// +// To use: +// $ ninja -C out/Release build_utf8_validator_tables +// $ out/Release/build_utf8_validator_tables +// --output=base/i18n/utf8_validator_tables.cc +// $ git add base/i18n/utf8_validator_tables.cc +// +// Because the table is not expected to ever change, it is checked into the +// repository rather than being regenerated at build time. +// +// This code uses type uint8 throughout to represent bytes, to avoid +// signed/unsigned char confusion. + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <algorithm> +#include <map> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/command_line.h" +#include "base/file_util.h" +#include "base/files/file_path.h" +#include "base/logging.h" +#include "base/numerics/safe_conversions.h" +#include "base/strings/stringprintf.h" +#include "third_party/icu/source/common/unicode/utf8.h" + +namespace { + +const char kHelpText[] = + "Usage: build_utf8_validator_tables [ --help ] [ --output=<file> ]\n"; + +const char kProlog[] = + "// Copyright 2013 The Chromium Authors. All rights reserved.\n" + "// Use of this source code is governed by a BSD-style license that can " + "be\n" + "// found in the LICENSE file.\n" + "\n" + "// This file is auto-generated by build_utf8_validator_tables.\n" + "// DO NOT EDIT.\n" + "\n" + "#include \"base/i18n/utf8_validator_tables.h\"\n" + "\n" + "namespace base {\n" + "namespace internal {\n" + "\n" + "const uint8 kUtf8ValidatorTables[] = {\n"; + +const char kEpilog[] = + "};\n" + "\n" + "const size_t kUtf8ValidatorTablesSize = arraysize(kUtf8ValidatorTables);\n" + "\n" + "} // namespace internal\n" + "} // namespace base\n"; + +// Ranges are inclusive at both ends--they represent [from, to] +class Range { + public: + // Ranges always start with just one byte. + explicit Range(uint8 value) : from_(value), to_(value) {} + + // Range objects are copyable and assignable to be used in STL + // containers. Since they only contain non-pointer POD types, the default copy + // constructor, assignment operator and destructor will work. + + // Add a byte to the range. We intentionally only support adding a byte at the + // end, since that is the only operation the code needs. + void AddByte(uint8 to) { + CHECK(to == to_ + 1); + to_ = to; + } + + uint8 from() const { return from_; } + uint8 to() const { return to_; } + + bool operator<(const Range& rhs) const { + return (from() < rhs.from() || (from() == rhs.from() && to() < rhs.to())); + } + + bool operator==(const Range& rhs) const { + return from() == rhs.from() && to() == rhs.to(); + } + + private: + uint8 from_; + uint8 to_; +}; + +// A vector of Ranges is like a simple regular expression--it corresponds to +// a set of strings of the same length that have bytes in each position in +// the appropriate range. +typedef std::vector<Range> StringSet; + +// A UTF-8 "character" is represented by a sequence of bytes. +typedef std::vector<uint8> Character; + +// In the second stage of the algorithm, we want to convert a large list of +// Characters into a small list of StringSets. +struct Pair { + Character character; + StringSet set; +}; + +typedef std::vector<Pair> PairVector; + +// A class to print a table of numbers in the same style as clang-format. +class TablePrinter { + public: + explicit TablePrinter(FILE* stream) + : stream_(stream), values_on_this_line_(0), current_offset_(0) {} + + void PrintValue(uint8 value) { + if (values_on_this_line_ == 0) { + fputs(" ", stream_); + } else if (values_on_this_line_ == kMaxValuesPerLine) { + fprintf(stream_, " // 0x%02x\n ", current_offset_); + values_on_this_line_ = 0; + } + fprintf(stream_, " 0x%02x,", static_cast<int>(value)); + ++values_on_this_line_; + ++current_offset_; + } + + void NewLine() { + while (values_on_this_line_ < kMaxValuesPerLine) { + fputs(" ", stream_); + ++values_on_this_line_; + } + fprintf(stream_, " // 0x%02x\n", current_offset_); + values_on_this_line_ = 0; + } + + private: + // stdio stream. Not owned. + FILE* stream_; + + // Number of values so far printed on this line. + int values_on_this_line_; + + // Total values printed so far. + int current_offset_; + + static const int kMaxValuesPerLine = 8; + + DISALLOW_COPY_AND_ASSIGN(TablePrinter); +}; + +// Start by filling a PairVector with characters. The resulting vector goes from +// "\x00" to "\xf4\x8f\xbf\xbf". +PairVector InitializeCharacters() { + PairVector vector; + for (int i = 0; i <= 0x10FFFF; ++i) { + if (i >= 0xD800 && i < 0xE000) { + // Surrogate codepoints are not permitted. Non-character code points are + // explicitly permitted. + continue; + } + uint8 bytes[4]; + unsigned int offset = 0; + UBool is_error = false; + U8_APPEND(bytes, offset, arraysize(bytes), i, is_error); + DCHECK(!is_error); + DCHECK_GT(offset, 0u); + DCHECK_LE(offset, arraysize(bytes)); + Pair pair = {Character(bytes, bytes + offset), StringSet()}; + vector.push_back(pair); + } + return vector; +} + +// Construct a new Pair from |character| and the concatenation of |new_range| +// and |existing_set|, and append it to |pairs|. +void ConstructPairAndAppend(const Character& character, + const Range& new_range, + const StringSet& existing_set, + PairVector* pairs) { + Pair new_pair = {character, StringSet(1, new_range)}; + new_pair.set.insert( + new_pair.set.end(), existing_set.begin(), existing_set.end()); + pairs->push_back(new_pair); +} + +// Each pass over the PairVector strips one byte off the right-hand-side of the +// characters and adds a range to the set on the right. For example, the first +// pass converts the range from "\xe0\xa0\x80" to "\xe0\xa0\xbf" to ("\xe0\xa0", +// [\x80-\xbf]), then the second pass converts the range from ("\xe0\xa0", +// [\x80-\xbf]) to ("\xe0\xbf", [\x80-\xbf]) to ("\xe0", +// [\xa0-\xbf][\x80-\xbf]). +void MoveRightMostCharToSet(PairVector* pairs) { + PairVector new_pairs; + PairVector::const_iterator it = pairs->begin(); + while (it != pairs->end() && it->character.empty()) { + new_pairs.push_back(*it); + ++it; + } + CHECK(it != pairs->end()); + Character unconverted_bytes(it->character.begin(), it->character.end() - 1); + Range new_range(it->character.back()); + StringSet converted = it->set; + ++it; + while (it != pairs->end()) { + const Pair& current_pair = *it++; + if (current_pair.character.size() == unconverted_bytes.size() + 1 && + std::equal(unconverted_bytes.begin(), + unconverted_bytes.end(), + current_pair.character.begin()) && + converted == current_pair.set) { + // The particular set of UTF-8 codepoints we are validating guarantees + // that each byte range will be contiguous. This would not necessarily be + // true for an arbitrary set of UTF-8 codepoints. + DCHECK_EQ(new_range.to() + 1, current_pair.character.back()); + new_range.AddByte(current_pair.character.back()); + continue; + } + ConstructPairAndAppend(unconverted_bytes, new_range, converted, &new_pairs); + unconverted_bytes = Character(current_pair.character.begin(), + current_pair.character.end() - 1); + new_range = Range(current_pair.character.back()); + converted = current_pair.set; + } + ConstructPairAndAppend(unconverted_bytes, new_range, converted, &new_pairs); + new_pairs.swap(*pairs); +} + +void MoveAllCharsToSets(PairVector* pairs) { + // Since each pass of the function moves one character, and UTF-8 sequences + // are at most 4 characters long, this simply runs the algorithm four times. + for (int i = 0; i < 4; ++i) { + MoveRightMostCharToSet(pairs); + } +#if DCHECK_IS_ON + for (PairVector::const_iterator it = pairs->begin(); it != pairs->end(); + ++it) { + DCHECK(it->character.empty()); + } +#endif +} + +// Logs the generated string sets in regular-expression style, ie. [\x00-\x7f], +// [\xc2-\xdf][\x80-\xbf], etc. This can be a useful sanity-check that the +// algorithm is working. Use the command-line option +// --vmodule=build_utf8_validator_tables=1 to see this output. +void LogStringSets(const PairVector& pairs) { + for (PairVector::const_iterator pair_it = pairs.begin(); + pair_it != pairs.end(); + ++pair_it) { + std::string set_as_string; + for (StringSet::const_iterator set_it = pair_it->set.begin(); + set_it != pair_it->set.end(); + ++set_it) { + set_as_string += base::StringPrintf("[\\x%02x-\\x%02x]", + static_cast<int>(set_it->from()), + static_cast<int>(set_it->to())); + } + VLOG(1) << set_as_string; + } +} + +// A single state in the state machine is represented by a sorted vector of +// start bytes and target states. All input bytes in the range between the start +// byte and the next entry in the vector (or 0xFF) result in a transition to the +// target state. +struct StateRange { + uint8 from; + uint8 target_state; +}; + +typedef std::vector<StateRange> State; + +// Generates a state where all bytes go to state 1 (invalid). This is also used +// as an initialiser for other states (since bytes from outside the desired +// range are invalid). +State GenerateInvalidState() { + const StateRange range = {0, 1}; + return State(1, range); +} + +// A map from a state (ie. a set of strings which will match from this state) to +// a number (which is an index into the array of states). +typedef std::map<StringSet, uint8> StateMap; + +// Create a new state corresponding to |set|, add it |states| and |state_map| +// and return the index it was given in |states|. +uint8 MakeState(const StringSet& set, + std::vector<State>* states, + StateMap* state_map) { + DCHECK(!set.empty()); + const Range& range = set.front(); + const StringSet rest(set.begin() + 1, set.end()); + const StateMap::const_iterator where = state_map->find(rest); + const uint8 target_state = where == state_map->end() + ? MakeState(rest, states, state_map) + : where->second; + DCHECK_LT(0, range.from()); + DCHECK_LT(range.to(), 0xFF); + const StateRange new_state_initializer[] = { + {0, 1}, {range.from(), target_state}, + {static_cast<uint8>(range.to() + 1), 1}}; + states->push_back( + State(new_state_initializer, + new_state_initializer + arraysize(new_state_initializer))); + const uint8 new_state_number = + base::checked_cast<uint8>(states->size() - 1); + CHECK(state_map->insert(std::make_pair(set, new_state_number)).second); + return new_state_number; +} + +std::vector<State> GenerateStates(const PairVector& pairs) { + // States 0 and 1 are the initial/valid state and invalid state, respectively. + std::vector<State> states(2, GenerateInvalidState()); + StateMap state_map; + state_map.insert(std::make_pair(StringSet(), 0)); + for (PairVector::const_iterator it = pairs.begin(); it != pairs.end(); ++it) { + DCHECK(it->character.empty()); + DCHECK(!it->set.empty()); + const Range& range = it->set.front(); + const StringSet rest(it->set.begin() + 1, it->set.end()); + const StateMap::const_iterator where = state_map.find(rest); + const uint8 target_state = where == state_map.end() + ? MakeState(rest, &states, &state_map) + : where->second; + if (states[0].back().from == range.from()) { + DCHECK_EQ(1, states[0].back().target_state); + states[0].back().target_state = target_state; + DCHECK_LT(range.to(), 0xFF); + const StateRange new_range = {static_cast<uint8>(range.to() + 1), 1}; + states[0].push_back(new_range); + } else { + DCHECK_LT(range.to(), 0xFF); + const StateRange new_range_initializer[] = {{range.from(), target_state}, + {static_cast<uint8>(range.to() + 1), 1}}; + states[0] + .insert(states[0].end(), + new_range_initializer, + new_range_initializer + arraysize(new_range_initializer)); + } + } + return states; +} + +// Output the generated states as a C++ table. Two tricks are used to compact +// the table: each state in the table starts with a shift value which indicates +// how many bits we can discard from the right-hand-side of the byte before +// doing the table lookup. Secondly, only the state-transitions for bytes +// with the top-bit set are included in the table; bytes without the top-bit set +// are just ASCII and are handled directly by the code. +void PrintStates(const std::vector<State>& states, FILE* stream) { + // First calculate the start-offset of each state. This allows the state + // machine to jump directly to the correct offset, avoiding an extra + // indirection. State 0 starts at offset 0. + std::vector<uint8> state_offset(1, 0); + std::vector<uint8> shifts; + uint8 pos = 0; + + for (std::vector<State>::const_iterator state_it = states.begin(); + state_it != states.end(); + ++state_it) { + // We want to set |shift| to the (0-based) index of the least-significant + // set bit in any of the ranges for this state, since this tells us how many + // bits we can discard and still determine what range a byte lies in. Sadly + // it appears that ffs() is not portable, so we do it clumsily. + uint8 shift = 7; + for (State::const_iterator range_it = state_it->begin(); + range_it != state_it->end(); + ++range_it) { + while (shift > 0 && range_it->from % (1 << shift) != 0) { + --shift; + } + } + shifts.push_back(shift); + pos += 1 + (1 << (7 - shift)); + state_offset.push_back(pos); + } + + DCHECK_EQ(129, state_offset[1]); + + fputs(kProlog, stream); + TablePrinter table_printer(stream); + + for (uint8 state_index = 0; state_index < states.size(); ++state_index) { + const uint8 shift = shifts[state_index]; + uint8 next_range = 0; + uint8 target_state = 1; + fprintf(stream, + " // State %d, offset 0x%02x\n", + static_cast<int>(state_index), + static_cast<int>(state_offset[state_index])); + table_printer.PrintValue(shift); + for (int i = 0; i < 0x100; i += (1 << shift)) { + if (next_range < states[state_index].size() && + states[state_index][next_range].from == i) { + target_state = states[state_index][next_range].target_state; + ++next_range; + } + if (i >= 0x80) { + table_printer.PrintValue(state_offset[target_state]); + } + } + table_printer.NewLine(); + } + + fputs(kEpilog, stream); +} + +} // namespace + +int main(int argc, char* argv[]) { + CommandLine::Init(argc, argv); + logging::LoggingSettings settings; + settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; + logging::InitLogging(settings); + if (CommandLine::ForCurrentProcess()->HasSwitch("help")) { + fwrite(kHelpText, 1, arraysize(kHelpText), stdout); + exit(EXIT_SUCCESS); + } + base::FilePath filename = + CommandLine::ForCurrentProcess()->GetSwitchValuePath("output"); + + FILE* output = stdout; + if (!filename.empty()) { + output = base::OpenFile(filename, "wb"); + if (!output) + PLOG(FATAL) << "Couldn't open '" << filename.AsUTF8Unsafe() + << "' for writing"; + } + + // Step 1: Enumerate the characters + PairVector pairs = InitializeCharacters(); + // Step 2: Convert to sets. + MoveAllCharsToSets(&pairs); + if (VLOG_IS_ON(1)) { + LogStringSets(pairs); + } + // Step 3: Generate states. + std::vector<State> states = GenerateStates(pairs); + // Step 4/5: Print output + PrintStates(states, output); + + if (!filename.empty()) { + if (!base::CloseFile(output)) + PLOG(FATAL) << "Couldn't finish writing '" << filename.AsUTF8Unsafe() + << "'"; + } + + return EXIT_SUCCESS; +} diff --git a/chromium/base/i18n/file_util_icu.cc b/chromium/base/i18n/file_util_icu.cc index 9b0525086d0..e250c29a5f7 100644 --- a/chromium/base/i18n/file_util_icu.cc +++ b/chromium/base/i18n/file_util_icu.cc @@ -97,7 +97,7 @@ void ReplaceIllegalCharactersInPath(base::FilePath::StringType* file_name, DCHECK(!(IllegalCharacters::GetInstance()->contains(replace_char))); // Remove leading and trailing whitespace. - TrimWhitespace(*file_name, TRIM_ALL, file_name); + base::TrimWhitespace(*file_name, base::TRIM_ALL, file_name); IllegalCharacters* illegal = IllegalCharacters::GetInstance(); int cursor = 0; // The ICU macros expect an int. @@ -145,14 +145,16 @@ bool LocaleAwareCompareFilenames(const base::FilePath& a, #if defined(OS_WIN) return base::i18n::CompareString16WithCollator(collator.get(), - WideToUTF16(a.value()), WideToUTF16(b.value())) == UCOL_LESS; + base::WideToUTF16(a.value()), base::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 base::i18n::CompareString16WithCollator(collator.get(), - WideToUTF16(base::SysNativeMBToWide(a.value().c_str())), - WideToUTF16(base::SysNativeMBToWide(b.value().c_str()))) == UCOL_LESS; + return base::i18n::CompareString16WithCollator( + collator.get(), + base::WideToUTF16(base::SysNativeMBToWide(a.value().c_str())), + base::WideToUTF16(base::SysNativeMBToWide(b.value().c_str())) + ) == UCOL_LESS; #else #error Not implemented on your system #endif diff --git a/chromium/base/i18n/file_util_icu_unittest.cc b/chromium/base/i18n/file_util_icu_unittest.cc index e3af9adf946..dd8122632e8 100644 --- a/chromium/base/i18n/file_util_icu_unittest.cc +++ b/chromium/base/i18n/file_util_icu_unittest.cc @@ -73,9 +73,9 @@ TEST_F(FileUtilICUTest, ReplaceIllegalCharactersInPathTest) { file_util::ReplaceIllegalCharactersInPath(&bad_name, '-'); EXPECT_EQ(kIllegalCharacterCases[i].good_name, bad_name); #elif defined(OS_MACOSX) - std::string bad_name(WideToUTF8(kIllegalCharacterCases[i].bad_name)); + std::string bad_name(base::WideToUTF8(kIllegalCharacterCases[i].bad_name)); file_util::ReplaceIllegalCharactersInPath(&bad_name, '-'); - EXPECT_EQ(WideToUTF8(kIllegalCharacterCases[i].good_name), bad_name); + EXPECT_EQ(base::WideToUTF8(kIllegalCharacterCases[i].good_name), bad_name); #endif } } diff --git a/chromium/base/i18n/icu_util.cc b/chromium/base/i18n/icu_util.cc index e5c698475e4..e0bd62cc50b 100644 --- a/chromium/base/i18n/icu_util.cc +++ b/chromium/base/i18n/icu_util.cc @@ -4,8 +4,6 @@ #include "base/i18n/icu_util.h" -#include "build/build_config.h" - #if defined(OS_WIN) #include <windows.h> #endif @@ -30,7 +28,10 @@ #define ICU_UTIL_DATA_STATIC 2 #if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE -#define ICU_UTIL_DATA_FILE_NAME "icudt" U_ICU_VERSION_SHORT "l.dat" +// 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 #define ICU_UTIL_DATA_SYMBOL "icudt" U_ICU_VERSION_SHORT "_dat" #if defined(OS_WIN) @@ -41,14 +42,48 @@ 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. +bool g_called_once = false; +bool g_check_called_once = true; +#endif +} + + +#if defined(OS_ANDROID) +bool InitializeICUWithFileDescriptor(int data_fd) { +#if !defined(NDEBUG) + DCHECK(!g_check_called_once || !g_called_once); + g_called_once = true; +#endif + +#if (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_STATIC) + // The ICU data is statically linked. + return true; +#elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE) + CR_DEFINE_STATIC_LOCAL(base::MemoryMappedFile, mapped_file, ()); + if (!mapped_file.IsValid()) { + if (!mapped_file.Initialize(base::File(data_fd))) { + LOG(ERROR) << "Couldn't mmap icu data file"; + return false; + } + } + UErrorCode err = U_ZERO_ERROR; + udata_setCommonData(const_cast<uint8*>(mapped_file.data()), &err); + return err == U_ZERO_ERROR; +#endif // ICU_UTIL_DATA_FILE +} +#endif + + bool InitializeICU() { -#ifndef 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. - static bool called_once = false; - DCHECK(!called_once); - called_once = true; +#if !defined(NDEBUG) + DCHECK(!g_check_called_once || !g_called_once); + g_called_once = true; #endif #if (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_SHARED) @@ -59,13 +94,13 @@ bool InitializeICU() { HMODULE module = LoadLibrary(data_path.value().c_str()); if (!module) { - DLOG(ERROR) << "Failed to load " << ICU_UTIL_DATA_SHARED_MODULE_NAME; + LOG(ERROR) << "Failed to load " << ICU_UTIL_DATA_SHARED_MODULE_NAME; return false; } FARPROC addr = GetProcAddress(module, ICU_UTIL_DATA_SYMBOL); if (!addr) { - DLOG(ERROR) << ICU_UTIL_DATA_SYMBOL << ": not found in " + LOG(ERROR) << ICU_UTIL_DATA_SYMBOL << ": not found in " << ICU_UTIL_DATA_SHARED_MODULE_NAME; return false; } @@ -86,25 +121,32 @@ bool InitializeICU() { // be released. CR_DEFINE_STATIC_LOCAL(base::MemoryMappedFile, mapped_file, ()); if (!mapped_file.IsValid()) { - // Assume it is in the framework bundle's Resources directory. #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); +#elif defined(OS_ANDROID) + bool path_ok = PathService::Get(base::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. - FilePath data_path; bool path_ok = PathService::Get(base::DIR_EXE, &data_path); +#endif DCHECK(path_ok); data_path = data_path.AppendASCII(ICU_UTIL_DATA_FILE_NAME); #else + // Assume it is in the framework bundle's Resources directory. FilePath data_path = base::mac::PathForFrameworkBundleResource(CFSTR(ICU_UTIL_DATA_FILE_NAME)); if (data_path.empty()) { - DLOG(ERROR) << ICU_UTIL_DATA_FILE_NAME << " not found in bundle"; + LOG(ERROR) << ICU_UTIL_DATA_FILE_NAME << " not found in bundle"; return false; } #endif // OS check if (!mapped_file.Initialize(data_path)) { - DLOG(ERROR) << "Couldn't mmap " << data_path.value(); + LOG(ERROR) << "Couldn't mmap " << data_path.AsUTF8Unsafe(); return false; } } @@ -114,5 +156,11 @@ bool InitializeICU() { #endif } +void AllowMultipleInitializeCallsForTesting() { +#if !defined(NDEBUG) + g_check_called_once = false; +#endif +} + } // namespace i18n } // namespace base diff --git a/chromium/base/i18n/icu_util.h b/chromium/base/i18n/icu_util.h index ef5dede235d..b0a5dbcce6d 100644 --- a/chromium/base/i18n/icu_util.h +++ b/chromium/base/i18n/icu_util.h @@ -5,6 +5,7 @@ #ifndef BASE_I18N_ICU_UTIL_H_ #define BASE_I18N_ICU_UTIL_H_ +#include "build/build_config.h" #include "base/i18n/base_i18n_export.h" namespace base { @@ -14,6 +15,15 @@ namespace i18n { // 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); +#endif + +// In a test binary, the call above might occur twice. +BASE_I18N_EXPORT void AllowMultipleInitializeCallsForTesting(); + } // namespace i18n } // namespace base diff --git a/chromium/base/i18n/rtl.cc b/chromium/base/i18n/rtl.cc index d9818e800e2..d878c40da02 100644 --- a/chromium/base/i18n/rtl.cc +++ b/chromium/base/i18n/rtl.cc @@ -14,10 +14,6 @@ #include "third_party/icu/source/common/unicode/uscript.h" #include "third_party/icu/source/i18n/unicode/coll.h" -#if defined(TOOLKIT_GTK) -#include <gtk/gtk.h> -#endif - namespace { // Extract language, country and variant, but ignore keywords. For example, @@ -123,12 +119,7 @@ void SetICUDefaultLocale(const std::string& locale_string) { } bool IsRTL() { -#if defined(TOOLKIT_GTK) - GtkTextDirection gtk_dir = gtk_widget_get_default_direction(); - return gtk_dir == GTK_TEXT_DIR_RTL; -#else return ICUIsRTL(); -#endif } bool ICUIsRTL() { @@ -163,6 +154,21 @@ TextDirection GetFirstStrongCharacterDirection(const string16& text) { return LEFT_TO_RIGHT; } +TextDirection GetLastStrongCharacterDirection(const string16& text) { + const UChar* string = text.c_str(); + size_t position = text.length(); + while (position > 0) { + UChar32 character; + size_t prev_position = position; + U16_PREV(string, 0, prev_position, character); + TextDirection direction = GetCharacterDirection(character); + if (direction != UNKNOWN_DIRECTION) + return direction; + position = prev_position; + } + return LEFT_TO_RIGHT; +} + TextDirection GetStringDirection(const string16& text) { const UChar* string = text.c_str(); size_t length = text.length(); @@ -248,15 +254,18 @@ bool AdjustStringForLocaleDirection(string16* text) { bool has_rtl_chars = StringContainsStrongRTLChars(*text); if (!ui_direction_is_rtl && has_rtl_chars) { WrapStringWithRTLFormatting(text); - text->insert(0U, 1U, kLeftToRightMark); + text->insert(static_cast<size_t>(0), static_cast<size_t>(1), + kLeftToRightMark); text->push_back(kLeftToRightMark); } else if (ui_direction_is_rtl && has_rtl_chars) { WrapStringWithRTLFormatting(text); - text->insert(0U, 1U, kRightToLeftMark); + text->insert(static_cast<size_t>(0), static_cast<size_t>(1), + kRightToLeftMark); text->push_back(kRightToLeftMark); } else if (ui_direction_is_rtl) { WrapStringWithLTRFormatting(text); - text->insert(0U, 1U, kRightToLeftMark); + text->insert(static_cast<size_t>(0), static_cast<size_t>(1), + kRightToLeftMark); text->push_back(kRightToLeftMark); } else { return false; @@ -317,7 +326,8 @@ void WrapStringWithLTRFormatting(string16* text) { return; // Inserting an LRE (Left-To-Right Embedding) mark as the first character. - text->insert(0U, 1U, kLeftToRightEmbeddingMark); + text->insert(static_cast<size_t>(0), static_cast<size_t>(1), + kLeftToRightEmbeddingMark); // Inserting a PDF (Pop Directional Formatting) mark as the last character. text->push_back(kPopDirectionalFormatting); @@ -328,7 +338,8 @@ void WrapStringWithRTLFormatting(string16* text) { return; // Inserting an RLE (Right-To-Left Embedding) mark as the first character. - text->insert(0U, 1U, kRightToLeftEmbeddingMark); + text->insert(static_cast<size_t>(0), static_cast<size_t>(1), + kRightToLeftEmbeddingMark); // Inserting a PDF (Pop Directional Formatting) mark as the last character. text->push_back(kPopDirectionalFormatting); diff --git a/chromium/base/i18n/rtl.h b/chromium/base/i18n/rtl.h index c80d2f85777..aa5f6810a9b 100644 --- a/chromium/base/i18n/rtl.h +++ b/chromium/base/i18n/rtl.h @@ -64,7 +64,7 @@ BASE_I18N_EXPORT bool ICUIsRTL(); BASE_I18N_EXPORT TextDirection GetTextDirectionForLocale( const char* locale_name); -// Given the string in |text|, returns the directionality of the first +// Given the string in |text|, returns the directionality of the first or last // character with strong directionality in the string. If no character in the // text has strong directionality, LEFT_TO_RIGHT is returned. The Bidi // character types L, LRE, LRO, R, AL, RLE, and RLO are considered as strong @@ -72,6 +72,8 @@ BASE_I18N_EXPORT TextDirection GetTextDirectionForLocale( // for more information. BASE_I18N_EXPORT TextDirection GetFirstStrongCharacterDirection( const string16& text); +BASE_I18N_EXPORT TextDirection GetLastStrongCharacterDirection( + const string16& text); // Given the string in |text|, returns LEFT_TO_RIGHT or RIGHT_TO_LEFT if all the // strong directionality characters in the string are of the same diff --git a/chromium/base/i18n/rtl_unittest.cc b/chromium/base/i18n/rtl_unittest.cc index 58772b05839..2d923acf0ba 100644 --- a/chromium/base/i18n/rtl_unittest.cc +++ b/chromium/base/i18n/rtl_unittest.cc @@ -14,10 +14,6 @@ #include "testing/platform_test.h" #include "third_party/icu/source/i18n/unicode/usearch.h" -#if defined(TOOLKIT_GTK) -#include <gtk/gtk.h> -#endif - namespace base { namespace i18n { @@ -27,10 +23,6 @@ namespace { void SetRTL(bool rtl) { // Override the current locale/direction. SetICUDefaultLocale(rtl ? "he" : "en"); -#if defined(TOOLKIT_GTK) - // Do the same for GTK, which does not rely on the ICU default locale. - gtk_widget_set_default_direction(rtl ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR); -#endif EXPECT_EQ(rtl, IsRTL()); } @@ -46,6 +38,8 @@ TEST_F(RTLTest, GetFirstStrongCharacterDirection) { } cases[] = { // Test pure LTR string. { L"foo bar", LEFT_TO_RIGHT }, + // Test pure RTL string. + { L"\x05d0\x05d1\x05d2 \x05d3\x0d4\x05d5", RIGHT_TO_LEFT}, // Test bidi string in which the first character with strong directionality // is a character with type L. { L"foo \x05d0 bar", LEFT_TO_RIGHT }, @@ -107,6 +101,68 @@ TEST_F(RTLTest, GetFirstStrongCharacterDirection) { GetFirstStrongCharacterDirection(WideToUTF16(cases[i].text))); } + +// Note that the cases with LRE, LRO, RLE and RLO are invalid for +// GetLastStrongCharacterDirection because they should be followed by PDF +// character. +TEST_F(RTLTest, GetLastStrongCharacterDirection) { + struct { + const wchar_t* text; + TextDirection direction; + } cases[] = { + // Test pure LTR string. + { L"foo bar", LEFT_TO_RIGHT }, + // Test pure RTL string. + { L"\x05d0\x05d1\x05d2 \x05d3\x0d4\x05d5", RIGHT_TO_LEFT}, + // Test bidi string in which the last character with strong directionality + // is a character with type L. + { L"foo \x05d0 bar", LEFT_TO_RIGHT }, + // Test bidi string in which the last character with strong directionality + // is a character with type R. + { L"\x05d0 foo bar \x05d3", RIGHT_TO_LEFT }, + // Test bidi string which ends with a character with weak directionality + // and in which the last character with strong directionality is a + // character with type L. + { L"!foo \x05d0 bar!", LEFT_TO_RIGHT }, + // Test bidi string which ends with a character with weak directionality + // and in which the last character with strong directionality is a + // character with type R. + { L",\x05d0 foo bar \x05d1,", RIGHT_TO_LEFT }, + // Test bidi string in which the last character with strong directionality + // is a character with type AL. + { L"\x0622 foo \x05d0 bar \x0622", RIGHT_TO_LEFT }, + // Test a string without strong directionality characters. + { L",!.{}", LEFT_TO_RIGHT }, + // Test empty string. + { L"", LEFT_TO_RIGHT }, + // Test characters in non-BMP (e.g. Phoenician letters. Please refer to + // http://demo.icu-project.org/icu-bin/ubrowse?scr=151&b=10910 for more + // information). + { +#if defined(WCHAR_T_IS_UTF32) + L"abc 123" L" ! \x10910 !", +#elif defined(WCHAR_T_IS_UTF16) + L"abc 123" L" ! \xd802\xdd10 !", +#else +#error wchar_t should be either UTF-16 or UTF-32 +#endif + RIGHT_TO_LEFT }, + { +#if defined(WCHAR_T_IS_UTF32) + L"abc 123" L" ! \x10401 !", +#elif defined(WCHAR_T_IS_UTF16) + L"abc 123" L" ! \xd801\xdc01 !", +#else +#error wchar_t should be either UTF-16 or UTF-32 +#endif + LEFT_TO_RIGHT }, + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) + EXPECT_EQ(cases[i].direction, + GetLastStrongCharacterDirection(WideToUTF16(cases[i].text))); +} + TEST_F(RTLTest, GetStringDirection) { struct { const wchar_t* text; diff --git a/chromium/base/i18n/streaming_utf8_validator.cc b/chromium/base/i18n/streaming_utf8_validator.cc new file mode 100644 index 00000000000..c809985e168 --- /dev/null +++ b/chromium/base/i18n/streaming_utf8_validator.cc @@ -0,0 +1,59 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This implementation doesn't use ICU. The ICU macros are oriented towards +// character-at-a-time processing, whereas byte-at-a-time processing is easier +// with streaming input. + +#include "base/i18n/streaming_utf8_validator.h" + +#include "base/i18n/utf8_validator_tables.h" +#include "base/logging.h" + +namespace base { +namespace { + +uint8 StateTableLookup(uint8 offset) { + DCHECK_LT(offset, internal::kUtf8ValidatorTablesSize); + return internal::kUtf8ValidatorTables[offset]; +} + +} // namespace + +StreamingUtf8Validator::State StreamingUtf8Validator::AddBytes(const char* data, + size_t size) { + // Copy |state_| into a local variable so that the compiler doesn't have to be + // careful of aliasing. + uint8 state = state_; + for (const char* p = data; p != data + size; ++p) { + if ((*p & 0x80) == 0) { + if (state == 0) + continue; + state = internal::I18N_UTF8_VALIDATOR_INVALID_INDEX; + break; + } + const uint8 shift_amount = StateTableLookup(state); + const uint8 shifted_char = (*p & 0x7F) >> shift_amount; + state = StateTableLookup(state + shifted_char + 1); + // State may be INVALID here, but this code is optimised for the case of + // valid UTF-8 and it is more efficient (by about 2%) to not attempt an + // early loop exit unless we hit an ASCII character. + } + state_ = state; + return state == 0 ? VALID_ENDPOINT + : state == internal::I18N_UTF8_VALIDATOR_INVALID_INDEX + ? INVALID + : VALID_MIDPOINT; +} + +void StreamingUtf8Validator::Reset() { + state_ = 0u; +} + +bool StreamingUtf8Validator::Validate(const std::string& string) { + return StreamingUtf8Validator().AddBytes(string.data(), string.size()) == + VALID_ENDPOINT; +} + +} // namespace base diff --git a/chromium/base/i18n/streaming_utf8_validator.h b/chromium/base/i18n/streaming_utf8_validator.h new file mode 100644 index 00000000000..f10ac721c54 --- /dev/null +++ b/chromium/base/i18n/streaming_utf8_validator.h @@ -0,0 +1,63 @@ +// 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. + +// A streaming validator for UTF-8. Validation is based on the definition in +// RFC-3629. In particular, it does not reject the invalid characters rejected +// by base::IsStringUTF8(). +// +// The implementation detects errors on the first possible byte. + +#ifndef BASE_I18N_STREAMING_UTF8_VALIDATOR_H_ +#define BASE_I18N_STREAMING_UTF8_VALIDATOR_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/i18n/base_i18n_export.h" + +namespace base { + +class BASE_I18N_EXPORT StreamingUtf8Validator { + public: + // The validator exposes 3 states. It starts in state VALID_ENDPOINT. As it + // processes characters it alternates between VALID_ENDPOINT and + // VALID_MIDPOINT. If it encounters an invalid byte or UTF-8 sequence the + // state changes permanently to INVALID. + enum State { + VALID_ENDPOINT, + VALID_MIDPOINT, + INVALID + }; + + StreamingUtf8Validator() : state_(0u) {} + // Trivial destructor intentionally omitted. + + // Validate |size| bytes starting at |data|. If the concatenation of all calls + // to AddBytes() since this object was constructed or reset is a valid UTF-8 + // string, returns VALID_ENDPOINT. If it could be the prefix of a valid UTF-8 + // string, returns VALID_MIDPOINT. If an invalid byte or UTF-8 sequence was + // present, returns INVALID. + State AddBytes(const char* data, size_t size); + + // Return the object to a freshly-constructed state so that it can be re-used. + void Reset(); + + // Validate a complete string using the same criteria. Returns true if the + // string only contains complete, valid UTF-8 codepoints. + static bool Validate(const std::string& string); + + private: + // The current state of the validator. Value 0 is the initial/valid state. + // The state is stored as an offset into |kUtf8ValidatorTables|. The special + // state |kUtf8InvalidState| is invalid. + uint8 state_; + + // This type could be made copyable but there is currently no use-case for + // it. + DISALLOW_COPY_AND_ASSIGN(StreamingUtf8Validator); +}; + +} // namespace base + +#endif // BASE_I18N_STREAMING_UTF8_VALIDATOR_H_ diff --git a/chromium/base/i18n/streaming_utf8_validator_perftest.cc b/chromium/base/i18n/streaming_utf8_validator_perftest.cc new file mode 100644 index 00000000000..ac2eb0820ba --- /dev/null +++ b/chromium/base/i18n/streaming_utf8_validator_perftest.cc @@ -0,0 +1,234 @@ +// 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. + +// All data that is passed through a WebSocket with type "Text" needs to be +// validated as UTF8. Since this is done on the IO thread, it needs to be +// reasonably fast. + +// We are only interested in the performance on valid UTF8. Invalid UTF8 will +// result in a connection failure, so is unlikely to become a source of +// performance issues. + +#include "base/i18n/streaming_utf8_validator.h" + +#include <string> + +#include "base/basictypes.h" +#include "base/bind.h" +#include "base/callback.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "base/test/perf_time_logger.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { +namespace { + +// We want to test ranges of valid UTF-8 sequences. These ranges are inclusive. +// They are intended to be large enough that the validator needs to do +// meaningful work while being in some sense "realistic" (eg. control characters +// are not included). +const char kOneByteSeqRangeStart[] = " "; // U+0020 +const char kOneByteSeqRangeEnd[] = "~"; // U+007E + +const char kTwoByteSeqRangeStart[] = "\xc2\xa0"; // U+00A0 non-breaking space +const char kTwoByteSeqRangeEnd[] = "\xc9\x8f"; // U+024F small y with stroke + +const char kThreeByteSeqRangeStart[] = "\xe3\x81\x82"; // U+3042 Hiragana "a" +const char kThreeByteSeqRangeEnd[] = "\xe9\xbf\x83"; // U+9FC3 "to blink" + +const char kFourByteSeqRangeStart[] = "\xf0\xa0\x80\x8b"; // U+2000B +const char kFourByteSeqRangeEnd[] = "\xf0\xaa\x9a\xb2"; // U+2A6B2 + +// The different lengths of strings to test. +const size_t kTestLengths[] = {1, 32, 256, 32768, 1 << 20}; + +// Simplest possible byte-at-a-time validator, to provide a baseline +// for comparison. This is only tried on 1-byte UTF-8 sequences, as +// the results will not be meaningful with sequences containing +// top-bit-set bytes. +bool IsString7Bit(const std::string& s) { + for (std::string::const_iterator it = s.begin(); it != s.end(); ++it) { + if (*it & 0x80) + return false; + } + return true; +} + +// Assumes that |previous| is a valid UTF-8 sequence, and attempts to return +// the next one. Is just barely smart enough to iterate through the ranges +// defined about. +std::string NextUtf8Sequence(const std::string& previous) { + DCHECK(StreamingUtf8Validator::Validate(previous)); + std::string next = previous; + for (int i = static_cast<int>(previous.length() - 1); i >= 0; --i) { + // All bytes in a UTF-8 sequence except the first one are + // constrained to the range 0x80 to 0xbf, inclusive. When we + // increment past 0xbf, we carry into the previous byte. + if (i > 0 && next[i] == '\xbf') { + next[i] = '\x80'; + continue; // carry + } + ++next[i]; + break; // no carry + } + DCHECK(StreamingUtf8Validator::Validate(next)) + << "Result \"" << next << "\" failed validation"; + return next; +} + +typedef bool (*TestTargetType)(const std::string&); + +// Run fuction |target| over |test_string| |times| times, and report the results +// using |description|. +bool RunTest(const std::string& description, + TestTargetType target, + const std::string& test_string, + int times) { + base::PerfTimeLogger timer(description.c_str()); + bool result = true; + for (int i = 0; i < times; ++i) { + result = target(test_string) && result; + } + timer.Done(); + return result; +} + +// Construct a string by repeating |input| enough times to equal or exceed +// |length|. +std::string ConstructRepeatedTestString(const std::string& input, + size_t length) { + std::string output = input; + while (output.length() * 2 < length) { + output += output; + } + if (output.length() < length) { + output += ConstructRepeatedTestString(input, length - output.length()); + } + return output; +} + +// Construct a string by expanding the range of UTF-8 sequences +// between |input_start| and |input_end|, inclusive, and then +// repeating the resulting string until it equals or exceeds |length| +// bytes. |input_start| and |input_end| must be valid UTF-8 +// sequences. +std::string ConstructRangedTestString(const std::string& input_start, + const std::string& input_end, + size_t length) { + std::string output = input_start; + std::string input = input_start; + while (output.length() < length && input != input_end) { + input = NextUtf8Sequence(input); + output += input; + } + if (output.length() < length) { + output = ConstructRepeatedTestString(output, length); + } + return output; +} + +struct TestFunctionDescription { + TestTargetType function; + const char* function_name; +}; + +// IsString7Bit is intentionally placed last so it can be excluded easily. +const TestFunctionDescription kTestFunctions[] = { + {&StreamingUtf8Validator::Validate, "StreamingUtf8Validator"}, + {&IsStringUTF8, "IsStringUTF8"}, {&IsString7Bit, "IsString7Bit"}}; + +// Construct a test string from |construct_test_string| for each of the lengths +// in |kTestLengths| in turn. For each string, run each test in |test_functions| +// for a number of iterations such that the total number of bytes validated +// is around 16MB. +void RunSomeTests( + const char format[], + base::Callback<std::string(size_t length)> construct_test_string, + const TestFunctionDescription* test_functions, + size_t test_count) { + for (size_t i = 0; i < arraysize(kTestLengths); ++i) { + const size_t length = kTestLengths[i]; + const std::string test_string = construct_test_string.Run(length); + const int real_length = static_cast<int>(test_string.length()); + const int times = (1 << 24) / real_length; + for (size_t test_index = 0; test_index < test_count; ++test_index) { + EXPECT_TRUE(RunTest(StringPrintf(format, + test_functions[test_index].function_name, + real_length, + times), + test_functions[test_index].function, + test_string, + times)); + } + } +} + +TEST(StreamingUtf8ValidatorPerfTest, OneByteRepeated) { + RunSomeTests("%s: bytes=1 repeated length=%d repeat=%d", + base::Bind(ConstructRepeatedTestString, kOneByteSeqRangeStart), + kTestFunctions, + 3); +} + +TEST(StreamingUtf8ValidatorPerfTest, OneByteRange) { + RunSomeTests("%s: bytes=1 ranged length=%d repeat=%d", + base::Bind(ConstructRangedTestString, + kOneByteSeqRangeStart, + kOneByteSeqRangeEnd), + kTestFunctions, + 3); +} + +TEST(StreamingUtf8ValidatorPerfTest, TwoByteRepeated) { + RunSomeTests("%s: bytes=2 repeated length=%d repeat=%d", + base::Bind(ConstructRepeatedTestString, kTwoByteSeqRangeStart), + kTestFunctions, + 2); +} + +TEST(StreamingUtf8ValidatorPerfTest, TwoByteRange) { + RunSomeTests("%s: bytes=2 ranged length=%d repeat=%d", + base::Bind(ConstructRangedTestString, + kTwoByteSeqRangeStart, + kTwoByteSeqRangeEnd), + kTestFunctions, + 2); +} + +TEST(StreamingUtf8ValidatorPerfTest, ThreeByteRepeated) { + RunSomeTests( + "%s: bytes=3 repeated length=%d repeat=%d", + base::Bind(ConstructRepeatedTestString, kThreeByteSeqRangeStart), + kTestFunctions, + 2); +} + +TEST(StreamingUtf8ValidatorPerfTest, ThreeByteRange) { + RunSomeTests("%s: bytes=3 ranged length=%d repeat=%d", + base::Bind(ConstructRangedTestString, + kThreeByteSeqRangeStart, + kThreeByteSeqRangeEnd), + kTestFunctions, + 2); +} + +TEST(StreamingUtf8ValidatorPerfTest, FourByteRepeated) { + RunSomeTests("%s: bytes=4 repeated length=%d repeat=%d", + base::Bind(ConstructRepeatedTestString, kFourByteSeqRangeStart), + kTestFunctions, + 2); +} + +TEST(StreamingUtf8ValidatorPerfTest, FourByteRange) { + RunSomeTests("%s: bytes=4 ranged length=%d repeat=%d", + base::Bind(ConstructRangedTestString, + kFourByteSeqRangeStart, + kFourByteSeqRangeEnd), + kTestFunctions, + 2); +} + +} // namespace +} // namespace base diff --git a/chromium/base/i18n/streaming_utf8_validator_unittest.cc b/chromium/base/i18n/streaming_utf8_validator_unittest.cc new file mode 100644 index 00000000000..20ea564c032 --- /dev/null +++ b/chromium/base/i18n/streaming_utf8_validator_unittest.cc @@ -0,0 +1,412 @@ +// 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/i18n/streaming_utf8_validator.h" + +#include <stdio.h> +#include <string.h> + +#include <string> + +#include "base/strings/string_piece.h" +#include "testing/gtest/include/gtest/gtest.h" + +// Define BASE_I18N_UTF8_VALIDATOR_THOROUGH_TEST to verify that this class +// accepts exactly the same set of 4-byte strings as ICU-based validation. This +// tests every possible 4-byte string, so it is too slow to run routinely on +// low-powered machines. +// +// #define BASE_I18N_UTF8_VALIDATOR_THOROUGH_TEST + +#ifdef BASE_I18N_UTF8_VALIDATOR_THOROUGH_TEST + +#include "base/basictypes.h" +#include "base/bind.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversion_utils.h" +#include "base/synchronization/condition_variable.h" +#include "base/synchronization/lock.h" +#include "base/threading/sequenced_worker_pool.h" +#include "third_party/icu/source/common/unicode/utf8.h" + +#endif // BASE_I18N_UTF8_VALIDATOR_THOROUGH_TEST + +namespace base { +namespace { + +// Avoid having to qualify the enum values in the tests. +const StreamingUtf8Validator::State VALID_ENDPOINT = + StreamingUtf8Validator::VALID_ENDPOINT; +const StreamingUtf8Validator::State VALID_MIDPOINT = + StreamingUtf8Validator::VALID_MIDPOINT; +const StreamingUtf8Validator::State INVALID = StreamingUtf8Validator::INVALID; + +#ifdef BASE_I18N_UTF8_VALIDATOR_THOROUGH_TEST + +const uint32 kThoroughTestChunkSize = 1 << 24; + +class StreamingUtf8ValidatorThoroughTest : public ::testing::Test { + protected: + StreamingUtf8ValidatorThoroughTest() + : all_done_(&lock_), tasks_dispatched_(0), tasks_finished_(0) {} + + // This uses the same logic as base::IsStringUTF8 except it considers + // non-characters valid (and doesn't require a string as input). + static bool IsStringUtf8(const char* src, int32 src_len) { + int32 char_index = 0; + + while (char_index < src_len) { + int32 code_point; + U8_NEXT(src, char_index, src_len, code_point); + if (!base::IsValidCodepoint(code_point)) + return false; + } + return true; + } + + // Converts the passed-in integer to a 4 byte string and then + // verifies that IsStringUtf8 and StreamingUtf8Validator agree on + // whether it is valid UTF-8 or not. + void TestNumber(uint32 n) const { + char test[sizeof n]; + memcpy(test, &n, sizeof n); + StreamingUtf8Validator validator; + EXPECT_EQ(IsStringUtf8(test, sizeof n), + validator.AddBytes(test, sizeof n) == VALID_ENDPOINT) + << "Difference of opinion for \"" + << base::StringPrintf("\\x%02X\\x%02X\\x%02X\\x%02X", + test[0] & 0xFF, + test[1] & 0xFF, + test[2] & 0xFF, + test[3] & 0xFF) << "\""; + } + + public: + // Tests the 4-byte sequences corresponding to the |size| integers + // starting at |begin|. This is intended to be run from a worker + // pool. Signals |all_done_| at the end if it thinks all tasks are + // finished. + void TestRange(uint32 begin, uint32 size) { + for (uint32 i = 0; i < size; ++i) { + TestNumber(begin + i); + } + base::AutoLock al(lock_); + ++tasks_finished_; + LOG(INFO) << tasks_finished_ << " / " << tasks_dispatched_ + << " tasks done\n"; + if (tasks_finished_ >= tasks_dispatched_) { + all_done_.Signal(); + } + } + + protected: + base::Lock lock_; + base::ConditionVariable all_done_; + int tasks_dispatched_; + int tasks_finished_; +}; + +TEST_F(StreamingUtf8ValidatorThoroughTest, TestEverything) { + scoped_refptr<base::SequencedWorkerPool> pool = + new base::SequencedWorkerPool(32, "TestEverything"); + base::AutoLock al(lock_); + uint32 begin = 0; + do { + pool->PostWorkerTask( + FROM_HERE, + base::Bind(&StreamingUtf8ValidatorThoroughTest::TestRange, + base::Unretained(this), + begin, + kThoroughTestChunkSize)); + ++tasks_dispatched_; + begin += kThoroughTestChunkSize; + } while (begin != 0); + while (tasks_finished_ < tasks_dispatched_) + all_done_.Wait(); +} + +#endif // BASE_I18N_UTF8_VALIDATOR_THOROUGH_TEST + +// These valid and invalid UTF-8 sequences are based on the tests from +// base/strings/string_util_unittest.cc + +// All of the strings in |valid| must represent a single codepoint, because +// partial sequences are constructed by taking non-empty prefixes of these +// strings. +const char* const valid[] = {"\r", "\n", "a", + "\xc2\x81", "\xe1\x80\xbf", "\xf1\x80\xa0\xbf", + "\xef\xbb\xbf", // UTF-8 BOM +}; + +const char* const* const valid_end = valid + arraysize(valid); + +const char* const invalid[] = { + // always invalid bytes + "\xc0", "\xc1", + "\xf5", "\xf6", "\xf7", + "\xf8", "\xf9", "\xfa", "\xfb", "\xfc", "\xfd", "\xfe", "\xff", + // surrogate code points + "\xed\xa0\x80", "\xed\x0a\x8f", "\xed\xbf\xbf", + // + // overlong sequences + "\xc0\x80" // U+0000 + "\xc1\x80", // "A" + "\xc1\x81", // "B" + "\xe0\x80\x80", // U+0000 + "\xe0\x82\x80", // U+0080 + "\xe0\x9f\xbf", // U+07ff + "\xf0\x80\x80\x8D", // U+000D + "\xf0\x80\x82\x91", // U+0091 + "\xf0\x80\xa0\x80", // U+0800 + "\xf0\x8f\xbb\xbf", // U+FEFF (BOM) + "\xf8\x80\x80\x80\xbf", // U+003F + "\xfc\x80\x80\x80\xa0\xa5", + // + // Beyond U+10FFFF + "\xf4\x90\x80\x80", // U+110000 + "\xf8\xa0\xbf\x80\xbf", // 5 bytes + "\xfc\x9c\xbf\x80\xbf\x80", // 6 bytes + // + // BOMs in UTF-16(BE|LE) + "\xfe\xff", "\xff\xfe", +}; + +const char* const* const invalid_end = invalid + arraysize(invalid); + +// A ForwardIterator which returns all the non-empty prefixes of the elements of +// "valid". +class PartialIterator { + public: + // The constructor returns the first iterator, ie. it is equivalent to + // begin(). + PartialIterator() : index_(0), prefix_length_(0) { Advance(); } + // The trivial destructor left intentionally undefined. + // This is a value type; the default copy constructor and assignment operator + // generated by the compiler are used. + + static PartialIterator end() { return PartialIterator(arraysize(valid), 1); } + + PartialIterator& operator++() { + Advance(); + return *this; + } + + base::StringPiece operator*() const { + return base::StringPiece(valid[index_], prefix_length_); + } + + bool operator==(const PartialIterator& rhs) const { + return index_ == rhs.index_ && prefix_length_ == rhs.prefix_length_; + } + + bool operator!=(const PartialIterator& rhs) const { return !(rhs == *this); } + + private: + // This constructor is used by the end() method. + PartialIterator(size_t index, size_t prefix_length) + : index_(index), prefix_length_(prefix_length) {} + + void Advance() { + if (index_ < arraysize(valid) && prefix_length_ < strlen(valid[index_])) + ++prefix_length_; + while (index_ < arraysize(valid) && + prefix_length_ == strlen(valid[index_])) { + ++index_; + prefix_length_ = 1; + } + } + + // The UTF-8 sequence, as an offset into the |valid| array. + size_t index_; + size_t prefix_length_; +}; + +// A test fixture for tests which test one UTF-8 sequence (or invalid +// byte sequence) at a time. +class StreamingUtf8ValidatorSingleSequenceTest : public ::testing::Test { + protected: + // Iterator must be convertible when de-referenced to StringPiece. + template <typename Iterator> + void CheckRange(Iterator begin, + Iterator end, + StreamingUtf8Validator::State expected) { + for (Iterator it = begin; it != end; ++it) { + StreamingUtf8Validator validator; + base::StringPiece sequence = *it; + EXPECT_EQ(expected, + validator.AddBytes(sequence.data(), sequence.size())) + << "Failed for \"" << sequence << "\""; + } + } + + // Adding input a byte at a time should make absolutely no difference. + template <typename Iterator> + void CheckRangeByteAtATime(Iterator begin, + Iterator end, + StreamingUtf8Validator::State expected) { + for (Iterator it = begin; it != end; ++it) { + StreamingUtf8Validator validator; + base::StringPiece sequence = *it; + StreamingUtf8Validator::State state = VALID_ENDPOINT; + for (base::StringPiece::const_iterator cit = sequence.begin(); + cit != sequence.end(); + ++cit) { + state = validator.AddBytes(&*cit, 1); + } + EXPECT_EQ(expected, state) << "Failed for \"" << sequence << "\""; + } + } +}; + +// A test fixture for tests which test the concatenation of byte sequences. +class StreamingUtf8ValidatorDoubleSequenceTest : public ::testing::Test { + protected: + // Check every possible concatenation of byte sequences from two + // ranges, and verify that the combination matches the expected + // state. + template <typename Iterator1, typename Iterator2> + void CheckCombinations(Iterator1 begin1, + Iterator1 end1, + Iterator2 begin2, + Iterator2 end2, + StreamingUtf8Validator::State expected) { + StreamingUtf8Validator validator; + for (Iterator1 it1 = begin1; it1 != end1; ++it1) { + base::StringPiece c1 = *it1; + for (Iterator2 it2 = begin2; it2 != end2; ++it2) { + base::StringPiece c2 = *it2; + validator.AddBytes(c1.data(), c1.size()); + EXPECT_EQ(expected, validator.AddBytes(c2.data(), c2.size())) + << "Failed for \"" << c1 << c2 << "\""; + validator.Reset(); + } + } + } +}; + +TEST(StreamingUtf8ValidatorTest, NothingIsValid) { + static const char kNothing[] = ""; + EXPECT_EQ(VALID_ENDPOINT, StreamingUtf8Validator().AddBytes(kNothing, 0)); +} + +// Because the members of the |valid| array need to be non-zero length +// sequences and are measured with strlen(), |valid| cannot be used it +// to test the NUL character '\0', so the NUL character gets its own +// test. +TEST(StreamingUtf8ValidatorTest, NulIsValid) { + static const char kNul[] = "\x00"; + EXPECT_EQ(VALID_ENDPOINT, StreamingUtf8Validator().AddBytes(kNul, 1)); +} + +// Just a basic sanity test before we start getting fancy. +TEST(StreamingUtf8ValidatorTest, HelloWorld) { + static const char kHelloWorld[] = "Hello, World!"; + EXPECT_EQ( + VALID_ENDPOINT, + StreamingUtf8Validator().AddBytes(kHelloWorld, strlen(kHelloWorld))); +} + +// Check that the Reset() method works. +TEST(StreamingUtf8ValidatorTest, ResetWorks) { + StreamingUtf8Validator validator; + EXPECT_EQ(INVALID, validator.AddBytes("\xC0", 1)); + EXPECT_EQ(INVALID, validator.AddBytes("a", 1)); + validator.Reset(); + EXPECT_EQ(VALID_ENDPOINT, validator.AddBytes("a", 1)); +} + +TEST_F(StreamingUtf8ValidatorSingleSequenceTest, Valid) { + CheckRange(valid, valid_end, VALID_ENDPOINT); +} + +TEST_F(StreamingUtf8ValidatorSingleSequenceTest, Partial) { + CheckRange(PartialIterator(), PartialIterator::end(), VALID_MIDPOINT); +} + +TEST_F(StreamingUtf8ValidatorSingleSequenceTest, Invalid) { + CheckRange(invalid, invalid_end, INVALID); +} + +TEST_F(StreamingUtf8ValidatorSingleSequenceTest, ValidByByte) { + CheckRangeByteAtATime(valid, valid_end, VALID_ENDPOINT); +} + +TEST_F(StreamingUtf8ValidatorSingleSequenceTest, PartialByByte) { + CheckRangeByteAtATime( + PartialIterator(), PartialIterator::end(), VALID_MIDPOINT); +} + +TEST_F(StreamingUtf8ValidatorSingleSequenceTest, InvalidByByte) { + CheckRangeByteAtATime(invalid, invalid_end, INVALID); +} + +TEST_F(StreamingUtf8ValidatorDoubleSequenceTest, ValidPlusValidIsValid) { + CheckCombinations(valid, valid_end, valid, valid_end, VALID_ENDPOINT); +} + +TEST_F(StreamingUtf8ValidatorDoubleSequenceTest, ValidPlusPartialIsPartial) { + CheckCombinations(valid, + valid_end, + PartialIterator(), + PartialIterator::end(), + VALID_MIDPOINT); +} + +TEST_F(StreamingUtf8ValidatorDoubleSequenceTest, PartialPlusValidIsInvalid) { + CheckCombinations( + PartialIterator(), PartialIterator::end(), valid, valid_end, INVALID); +} + +TEST_F(StreamingUtf8ValidatorDoubleSequenceTest, PartialPlusPartialIsInvalid) { + CheckCombinations(PartialIterator(), + PartialIterator::end(), + PartialIterator(), + PartialIterator::end(), + INVALID); +} + +TEST_F(StreamingUtf8ValidatorDoubleSequenceTest, ValidPlusInvalidIsInvalid) { + CheckCombinations(valid, valid_end, invalid, invalid_end, INVALID); +} + +TEST_F(StreamingUtf8ValidatorDoubleSequenceTest, InvalidPlusValidIsInvalid) { + CheckCombinations(invalid, invalid_end, valid, valid_end, INVALID); +} + +TEST_F(StreamingUtf8ValidatorDoubleSequenceTest, InvalidPlusInvalidIsInvalid) { + CheckCombinations(invalid, invalid_end, invalid, invalid_end, INVALID); +} + +TEST_F(StreamingUtf8ValidatorDoubleSequenceTest, InvalidPlusPartialIsInvalid) { + CheckCombinations( + invalid, invalid_end, PartialIterator(), PartialIterator::end(), INVALID); +} + +TEST_F(StreamingUtf8ValidatorDoubleSequenceTest, PartialPlusInvalidIsInvalid) { + CheckCombinations( + PartialIterator(), PartialIterator::end(), invalid, invalid_end, INVALID); +} + +TEST(StreamingUtf8ValidatorValidateTest, EmptyIsValid) { + EXPECT_TRUE(StreamingUtf8Validator::Validate(std::string())); +} + +TEST(StreamingUtf8ValidatorValidateTest, SimpleValidCase) { + EXPECT_TRUE(StreamingUtf8Validator::Validate("\xc2\x81")); +} + +TEST(StreamingUtf8ValidatorValidateTest, SimpleInvalidCase) { + EXPECT_FALSE(StreamingUtf8Validator::Validate("\xc0\x80")); +} + +TEST(StreamingUtf8ValidatorValidateTest, TruncatedIsInvalid) { + EXPECT_FALSE(StreamingUtf8Validator::Validate("\xc2")); +} + +} // namespace +} // namespace base diff --git a/chromium/base/i18n/utf8_validator_tables.cc b/chromium/base/i18n/utf8_validator_tables.cc new file mode 100644 index 00000000000..8dfa10caf79 --- /dev/null +++ b/chromium/base/i18n/utf8_validator_tables.cc @@ -0,0 +1,55 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file is auto-generated by build_utf8_validator_tables. +// DO NOT EDIT. + +#include "base/i18n/utf8_validator_tables.h" + +namespace base { +namespace internal { + +const uint8 kUtf8ValidatorTables[] = { + // State 0, offset 0x00 + 0x00, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, // 0x08 + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, // 0x10 + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, // 0x18 + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, // 0x20 + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, // 0x28 + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, // 0x30 + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, // 0x38 + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, // 0x40 + 0x81, 0x81, 0x81, 0x83, 0x83, 0x83, 0x83, 0x83, // 0x48 + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, // 0x50 + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, // 0x58 + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, // 0x60 + 0x83, 0x86, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, // 0x68 + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8e, 0x8b, // 0x70 + 0x8b, 0x93, 0x9c, 0x9c, 0x9c, 0x9f, 0x81, 0x81, // 0x78 + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, // 0x80 + 0x81, // 0x81 + // State 1, offset 0x81 + 0x07, 0x81, // 0x83 + // State 2, offset 0x83 + 0x06, 0x00, 0x81, // 0x86 + // State 3, offset 0x86 + 0x05, 0x81, 0x83, 0x81, 0x81, // 0x8b + // State 4, offset 0x8b + 0x06, 0x83, 0x81, // 0x8e + // State 5, offset 0x8e + 0x05, 0x83, 0x81, 0x81, 0x81, // 0x93 + // State 6, offset 0x93 + 0x04, 0x81, 0x8b, 0x8b, 0x8b, 0x81, 0x81, 0x81, // 0x9b + 0x81, // 0x9c + // State 7, offset 0x9c + 0x06, 0x8b, 0x81, // 0x9f + // State 8, offset 0x9f + 0x04, 0x8b, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, // 0xa7 + 0x81, // 0xa8 +}; + +const size_t kUtf8ValidatorTablesSize = arraysize(kUtf8ValidatorTables); + +} // namespace internal +} // namespace base diff --git a/chromium/base/i18n/utf8_validator_tables.h b/chromium/base/i18n/utf8_validator_tables.h new file mode 100644 index 00000000000..b7db56e43e4 --- /dev/null +++ b/chromium/base/i18n/utf8_validator_tables.h @@ -0,0 +1,29 @@ +// 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_I18N_UTF8_VALIDATOR_TABLES_H_ +#define BASE_I18N_UTF8_VALIDATOR_TABLES_H_ + +#include "base/basictypes.h" + +namespace base { +namespace internal { + +// The tables for all states; a list of entries of the form (right_shift, +// next_state, next_state, ....). The right_shifts are used to reduce the +// overall size of the table. The table only covers bytes in the range +// [0x80, 0xFF] to save space. +extern const uint8 kUtf8ValidatorTables[]; + +extern const size_t kUtf8ValidatorTablesSize; + +// The offset of the INVALID state in kUtf8ValidatorTables. +enum { + I18N_UTF8_VALIDATOR_INVALID_INDEX = 129 +}; + +} // namespace internal +} // namespace base + +#endif // BASE_I18N_UTF8_VALIDATOR_TABLES_H_ diff --git a/chromium/base/id_map.h b/chromium/base/id_map.h index 27098d29768..9cbc1f8978f 100644 --- a/chromium/base/id_map.h +++ b/chromium/base/id_map.h @@ -32,8 +32,10 @@ enum IDMapOwnershipSemantics { // ownership semantics are set to own because pointers will leak. template<typename T, IDMapOwnershipSemantics OS = IDMapExternalPointer> class IDMap : public base::NonThreadSafe { - private: + public: typedef int32 KeyType; + + private: typedef base::hash_map<KeyType, T*> HashTable; public: diff --git a/chromium/base/id_map_unittest.cc b/chromium/base/id_map_unittest.cc index 76d7c3ed4e9..c005a690578 100644 --- a/chromium/base/id_map_unittest.cc +++ b/chromium/base/id_map_unittest.cc @@ -94,31 +94,41 @@ TEST(IDMapTest, IteratorRemainsValidWhenRemovingOtherElements) { const int kCount = 5; TestObject obj[kCount]; - int32 ids[kCount]; for (int i = 0; i < kCount; i++) - ids[i] = map.Add(&obj[i]); + map.Add(&obj[i]); + // IDMap uses a hash_map, which has no predictable iteration order. + int32 ids_in_iteration_order[kCount]; + const TestObject* objs_in_iteration_order[kCount]; int counter = 0; for (IDMap<TestObject>::const_iterator iter(&map); !iter.IsAtEnd(); iter.Advance()) { + ids_in_iteration_order[counter] = iter.GetCurrentKey(); + objs_in_iteration_order[counter] = iter.GetCurrentValue(); + counter++; + } + + counter = 0; + for (IDMap<TestObject>::const_iterator iter(&map); + !iter.IsAtEnd(); iter.Advance()) { EXPECT_EQ(1, map.iteration_depth()); switch (counter) { case 0: - EXPECT_EQ(ids[0], iter.GetCurrentKey()); - EXPECT_EQ(&obj[0], iter.GetCurrentValue()); - map.Remove(ids[1]); + EXPECT_EQ(ids_in_iteration_order[0], iter.GetCurrentKey()); + EXPECT_EQ(objs_in_iteration_order[0], iter.GetCurrentValue()); + map.Remove(ids_in_iteration_order[1]); break; case 1: - EXPECT_EQ(ids[2], iter.GetCurrentKey()); - EXPECT_EQ(&obj[2], iter.GetCurrentValue()); - map.Remove(ids[3]); + EXPECT_EQ(ids_in_iteration_order[2], iter.GetCurrentKey()); + EXPECT_EQ(objs_in_iteration_order[2], iter.GetCurrentValue()); + map.Remove(ids_in_iteration_order[3]); break; case 2: - EXPECT_EQ(ids[4], iter.GetCurrentKey()); - EXPECT_EQ(&obj[4], iter.GetCurrentValue()); - map.Remove(ids[0]); + EXPECT_EQ(ids_in_iteration_order[4], iter.GetCurrentKey()); + EXPECT_EQ(objs_in_iteration_order[4], iter.GetCurrentValue()); + map.Remove(ids_in_iteration_order[0]); break; default: FAIL() << "should not have that many elements"; @@ -194,22 +204,32 @@ TEST(IDMapTest, IteratorRemainsValidWhenClearing) { const int kCount = 5; TestObject obj[kCount]; - int32 ids[kCount]; for (int i = 0; i < kCount; i++) - ids[i] = map.Add(&obj[i]); + map.Add(&obj[i]); + // IDMap uses a hash_map, which has no predictable iteration order. + int32 ids_in_iteration_order[kCount]; + const TestObject* objs_in_iteration_order[kCount]; int counter = 0; for (IDMap<TestObject>::const_iterator iter(&map); !iter.IsAtEnd(); iter.Advance()) { + ids_in_iteration_order[counter] = iter.GetCurrentKey(); + objs_in_iteration_order[counter] = iter.GetCurrentValue(); + counter++; + } + + counter = 0; + for (IDMap<TestObject>::const_iterator iter(&map); + !iter.IsAtEnd(); iter.Advance()) { switch (counter) { case 0: - EXPECT_EQ(ids[0], iter.GetCurrentKey()); - EXPECT_EQ(&obj[0], iter.GetCurrentValue()); + EXPECT_EQ(ids_in_iteration_order[0], iter.GetCurrentKey()); + EXPECT_EQ(objs_in_iteration_order[0], iter.GetCurrentValue()); break; case 1: - EXPECT_EQ(ids[1], iter.GetCurrentKey()); - EXPECT_EQ(&obj[1], iter.GetCurrentValue()); + EXPECT_EQ(ids_in_iteration_order[1], iter.GetCurrentKey()); + EXPECT_EQ(objs_in_iteration_order[1], iter.GetCurrentValue()); map.Clear(); EXPECT_TRUE(map.IsEmpty()); EXPECT_EQ(0U, map.size()); diff --git a/chromium/base/ios/device_util.h b/chromium/base/ios/device_util.h index a4fa4887d35..1cd9a043bc9 100644 --- a/chromium/base/ios/device_util.h +++ b/chromium/base/ios/device_util.h @@ -40,8 +40,21 @@ namespace device_util { // x86_64 -> Simulator std::string GetPlatform(); -// Returns true if the application is running on a high-ram device. (>=500M). -bool IsRunningOnHighRamDevice(); +// Returns true if the application is running on a device with 512MB or more +// RAM. +bool RamIsAtLeast512Mb(); + +// Returns true if the application is running on a device with 1024MB or more +// RAM. +bool RamIsAtLeast1024Mb(); + +// Returns true if the application is running on a device with |ram_in_mb| MB or +// more RAM. +// Use with caution! Actual RAM reported by devices is less than the commonly +// used powers-of-two values. For example, a 512MB device may report only 502MB +// RAM. The convenience methods above should be used in most cases because they +// correctly handle this issue. +bool RamIsAtLeast(uint64_t ram_in_mb); // Returns true if the device has only one core. bool IsSingleCoreDevice(); @@ -59,6 +72,12 @@ std::string GetRandomId(); // something that should be anonymous, you should probably pass NULL. std::string GetDeviceIdentifier(const char* salt); +// Returns a hashed version of |in_string| using |salt| (which must not be +// zero-length). Different salt values should result in differently hashed +// strings. +std::string GetSaltedString(const std::string& in_string, + const std::string& salt); + } // namespace device_util } // namespace ios diff --git a/chromium/base/ios/device_util.mm b/chromium/base/ios/device_util.mm index 0d516f9a2b4..ff7be36875c 100644 --- a/chromium/base/ios/device_util.mm +++ b/chromium/base/ios/device_util.mm @@ -44,13 +44,9 @@ NSString* GenerateClientId() { // http://openradar.appspot.com/12377282. If this is the case, revert to // generating a new one. if (!client_id || [client_id isEqualToString:kZeroUUID]) { - if (base::ios::IsRunningOnIOS6OrLater()) { - client_id = [[[UIDevice currentDevice] identifierForVendor] UUIDString]; - if ([client_id isEqualToString:kZeroUUID]) - client_id = base::SysUTF8ToNSString(ios::device_util::GetRandomId()); - } else { + client_id = [[[UIDevice currentDevice] identifierForVendor] UUIDString]; + if ([client_id isEqualToString:kZeroUUID]) client_id = base::SysUTF8ToNSString(ios::device_util::GetRandomId()); - } } return client_id; } @@ -68,12 +64,22 @@ std::string GetPlatform() { return platform; } -bool IsRunningOnHighRamDevice() { +bool RamIsAtLeast512Mb() { + // 512MB devices report anywhere from 502-504 MB, use 450 MB just to be safe. + return RamIsAtLeast(450); +} + +bool RamIsAtLeast1024Mb() { + // 1GB devices report anywhere from 975-999 MB, use 900 MB just to be safe. + return RamIsAtLeast(900); +} + +bool RamIsAtLeast(uint64_t ram_in_mb) { uint64_t memory_size = 0; size_t size = sizeof(memory_size); if (sysctlbyname("hw.memsize", &memory_size, &size, NULL, 0) == 0) { // Anything >= 500M, call high ram. - return memory_size >= 500 * 1024 * 1024; + return memory_size >= ram_in_mb * 1024 * 1024; } return false; } @@ -147,8 +153,15 @@ std::string GetDeviceIdentifier(const char* salt) { [defaults synchronize]; } - NSData* hash_data = [[NSString stringWithFormat:@"%@%s", client_id, - salt ? salt : kDefaultSalt] dataUsingEncoding:NSUTF8StringEncoding]; + return GetSaltedString(base::SysNSStringToUTF8(client_id), + salt ? salt : kDefaultSalt); +} + +std::string GetSaltedString(const std::string& in_string, + const std::string& salt) { + DCHECK(salt.length()); + NSData* hash_data = [base::SysUTF8ToNSString(in_string + salt) + dataUsingEncoding:NSUTF8StringEncoding]; unsigned char hash[CC_SHA256_DIGEST_LENGTH]; CC_SHA256([hash_data bytes], [hash_data length], hash); diff --git a/chromium/base/ios/device_util_unittest.mm b/chromium/base/ios/device_util_unittest.mm index c2aefe0f737..3494e00a73e 100644 --- a/chromium/base/ios/device_util_unittest.mm +++ b/chromium/base/ios/device_util_unittest.mm @@ -30,10 +30,6 @@ TEST_F(DeviceUtilTest, GetPlatform) { GTEST_ASSERT_GT(ios::device_util::GetPlatform().length(), 0U); } -TEST_F(DeviceUtilTest, IsRunningOnHighRamDevice) { - ios::device_util::IsRunningOnHighRamDevice(); -} - TEST_F(DeviceUtilTest, IsSingleCoreDevice) { ios::device_util::IsSingleCoreDevice(); } @@ -56,8 +52,7 @@ TEST_F(DeviceUtilTest, GetDeviceIdentifier) { CleanNSUserDefaultsForDeviceId(); std::string new_default_id = ios::device_util::GetDeviceIdentifier(NULL); - if (base::ios::IsRunningOnIOS6OrLater() && - ![[[[UIDevice currentDevice] identifierForVendor] UUIDString] + if (![[[[UIDevice currentDevice] identifierForVendor] UUIDString] isEqualToString:@"00000000-0000-0000-0000-000000000000"]) { EXPECT_EQ(default_id, new_default_id); } else { @@ -103,6 +98,33 @@ TEST_F(DeviceUtilTest, CheckMigrationFromZero) { CleanNSUserDefaultsForDeviceId(); } +TEST_F(DeviceUtilTest, GetSaltedStringEquals) { + std::string string1("The quick brown fox jumps over the lazy dog"); + std::string string2("The quick brown fox jumps over the lazy dog"); + std::string salt("salt"); + // Same string and same salt should result in the same salted string. + EXPECT_EQ(ios::device_util::GetSaltedString(string1, salt), + ios::device_util::GetSaltedString(string2, salt)); +} + +TEST_F(DeviceUtilTest, GetSaltedStringNotEquals) { + std::string string1("The quick brown fox jumps over the lazy dog"); + std::string string2("The lazy brown fox jumps over the quick dog"); + std::string salt("salt"); + // Different string and same salt should result in different salted strings. + EXPECT_NE(ios::device_util::GetSaltedString(string1, salt), + ios::device_util::GetSaltedString(string2, salt)); +} + +TEST_F(DeviceUtilTest, GetSaltedStringDifferentSalt) { + std::string string1("The quick brown fox jumps over the lazy dog"); + std::string salt1("salt"); + std::string salt2("pepper"); + // Same string with different salt should result in different salted strings. + EXPECT_NE(ios::device_util::GetSaltedString(string1, salt1), + ios::device_util::GetSaltedString(string1, salt2)); +} + TEST_F(DeviceUtilTest, CheckDeviceMigration) { CleanNSUserDefaultsForDeviceId(); diff --git a/chromium/base/ios/ios_util.h b/chromium/base/ios/ios_util.h index 7e2e621dfdb..f9ddb262592 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 6 or later. -BASE_EXPORT bool IsRunningOnIOS6OrLater(); - // Returns whether the operating system is iOS 7 or later. BASE_EXPORT bool IsRunningOnIOS7OrLater(); diff --git a/chromium/base/ios/ios_util.mm b/chromium/base/ios/ios_util.mm index a76911017f9..0f106de566d 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 IsRunningOnIOS6OrLater() { - return IsRunningOnOrLater(6, 0, 0); -} - bool IsRunningOnIOS7OrLater() { return IsRunningOnOrLater(7, 0, 0); } diff --git a/chromium/base/json/json_file_value_serializer.cc b/chromium/base/json/json_file_value_serializer.cc index 70d0c88f3cc..e33080ccd6b 100644 --- a/chromium/base/json/json_file_value_serializer.cc +++ b/chromium/base/json/json_file_value_serializer.cc @@ -36,9 +36,8 @@ bool JSONFileValueSerializer::SerializeInternal(const base::Value& root, return false; int data_size = static_cast<int>(json_string.size()); - if (file_util::WriteFile(json_file_path_, - json_string.data(), - data_size) != data_size) + if (base::WriteFile(json_file_path_, json_string.data(), data_size) != + data_size) return false; return true; diff --git a/chromium/base/json/json_string_value_serializer.cc b/chromium/base/json/json_string_value_serializer.cc index 7611fbeed65..59f030dff2f 100644 --- a/chromium/base/json/json_string_value_serializer.cc +++ b/chromium/base/json/json_string_value_serializer.cc @@ -32,8 +32,7 @@ bool JSONStringValueSerializer::SerializeInternal(const Value& root, if (pretty_print_) options |= base::JSONWriter::OPTIONS_PRETTY_PRINT; - base::JSONWriter::WriteWithOptions(&root, options, json_string_); - return true; + return base::JSONWriter::WriteWithOptions(&root, options, json_string_); } Value* JSONStringValueSerializer::Deserialize(int* error_code, diff --git a/chromium/base/json/json_value_serializer_unittest.cc b/chromium/base/json/json_value_serializer_unittest.cc index 44c0a57cd27..f8d3a20109a 100644 --- a/chromium/base/json/json_value_serializer_unittest.cc +++ b/chromium/base/json/json_value_serializer_unittest.cc @@ -118,7 +118,7 @@ TEST(JSONValueSerializerTest, ReadProperJSONFromFile) { // Write it down in the file. FilePath temp_file(tempdir.path().AppendASCII("test.json")); ASSERT_EQ(static_cast<int>(strlen(kProperJSON)), - file_util::WriteFile(temp_file, kProperJSON, strlen(kProperJSON))); + WriteFile(temp_file, kProperJSON, strlen(kProperJSON))); // Try to deserialize it through the serializer. JSONFileValueSerializer file_deserializer(temp_file); @@ -142,9 +142,8 @@ TEST(JSONValueSerializerTest, ReadJSONWithCommasFromFile) { // Write it down in the file. FilePath temp_file(tempdir.path().AppendASCII("test.json")); ASSERT_EQ(static_cast<int>(strlen(kProperJSONWithCommas)), - file_util::WriteFile(temp_file, - kProperJSONWithCommas, - strlen(kProperJSONWithCommas))); + WriteFile(temp_file, kProperJSONWithCommas, + strlen(kProperJSONWithCommas))); // Try to deserialize it through the serializer. JSONFileValueSerializer file_deserializer(temp_file); diff --git a/chromium/base/json/json_writer.cc b/chromium/base/json/json_writer.cc index d6006638219..d14c92c06e5 100644 --- a/chromium/base/json/json_writer.cc +++ b/chromium/base/json/json_writer.cc @@ -9,211 +9,199 @@ #include "base/json/string_escape.h" #include "base/logging.h" #include "base/strings/string_number_conversions.h" -#include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/values.h" namespace base { #if defined(OS_WIN) -static const char kPrettyPrintLineEnding[] = "\r\n"; +const char kPrettyPrintLineEnding[] = "\r\n"; #else -static const char kPrettyPrintLineEnding[] = "\n"; +const char kPrettyPrintLineEnding[] = "\n"; #endif // static -void JSONWriter::Write(const Value* const node, std::string* json) { - WriteWithOptions(node, 0, json); +bool JSONWriter::Write(const Value* const node, std::string* json) { + return WriteWithOptions(node, 0, json); } // static -void JSONWriter::WriteWithOptions(const Value* const node, int options, +bool JSONWriter::WriteWithOptions(const Value* const node, int options, std::string* json) { json->clear(); // Is there a better way to estimate the size of the output? json->reserve(1024); - bool omit_binary_values = !!(options & OPTIONS_OMIT_BINARY_VALUES); - bool omit_double_type_preservation = - !!(options & OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION); - bool pretty_print = !!(options & OPTIONS_PRETTY_PRINT); + JSONWriter writer(options, json); + bool result = writer.BuildJSONString(node, 0U); - JSONWriter writer(omit_binary_values, omit_double_type_preservation, - pretty_print, json); - writer.BuildJSONString(node, 0); - - if (pretty_print) + if (options & OPTIONS_PRETTY_PRINT) json->append(kPrettyPrintLineEnding); + + return result; } -JSONWriter::JSONWriter(bool omit_binary_values, - bool omit_double_type_preservation, bool pretty_print, - std::string* json) - : omit_binary_values_(omit_binary_values), - omit_double_type_preservation_(omit_double_type_preservation), - pretty_print_(pretty_print), +JSONWriter::JSONWriter(int options, std::string* json) + : omit_binary_values_((options & OPTIONS_OMIT_BINARY_VALUES) != 0), + omit_double_type_preservation_( + (options & OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION) != 0), + pretty_print_((options & OPTIONS_PRETTY_PRINT) != 0), json_string_(json) { DCHECK(json); } -void JSONWriter::BuildJSONString(const Value* const node, int depth) { +bool JSONWriter::BuildJSONString(const Value* const node, size_t depth) { switch (node->GetType()) { - case Value::TYPE_NULL: + case Value::TYPE_NULL: { json_string_->append("null"); - break; - - case Value::TYPE_BOOLEAN: - { - bool value; - bool result = node->GetAsBoolean(&value); - DCHECK(result); - json_string_->append(value ? "true" : "false"); - break; - } - - case Value::TYPE_INTEGER: - { - int value; - bool result = node->GetAsInteger(&value); - DCHECK(result); - base::StringAppendF(json_string_, "%d", value); - break; + return true; + } + + case Value::TYPE_BOOLEAN: { + bool value; + bool result = node->GetAsBoolean(&value); + DCHECK(result); + json_string_->append(value ? "true" : "false"); + return result; + } + + case Value::TYPE_INTEGER: { + int value; + bool result = node->GetAsInteger(&value); + DCHECK(result); + json_string_->append(IntToString(value)); + return result; + } + + case Value::TYPE_DOUBLE: { + double value; + bool result = node->GetAsDouble(&value); + DCHECK(result); + if (omit_double_type_preservation_ && + value <= kint64max && + value >= kint64min && + std::floor(value) == value) { + json_string_->append(Int64ToString(static_cast<int64>(value))); + return result; } - - case Value::TYPE_DOUBLE: - { - double value; - bool result = node->GetAsDouble(&value); - DCHECK(result); - if (omit_double_type_preservation_ && - value <= kint64max && - value >= kint64min && - std::floor(value) == value) { - json_string_->append(Int64ToString(static_cast<int64>(value))); - break; - } - std::string real = DoubleToString(value); - // 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 - // real rather than an int. - if (real.find('.') == std::string::npos && - real.find('e') == std::string::npos && - real.find('E') == std::string::npos) { - real.append(".0"); - } - // The JSON spec requires that non-integer values in the range (-1,1) - // have a zero before the decimal point - ".52" is not valid, "0.52" is. - if (real[0] == '.') { - real.insert(0, "0"); - } else if (real.length() > 1 && real[0] == '-' && real[1] == '.') { - // "-.1" bad "-0.1" good - real.insert(1, "0"); - } - json_string_->append(real); - break; + std::string real = DoubleToString(value); + // 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 + // real rather than an int. + if (real.find('.') == std::string::npos && + real.find('e') == std::string::npos && + real.find('E') == std::string::npos) { + real.append(".0"); } - - case Value::TYPE_STRING: - { - std::string value; - bool result = node->GetAsString(&value); - DCHECK(result); - EscapeJSONString(value, true, json_string_); - break; + // The JSON spec requires that non-integer values in the range (-1,1) + // have a zero before the decimal point - ".52" is not valid, "0.52" is. + if (real[0] == '.') { + real.insert(static_cast<size_t>(0), static_cast<size_t>(1), '0'); + } else if (real.length() > 1 && real[0] == '-' && real[1] == '.') { + // "-.1" bad "-0.1" good + real.insert(static_cast<size_t>(1), static_cast<size_t>(1), '0'); } + json_string_->append(real); + return result; + } + + case Value::TYPE_STRING: { + std::string value; + bool result = node->GetAsString(&value); + DCHECK(result); + EscapeJSONString(value, true, json_string_); + return result; + } + + case Value::TYPE_LIST: { + json_string_->push_back('['); + if (pretty_print_) + json_string_->push_back(' '); + + const ListValue* list = NULL; + bool first_value_has_been_output = false; + bool result = node->GetAsList(&list); + DCHECK(result); + for (ListValue::const_iterator it = list->begin(); it != list->end(); + ++it) { + const Value* value = *it; + if (omit_binary_values_ && value->GetType() == Value::TYPE_BINARY) + continue; + + if (first_value_has_been_output) { + json_string_->push_back(','); + if (pretty_print_) + json_string_->push_back(' '); + } - case Value::TYPE_LIST: - { - json_string_->append("["); - if (pretty_print_) - json_string_->append(" "); - - const ListValue* list = static_cast<const ListValue*>(node); - for (size_t i = 0; i < list->GetSize(); ++i) { - const Value* value = NULL; - bool result = list->Get(i, &value); - DCHECK(result); + if (!BuildJSONString(value, depth)) + result = false; - if (omit_binary_values_ && value->GetType() == Value::TYPE_BINARY) { - continue; - } + first_value_has_been_output = true; + } - if (i != 0) { - json_string_->append(","); - if (pretty_print_) - json_string_->append(" "); - } + if (pretty_print_) + json_string_->push_back(' '); + json_string_->push_back(']'); + return result; + } + + case Value::TYPE_DICTIONARY: { + json_string_->push_back('{'); + if (pretty_print_) + json_string_->append(kPrettyPrintLineEnding); + + const DictionaryValue* dict = NULL; + bool first_value_has_been_output = false; + bool result = node->GetAsDictionary(&dict); + DCHECK(result); + for (DictionaryValue::Iterator itr(*dict); !itr.IsAtEnd(); + itr.Advance()) { + if (omit_binary_values_ && + itr.value().GetType() == Value::TYPE_BINARY) { + continue; + } - BuildJSONString(value, depth); + if (first_value_has_been_output) { + json_string_->push_back(','); + if (pretty_print_) + json_string_->append(kPrettyPrintLineEnding); } if (pretty_print_) - json_string_->append(" "); - json_string_->append("]"); - break; - } + IndentLine(depth + 1U); - case Value::TYPE_DICTIONARY: - { - json_string_->append("{"); + EscapeJSONString(itr.key(), true, json_string_); + json_string_->push_back(':'); if (pretty_print_) - json_string_->append(kPrettyPrintLineEnding); - - const DictionaryValue* dict = - static_cast<const DictionaryValue*>(node); - bool first_entry = true; - for (DictionaryValue::Iterator itr(*dict); !itr.IsAtEnd(); - itr.Advance(), first_entry = false) { - if (omit_binary_values_ && - itr.value().GetType() == Value::TYPE_BINARY) { - continue; - } - - if (!first_entry) { - json_string_->append(","); - if (pretty_print_) - json_string_->append(kPrettyPrintLineEnding); - } + json_string_->push_back(' '); - if (pretty_print_) - IndentLine(depth + 1); - - EscapeJSONString(itr.key(), true, json_string_); - if (pretty_print_) { - json_string_->append(": "); - } else { - json_string_->append(":"); - } - BuildJSONString(&itr.value(), depth + 1); - } + if (!BuildJSONString(&itr.value(), depth + 1U)) + result = false; - if (pretty_print_) { - json_string_->append(kPrettyPrintLineEnding); - IndentLine(depth); - json_string_->append("}"); - } else { - json_string_->append("}"); - } - break; + first_value_has_been_output = true; } - case Value::TYPE_BINARY: - { - if (!omit_binary_values_) { - NOTREACHED() << "Cannot serialize binary value."; - } - break; + if (pretty_print_) { + json_string_->append(kPrettyPrintLineEnding); + IndentLine(depth); } - default: - NOTREACHED() << "unknown json type"; + json_string_->push_back('}'); + return result; + } + + case Value::TYPE_BINARY: + // Successful only if we're allowed to omit it. + DLOG_IF(ERROR, !omit_binary_values_) << "Cannot serialize binary value."; + return omit_binary_values_; } + NOTREACHED(); + return false; } -void JSONWriter::IndentLine(int depth) { - // It may be faster to keep an indent string so we don't have to keep - // reallocating. - json_string_->append(std::string(depth * 3, ' ')); +void JSONWriter::IndentLine(size_t depth) { + json_string_->append(depth * 3U, ' '); } } // namespace base diff --git a/chromium/base/json/json_writer.h b/chromium/base/json/json_writer.h index e4a143c7780..9709c7e723f 100644 --- a/chromium/base/json/json_writer.h +++ b/chromium/base/json/json_writer.h @@ -17,8 +17,10 @@ class Value; class BASE_EXPORT JSONWriter { public: enum Options { - // For values of binary type, the value (and key if within a dictionary) - // will be omitted from the output. + // This option instructs the writer that if a Binary value is encountered, + // the value (and key if within a dictionary) will be omitted from the + // output, and success will be returned. Otherwise, if a binary value is + // encountered, failure will be returned. OPTIONS_OMIT_BINARY_VALUES = 1 << 0, // This option instructs the writer to write doubles that have no fractional @@ -35,25 +37,23 @@ class BASE_EXPORT JSONWriter { // Given a root node, generates a JSON string and puts it into |json|. // TODO(tc): Should we generate json if it would be invalid json (e.g., // |node| is not a DictionaryValue/ListValue or if there are inf/-inf float - // values)? - static void Write(const Value* const node, std::string* json); + // values)? Return true on success and false on failure. + static bool Write(const Value* const node, std::string* json); // Same as above but with |options| which is a bunch of JSONWriter::Options - // bitwise ORed together. - static void WriteWithOptions(const Value* const node, int options, + // bitwise ORed together. Return true on success and false on failure. + static bool WriteWithOptions(const Value* const node, int options, std::string* json); private: - JSONWriter(bool omit_binary_values, - bool omit_double_type_preservation, bool pretty_print, - std::string* json); + JSONWriter(int options, std::string* json); - // Called recursively to build the JSON string. Whe completed, value is - // json_string_ will contain the JSON. - void BuildJSONString(const Value* const node, int depth); + // Called recursively to build the JSON string. When completed, + // |json_string_| will contain the JSON. + bool BuildJSONString(const Value* const node, size_t depth); // Adds space to json_string_ for the indent level. - void IndentLine(int depth); + void IndentLine(size_t depth); bool omit_binary_values_; bool omit_double_type_preservation_; diff --git a/chromium/base/json/json_writer_unittest.cc b/chromium/base/json/json_writer_unittest.cc index 7ddd7b462d6..ae46800dde6 100644 --- a/chromium/base/json/json_writer_unittest.cc +++ b/chromium/base/json/json_writer_unittest.cc @@ -8,44 +8,68 @@ namespace base { -TEST(JSONWriterTest, Writing) { - // Test null - Value* root = Value::CreateNullValue(); +TEST(JSONWriterTest, BasicTypes) { std::string output_js; - JSONWriter::Write(root, &output_js); - ASSERT_EQ("null", output_js); + + // Test null. + Value* root = Value::CreateNullValue(); + EXPECT_TRUE(JSONWriter::Write(root, &output_js)); + EXPECT_EQ("null", output_js); delete root; - // Test empty dict + // Test empty dict. root = new DictionaryValue; - JSONWriter::Write(root, &output_js); - ASSERT_EQ("{}", output_js); + EXPECT_TRUE(JSONWriter::Write(root, &output_js)); + EXPECT_EQ("{}", output_js); delete root; - // Test empty list + // Test empty list. root = new ListValue; - JSONWriter::Write(root, &output_js); - ASSERT_EQ("[]", output_js); + EXPECT_TRUE(JSONWriter::Write(root, &output_js)); + EXPECT_EQ("[]", output_js); + delete root; + + // Test integer values. + root = new FundamentalValue(42); + EXPECT_TRUE(JSONWriter::Write(root, &output_js)); + EXPECT_EQ("42", output_js); + delete root; + + // Test boolean values. + root = new FundamentalValue(true); + EXPECT_TRUE(JSONWriter::Write(root, &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); - JSONWriter::Write(root, &output_js); - ASSERT_EQ("1.0", output_js); + EXPECT_TRUE(JSONWriter::Write(root, &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); - JSONWriter::Write(root, &output_js); - ASSERT_EQ("0.2", output_js); + EXPECT_TRUE(JSONWriter::Write(root, &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); - JSONWriter::Write(root, &output_js); - ASSERT_EQ("-0.8", output_js); + EXPECT_TRUE(JSONWriter::Write(root, &output_js)); + EXPECT_EQ("-0.8", output_js); delete root; + // Test String values. + root = new StringValue("foo"); + EXPECT_TRUE(JSONWriter::Write(root, &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; @@ -59,10 +83,12 @@ TEST(JSONWriterTest, Writing) { list->Append(new FundamentalValue(true)); // Test the pretty-printer. - JSONWriter::Write(&root_dict, &output_js); - ASSERT_EQ("{\"list\":[{\"inner int\":10},[],true]}", output_js); - JSONWriter::WriteWithOptions(&root_dict, JSONWriter::OPTIONS_PRETTY_PRINT, - &output_js); + EXPECT_TRUE(JSONWriter::Write(&root_dict, &output_js)); + EXPECT_EQ("{\"list\":[{\"inner int\":10},[],true]}", output_js); + EXPECT_TRUE(JSONWriter::WriteWithOptions(&root_dict, + JSONWriter::OPTIONS_PRETTY_PRINT, + &output_js)); + // The pretty-printer uses a different newline style on Windows than on // other platforms. #if defined(OS_WIN) @@ -70,62 +96,79 @@ TEST(JSONWriterTest, Writing) { #else #define JSON_NEWLINE "\n" #endif - ASSERT_EQ("{" JSON_NEWLINE + EXPECT_EQ("{" JSON_NEWLINE " \"list\": [ {" JSON_NEWLINE " \"inner int\": 10" JSON_NEWLINE " }, [ ], true ]" JSON_NEWLINE "}" JSON_NEWLINE, output_js); #undef JSON_NEWLINE +} + +TEST(JSONWriterTest, KeysWithPeriods) { + std::string output_js; - // Test keys with periods 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); - JSONWriter::Write(&period_dict, &output_js); - ASSERT_EQ("{\"a.b\":3,\"c\":2,\"d.e.f\":{\"g.h.i.j\":1}}", output_js); + 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)); - JSONWriter::Write(&period_dict3, &output_js); - ASSERT_EQ("{\"a\":{\"b\":2},\"a.b\":1}", output_js); - - // Test omitting binary values. - root = BinaryValue::CreateWithCopiedBuffer("asdf", 4); - JSONWriter::WriteWithOptions(root, JSONWriter::OPTIONS_OMIT_BINARY_VALUES, - &output_js); - ASSERT_TRUE(output_js.empty()); + EXPECT_TRUE(JSONWriter::Write(&period_dict3, &output_js)); + EXPECT_EQ("{\"a\":{\"b\":2},\"a.b\":1}", output_js); +} + +TEST(JSONWriterTest, BinaryValues) { + std::string output_js; + + // 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)); + EXPECT_TRUE(JSONWriter::WriteWithOptions( + root, 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(BinaryValue::CreateWithCopiedBuffer("asdf", 4)); binary_list.Append(new FundamentalValue(2)); - JSONWriter::WriteWithOptions(&binary_list, - JSONWriter::OPTIONS_OMIT_BINARY_VALUES, - &output_js); - ASSERT_EQ("[5,2]", output_js); + binary_list.Append(BinaryValue::CreateWithCopiedBuffer("asdf", 4)); + EXPECT_FALSE(JSONWriter::Write(&binary_list, &output_js)); + EXPECT_TRUE(JSONWriter::WriteWithOptions( + &binary_list, JSONWriter::OPTIONS_OMIT_BINARY_VALUES, &output_js)); + EXPECT_EQ("[5,2]", output_js); DictionaryValue binary_dict; - binary_dict.Set("a", new FundamentalValue(5)); - binary_dict.Set("b", BinaryValue::CreateWithCopiedBuffer("asdf", 4)); - binary_dict.Set("c", new FundamentalValue(2)); - JSONWriter::WriteWithOptions(&binary_dict, - JSONWriter::OPTIONS_OMIT_BINARY_VALUES, - &output_js); - ASSERT_EQ("{\"a\":5,\"c\":2}", output_js); + 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)); + EXPECT_FALSE(JSONWriter::Write(&binary_dict, &output_js)); + EXPECT_TRUE(JSONWriter::WriteWithOptions( + &binary_dict, JSONWriter::OPTIONS_OMIT_BINARY_VALUES, &output_js)); + EXPECT_EQ("{\"b\":5,\"d\":2}", output_js); +} + +TEST(JSONWriterTest, DoublesAsInts) { + std::string output_js; // Test allowing a double with no fractional part to be written as an integer. FundamentalValue double_value(1e10); - JSONWriter::WriteWithOptions( + EXPECT_TRUE(JSONWriter::WriteWithOptions( &double_value, JSONWriter::OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION, - &output_js); - ASSERT_EQ("10000000000", output_js); + &output_js)); + EXPECT_EQ("10000000000", output_js); } } // namespace base diff --git a/chromium/base/lazy_instance.h b/chromium/base/lazy_instance.h index 3935780a55b..05a7c5d81ec 100644 --- a/chromium/base/lazy_instance.h +++ b/chromium/base/lazy_instance.h @@ -57,7 +57,9 @@ namespace base { template <typename Type> struct DefaultLazyInstanceTraits { static const bool kRegisterOnExit = true; +#ifndef NDEBUG static const bool kAllowedToAccessOnNonjoinableThread = false; +#endif static Type* New(void* instance) { DCHECK_EQ(reinterpret_cast<uintptr_t>(instance) & (ALIGNOF(Type) - 1), 0u) @@ -89,7 +91,9 @@ namespace internal { template <typename Type> struct LeakyLazyInstanceTraits { static const bool kRegisterOnExit = false; +#ifndef NDEBUG static const bool kAllowedToAccessOnNonjoinableThread = true; +#endif static Type* New(void* instance) { ANNOTATE_SCOPED_MEMORY_LEAK; diff --git a/chromium/base/linux_util.cc b/chromium/base/linux_util.cc index f8dd2d0088b..36710f28137 100644 --- a/chromium/base/linux_util.cc +++ b/chromium/base/linux_util.cc @@ -71,61 +71,10 @@ class LinuxDistroHelper { }; #endif // if defined(OS_LINUX) -// expected prefix of the target of the /proc/self/fd/%d link for a socket -const char kSocketLinkPrefix[] = "socket:["; - -// Parse a symlink in /proc/pid/fd/$x and return the inode number of the -// socket. -// inode_out: (output) set to the inode number on success -// path: e.g. /proc/1234/fd/5 (must be a UNIX domain socket descriptor) -// log: if true, log messages about failure details -bool ProcPathGetInode(ino_t* inode_out, const char* path, bool log = false) { - DCHECK(inode_out); - DCHECK(path); - - char buf[256]; - const ssize_t n = readlink(path, buf, sizeof(buf) - 1); - if (n == -1) { - if (log) { - DLOG(WARNING) << "Failed to read the inode number for a socket from /proc" - "(" << errno << ")"; - } - return false; - } - buf[n] = 0; - - if (memcmp(kSocketLinkPrefix, buf, sizeof(kSocketLinkPrefix) - 1)) { - if (log) { - DLOG(WARNING) << "The descriptor passed from the crashing process wasn't " - " a UNIX domain socket."; - } - return false; - } - - char* endptr; - const unsigned long long int inode_ul = - strtoull(buf + sizeof(kSocketLinkPrefix) - 1, &endptr, 10); - if (*endptr != ']') - return false; - - if (inode_ul == ULLONG_MAX) { - if (log) { - DLOG(WARNING) << "Failed to parse a socket's inode number: the number " - "was too large. Please report this bug: " << buf; - } - return false; - } - - *inode_out = inode_ul; - return true; -} - } // namespace namespace base { -const char kFindInodeSwitch[] = "--find-inode"; - // Account for the terminating null character. static const int kDistroSize = 128 + 1; @@ -176,82 +125,10 @@ std::string GetLinuxDistro() { void SetLinuxDistro(const std::string& distro) { std::string trimmed_distro; - TrimWhitespaceASCII(distro, TRIM_ALL, &trimmed_distro); + base::TrimWhitespaceASCII(distro, base::TRIM_ALL, &trimmed_distro); base::strlcpy(g_linux_distro, trimmed_distro.c_str(), kDistroSize); } -bool FileDescriptorGetInode(ino_t* inode_out, int fd) { - DCHECK(inode_out); - - struct stat buf; - if (fstat(fd, &buf) < 0) - return false; - - if (!S_ISSOCK(buf.st_mode)) - return false; - - *inode_out = buf.st_ino; - return true; -} - -bool FindProcessHoldingSocket(pid_t* pid_out, ino_t socket_inode) { - DCHECK(pid_out); - bool already_found = false; - - DIR* proc = opendir("/proc"); - if (!proc) { - DLOG(WARNING) << "Cannot open /proc"; - return false; - } - - std::vector<pid_t> pids; - - struct dirent* dent; - while ((dent = readdir(proc))) { - char* endptr; - const unsigned long int pid_ul = strtoul(dent->d_name, &endptr, 10); - if (pid_ul == ULONG_MAX || *endptr) - continue; - pids.push_back(pid_ul); - } - closedir(proc); - - for (std::vector<pid_t>::const_iterator - i = pids.begin(); i != pids.end(); ++i) { - const pid_t current_pid = *i; - char buf[256]; - snprintf(buf, sizeof(buf), "/proc/%d/fd", current_pid); - DIR* fd = opendir(buf); - if (!fd) - continue; - - while ((dent = readdir(fd))) { - if (snprintf(buf, sizeof(buf), "/proc/%d/fd/%s", current_pid, - dent->d_name) >= static_cast<int>(sizeof(buf))) { - continue; - } - - ino_t fd_inode = static_cast<ino_t>(-1); - if (ProcPathGetInode(&fd_inode, buf)) { - if (fd_inode == socket_inode) { - if (already_found) { - closedir(fd); - return false; - } - - already_found = true; - *pid_out = current_pid; - break; - } - } - } - - closedir(fd); - } - - return already_found; -} - pid_t FindThreadIDWithSyscall(pid_t pid, const std::string& expected_data, bool* syscall_supported) { char buf[256]; diff --git a/chromium/base/linux_util.h b/chromium/base/linux_util.h index b9ba56dfc97..83523df831d 100644 --- a/chromium/base/linux_util.h +++ b/chromium/base/linux_util.h @@ -14,8 +14,6 @@ namespace base { -BASE_EXPORT extern const char kFindInodeSwitch[]; - // This is declared here so the crash reporter can access the memory directly // in compromised context without going through the standard library. BASE_EXPORT extern char g_linux_distro[]; @@ -26,13 +24,6 @@ BASE_EXPORT std::string GetLinuxDistro(); // Set the Linux Distro string. BASE_EXPORT void SetLinuxDistro(const std::string& distro); -// Return the inode number for the UNIX domain socket |fd|. -BASE_EXPORT bool FileDescriptorGetInode(ino_t* inode_out, int fd); - -// Find the process which holds the given socket, named by inode number. If -// multiple processes hold the socket, this function returns false. -BASE_EXPORT bool FindProcessHoldingSocket(pid_t* pid_out, ino_t socket_inode); - // For a given process |pid|, look through all its threads and find the first // thread with /proc/[pid]/task/[thread_id]/syscall whose first N bytes matches // |expected_data|, where N is the length of |expected_data|. diff --git a/chromium/base/location.cc b/chromium/base/location.cc index b5da027ee82..08263cebaad 100644 --- a/chromium/base/location.cc +++ b/chromium/base/location.cc @@ -92,11 +92,11 @@ __declspec(noinline) BASE_EXPORT const void* GetProgramCounter() { #if defined(COMPILER_MSVC) return _ReturnAddress(); -#elif defined(COMPILER_GCC) +#elif defined(COMPILER_GCC) && !defined(OS_NACL) return __builtin_extract_return_addr(__builtin_return_address(0)); -#endif // COMPILER_GCC - +#else return NULL; +#endif } } // namespace tracked_objects diff --git a/chromium/base/logging.cc b/chromium/base/logging.cc index e836092e766..b2938f31144 100644 --- a/chromium/base/logging.cc +++ b/chromium/base/logging.cc @@ -43,6 +43,7 @@ typedef pthread_mutex_t* MutexHandle; #include <ctime> #include <iomanip> #include <ostream> +#include <string> #include "base/base_switches.h" #include "base/command_line.h" @@ -51,6 +52,8 @@ typedef pthread_mutex_t* MutexHandle; #include "base/debug/stack_trace.h" #include "base/posix/eintr_wrapper.h" #include "base/strings/string_piece.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/synchronization/lock_impl.h" #include "base/threading/platform_thread.h" @@ -65,23 +68,20 @@ typedef pthread_mutex_t* MutexHandle; namespace logging { -DcheckState g_dcheck_state = DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS; - -DcheckState get_dcheck_state() { - return g_dcheck_state; -} - -void set_dcheck_state(DcheckState state) { - g_dcheck_state = state; -} - namespace { VlogInfo* g_vlog_info = NULL; VlogInfo* g_vlog_info_prev = NULL; const char* const log_severity_names[LOG_NUM_SEVERITIES] = { - "INFO", "WARNING", "ERROR", "ERROR_REPORT", "FATAL" }; + "INFO", "WARNING", "ERROR", "FATAL" }; + +const char* log_severity_name(int severity) +{ + if (severity >= 0 && severity < LOG_NUM_SEVERITIES) + return log_severity_names[severity]; + return "UNKNOWN"; +} int min_log_level = 0; @@ -115,9 +115,6 @@ bool show_error_dialogs = false; // An assert handler override specified by the client to be called instead of // the debug message dialog and process termination. LogAssertHandlerFunction log_assert_handler = NULL; -// An report handler override specified by the client to be called instead of -// the debug message dialog. -LogReportHandlerFunction log_report_handler = NULL; // A log message handler that gets notified of every log message we process. LogMessageHandlerFunction log_message_handler = NULL; @@ -355,15 +352,13 @@ LoggingSettings::LoggingSettings() : logging_dest(LOG_DEFAULT), log_file(NULL), lock_log(LOCK_LOG_FILE), - delete_old(APPEND_TO_OLD_LOG_FILE), - dcheck_state(DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS) {} + delete_old(APPEND_TO_OLD_LOG_FILE) {} bool BaseInitLoggingImpl(const LoggingSettings& settings) { #if defined(OS_NACL) // Can log only to the system debug log. CHECK_EQ(settings.logging_dest & ~LOG_TO_SYSTEM_DEBUG_LOG, 0); #endif - g_dcheck_state = settings.dcheck_state; CommandLine* command_line = CommandLine::ForCurrentProcess(); // Don't bother initializing g_vlog_info unless we use one of the // vlog switches. @@ -404,7 +399,7 @@ bool BaseInitLoggingImpl(const LoggingSettings& settings) { } void SetMinLogLevel(int level) { - min_log_level = std::min(LOG_ERROR_REPORT, level); + min_log_level = std::min(LOG_FATAL, level); } int GetMinLogLevel() { @@ -441,10 +436,6 @@ void SetLogAssertHandler(LogAssertHandlerFunction handler) { log_assert_handler = handler; } -void SetLogReportHandler(LogReportHandlerFunction handler) { - log_report_handler = handler; -} - void SetLogMessageHandler(LogMessageHandlerFunction handler) { log_message_handler = handler; } @@ -468,6 +459,7 @@ template std::string* MakeCheckOpString<std::string, std::string>( const std::string&, const std::string&, const char* name); #endif +#if !defined(NDEBUG) // Displays a message box to the user with the error message in it. // Used for fatal messages, where we close the app simultaneously. // This is for developers only; we don't use this in circumstances @@ -494,7 +486,7 @@ void DisplayDebugMessageInDialog(const std::string& str) { backslash[1] = 0; wcscat_s(prog_name, MAX_PATH, L"debug_message.exe"); - std::wstring cmdline = UTF8ToWide(str); + std::wstring cmdline = base::UTF8ToWide(str); if (cmdline.empty()) return; @@ -518,6 +510,7 @@ void DisplayDebugMessageInDialog(const std::string& str) { // You can just look at stderr. #endif } +#endif // !defined(NDEBUG) #if defined(OS_WIN) LogMessage::SaveLastError::SaveLastError() : last_error_(::GetLastError()) { @@ -528,17 +521,6 @@ LogMessage::SaveLastError::~SaveLastError() { } #endif // defined(OS_WIN) -LogMessage::LogMessage(const char* file, int line, LogSeverity severity, - int ctr) - : severity_(severity), file_(file), line_(line) { - Init(file, line); -} - -LogMessage::LogMessage(const char* file, int line) - : severity_(LOG_INFO), file_(file), line_(line) { - Init(file, line); -} - LogMessage::LogMessage(const char* file, int line, LogSeverity severity) : severity_(severity), file_(file), line_(line) { Init(file, line); @@ -560,7 +542,7 @@ LogMessage::LogMessage(const char* file, int line, LogSeverity severity, } LogMessage::~LogMessage() { -#if !defined(NDEBUG) && !defined(OS_NACL) +#if !defined(NDEBUG) && !defined(OS_NACL) && !defined(__UCLIBC__) if (severity_ == LOG_FATAL) { // Include a stack trace on a fatal. base::debug::StackTrace trace; @@ -593,7 +575,6 @@ LogMessage::~LogMessage() { priority = ANDROID_LOG_WARN; break; case LOG_ERROR: - case LOG_ERROR_REPORT: priority = ANDROID_LOG_ERROR; break; case LOG_FATAL: @@ -602,13 +583,13 @@ LogMessage::~LogMessage() { } __android_log_write(priority, "chromium", str_newline.c_str()); #endif - fprintf(stderr, "%s", str_newline.c_str()); + ignore_result(fwrite(str_newline.data(), str_newline.size(), 1, stderr)); fflush(stderr); } else if (severity_ >= kAlwaysPrintErrorLevel) { // When we're only outputting to a log file, above a certain log level, we // should still output to stderr so that we can better detect and diagnose // problems with unit tests, especially on the buildbots. - fprintf(stderr, "%s", str_newline.c_str()); + ignore_result(fwrite(str_newline.data(), str_newline.size(), 1, stderr)); fflush(stderr); } @@ -633,7 +614,8 @@ LogMessage::~LogMessage() { &num_written, NULL); #else - fprintf(log_file, "%s", str_newline.c_str()); + ignore_result(fwrite( + str_newline.data(), str_newline.size(), 1, log_file)); fflush(log_file); #endif } @@ -646,32 +628,20 @@ LogMessage::~LogMessage() { str_newline.copy(str_stack, arraysize(str_stack)); base::debug::Alias(str_stack); - // display a message or break into the debugger on a fatal error - if (base::debug::BeingDebugged()) { - base::debug::BreakDebugger(); + if (log_assert_handler) { + // Make a copy of the string for the handler out of paranoia. + log_assert_handler(std::string(stream_.str())); } else { - if (log_assert_handler) { - // make a copy of the string for the handler out of paranoia - log_assert_handler(std::string(stream_.str())); - } else { - // Don't use the string with the newline, get a fresh version to send to - // the debug message process. We also don't display assertions to the - // user in release mode. The enduser can't do anything with this - // information, and displaying message boxes when the application is - // hosed can cause additional problems. + // Don't use the string with the newline, get a fresh version to send to + // the debug message process. We also don't display assertions to the + // user in release mode. The enduser can't do anything with this + // information, and displaying message boxes when the application is + // hosed can cause additional problems. #ifndef NDEBUG - DisplayDebugMessageInDialog(stream_.str()); -#endif - // Crash the process to generate a dump. - base::debug::BreakDebugger(); - } - } - } else if (severity_ == LOG_ERROR_REPORT) { - // We are here only if the user runs with --enable-dcheck in release mode. - if (log_report_handler) { - log_report_handler(std::string(stream_.str())); - } else { DisplayDebugMessageInDialog(stream_.str()); +#endif + // Crash the process to generate a dump. + base::debug::BreakDebugger(); } } } @@ -711,7 +681,7 @@ void LogMessage::Init(const char* file, int line) { if (log_tickcount) stream_ << TickCount() << ':'; if (severity_ >= 0) - stream_ << log_severity_names[severity_]; + stream_ << log_severity_name(severity_); else stream_ << "VERBOSE" << -severity_; @@ -738,61 +708,40 @@ SystemErrorCode GetLastSystemErrorCode() { } #if defined(OS_WIN) -Win32ErrorLogMessage::Win32ErrorLogMessage(const char* file, - int line, - LogSeverity severity, - SystemErrorCode err, - const char* module) - : err_(err), - module_(module), - log_message_(file, line, severity) { +BASE_EXPORT std::string SystemErrorCodeToString(SystemErrorCode error_code) { + const int error_message_buffer_size = 256; + char msgbuf[error_message_buffer_size]; + DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; + DWORD len = FormatMessageA(flags, NULL, error_code, 0, msgbuf, + arraysize(msgbuf), NULL); + if (len) { + // Messages returned by system end with line breaks. + return base::CollapseWhitespaceASCII(msgbuf, true) + + base::StringPrintf(" (0x%X)", error_code); + } + return base::StringPrintf("Error (0x%X) while retrieving error. (0x%X)", + GetLastError(), error_code); +} +#elif defined(OS_POSIX) +BASE_EXPORT std::string SystemErrorCodeToString(SystemErrorCode error_code) { + return safe_strerror(error_code); } +#else +#error Not implemented +#endif + +#if defined(OS_WIN) Win32ErrorLogMessage::Win32ErrorLogMessage(const char* file, int line, LogSeverity severity, SystemErrorCode err) : err_(err), - module_(NULL), log_message_(file, line, severity) { } Win32ErrorLogMessage::~Win32ErrorLogMessage() { - const int error_message_buffer_size = 256; - char msgbuf[error_message_buffer_size]; - DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; - HMODULE hmod; - if (module_) { - hmod = GetModuleHandleA(module_); - if (hmod) { - flags |= FORMAT_MESSAGE_FROM_HMODULE; - } else { - // This makes a nested Win32ErrorLogMessage. It will have module_ of NULL - // so it will not call GetModuleHandle, so recursive errors are - // impossible. - DPLOG(WARNING) << "Couldn't open module " << module_ - << " for error message query"; - } - } else { - hmod = NULL; - } - DWORD len = FormatMessageA(flags, - hmod, - err_, - 0, - msgbuf, - sizeof(msgbuf) / sizeof(msgbuf[0]), - NULL); - if (len) { - while ((len > 0) && - isspace(static_cast<unsigned char>(msgbuf[len - 1]))) { - msgbuf[--len] = 0; - } - stream() << ": " << msgbuf; - } else { - stream() << ": Error " << GetLastError() << " while retrieving error " - << err_; - } + stream() << ": " << SystemErrorCodeToString(err_); // We're about to crash (CHECK). Put |err_| on the stack (by placing it in a // field) and use Alias in hopes that it makes it into crash dumps. DWORD last_error = err_; @@ -808,7 +757,7 @@ ErrnoLogMessage::ErrnoLogMessage(const char* file, } ErrnoLogMessage::~ErrnoLogMessage() { - stream() << ": " << safe_strerror(err_); + stream() << ": " << SystemErrorCodeToString(err_); } #endif // OS_WIN @@ -862,5 +811,5 @@ std::wstring GetLogFileFullPath() { } // namespace logging std::ostream& operator<<(std::ostream& out, const wchar_t* wstr) { - return out << WideToUTF8(std::wstring(wstr)); + return out << base::WideToUTF8(std::wstring(wstr)); } diff --git a/chromium/base/logging.h b/chromium/base/logging.h index 71f391f8aff..1a110a565e9 100644 --- a/chromium/base/logging.h +++ b/chromium/base/logging.h @@ -52,10 +52,6 @@ // // LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies"; // -// The above will cause log messages to be output on the 1st, 11th, 21st, ... -// times it is executed. Note that the special COUNTER value is used to -// identify which repetition is happening. -// // The CHECK(condition) macro is active in both debug and release builds and // effectively performs a LOG(FATAL) which terminates the process and // generates a crashdump unless a debugger is attached. @@ -131,18 +127,13 @@ // GetLastError() on Windows and errno on POSIX). // // The supported severity levels for macros that allow you to specify one -// are (in increasing order of severity) INFO, WARNING, ERROR, ERROR_REPORT, -// and FATAL. +// are (in increasing order of severity) INFO, WARNING, ERROR, and FATAL. // // Very important: logging a message at the FATAL severity level causes // the program to terminate (after the message is logged). // -// Note the special severity of ERROR_REPORT only available/relevant in normal -// mode, which displays error dialog without terminating the program. There is -// no error dialog for severity ERROR or below in normal mode. -// -// There is also the special severity of DFATAL, which logs FATAL in -// debug mode, ERROR in normal mode. +// There is the special severity of DFATAL, which logs FATAL in debug mode, +// ERROR in normal mode. namespace logging { @@ -175,7 +166,7 @@ enum LoggingDestination { // Indicates that the log file should be locked when being written to. // Unless there is only one single-threaded process that is logging to // the log file, the file should be locked during writes to make each -// log outut atomic. Other writers will block. +// log output atomic. Other writers will block. // // All processes writing to the log file must have their locking set for it to // work properly. Defaults to LOCK_LOG_FILE. @@ -185,11 +176,6 @@ enum LogLockingState { LOCK_LOG_FILE, DONT_LOCK_LOG_FILE }; // Defaults to APPEND_TO_OLD_LOG_FILE. enum OldFileDeletionState { DELETE_OLD_LOG_FILE, APPEND_TO_OLD_LOG_FILE }; -enum DcheckState { - DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS, - ENABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS -}; - struct BASE_EXPORT LoggingSettings { // The defaults values are: // @@ -197,7 +183,6 @@ struct BASE_EXPORT LoggingSettings { // log_file: NULL // lock_log: LOCK_LOG_FILE // delete_old: APPEND_TO_OLD_LOG_FILE - // dcheck_state: DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS LoggingSettings(); LoggingDestination logging_dest; @@ -207,8 +192,6 @@ struct BASE_EXPORT LoggingSettings { const PathChar* log_file; LogLockingState lock_log; OldFileDeletionState delete_old; - - DcheckState dcheck_state; }; // Define different names for the BaseInitLoggingImpl() function depending on @@ -288,13 +271,6 @@ BASE_EXPORT void SetShowErrorDialogs(bool enable_dialogs); typedef void (*LogAssertHandlerFunction)(const std::string& str); BASE_EXPORT void SetLogAssertHandler(LogAssertHandlerFunction handler); -// Sets the Log Report Handler that will be used to notify of check failures -// in non-debug mode. The default handler shows a dialog box and continues -// the execution, however clients can use this function to override with their -// own handling. -typedef void (*LogReportHandlerFunction)(const std::string& str); -BASE_EXPORT void SetLogReportHandler(LogReportHandlerFunction handler); - // Sets the Log Message Handler that gets passed every log message before // it's sent to other log destinations (if any). // Returns true to signal that it handled the message and the message @@ -311,9 +287,8 @@ const LogSeverity LOG_VERBOSE = -1; // This is level 1 verbosity const LogSeverity LOG_INFO = 0; const LogSeverity LOG_WARNING = 1; const LogSeverity LOG_ERROR = 2; -const LogSeverity LOG_ERROR_REPORT = 3; -const LogSeverity LOG_FATAL = 4; -const LogSeverity LOG_NUM_SEVERITIES = 5; +const LogSeverity LOG_FATAL = 3; +const LogSeverity LOG_NUM_SEVERITIES = 4; // LOG_DFATAL is LOG_FATAL in debug mode, ERROR in normal mode #ifdef NDEBUG @@ -331,9 +306,6 @@ const LogSeverity LOG_DFATAL = LOG_FATAL; logging::ClassName(__FILE__, __LINE__, logging::LOG_WARNING , ##__VA_ARGS__) #define COMPACT_GOOGLE_LOG_EX_ERROR(ClassName, ...) \ logging::ClassName(__FILE__, __LINE__, logging::LOG_ERROR , ##__VA_ARGS__) -#define COMPACT_GOOGLE_LOG_EX_ERROR_REPORT(ClassName, ...) \ - logging::ClassName(__FILE__, __LINE__, \ - logging::LOG_ERROR_REPORT , ##__VA_ARGS__) #define COMPACT_GOOGLE_LOG_EX_FATAL(ClassName, ...) \ logging::ClassName(__FILE__, __LINE__, logging::LOG_FATAL , ##__VA_ARGS__) #define COMPACT_GOOGLE_LOG_EX_DFATAL(ClassName, ...) \ @@ -345,8 +317,6 @@ const LogSeverity LOG_DFATAL = LOG_FATAL; COMPACT_GOOGLE_LOG_EX_WARNING(LogMessage) #define COMPACT_GOOGLE_LOG_ERROR \ COMPACT_GOOGLE_LOG_EX_ERROR(LogMessage) -#define COMPACT_GOOGLE_LOG_ERROR_REPORT \ - COMPACT_GOOGLE_LOG_EX_ERROR_REPORT(LogMessage) #define COMPACT_GOOGLE_LOG_FATAL \ COMPACT_GOOGLE_LOG_EX_FATAL(LogMessage) #define COMPACT_GOOGLE_LOG_DFATAL \ @@ -366,10 +336,9 @@ const LogSeverity LOG_DFATAL = LOG_FATAL; const LogSeverity LOG_0 = LOG_ERROR; #endif -// As special cases, we can assume that LOG_IS_ON(ERROR_REPORT) and -// LOG_IS_ON(FATAL) always hold. Also, LOG_IS_ON(DFATAL) always holds -// in debug mode. In particular, CHECK()s will always fire if they -// fail. +// As special cases, we can assume that LOG_IS_ON(FATAL) always holds. Also, +// LOG_IS_ON(DFATAL) always holds in debug mode. In particular, CHECK()s will +// always fire if they fail. #define LOG_IS_ON(severity) \ ((::logging::LOG_ ## severity) >= ::logging::GetMinLogLevel()) @@ -438,29 +407,13 @@ const LogSeverity LOG_0 = LOG_ERROR; SYSLOG_IF(FATAL, !(condition)) << "Assert failed: " #condition ". " #if defined(OS_WIN) -#define LOG_GETLASTERROR_STREAM(severity) \ +#define PLOG_STREAM(severity) \ COMPACT_GOOGLE_LOG_EX_ ## severity(Win32ErrorLogMessage, \ ::logging::GetLastSystemErrorCode()).stream() -#define LOG_GETLASTERROR(severity) \ - LAZY_STREAM(LOG_GETLASTERROR_STREAM(severity), LOG_IS_ON(severity)) -#define LOG_GETLASTERROR_MODULE_STREAM(severity, module) \ - COMPACT_GOOGLE_LOG_EX_ ## severity(Win32ErrorLogMessage, \ - ::logging::GetLastSystemErrorCode(), module).stream() -#define LOG_GETLASTERROR_MODULE(severity, module) \ - LAZY_STREAM(LOG_GETLASTERROR_STREAM(severity, module), \ - LOG_IS_ON(severity)) -// PLOG_STREAM is used by PLOG, which is the usual error logging macro -// for each platform. -#define PLOG_STREAM(severity) LOG_GETLASTERROR_STREAM(severity) #elif defined(OS_POSIX) -#define LOG_ERRNO_STREAM(severity) \ +#define PLOG_STREAM(severity) \ COMPACT_GOOGLE_LOG_EX_ ## severity(ErrnoLogMessage, \ ::logging::GetLastSystemErrorCode()).stream() -#define LOG_ERRNO(severity) \ - LAZY_STREAM(LOG_ERRNO_STREAM(severity), LOG_IS_ON(severity)) -// PLOG_STREAM is used by PLOG, which is the usual error logging macro -// for each platform. -#define PLOG_STREAM(severity) LOG_ERRNO_STREAM(severity) #endif #define PLOG(severity) \ @@ -469,20 +422,6 @@ const LogSeverity LOG_0 = LOG_ERROR; #define PLOG_IF(severity, condition) \ LAZY_STREAM(PLOG_STREAM(severity), LOG_IS_ON(severity) && (condition)) -#if !defined(NDEBUG) -// Debug builds always include DCHECK and DLOG. -#undef LOGGING_IS_OFFICIAL_BUILD -#define LOGGING_IS_OFFICIAL_BUILD 0 -#elif defined(OFFICIAL_BUILD) -// Official release builds always disable and remove DCHECK and DLOG. -#undef LOGGING_IS_OFFICIAL_BUILD -#define LOGGING_IS_OFFICIAL_BUILD 1 -#elif !defined(LOGGING_IS_OFFICIAL_BUILD) -// Unless otherwise specified, unofficial release builds include -// DCHECK and DLOG. -#define LOGGING_IS_OFFICIAL_BUILD 0 -#endif - // The actual stream used isn't important. #define EAT_STREAM_PARAMETERS \ true ? (void) 0 : ::logging::LogMessageVoidify() & LOG_STREAM(FATAL) @@ -494,10 +433,10 @@ const LogSeverity LOG_0 = LOG_ERROR; // We make sure CHECK et al. always evaluates their arguments, as // doing CHECK(FunctionWithSideEffect()) is a common idiom. -#if LOGGING_IS_OFFICIAL_BUILD +#if defined(OFFICIAL_BUILD) && defined(NDEBUG) && !defined(OS_ANDROID) // Make all CHECK functions discard their log strings to reduce code -// bloat for official builds. +// bloat for official release builds. // TODO(akalin): This would be more valuable if there were some way to // remove BreakDebugger() from the backtrace, perhaps by turning it @@ -594,22 +533,16 @@ DEFINE_CHECK_OP_IMPL(GT, > ) #define CHECK_GE(val1, val2) CHECK_OP(GE, >=, val1, val2) #define CHECK_GT(val1, val2) CHECK_OP(GT, > , val1, val2) -#if LOGGING_IS_OFFICIAL_BUILD -// In order to have optimized code for official builds, remove DLOGs and -// DCHECKs. -#define ENABLE_DLOG 0 -#define ENABLE_DCHECK 0 - -#elif defined(NDEBUG) -// Otherwise, if we're a release build, remove DLOGs but not DCHECKs -// (since those can still be turned on via a command-line flag). +#if defined(NDEBUG) #define ENABLE_DLOG 0 -#define ENABLE_DCHECK 1 - #else -// Otherwise, we're a debug build so enable DLOGs and DCHECKs. #define ENABLE_DLOG 1 -#define ENABLE_DCHECK 1 +#endif + +#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON) +#define DCHECK_IS_ON 0 +#else +#define DCHECK_IS_ON 1 #endif // Definitions for DLOG et al. @@ -654,17 +587,6 @@ enum { DEBUG_MODE = ENABLE_DLOG }; #define DLOG(severity) \ LAZY_STREAM(LOG_STREAM(severity), DLOG_IS_ON(severity)) -#if defined(OS_WIN) -#define DLOG_GETLASTERROR(severity) \ - LAZY_STREAM(LOG_GETLASTERROR_STREAM(severity), DLOG_IS_ON(severity)) -#define DLOG_GETLASTERROR_MODULE(severity, module) \ - LAZY_STREAM(LOG_GETLASTERROR_STREAM(severity, module), \ - DLOG_IS_ON(severity)) -#elif defined(OS_POSIX) -#define DLOG_ERRNO(severity) \ - LAZY_STREAM(LOG_ERRNO_STREAM(severity), DLOG_IS_ON(severity)) -#endif - #define DPLOG(severity) \ LAZY_STREAM(PLOG_STREAM(severity), DLOG_IS_ON(severity)) @@ -674,75 +596,40 @@ enum { DEBUG_MODE = ENABLE_DLOG }; // Definitions for DCHECK et al. -#if ENABLE_DCHECK - -#if defined(NDEBUG) - -BASE_EXPORT DcheckState get_dcheck_state(); -BASE_EXPORT void set_dcheck_state(DcheckState state); +#if DCHECK_IS_ON -#if defined(DCHECK_ALWAYS_ON) - -#define DCHECK_IS_ON() true -#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 - -#define COMPACT_GOOGLE_LOG_EX_DCHECK(ClassName, ...) \ - COMPACT_GOOGLE_LOG_EX_ERROR_REPORT(ClassName , ##__VA_ARGS__) -#define COMPACT_GOOGLE_LOG_DCHECK COMPACT_GOOGLE_LOG_ERROR_REPORT -const LogSeverity LOG_DCHECK = LOG_ERROR_REPORT; -#define DCHECK_IS_ON() \ - ((::logging::get_dcheck_state() == \ - ::logging::ENABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS) && \ - LOG_IS_ON(DCHECK)) - -#endif // defined(DCHECK_ALWAYS_ON) - -#else // defined(NDEBUG) - -// On a regular debug build, we want to have DCHECKs enabled. #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; -#define DCHECK_IS_ON() true - -#endif // defined(NDEBUG) -#else // ENABLE_DCHECK +#else // DCHECK_IS_ON -// These are just dummy values since DCHECK_IS_ON() is always false in -// this case. +// These are just dummy values. #define COMPACT_GOOGLE_LOG_EX_DCHECK(ClassName, ...) \ COMPACT_GOOGLE_LOG_EX_INFO(ClassName , ##__VA_ARGS__) #define COMPACT_GOOGLE_LOG_DCHECK COMPACT_GOOGLE_LOG_INFO const LogSeverity LOG_DCHECK = LOG_INFO; -#define DCHECK_IS_ON() false -#endif // ENABLE_DCHECK -#undef ENABLE_DCHECK +#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 // variable warnings if the only use of a variable is in a DCHECK. // This behavior is different from DLOG_IF et al. -#define DCHECK(condition) \ - LAZY_STREAM(LOG_STREAM(DCHECK), DCHECK_IS_ON() && !(condition)) \ +#define DCHECK(condition) \ + LAZY_STREAM(LOG_STREAM(DCHECK), DCHECK_IS_ON && !(condition)) \ << "Check failed: " #condition ". " -#define DPCHECK(condition) \ - LAZY_STREAM(PLOG_STREAM(DCHECK), DCHECK_IS_ON() && !(condition)) \ +#define DPCHECK(condition) \ + LAZY_STREAM(PLOG_STREAM(DCHECK), DCHECK_IS_ON && !(condition)) \ << "Check failed: " #condition ". " // 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 (DCHECK_IS_ON) \ if (std::string* _result = \ logging::Check##name##Impl((val1), (val2), \ #val1 " " #op " " #val2)) \ @@ -797,32 +684,14 @@ const LogSeverity LOG_DCHECK = LOG_INFO; // above. class BASE_EXPORT LogMessage { public: - LogMessage(const char* file, int line, LogSeverity severity, int ctr); - - // Two special constructors that generate reduced amounts of code at - // LOG call sites for common cases. - // - // Used for LOG(INFO): Implied are: - // severity = LOG_INFO, ctr = 0 - // - // Using this constructor instead of the more complex constructor above - // saves a couple of bytes per call site. - LogMessage(const char* file, int line); - - // Used for LOG(severity) where severity != INFO. Implied - // are: ctr = 0 - // - // Using this constructor instead of the more complex constructor above - // saves a couple of bytes per call site. + // Used for LOG(severity). LogMessage(const char* file, int line, LogSeverity severity); - // A special constructor used for check failures. Takes ownership - // of the given string. - // Implied severity = LOG_FATAL + // Used for CHECK_EQ(), etc. Takes ownership of the given string. + // Implied severity = LOG_FATAL. LogMessage(const char* file, int line, std::string* result); - // A special constructor used for check failures, with the option to - // specify severity. Takes ownership of the given string. + // Used for DCHECK_EQ(), etc. Takes ownership of the given string. LogMessage(const char* file, int line, LogSeverity severity, std::string* result); @@ -890,6 +759,7 @@ typedef int SystemErrorCode; // Alias for ::GetLastError() on Windows and errno on POSIX. Avoids having to // pull in windows.h just for GetLastError() and DWORD. BASE_EXPORT SystemErrorCode GetLastSystemErrorCode(); +BASE_EXPORT std::string SystemErrorCodeToString(SystemErrorCode error_code); #if defined(OS_WIN) // Appends a formatted system message of the GetLastError() type. @@ -898,12 +768,6 @@ class BASE_EXPORT Win32ErrorLogMessage { Win32ErrorLogMessage(const char* file, int line, LogSeverity severity, - SystemErrorCode err, - const char* module); - - Win32ErrorLogMessage(const char* file, - int line, - LogSeverity severity, SystemErrorCode err); // Appends the error message before destructing the encapsulated class. @@ -913,8 +777,6 @@ class BASE_EXPORT Win32ErrorLogMessage { private: SystemErrorCode err_; - // Optional name of the module defining the error. - const char* module_; LogMessage log_message_; DISALLOW_COPY_AND_ASSIGN(Win32ErrorLogMessage); diff --git a/chromium/base/logging_unittest.cc b/chromium/base/logging_unittest.cc index 4996abc2240..f823d6ee2d7 100644 --- a/chromium/base/logging_unittest.cc +++ b/chromium/base/logging_unittest.cc @@ -17,11 +17,11 @@ using ::testing::Return; // Needs to be global since log assert handlers can't maintain state. int log_sink_call_count = 0; -#if !LOGGING_IS_OFFICIAL_BUILD +#if !defined(OFFICIAL_BUILD) || defined(DCHECK_ALWAYS_ON) || !defined(NDEBUG) void LogSink(const std::string& str) { ++log_sink_call_count; } -#endif // !LOGGING_IS_OFFICIAL_BUILD +#endif // Class to make sure any manipulations we do to the min log level are // contained (i.e., do not affect other unit tests). @@ -32,7 +32,6 @@ class LogStateSaver { ~LogStateSaver() { SetMinLogLevel(old_min_log_level_); SetLogAssertHandler(NULL); - SetLogReportHandler(NULL); log_sink_call_count = 0; } @@ -54,11 +53,7 @@ class MockLogSource { TEST_F(LoggingTest, BasicLogging) { MockLogSource mock_log_source; - const int kExpectedDebugOrReleaseCalls = 6; - const int kExpectedDebugCalls = 6; - const int kExpectedCalls = - kExpectedDebugOrReleaseCalls + (DEBUG_MODE ? kExpectedDebugCalls : 0); - EXPECT_CALL(mock_log_source, Log()).Times(kExpectedCalls). + EXPECT_CALL(mock_log_source, Log()).Times(DEBUG_MODE ? 16 : 8). WillRepeatedly(Return("log message")); SetMinLogLevel(LOG_INFO); @@ -76,6 +71,8 @@ TEST_F(LoggingTest, BasicLogging) { PLOG_IF(INFO, true) << mock_log_source.Log(); VLOG(0) << mock_log_source.Log(); VLOG_IF(0, true) << mock_log_source.Log(); + VPLOG(0) << mock_log_source.Log(); + VPLOG_IF(0, true) << mock_log_source.Log(); DLOG(INFO) << mock_log_source.Log(); DLOG_IF(INFO, true) << mock_log_source.Log(); @@ -83,6 +80,8 @@ TEST_F(LoggingTest, BasicLogging) { DPLOG_IF(INFO, true) << mock_log_source.Log(); DVLOG(0) << mock_log_source.Log(); DVLOG_IF(0, true) << mock_log_source.Log(); + DVPLOG(0) << mock_log_source.Log(); + DVPLOG_IF(0, true) << mock_log_source.Log(); } TEST_F(LoggingTest, LogIsOn) { @@ -96,7 +95,6 @@ TEST_F(LoggingTest, LogIsOn) { EXPECT_TRUE(LOG_IS_ON(INFO)); EXPECT_TRUE(LOG_IS_ON(WARNING)); EXPECT_TRUE(LOG_IS_ON(ERROR)); - EXPECT_TRUE(LOG_IS_ON(ERROR_REPORT)); EXPECT_TRUE(LOG_IS_ON(FATAL)); EXPECT_TRUE(LOG_IS_ON(DFATAL)); @@ -104,7 +102,6 @@ TEST_F(LoggingTest, LogIsOn) { EXPECT_FALSE(LOG_IS_ON(INFO)); EXPECT_TRUE(LOG_IS_ON(WARNING)); EXPECT_TRUE(LOG_IS_ON(ERROR)); - EXPECT_TRUE(LOG_IS_ON(ERROR_REPORT)); EXPECT_TRUE(LOG_IS_ON(FATAL)); EXPECT_TRUE(LOG_IS_ON(DFATAL)); @@ -112,33 +109,14 @@ TEST_F(LoggingTest, LogIsOn) { EXPECT_FALSE(LOG_IS_ON(INFO)); EXPECT_FALSE(LOG_IS_ON(WARNING)); EXPECT_TRUE(LOG_IS_ON(ERROR)); - EXPECT_TRUE(LOG_IS_ON(ERROR_REPORT)); EXPECT_TRUE(LOG_IS_ON(FATAL)); EXPECT_TRUE(LOG_IS_ON(DFATAL)); - SetMinLogLevel(LOG_ERROR_REPORT); - EXPECT_FALSE(LOG_IS_ON(INFO)); - EXPECT_FALSE(LOG_IS_ON(WARNING)); - EXPECT_FALSE(LOG_IS_ON(ERROR)); - EXPECT_TRUE(LOG_IS_ON(ERROR_REPORT)); - EXPECT_TRUE(LOG_IS_ON(FATAL)); - EXPECT_TRUE(kDfatalIsFatal == LOG_IS_ON(DFATAL)); - - // LOG_IS_ON(ERROR_REPORT) should always be true. - SetMinLogLevel(LOG_FATAL); - EXPECT_FALSE(LOG_IS_ON(INFO)); - EXPECT_FALSE(LOG_IS_ON(WARNING)); - EXPECT_FALSE(LOG_IS_ON(ERROR)); - EXPECT_TRUE(LOG_IS_ON(ERROR_REPORT)); - EXPECT_TRUE(LOG_IS_ON(FATAL)); - EXPECT_TRUE(kDfatalIsFatal == LOG_IS_ON(DFATAL)); - - // So should LOG_IS_ON(FATAL). + // LOG_IS_ON(FATAL) should always be true. SetMinLogLevel(LOG_FATAL + 1); EXPECT_FALSE(LOG_IS_ON(INFO)); EXPECT_FALSE(LOG_IS_ON(WARNING)); EXPECT_FALSE(LOG_IS_ON(ERROR)); - EXPECT_TRUE(LOG_IS_ON(ERROR_REPORT)); EXPECT_TRUE(LOG_IS_ON(FATAL)); EXPECT_TRUE(kDfatalIsFatal == LOG_IS_ON(DFATAL)); } @@ -159,6 +137,8 @@ TEST_F(LoggingTest, LoggingIsLazy) { PLOG_IF(INFO, false) << mock_log_source.Log(); VLOG(1) << mock_log_source.Log(); VLOG_IF(1, true) << mock_log_source.Log(); + VPLOG(1) << mock_log_source.Log(); + VPLOG_IF(1, true) << mock_log_source.Log(); DLOG(INFO) << mock_log_source.Log(); DLOG_IF(INFO, true) << mock_log_source.Log(); @@ -166,10 +146,12 @@ TEST_F(LoggingTest, LoggingIsLazy) { DPLOG_IF(INFO, true) << mock_log_source.Log(); DVLOG(1) << mock_log_source.Log(); DVLOG_IF(1, true) << mock_log_source.Log(); + DVPLOG(1) << mock_log_source.Log(); + DVPLOG_IF(1, true) << mock_log_source.Log(); } // Official builds have CHECKs directly call BreakDebugger. -#if !LOGGING_IS_OFFICIAL_BUILD +#if !defined(OFFICIAL_BUILD) TEST_F(LoggingTest, CheckStreamsAreLazy) { MockLogSource mock_log_source, uncalled_mock_log_source; @@ -204,9 +186,10 @@ TEST_F(LoggingTest, DebugLoggingReleaseBehavior) { TEST_F(LoggingTest, DcheckStreamsAreLazy) { MockLogSource mock_log_source; EXPECT_CALL(mock_log_source, Log()).Times(0); -#if !LOGGING_IS_OFFICIAL_BUILD && defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON) - // Unofficial release build without dcheck enabled. - set_dcheck_state(DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS); +#if DCHECK_IS_ON + DCHECK(true) << mock_log_source.Log(); + DCHECK_EQ(0, 0) << mock_log_source.Log(); +#else DCHECK(mock_log_source.Log()) << mock_log_source.Log(); DPCHECK(mock_log_source.Log()) << mock_log_source.Log(); DCHECK_EQ(0, 0) << mock_log_source.Log(); @@ -216,36 +199,29 @@ TEST_F(LoggingTest, DcheckStreamsAreLazy) { } TEST_F(LoggingTest, Dcheck) { -#if LOGGING_IS_OFFICIAL_BUILD - // Official build. - EXPECT_FALSE(DCHECK_IS_ON()); - EXPECT_FALSE(DLOG_IS_ON(DCHECK)); -#elif defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON) - // Unofficial release build. - set_dcheck_state(ENABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS); - SetLogReportHandler(&LogSink); - EXPECT_TRUE(DCHECK_IS_ON()); +#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON) + // Release build. + EXPECT_FALSE(DCHECK_IS_ON); EXPECT_FALSE(DLOG_IS_ON(DCHECK)); #elif defined(NDEBUG) && defined(DCHECK_ALWAYS_ON) - // Unofficial release build with real DCHECKS. - set_dcheck_state(ENABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS); + // Release build with real DCHECKS. SetLogAssertHandler(&LogSink); - EXPECT_TRUE(DCHECK_IS_ON()); + EXPECT_TRUE(DCHECK_IS_ON); EXPECT_FALSE(DLOG_IS_ON(DCHECK)); #else - // Unofficial debug build. + // Debug build. SetLogAssertHandler(&LogSink); - EXPECT_TRUE(DCHECK_IS_ON()); + EXPECT_TRUE(DCHECK_IS_ON); EXPECT_TRUE(DLOG_IS_ON(DCHECK)); -#endif // defined(LOGGING_IS_OFFICIAL_BUILD) +#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.cc b/chromium/base/logging_win.cc index a7146657884..a3c3a5befe0 100644 --- a/chromium/base/logging_win.cc +++ b/chromium/base/logging_win.cc @@ -37,7 +37,6 @@ bool LogEventProvider::LogMessage(logging::LogSeverity severity, level = TRACE_LEVEL_WARNING; break; case LOG_ERROR: - case LOG_ERROR_REPORT: level = TRACE_LEVEL_ERROR; break; case LOG_FATAL: diff --git a/chromium/base/mac/bind_objc_block.h b/chromium/base/mac/bind_objc_block.h index 75da437b5c5..9deb2d22e75 100644 --- a/chromium/base/mac/bind_objc_block.h +++ b/chromium/base/mac/bind_objc_block.h @@ -14,7 +14,13 @@ // BindBlock builds a callback from an Objective-C block. Example usages: // // Closure closure = BindBlock(^{DoSomething();}); +// // Callback<int(void)> callback = BindBlock(^{return 42;}); +// +// Callback<void(const std::string&, const std::string&)> callback = +// BindBlock(^(const std::string& arg0, const std::string& arg1) { +// ... +// }); namespace base { @@ -33,6 +39,12 @@ R RunBlock(base::mac::ScopedBlock<R(^)(A1)> block, A1 a) { return extracted_block(a); } +template<typename R, typename A1, typename A2> +R RunBlock(base::mac::ScopedBlock<R(^)(A1, A2)> block, A1 a, A2 b) { + R(^extracted_block)(A1, A2) = block.get(); + return extracted_block(a, b); +} + } // namespace internal // Construct a callback with no argument from an objective-C block. @@ -49,6 +61,13 @@ base::Callback<R(A1)> BindBlock(R(^block)(A1)) { base::mac::ScopedBlock<R(^)(A1)>(Block_copy(block))); } +// Construct a callback with two arguments from an objective-C block. +template<typename R, typename A1, typename A2> +base::Callback<R(A1, A2)> BindBlock(R(^block)(A1, A2)) { + return base::Bind(&base::internal::RunBlock<R, A1, A2>, + base::mac::ScopedBlock<R(^)(A1, A2)>(Block_copy(block))); +} + } // namespace base #endif // BASE_MAC_BIND_OBJC_BLOCK_H_ diff --git a/chromium/base/mac/bind_objc_block_unittest.mm b/chromium/base/mac/bind_objc_block_unittest.mm index a4bcd76dbb8..c72fd4a8c20 100644 --- a/chromium/base/mac/bind_objc_block_unittest.mm +++ b/chromium/base/mac/bind_objc_block_unittest.mm @@ -4,6 +4,8 @@ #import "base/mac/bind_objc_block.h" +#include <string> + #include "base/callback.h" #include "base/bind.h" #include "base/callback_helpers.h" @@ -51,4 +53,15 @@ TEST(BindObjcBlockTest, TestArgument) { EXPECT_EQ(kArgument + 1, c.Run(kArgument)); } +TEST(BindObjcBlockTest, TestTwoArguments) { + std::string result; + std::string* ptr = &result; + base::Callback<void(const std::string&, const std::string&)> c = + base::BindBlock(^(const std::string& a, const std::string& b) { + *ptr = a + b; + }); + c.Run("forty", "two"); + EXPECT_EQ(result, "fortytwo"); +} + } // namespace diff --git a/chromium/base/mac/cocoa_protocols.h b/chromium/base/mac/cocoa_protocols.h index e83fcbb2996..e10001f6be0 100644 --- a/chromium/base/mac/cocoa_protocols.h +++ b/chromium/base/mac/cocoa_protocols.h @@ -7,9 +7,9 @@ #import <Cocoa/Cocoa.h> -// GTM also maintinas a list of empty protocols, but only the ones the library +// GTM also maintains a list of empty protocols, but only the ones the library // requires. Augment that below. -#import "third_party/GTM/GTMDefines.h" +#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 @@ -30,13 +30,6 @@ DEFINE_EMPTY_PROTOCOL(ICCameraDeviceDownloadDelegate) #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 - -DEFINE_EMPTY_PROTOCOL(NSUserNotificationCenterDelegate) - -#endif // MAC_OS_X_VERSION_10_8 - #undef DEFINE_EMPTY_PROTOCOL #endif // BASE_COCOA_PROTOCOLS_MAC_H_ diff --git a/chromium/base/mac/foundation_util_unittest.mm b/chromium/base/mac/foundation_util_unittest.mm index 3b72b1225a8..916a13bd7fa 100644 --- a/chromium/base/mac/foundation_util_unittest.mm +++ b/chromium/base/mac/foundation_util_unittest.mm @@ -5,9 +5,12 @@ #include "base/mac/foundation_util.h" #include "base/basictypes.h" +#include "base/compiler_specific.h" #include "base/files/file_path.h" +#include "base/format_macros.h" #include "base/mac/scoped_cftyperef.h" #include "base/mac/scoped_nsautorelease_pool.h" +#include "base/strings/stringprintf.h" #include "testing/gtest/include/gtest/gtest.h" #import "testing/gtest_mac.h" @@ -314,5 +317,75 @@ TEST(FoundationUtilTest, NSStringToFilePath) { EXPECT_EQ(FilePath("/a/b"), NSStringToFilePath(@"/a/b")); } +TEST(StringNumberConversionsTest, FormatNSInteger) { + // The PRI[dxu]NS macro assumes that NSInteger is a typedef to "int" on + // 32-bit architecture and a typedef to "long" on 64-bit architecture + // (respectively "unsigned int" and "unsigned long" for NSUInteger). Use + // pointer incompatibility to validate this at compilation. +#if defined(ARCH_CPU_64_BITS) + typedef long FormatNSIntegerAsType; + typedef unsigned long FormatNSUIntegerAsType; +#else + typedef int FormatNSIntegerAsType; + typedef unsigned int FormatNSUIntegerAsType; +#endif // defined(ARCH_CPU_64_BITS) + + NSInteger some_nsinteger; + FormatNSIntegerAsType* pointer_to_some_nsinteger ALLOW_UNUSED = + &some_nsinteger; + + NSUInteger some_nsuinteger; + FormatNSUIntegerAsType* pointer_to_some_nsuinteger ALLOW_UNUSED = + &some_nsuinteger; + + // Check that format specifier works correctly for NSInteger. + const struct { + NSInteger value; + const char* expected; + const char* expected_hex; + } nsinteger_cases[] = { +#if !defined(ARCH_CPU_64_BITS) + {12345678, "12345678", "bc614e"}, + {-12345678, "-12345678", "ff439eb2"}, +#else + {12345678, "12345678", "bc614e"}, + {-12345678, "-12345678", "ffffffffff439eb2"}, + {137451299150l, "137451299150", "2000bc614e"}, + {-137451299150l, "-137451299150", "ffffffdfff439eb2"}, +#endif // !defined(ARCH_CPU_64_BITS) + }; + + for (size_t i = 0; i < arraysize(nsinteger_cases); ++i) { + EXPECT_EQ(nsinteger_cases[i].expected, + StringPrintf("%" PRIdNS, nsinteger_cases[i].value)); + EXPECT_EQ(nsinteger_cases[i].expected_hex, + StringPrintf("%" PRIxNS, nsinteger_cases[i].value)); + } + + // Check that format specifier works correctly for NSUInteger. + const struct { + NSUInteger value; + const char* expected; + const char* expected_hex; + } nsuinteger_cases[] = { +#if !defined(ARCH_CPU_64_BITS) + {12345678u, "12345678", "bc614e"}, + {4282621618u, "4282621618", "ff439eb2"}, +#else + {12345678u, "12345678", "bc614e"}, + {4282621618u, "4282621618", "ff439eb2"}, + {137451299150ul, "137451299150", "2000bc614e"}, + {18446743936258252466ul, "18446743936258252466", "ffffffdfff439eb2"}, +#endif // !defined(ARCH_CPU_64_BITS) + }; + + for (size_t i = 0; i < arraysize(nsuinteger_cases); ++i) { + EXPECT_EQ(nsuinteger_cases[i].expected, + StringPrintf("%" PRIuNS, nsuinteger_cases[i].value)); + EXPECT_EQ(nsuinteger_cases[i].expected_hex, + StringPrintf("%" PRIxNS, nsuinteger_cases[i].value)); + } +} + } // namespace mac } // namespace base diff --git a/chromium/base/mac/launch_services_util.h b/chromium/base/mac/launch_services_util.h index d4aa9ffcbeb..0c52ca94c52 100644 --- a/chromium/base/mac/launch_services_util.h +++ b/chromium/base/mac/launch_services_util.h @@ -22,7 +22,7 @@ namespace mac { // |out_psn|, if not NULL, will be set to the process serial number of the // application's main process if the app was successfully launched. // Returns true if the app was successfully launched. -BASE_EXPORT bool OpenApplicationWithPath(const base::FilePath& bundle_path, +BASE_EXPORT bool OpenApplicationWithPath(const FilePath& bundle_path, const CommandLine& command_line, LSLaunchFlags launch_flags, ProcessSerialNumber* out_psn); diff --git a/chromium/base/mac/mac_logging.h b/chromium/base/mac/mac_logging.h index 9a0003e9ad8..1081490ec4a 100644 --- a/chromium/base/mac/mac_logging.h +++ b/chromium/base/mac/mac_logging.h @@ -5,6 +5,8 @@ #ifndef BASE_MAC_MAC_LOGGING_H_ #define BASE_MAC_MAC_LOGGING_H_ +#include "base/base_export.h" +#include "base/basictypes.h" #include "base/logging.h" #include "build/build_config.h" @@ -43,6 +45,12 @@ class BASE_EXPORT OSStatusLogMessage : public logging::LogMessage { } // namespace logging +#if defined(NDEBUG) +#define MAC_DVLOG_IS_ON(verbose_level) 0 +#else +#define MAC_DVLOG_IS_ON(verbose_level) VLOG_IS_ON(verbose_level) +#endif + #define OSSTATUS_LOG_STREAM(severity, status) \ COMPACT_GOOGLE_LOG_EX_ ## severity(OSStatusLogMessage, status).stream() #define OSSTATUS_VLOG_STREAM(verbose_level, status) \ @@ -73,15 +81,15 @@ class BASE_EXPORT OSStatusLogMessage : public logging::LogMessage { DLOG_IS_ON(severity) && (condition)) #define OSSTATUS_DVLOG(verbose_level, status) \ - LAZY_STREAM(OSSTATUS_VPLOG_STREAM(verbose_level, status), \ - DVLOG_IS_ON(verbose_level)) + LAZY_STREAM(OSSTATUS_VLOG_STREAM(verbose_level, status), \ + MAC_DVLOG_IS_ON(verbose_level)) #define OSSTATUS_DVLOG_IF(verbose_level, condition, status) \ - LAZY_STREAM(OSSTATUS_VPLOG_STREAM(verbose_level, status) \ - DVLOG_IS_ON(verbose_level) && (condition)) + 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)) \ + 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 e827f37c9fb..db399a8ca06 100644 --- a/chromium/base/mac/mac_util.h +++ b/chromium/base/mac/mac_util.h @@ -113,6 +113,12 @@ BASE_EXPORT void RemoveFromLoginItems(); BASE_EXPORT bool WasLaunchedAsLoginOrResumeItem(); // Returns true if the current process was automatically launched as a +// 'Login Item' or via Resume, and the 'Reopen windows when logging back in' +// checkbox was selected by the user. This indicates that the previous +// session should be restored. +BASE_EXPORT bool WasLaunchedAsLoginItemRestoreState(); + +// Returns true if the current process was automatically launched as a // 'Login Item' with 'hide on startup' flag. Used to suppress opening windows. BASE_EXPORT bool WasLaunchedAsHiddenLoginItem(); @@ -140,17 +146,23 @@ BASE_EXPORT bool IsOSMountainLionOrLater(); // Mavericks is Mac OS X 10.9, Darwin 13. BASE_EXPORT bool IsOSMavericks(); +BASE_EXPORT bool IsOSMavericksOrEarlier(); BASE_EXPORT bool IsOSMavericksOrLater(); +// Yosemite is Mac OS X 10.10, Darwin 14. +BASE_EXPORT bool IsOSYosemite(); +BASE_EXPORT bool IsOSYosemiteOrLater(); + // This should be infrequently used. It only makes sense to use this to avoid // codepaths that are very likely to break on future (unreleased, untested, // unborn) OS releases, or to log when the OS is newer than any known version. -BASE_EXPORT bool IsOSLaterThanMavericks_DontCallThis(); +BASE_EXPORT bool IsOSLaterThanYosemite_DontCallThis(); // Inline functions that are redundant due to version ranges being mutually- // exclusive. inline bool IsOSLionOrEarlier() { return !IsOSMountainLionOrLater(); } inline bool IsOSMountainLionOrEarlier() { return !IsOSMavericksOrLater(); } +inline bool IsOSMavericksOrEarlier() { return !IsOSYosemiteOrLater(); } // When the deployment target is set, the code produced cannot run on earlier // OS releases. That enables some of the IsOS* family to be implemented as @@ -192,7 +204,19 @@ inline bool IsOSMavericksOrLater() { return true; } MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_9 #define BASE_MAC_MAC_UTIL_H_INLINED_GT_10_9 inline bool IsOSMavericks() { return false; } -inline bool IsOSLaterThanMavericks_DontCallThis() { return true; } +#endif + +#if defined(MAC_OS_X_VERSION_10_10) && \ + MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10 +#define BASE_MAC_MAC_UTIL_H_INLINED_GE_10_10 +inline bool IsOSYosemiteOrLater() { return true; } +#endif + +#if defined(MAC_OS_X_VERSION_10_10) && \ + MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_10 +#define BASE_MAC_MAC_UTIL_H_INLINED_GT_10_10 +inline bool IsOSYosemite() { return false; } +inline bool IsOSLaterThanYosemite_DontCallThis() { return true; } #endif // Retrieve the system's model identifier string from the IOKit registry: diff --git a/chromium/base/mac/mac_util.mm b/chromium/base/mac/mac_util.mm index c2565991abb..4ff1c879237 100644 --- a/chromium/base/mac/mac_util.mm +++ b/chromium/base/mac/mac_util.mm @@ -20,6 +20,7 @@ #include "base/mac/scoped_cftyperef.h" #include "base/mac/scoped_ioobject.h" #include "base/mac/scoped_nsobject.h" +#include "base/mac/sdk_forward_declarations.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_piece.h" #include "base/strings/sys_string_conversions.h" @@ -27,16 +28,6 @@ namespace base { namespace mac { -// 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 - -enum { - NSApplicationPresentationFullScreen = 1 << 10 -}; - -#endif // MAC_OS_X_VERSION_10_7 - namespace { // The current count of outstanding requests for full screen mode from browser @@ -385,6 +376,30 @@ bool WasLaunchedAsLoginOrResumeItem() { return result == YES; } +bool WasLaunchedAsLoginItemRestoreState() { + // "Reopen windows..." option was added for Lion. Prior OS versions should + // not have this behavior. + if (IsOSSnowLeopard() || !WasLaunchedAsLoginOrResumeItem()) + return false; + + CFStringRef app = CFSTR("com.apple.loginwindow"); + CFStringRef save_state = CFSTR("TALLogoutSavesState"); + ScopedCFTypeRef<CFPropertyListRef> plist( + CFPreferencesCopyAppValue(save_state, app)); + // According to documentation, com.apple.loginwindow.plist does not exist on a + // fresh installation until the user changes a login window setting. The + // "reopen windows" option is checked by default, so the plist would exist had + // the user unchecked it. + // https://developer.apple.com/library/mac/documentation/macosx/conceptual/bpsystemstartup/chapters/CustomLogin.html + if (!plist) + return true; + + if (CFBooleanRef restore_state = base::mac::CFCast<CFBooleanRef>(plist)) + return CFBooleanGetValue(restore_state); + + return false; +} + bool WasLaunchedAsHiddenLoginItem() { if (!WasLaunchedAsLoginOrResumeItem()) return false; @@ -469,7 +484,7 @@ int MacOSXMinorVersionInternal() { // immediate death. CHECK(darwin_major_version >= 6); int mac_os_x_minor_version = darwin_major_version - 4; - DLOG_IF(WARNING, darwin_major_version > 13) << "Assuming Darwin " + DLOG_IF(WARNING, darwin_major_version > 14) << "Assuming Darwin " << base::IntToString(darwin_major_version) << " is Mac OS X 10." << base::IntToString(mac_os_x_minor_version); @@ -488,6 +503,7 @@ enum { LION_MINOR_VERSION = 7, MOUNTAIN_LION_MINOR_VERSION = 8, MAVERICKS_MINOR_VERSION = 9, + YOSEMITE_MINOR_VERSION = 10, }; } // namespace @@ -534,9 +550,21 @@ bool IsOSMavericksOrLater() { } #endif -#if !defined(BASE_MAC_MAC_UTIL_H_INLINED_GT_10_9) -bool IsOSLaterThanMavericks_DontCallThis() { - return MacOSXMinorVersion() > MAVERICKS_MINOR_VERSION; +#if !defined(BASE_MAC_MAC_UTIL_H_INLINED_GT_10_10) +bool IsOSYosemite() { + return MacOSXMinorVersion() == YOSEMITE_MINOR_VERSION; +} +#endif + +#if !defined(BASE_MAC_MAC_UTIL_H_INLINED_GE_10_10) +bool IsOSYosemiteOrLater() { + return MacOSXMinorVersion() >= YOSEMITE_MINOR_VERSION; +} +#endif + +#if !defined(BASE_MAC_MAC_UTIL_H_INLINED_GT_10_10) +bool IsOSLaterThanYosemite_DontCallThis() { + return MacOSXMinorVersion() > YOSEMITE_MINOR_VERSION; } #endif diff --git a/chromium/base/mac/mac_util_unittest.mm b/chromium/base/mac/mac_util_unittest.mm index 1b56814abec..956036eaf1b 100644 --- a/chromium/base/mac/mac_util_unittest.mm +++ b/chromium/base/mac/mac_util_unittest.mm @@ -108,7 +108,7 @@ TEST_F(MacUtilTest, TestExcludeFileFromBackups) { const char dummy_data[] = "All your base are belong to us!"; // Dump something real into the file. ASSERT_EQ(static_cast<int>(arraysize(dummy_data)), - file_util::WriteFile(dummy_file_path, dummy_data, arraysize(dummy_data))); + WriteFile(dummy_file_path, dummy_data, arraysize(dummy_data))); NSString* fileURLString = [NSString stringWithUTF8String:dummy_file_path.value().c_str()]; NSURL* fileURL = [NSURL URLWithString:fileURLString]; @@ -150,8 +150,11 @@ TEST_F(MacUtilTest, IsOSEllipsis) { EXPECT_TRUE(IsOSMountainLionOrEarlier()); EXPECT_FALSE(IsOSMountainLionOrLater()); EXPECT_FALSE(IsOSMavericks()); + EXPECT_TRUE(IsOSMavericksOrEarlier()); EXPECT_FALSE(IsOSMavericksOrLater()); - EXPECT_FALSE(IsOSLaterThanMavericks_DontCallThis()); + EXPECT_FALSE(IsOSYosemite()); + EXPECT_FALSE(IsOSYosemiteOrLater()); + EXPECT_FALSE(IsOSLaterThanYosemite_DontCallThis()); } else if (minor == 7) { EXPECT_FALSE(IsOSSnowLeopard()); EXPECT_TRUE(IsOSLion()); @@ -161,8 +164,11 @@ TEST_F(MacUtilTest, IsOSEllipsis) { EXPECT_TRUE(IsOSMountainLionOrEarlier()); EXPECT_FALSE(IsOSMountainLionOrLater()); EXPECT_FALSE(IsOSMavericks()); + EXPECT_TRUE(IsOSMavericksOrEarlier()); EXPECT_FALSE(IsOSMavericksOrLater()); - EXPECT_FALSE(IsOSLaterThanMavericks_DontCallThis()); + EXPECT_FALSE(IsOSYosemite()); + EXPECT_FALSE(IsOSYosemiteOrLater()); + EXPECT_FALSE(IsOSLaterThanYosemite_DontCallThis()); } else if (minor == 8) { EXPECT_FALSE(IsOSSnowLeopard()); EXPECT_FALSE(IsOSLion()); @@ -172,8 +178,11 @@ TEST_F(MacUtilTest, IsOSEllipsis) { EXPECT_TRUE(IsOSMountainLionOrEarlier()); EXPECT_TRUE(IsOSMountainLionOrLater()); EXPECT_FALSE(IsOSMavericks()); + EXPECT_TRUE(IsOSMavericksOrEarlier()); EXPECT_FALSE(IsOSMavericksOrLater()); - EXPECT_FALSE(IsOSLaterThanMavericks_DontCallThis()); + EXPECT_FALSE(IsOSYosemite()); + EXPECT_FALSE(IsOSYosemiteOrLater()); + EXPECT_FALSE(IsOSLaterThanYosemite_DontCallThis()); } else if (minor == 9) { EXPECT_FALSE(IsOSSnowLeopard()); EXPECT_FALSE(IsOSLion()); @@ -183,10 +192,27 @@ TEST_F(MacUtilTest, IsOSEllipsis) { EXPECT_FALSE(IsOSMountainLionOrEarlier()); EXPECT_TRUE(IsOSMountainLionOrLater()); EXPECT_TRUE(IsOSMavericks()); + EXPECT_TRUE(IsOSMavericksOrEarlier()); EXPECT_TRUE(IsOSMavericksOrLater()); - EXPECT_FALSE(IsOSLaterThanMavericks_DontCallThis()); + EXPECT_FALSE(IsOSYosemite()); + EXPECT_FALSE(IsOSYosemiteOrLater()); + EXPECT_FALSE(IsOSLaterThanYosemite_DontCallThis()); + } else if (minor == 10) { + EXPECT_FALSE(IsOSSnowLeopard()); + EXPECT_FALSE(IsOSLion()); + EXPECT_FALSE(IsOSLionOrEarlier()); + EXPECT_TRUE(IsOSLionOrLater()); + EXPECT_FALSE(IsOSMountainLion()); + EXPECT_FALSE(IsOSMountainLionOrEarlier()); + EXPECT_TRUE(IsOSMountainLionOrLater()); + EXPECT_FALSE(IsOSMavericks()); + EXPECT_FALSE(IsOSMavericksOrEarlier()); + EXPECT_TRUE(IsOSMavericksOrLater()); + EXPECT_TRUE(IsOSYosemite()); + EXPECT_TRUE(IsOSYosemiteOrLater()); + EXPECT_FALSE(IsOSLaterThanYosemite_DontCallThis()); } else { - // Not five, six, seven, eight, or nine. Ah, ah, ah. + // Not six, seven, eight, nine, or ten. Ah, ah, ah. EXPECT_TRUE(false); } } else { diff --git a/chromium/base/mac/mach_logging.cc b/chromium/base/mac/mach_logging.cc new file mode 100644 index 00000000000..c5ff85e662f --- /dev/null +++ b/chromium/base/mac/mach_logging.cc @@ -0,0 +1,87 @@ +// 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/mach_logging.h" + +#include <iomanip> +#include <string> + +#include "base/strings/stringprintf.h" + +#if !defined(OS_IOS) +#include <servers/bootstrap.h> +#endif // !OS_IOS + +namespace { + +std::string FormatMachErrorNumber(mach_error_t mach_err) { + // For the os/kern subsystem, give the error number in decimal as in + // <mach/kern_return.h>. Otherwise, give it in hexadecimal to make it easier + // to visualize the various bits. See <mach/error.h>. + if (mach_err >= 0 && mach_err < KERN_RETURN_MAX) { + return base::StringPrintf(" (%d)", mach_err); + } + return base::StringPrintf(" (0x%08x)", mach_err); +} + +} // namespace + +namespace logging { + +MachLogMessage::MachLogMessage(const char* file_path, + int line, + LogSeverity severity, + mach_error_t mach_err) + : LogMessage(file_path, line, severity), + mach_err_(mach_err) { +} + +MachLogMessage::~MachLogMessage() { + stream() << ": " + << mach_error_string(mach_err_) + << FormatMachErrorNumber(mach_err_); +} + +#if !defined(OS_IOS) + +BootstrapLogMessage::BootstrapLogMessage(const char* file_path, + int line, + LogSeverity severity, + kern_return_t bootstrap_err) + : LogMessage(file_path, line, severity), + bootstrap_err_(bootstrap_err) { +} + +BootstrapLogMessage::~BootstrapLogMessage() { + stream() << ": " + << bootstrap_strerror(bootstrap_err_); + + switch (bootstrap_err_) { + case BOOTSTRAP_SUCCESS: + case BOOTSTRAP_NOT_PRIVILEGED: + case BOOTSTRAP_NAME_IN_USE: + case BOOTSTRAP_UNKNOWN_SERVICE: + case BOOTSTRAP_SERVICE_ACTIVE: + case BOOTSTRAP_BAD_COUNT: + case BOOTSTRAP_NO_MEMORY: + case BOOTSTRAP_NO_CHILDREN: { + // Show known bootstrap errors in decimal because that's how they're + // defined in <servers/bootstrap.h>. + stream() << " (" << bootstrap_err_ << ")"; + break; + } + + default: { + // bootstrap_strerror passes unknown errors to mach_error_string, so + // format them as they would be if they were handled by + // MachErrorMessage. + stream() << FormatMachErrorNumber(bootstrap_err_); + break; + } + } +} + +#endif // !OS_IOS + +} // namespace logging diff --git a/chromium/base/mac/mach_logging.h b/chromium/base/mac/mach_logging.h new file mode 100644 index 00000000000..a9b3b65d648 --- /dev/null +++ b/chromium/base/mac/mach_logging.h @@ -0,0 +1,167 @@ +// 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_MACH_LOGGING_H_ +#define BASE_MAC_MACH_LOGGING_H_ + +#include <mach/mach.h> + +#include "base/base_export.h" +#include "base/basictypes.h" +#include "base/logging.h" +#include "build/build_config.h" + +// Use the MACH_LOG family of macros along with a mach_error_t (kern_return_t) +// containing a Mach error. The error value will be decoded so that logged +// messages explain the error. +// +// Use the BOOTSTRAP_LOG family of macros specifically for errors that occur +// while interoperating with the bootstrap subsystem. These errors will first +// be looked up as bootstrap error messages. If no match is found, they will +// be treated as generic Mach errors, as in MACH_LOG. +// +// Examples: +// +// kern_return_t kr = mach_timebase_info(&info); +// if (kr != KERN_SUCCESS) { +// MACH_LOG(ERROR, kr) << "mach_timebase_info"; +// } +// +// kr = vm_deallocate(task, address, size); +// MACH_DCHECK(kr == KERN_SUCCESS, kr) << "vm_deallocate"; + +namespace logging { + +class BASE_EXPORT MachLogMessage : public logging::LogMessage { + public: + MachLogMessage(const char* file_path, + int line, + LogSeverity severity, + mach_error_t mach_err); + ~MachLogMessage(); + + private: + mach_error_t mach_err_; + + DISALLOW_COPY_AND_ASSIGN(MachLogMessage); +}; + +} // namespace logging + +#if defined(NDEBUG) +#define MACH_DVLOG_IS_ON(verbose_level) 0 +#else +#define MACH_DVLOG_IS_ON(verbose_level) VLOG_IS_ON(verbose_level) +#endif + +#define MACH_LOG_STREAM(severity, mach_err) \ + COMPACT_GOOGLE_LOG_EX_ ## severity(MachLogMessage, mach_err).stream() +#define MACH_VLOG_STREAM(verbose_level, mach_err) \ + logging::MachLogMessage(__FILE__, __LINE__, \ + -verbose_level, mach_err).stream() + +#define MACH_LOG(severity, mach_err) \ + LAZY_STREAM(MACH_LOG_STREAM(severity, mach_err), LOG_IS_ON(severity)) +#define MACH_LOG_IF(severity, condition, mach_err) \ + LAZY_STREAM(MACH_LOG_STREAM(severity, mach_err), \ + LOG_IS_ON(severity) && (condition)) + +#define MACH_VLOG(verbose_level, mach_err) \ + LAZY_STREAM(MACH_VLOG_STREAM(verbose_level, mach_err), \ + VLOG_IS_ON(verbose_level)) +#define MACH_VLOG_IF(verbose_level, condition, mach_err) \ + LAZY_STREAM(MACH_VLOG_STREAM(verbose_level, mach_err), \ + VLOG_IS_ON(verbose_level) && (condition)) + +#define MACH_CHECK(condition, mach_err) \ + LAZY_STREAM(MACH_LOG_STREAM(FATAL, mach_err), !(condition)) \ + << "Check failed: " # condition << ". " + +#define MACH_DLOG(severity, mach_err) \ + LAZY_STREAM(MACH_LOG_STREAM(severity, mach_err), DLOG_IS_ON(severity)) +#define MACH_DLOG_IF(severity, condition, mach_err) \ + LAZY_STREAM(MACH_LOG_STREAM(severity, mach_err), \ + DLOG_IS_ON(severity) && (condition)) + +#define MACH_DVLOG(verbose_level, mach_err) \ + LAZY_STREAM(MACH_VLOG_STREAM(verbose_level, mach_err), \ + MACH_DVLOG_IS_ON(verbose_level)) +#define MACH_DVLOG_IF(verbose_level, condition, mach_err) \ + 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 << ". " + +#if !defined(OS_IOS) + +namespace logging { + +class BASE_EXPORT BootstrapLogMessage : public logging::LogMessage { + public: + BootstrapLogMessage(const char* file_path, + int line, + LogSeverity severity, + kern_return_t bootstrap_err); + ~BootstrapLogMessage(); + + private: + kern_return_t bootstrap_err_; + + DISALLOW_COPY_AND_ASSIGN(BootstrapLogMessage); +}; + +} // namespace logging + +#define BOOTSTRAP_DVLOG_IS_ON MACH_DVLOG_IS_ON + +#define BOOTSTRAP_LOG_STREAM(severity, bootstrap_err) \ + COMPACT_GOOGLE_LOG_EX_ ## severity(BootstrapLogMessage, \ + bootstrap_err).stream() +#define BOOTSTRAP_VLOG_STREAM(verbose_level, bootstrap_err) \ + logging::BootstrapLogMessage(__FILE__, __LINE__, \ + -verbose_level, bootstrap_err).stream() + +#define BOOTSTRAP_LOG(severity, bootstrap_err) \ + LAZY_STREAM(BOOTSTRAP_LOG_STREAM(severity, \ + bootstrap_err), LOG_IS_ON(severity)) +#define BOOTSTRAP_LOG_IF(severity, condition, bootstrap_err) \ + LAZY_STREAM(BOOTSTRAP_LOG_STREAM(severity, bootstrap_err), \ + LOG_IS_ON(severity) && (condition)) + +#define BOOTSTRAP_VLOG(verbose_level, bootstrap_err) \ + LAZY_STREAM(BOOTSTRAP_VLOG_STREAM(verbose_level, bootstrap_err), \ + VLOG_IS_ON(verbose_level)) +#define BOOTSTRAP_VLOG_IF(verbose_level, condition, bootstrap_err) \ + LAZY_STREAM(BOOTSTRAP_VLOG_STREAM(verbose_level, bootstrap_err), \ + VLOG_IS_ON(verbose_level) && (condition)) + +#define BOOTSTRAP_CHECK(condition, bootstrap_err) \ + LAZY_STREAM(BOOTSTRAP_LOG_STREAM(FATAL, bootstrap_err), !(condition)) \ + << "Check failed: " # condition << ". " + +#define BOOTSTRAP_DLOG(severity, bootstrap_err) \ + LAZY_STREAM(BOOTSTRAP_LOG_STREAM(severity, bootstrap_err), \ + DLOG_IS_ON(severity)) +#define BOOTSTRAP_DLOG_IF(severity, condition, bootstrap_err) \ + LAZY_STREAM(BOOTSTRAP_LOG_STREAM(severity, bootstrap_err), \ + DLOG_IS_ON(severity) && (condition)) + +#define BOOTSTRAP_DVLOG(verbose_level, bootstrap_err) \ + LAZY_STREAM(BOOTSTRAP_VLOG_STREAM(verbose_level, bootstrap_err), \ + BOOTSTRAP_DVLOG_IS_ON(verbose_level)) +#define BOOTSTRAP_DVLOG_IF(verbose_level, condition, bootstrap_err) \ + 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 << ". " + +#endif // !OS_IOS + +#endif // BASE_MAC_MACH_LOGGING_H_ diff --git a/chromium/base/mac/os_crash_dumps.cc b/chromium/base/mac/os_crash_dumps.cc index e6b0996ac6d..5d65b469ddd 100644 --- a/chromium/base/mac/os_crash_dumps.cc +++ b/chromium/base/mac/os_crash_dumps.cc @@ -30,11 +30,14 @@ void DisableOSCrashDumps() { // bsd/uxkern/ux_exception.c and machine_exception() in xnu's // bsd/dev/*/unix_signal.c. const int signals_to_intercept[] = { + // Hardware faults SIGILL, // EXC_BAD_INSTRUCTION SIGTRAP, // EXC_BREAKPOINT SIGFPE, // EXC_ARITHMETIC SIGBUS, // EXC_BAD_ACCESS - SIGSEGV // EXC_BAD_ACCESS + SIGSEGV, // EXC_BAD_ACCESS + // Not a hardware fault + SIGABRT }; // For all these signals, just wire things up so we exit immediately. @@ -47,9 +50,9 @@ void DisableOSCrashDumps() { act.sa_flags = SA_ONSTACK; if (sigemptyset(&act.sa_mask) != 0) - DLOG_ERRNO(FATAL) << "sigemptyset() failed"; + DPLOG(FATAL) << "sigemptyset() failed"; if (sigaction(signals_to_intercept[i], &act, NULL) != 0) - DLOG_ERRNO(FATAL) << "sigaction() failed"; + DPLOG(FATAL) << "sigaction() failed"; } } diff --git a/chromium/base/mac/scoped_cftyperef.h b/chromium/base/mac/scoped_cftyperef.h index c41de80d805..8567f85ffcd 100644 --- a/chromium/base/mac/scoped_cftyperef.h +++ b/chromium/base/mac/scoped_cftyperef.h @@ -7,9 +7,7 @@ #include <CoreFoundation/CoreFoundation.h> -#include "base/basictypes.h" -#include "base/compiler_specific.h" -#include "base/memory/scoped_policy.h" +#include "base/mac/scoped_typeref.h" namespace base { @@ -27,78 +25,33 @@ namespace base { // then ScopedCFTypeRef<> will call CFRetain() on the object, and the initial // ownership is not changed. +namespace internal { + +struct ScopedCFTypeRefTraits { + static void Retain(CFTypeRef object) { + CFRetain(object); + } + static void Release(CFTypeRef object) { + CFRelease(object); + } +}; + +} // namespace internal + template<typename CFT> -class ScopedCFTypeRef { +class ScopedCFTypeRef + : public ScopedTypeRef<CFT, internal::ScopedCFTypeRefTraits> { public: typedef CFT element_type; explicit ScopedCFTypeRef( CFT object = NULL, base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME) - : object_(object) { - if (object_ && policy == base::scoped_policy::RETAIN) - CFRetain(object_); - } + : ScopedTypeRef<CFT, + internal::ScopedCFTypeRefTraits>(object, policy) {} ScopedCFTypeRef(const ScopedCFTypeRef<CFT>& that) - : object_(that.object_) { - if (object_) - CFRetain(object_); - } - - ~ScopedCFTypeRef() { - if (object_) - CFRelease(object_); - } - - ScopedCFTypeRef& operator=(const ScopedCFTypeRef<CFT>& that) { - reset(that.get(), base::scoped_policy::RETAIN); - return *this; - } - - void reset(CFT object = NULL, - base::scoped_policy::OwnershipPolicy policy = - base::scoped_policy::ASSUME) { - if (object && policy == base::scoped_policy::RETAIN) - CFRetain(object); - if (object_) - CFRelease(object_); - object_ = object; - } - - bool operator==(CFT that) const { - return object_ == that; - } - - bool operator!=(CFT that) const { - return object_ != that; - } - - operator CFT() const { - return object_; - } - - CFT get() const { - return object_; - } - - void swap(ScopedCFTypeRef& that) { - CFT temp = that.object_; - that.object_ = object_; - object_ = temp; - } - - // ScopedCFTypeRef<>::release() is like scoped_ptr<>::release. It is NOT - // a wrapper for CFRelease(). To force a ScopedCFTypeRef<> object to call - // CFRelease(), use ScopedCFTypeRef<>::reset(). - CFT release() WARN_UNUSED_RESULT { - CFT temp = object_; - object_ = NULL; - return temp; - } - - private: - CFT object_; + : ScopedTypeRef<CFT, internal::ScopedCFTypeRefTraits>(that) {} }; } // namespace base diff --git a/chromium/base/mac/scoped_mach_port.cc b/chromium/base/mac/scoped_mach_port.cc index 9e45a856a8c..de94602e36e 100644 --- a/chromium/base/mac/scoped_mach_port.cc +++ b/chromium/base/mac/scoped_mach_port.cc @@ -4,22 +4,27 @@ #include "base/mac/scoped_mach_port.h" +#include "base/mac/mach_logging.h" + namespace base { namespace mac { +namespace internal { -ScopedMachPort::ScopedMachPort(mach_port_t port) : port_(port) { -} - -ScopedMachPort::~ScopedMachPort() { - reset(); +// static +void SendRightTraits::Free(mach_port_t port) { + kern_return_t kr = mach_port_deallocate(mach_task_self(), port); + MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) + << "ScopedMachSendRight mach_port_deallocate"; } -void ScopedMachPort::reset(mach_port_t port) { - if (port_ != MACH_PORT_NULL) { - mach_port_deallocate(mach_task_self(), port_); - } - port_ = port; +// static +void ReceiveRightTraits::Free(mach_port_t port) { + kern_return_t kr = + mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1); + MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) + << "ScopedMachReceiveRight 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 cc2ef20fe76..36087c9bdec 100644 --- a/chromium/base/mac/scoped_mach_port.h +++ b/chromium/base/mac/scoped_mach_port.h @@ -7,35 +7,56 @@ #include <mach/mach.h> -#include "base/basictypes.h" #include "base/base_export.h" +#include "base/scoped_generic.h" namespace base { namespace mac { -// A class for managing the life of a Mach port, releasing via -// mach_port_deallocate either its send and/or receive rights. -class BASE_EXPORT ScopedMachPort { - public: - // Creates a scoper by taking ownership of the port. - explicit ScopedMachPort(mach_port_t port); +namespace internal { - ~ScopedMachPort(); +struct BASE_EXPORT SendRightTraits { + static mach_port_t InvalidValue() { + return MACH_PORT_NULL; + } - void reset(mach_port_t port = MACH_PORT_NULL); + static void Free(mach_port_t port); +}; - operator mach_port_t() const { - return port_; +struct BASE_EXPORT ReceiveRightTraits { + static mach_port_t InvalidValue() { + return MACH_PORT_NULL; } - mach_port_t get() const { - return port_; - } + static void Free(mach_port_t port); +}; + +} // namespace internal - private: - mach_port_t port_; +// A scoper for handling a Mach port that names a send right. Send rights are +// reference counted, and this takes ownership of the right on construction +// and then removes a reference to the right on destruction. If the reference +// is the last one on the right, the right is deallocated. +class BASE_EXPORT ScopedMachSendRight : + public base::ScopedGeneric<mach_port_t, internal::SendRightTraits> { + public: + explicit ScopedMachSendRight(mach_port_t port = traits_type::InvalidValue()) + : ScopedGeneric(port) {} + + operator mach_port_t() const { return get(); } +}; + +// A scoper for handling a Mach port's receive right. There is only one +// receive right per port. This takes ownership of the receive right on +// construction and then destroys the right on destruction, turning all +// outstanding send rights into dead names. +class BASE_EXPORT ScopedMachReceiveRight : + public base::ScopedGeneric<mach_port_t, internal::ReceiveRightTraits> { + public: + explicit ScopedMachReceiveRight( + mach_port_t port = traits_type::InvalidValue()) : ScopedGeneric(port) {} - DISALLOW_COPY_AND_ASSIGN(ScopedMachPort); + operator mach_port_t() const { return get(); } }; } // namespace mac diff --git a/chromium/base/mac/scoped_mach_vm.cc b/chromium/base/mac/scoped_mach_vm.cc new file mode 100644 index 00000000000..1c28d9c7c42 --- /dev/null +++ b/chromium/base/mac/scoped_mach_vm.cc @@ -0,0 +1,33 @@ +// 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/scoped_mach_vm.h" + +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); + + if (size_) { + if (address_ < address) { + vm_deallocate(mach_task_self(), + address_, + std::min(size_, address - address_)); + } + if (address_ + size_ > address + size) { + vm_address_t deallocate_start = std::max(address_, address + size); + vm_deallocate(mach_task_self(), + deallocate_start, + address_ + size_ - deallocate_start); + } + } + + address_ = address; + size_ = size; +} + +} // namespace mac +} // namespace base diff --git a/chromium/base/mac/scoped_mach_vm.h b/chromium/base/mac/scoped_mach_vm.h new file mode 100644 index 00000000000..b130a79fb63 --- /dev/null +++ b/chromium/base/mac/scoped_mach_vm.h @@ -0,0 +1,93 @@ +// 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_SCOPED_MACH_VM_H_ +#define BASE_MAC_SCOPED_MACH_VM_H_ + +#include <mach/mach.h> + +#include <algorithm> + +#include "base/base_export.h" +#include "base/basictypes.h" +#include "base/logging.h" + +// Use ScopedMachVM to supervise ownership of pages in the current process +// through the Mach VM subsystem. Pages allocated with vm_allocate can be +// released when exiting a scope with ScopedMachVM. +// +// The Mach VM subsystem operates on a page-by-page basis, and a single VM +// allocation managed by a ScopedMachVM object may span multiple pages. As far +// as Mach is concerned, allocated pages may be deallocated individually. This +// is in contrast to higher-level allocators such as malloc, where the base +// address of an allocation implies the size of an allocated block. +// Consequently, it is not sufficient to just pass the base address of an +// allocation to ScopedMachVM, it also needs to know the size of the +// allocation. To avoid any confusion, both the base address and size must +// be page-aligned. +// +// When dealing with Mach VM, base addresses will naturally be page-aligned, +// but user-specified sizes may not be. If there's a concern that a size is +// not page-aligned, use the mach_vm_round_page macro to correct it. +// +// Example: +// +// vm_address_t address = 0; +// vm_size_t size = 12345; // This requested size is not page-aligned. +// kern_return_t kr = +// vm_allocate(mach_task_self(), &address, size, VM_FLAGS_ANYWHERE); +// if (kr != KERN_SUCCESS) { +// return false; +// } +// ScopedMachVM vm_owner(address, mach_vm_round_page(size)); + +namespace base { +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); + } + + ~ScopedMachVM() { + if (size_) { + vm_deallocate(mach_task_self(), address_, size_); + } + } + + void reset(vm_address_t address = 0, vm_size_t size = 0); + + vm_address_t address() const { + return address_; + } + + vm_size_t size() const { + return size_; + } + + void swap(ScopedMachVM& that) { + std::swap(address_, that.address_); + std::swap(size_, that.size_); + } + + void release() { + address_ = 0; + size_ = 0; + } + + private: + vm_address_t address_; + vm_size_t size_; + + DISALLOW_COPY_AND_ASSIGN(ScopedMachVM); +}; + +} // namespace mac +} // namespace base + +#endif // BASE_MAC_SCOPED_MACH_VM_H_ diff --git a/chromium/base/mac/scoped_typeref.h b/chromium/base/mac/scoped_typeref.h new file mode 100644 index 00000000000..61ee3119817 --- /dev/null +++ b/chromium/base/mac/scoped_typeref.h @@ -0,0 +1,132 @@ +// 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_SCOPED_TYPEREF_H_ +#define BASE_MAC_SCOPED_TYPEREF_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/memory/scoped_policy.h" + +namespace base { + +// ScopedTypeRef<> is patterned after scoped_ptr<>, but maintains a ownership +// of a reference to any type that is maintained by Retain and Release methods. +// +// The Traits structure must provide the Retain and Release methods for type T. +// A default ScopedTypeRefTraits is used but not defined, and should be defined +// for each type to use this interface. For example, an appropriate definition +// of ScopedTypeRefTraits for CGLContextObj would be: +// +// template<> +// struct ScopedTypeRefTraits<CGLContextObj> { +// void Retain(CGLContextObj object) { CGLContextRetain(object); } +// void Release(CGLContextObj object) { CGLContextRelease(object); } +// }; +// +// For the many types that have pass-by-pointer create functions, the function +// InitializeInto() is provided to allow direct initialization and assumption +// of ownership of the object. For example, continuing to use the above +// CGLContextObj specialization: +// +// base::ScopedTypeRef<CGLContextObj> context; +// CGLCreateContext(pixel_format, share_group, context.InitializeInto()); +// +// For initialization with an existing object, the caller may specify whether +// the ScopedTypeRef<> being initialized is assuming the caller's existing +// ownership of the object (and should not call Retain in initialization) or if +// it should not assume this ownership and must create its own (by calling +// Retain in initialization). This behavior is based on the |policy| parameter, +// with |ASSUME| for the former and |RETAIN| for the latter. The default policy +// is to |ASSUME|. + +template<typename T> +struct ScopedTypeRefTraits; + +template<typename T, typename Traits = ScopedTypeRefTraits<T>> +class ScopedTypeRef { + public: + typedef T element_type; + + ScopedTypeRef( + T object = NULL, + base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME) + : object_(object) { + if (object_ && policy == base::scoped_policy::RETAIN) + Traits::Retain(object_); + } + + ScopedTypeRef(const ScopedTypeRef<T, Traits>& that) + : object_(that.object_) { + if (object_) + Traits::Retain(object_); + } + + ~ScopedTypeRef() { + if (object_) + Traits::Release(object_); + } + + ScopedTypeRef& operator=(const ScopedTypeRef<T, Traits>& that) { + reset(that.get(), base::scoped_policy::RETAIN); + return *this; + } + + // This is to be used only to take ownership of objects that are created + // by pass-by-pointer create functions. To enforce this, require that the + // object be reset to NULL before this may be used. + T* InitializeInto() WARN_UNUSED_RESULT { + DCHECK(!object_); + return &object_; + } + + void reset(T object = NULL, + base::scoped_policy::OwnershipPolicy policy = + base::scoped_policy::ASSUME) { + if (object && policy == base::scoped_policy::RETAIN) + Traits::Retain(object); + if (object_) + Traits::Release(object_); + object_ = object; + } + + bool operator==(T that) const { + return object_ == that; + } + + bool operator!=(T that) const { + return object_ != that; + } + + operator T() const { + return object_; + } + + T get() const { + return object_; + } + + void swap(ScopedTypeRef& that) { + T temp = that.object_; + that.object_ = object_; + object_ = temp; + } + + // ScopedTypeRef<>::release() is like scoped_ptr<>::release. It is NOT + // a wrapper for Release(). To force a ScopedTypeRef<> object to call + // Release(), use ScopedTypeRef<>::reset(). + T release() WARN_UNUSED_RESULT { + T temp = object_; + object_ = NULL; + return temp; + } + + private: + T object_; +}; + +} // namespace base + +#endif // BASE_MAC_SCOPED_TYPEREF_H_ diff --git a/chromium/base/mac/sdk_forward_declarations.h b/chromium/base/mac/sdk_forward_declarations.h index bbaf962b639..3a0878daef6 100644 --- a/chromium/base/mac/sdk_forward_declarations.h +++ b/chromium/base/mac/sdk_forward_declarations.h @@ -12,21 +12,39 @@ #define BASE_MAC_SDK_FORWARD_DECLARATIONS_H_ #import <AppKit/AppKit.h> +#import <CoreWLAN/CoreWLAN.h> +#import <ImageCaptureCore/ImageCaptureCore.h> +#import <IOBluetooth/IOBluetooth.h> + +#include "base/base_export.h" #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, - NSEventPhaseMayBegin = 0x1 << 5 + NSEventPhaseCancelled = 0x1 << 4 }; typedef NSUInteger NSEventPhase; enum { + NSFullScreenWindowMask = 1 << 14, +}; + +enum { + NSApplicationPresentationFullScreen = 1 << 10, +}; + +enum { + NSWindowCollectionBehaviorFullScreenPrimary = 1 << 7, + NSWindowCollectionBehaviorFullScreenAuxiliary = 1 << 8, +}; + +enum { NSEventSwipeTrackingLockDirection = 0x1 << 0, NSEventSwipeTrackingClampGestureAmount = 0x1 << 1, }; @@ -41,6 +59,12 @@ enum { }; typedef NSInteger NSWindowAnimationBehavior; +enum { + NSWindowDocumentVersionsButton = 6, + NSWindowFullScreenButton, +}; +typedef NSUInteger NSWindowButton; + @interface NSEvent (LionSDK) + (BOOL)isSwipeTrackingFromScrollEventsEnabled; @@ -60,7 +84,11 @@ typedef NSInteger NSWindowAnimationBehavior; @end -@interface CALayer (LionAPI) +@interface NSApplication (LionSDK) +- (void)disableRelaunchOnLogin; +@end + +@interface CALayer (LionSDK) - (CGFloat)contentsScale; - (void)setContentsScale:(CGFloat)contentsScale; @end @@ -74,7 +102,167 @@ typedef NSInteger NSWindowAnimationBehavior; - (CGFloat)backingScaleFactor; - (NSWindowAnimationBehavior)animationBehavior; - (void)setAnimationBehavior:(NSWindowAnimationBehavior)newAnimationBehavior; +- (void)toggleFullScreen:(id)sender; +- (void)setRestorable:(BOOL)flag; +@end + +@interface NSCursor (LionSDKDeclarations) ++ (NSCursor*)IBeamCursorForVerticalLayout; +@end + +@interface NSAnimationContext (LionSDK) ++ (void)runAnimationGroup:(void (^)(NSAnimationContext *context))changes + completionHandler:(void (^)(void))completionHandler; +@property(copy) void(^completionHandler)(void); +@end + +@interface NSView (LionSDK) +- (NSSize)convertSizeFromBacking:(NSSize)size; +- (void)setWantsBestResolutionOpenGLSurface:(BOOL)flag; +@end + +@interface NSObject (ICCameraDeviceDelegateLionSDK) +- (void)deviceDidBecomeReadyWithCompleteContentCatalog:(ICDevice*)device; +- (void)didDownloadFile:(ICCameraFile*)file + error:(NSError*)error + options:(NSDictionary*)options + contextInfo:(void*)contextInfo; +@end + +@interface NSScroller (LionSDK) ++ (NSInteger)preferredScrollerStyle; +@end + +@interface CWInterface (LionSDK) +- (BOOL)associateToNetwork:(CWNetwork*)network + password:(NSString*)password + error:(NSError**)error; +- (NSSet*)scanForNetworksWithName:(NSString*)networkName + error:(NSError**)error; +@end + +enum CWChannelBand { + kCWChannelBandUnknown = 0, + kCWChannelBand2GHz = 1, + kCWChannelBand5GHz = 2, +}; + +@interface CWChannel : NSObject +@property(readonly) CWChannelBand channelBand; +@end + +@interface CWNetwork (LionSDK) +@property(readonly) CWChannel* wlanChannel; +@end + +@interface IOBluetoothHostController (LionSDK) +- (NSString*)nameAsString; +- (BluetoothHCIPowerState)powerState; +@end + +@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 + +@interface IOBluetoothDevice (LionSDK) +- (NSString*)addressString; +- (unsigned int)classOfDevice; +- (BluetoothConnectionHandle)connectionHandle; +- (BluetoothHCIRSSIValue)rawRSSI; +- (NSArray*)services; +- (IOReturn)performSDPQuery:(id)target uuids:(NSArray*)uuids; +@end + +BASE_EXPORT extern "C" NSString* const NSWindowWillEnterFullScreenNotification; + #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 + +// NSProgress is public API in 10.9, but a version of it exists and is usable +// in 10.8. + +@interface NSProgress : NSObject + +- (instancetype)initWithParent:(NSProgress*)parentProgressOrNil + userInfo:(NSDictionary*)userInfoOrNil; +@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); +- (void)cancel; +- (void)pause; + +- (void)setUserInfoObject:(id)objectOrNil forKey:(NSString*)key; +- (NSDictionary*)userInfo; + +@property (readonly, getter=isIndeterminate) BOOL indeterminate; +@property (readonly) double fractionCompleted; + +- (void)publish; +- (void)unpublish; + +@end + +@interface NSView (MavericksSDK) +- (void)setCanDrawSubviewsIntoLayer:(BOOL)flag; +@end + +enum { + NSWindowOcclusionStateVisible = 1UL << 1, +}; +typedef NSUInteger NSWindowOcclusionState; + +@interface NSWindow (MavericksSDK) +- (NSWindowOcclusionState)occlusionState; +@end + +#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 + +enum { + NSWindowTitleVisible = 0, + NSWindowTitleHidden = 1, + NSWindowTitleHiddenWhenActive = 2, +}; +typedef NSInteger NSWindowTitleVisibility; + +@interface NSWindow (YosemiteSDK) + +@property NSWindowTitleVisibility titleVisibility; + +@end + +#endif // MAC_OS_X_VERSION_10_10 + #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 new file mode 100644 index 00000000000..a402a417d85 --- /dev/null +++ b/chromium/base/mac/sdk_forward_declarations.mm @@ -0,0 +1,14 @@ +// 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/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 + +NSString* const NSWindowWillEnterFullScreenNotification = + @"NSWindowWillEnterFullScreenNotification"; + +#endif // MAC_OS_X_VERSION_10_7 diff --git a/chromium/base/macros.h b/chromium/base/macros.h new file mode 100644 index 00000000000..781751569d7 --- /dev/null +++ b/chromium/base/macros.h @@ -0,0 +1,313 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file contains macros and macro-like constructs (e.g., templates) that +// are commonly used throughout Chromium source. (It may also contain things +// that are closely related to things that are commonly used that belong in this +// file.) + +#ifndef BASE_MACROS_H_ +#define BASE_MACROS_H_ + +#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&) + +// Put this in the private: declarations for a class to be unassignable. +#define DISALLOW_ASSIGN(TypeName) \ + void operator=(const TypeName&) + +// A macro to disallow the copy constructor and operator= functions +// This should be used in the private: declarations for a class +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) + +// An older, deprecated, politically incorrect name for the above. +// NOTE: The usage of this macro was banned from our code base, but some +// third_party libraries are yet using it. +// TODO(tfarina): Figure out how to fix the usage of this macro in the +// third_party libraries and get rid of it. +#define DISALLOW_EVIL_CONSTRUCTORS(TypeName) DISALLOW_COPY_AND_ASSIGN(TypeName) + +// A macro to disallow all the implicit constructors, namely the +// default constructor, copy constructor and operator= functions. +// +// This should be used in the private: declarations for a class +// that wants to prevent anyone from instantiating it. This is +// especially useful for classes containing only static methods. +#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ + TypeName(); \ + DISALLOW_COPY_AND_ASSIGN(TypeName) + +// The arraysize(arr) macro returns the # of elements in an array arr. +// The expression is a compile-time constant, and therefore can be +// used in defining new arrays, for example. If you use arraysize on +// a pointer by mistake, you will get a compile-time error. +// +// One caveat is that arraysize() doesn't accept any array of an +// anonymous type or a type defined inside a function. In these rare +// cases, you have to use the unsafe ARRAYSIZE_UNSAFE() macro below. This is +// due to a limitation in C++'s template system. The limitation might +// eventually be removed, but it hasn't happened yet. + +// 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 + +#define arraysize(array) (sizeof(ArraySizeHelper(array))) + +// ARRAYSIZE_UNSAFE performs essentially the same calculation as arraysize, +// but can be used on anonymous types or types defined inside +// functions. It's less safe than arraysize as it accepts some +// (although not all) pointers. Therefore, you should use arraysize +// whenever possible. +// +// The expression ARRAYSIZE_UNSAFE(a) is a compile-time constant of type +// size_t. +// +// ARRAYSIZE_UNSAFE catches a few type errors. If you see a compiler error +// +// "warning: division by zero in ..." +// +// when using ARRAYSIZE_UNSAFE, you are (wrongfully) giving it a pointer. +// You should only use ARRAYSIZE_UNSAFE on statically allocated arrays. +// +// The following comments are on the implementation details, and can +// be ignored by the users. +// +// ARRAYSIZE_UNSAFE(arr) works by inspecting sizeof(arr) (the # of bytes in +// the array) and sizeof(*(arr)) (the # of bytes in one array +// element). If the former is divisible by the latter, perhaps arr is +// indeed an array, in which case the division result is the # of +// elements in the array. Otherwise, arr cannot possibly be an array, +// and we generate a compiler error to prevent the code from +// compiling. +// +// Since the size of bool is implementation-defined, we need to cast +// !(sizeof(a) & sizeof(*(a))) to size_t in order to ensure the final +// result has type size_t. +// +// This macro is not perfect as it wrongfully accepts certain +// pointers, namely where the pointer size is divisible by the pointee +// size. Since all our code has to go through a 32-bit compiler, +// where a pointer is 4 bytes, this means all pointers to a type whose +// size is 3 or greater than 4 will be (righteously) rejected. + +#define ARRAYSIZE_UNSAFE(a) \ + ((sizeof(a) / sizeof(*(a))) / \ + static_cast<size_t>(!(sizeof(a) % sizeof(*(a))))) + + +// Use implicit_cast as a safe version of static_cast or const_cast +// for upcasting in the type hierarchy (i.e. casting a pointer to Foo +// to a pointer to SuperclassOfFoo or casting a pointer to Foo to +// a const pointer to Foo). +// When you use implicit_cast, the compiler checks that the cast is safe. +// Such explicit implicit_casts are necessary in surprisingly many +// situations where C++ demands an exact type match instead of an +// argument type convertible to a target type. +// +// The From type can be inferred, so the preferred syntax for using +// implicit_cast is the same as for static_cast etc.: +// +// implicit_cast<ToType>(expr) +// +// implicit_cast would have been part of the C++ standard library, +// but the proposal was submitted too late. It will probably make +// its way into the language in the future. +template<typename To, typename From> +inline To implicit_cast(From const &f) { + return f; +} + +// The COMPILE_ASSERT macro can be used to verify that a compile time +// expression is true. For example, you could use it to verify the +// size of a static array: +// +// COMPILE_ASSERT(ARRAYSIZE_UNSAFE(content_type_names) == CONTENT_NUM_TYPES, +// content_type_names_incorrect_size); +// +// or to make sure a struct is smaller than a certain size: +// +// COMPILE_ASSERT(sizeof(foo) < 128, foo_too_large); +// +// The second argument to the macro is the name of the variable. If +// the expression is false, most compilers will issue a warning/error +// containing the name of the variable. + +#undef COMPILE_ASSERT + +#if __cplusplus >= 201103L + +// Under C++11, just use static_assert. +#define COMPILE_ASSERT(expr, msg) static_assert(expr, #msg) + +#else + +template <bool> +struct CompileAssert { +}; + +#define COMPILE_ASSERT(expr, msg) \ + typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] ALLOW_UNUSED + +// Implementation details of COMPILE_ASSERT: +// +// - COMPILE_ASSERT works by defining an array type that has -1 +// elements (and thus is invalid) when the expression is false. +// +// - The simpler definition +// +// #define COMPILE_ASSERT(expr, msg) typedef char msg[(expr) ? 1 : -1] +// +// does not work, as gcc supports variable-length arrays whose sizes +// are determined at run-time (this is gcc's extension and not part +// of the C++ standard). As a result, gcc fails to reject the +// following code with the simple definition: +// +// int foo; +// COMPILE_ASSERT(foo, msg); // not supposed to compile as foo is +// // not a compile-time constant. +// +// - By using the type CompileAssert<(bool(expr))>, we ensures that +// expr is a compile-time constant. (Template arguments must be +// determined at compile-time.) +// +// - The outer parentheses in CompileAssert<(bool(expr))> are necessary +// to work around a bug in gcc 3.4.4 and 4.0.1. If we had written +// +// CompileAssert<bool(expr)> +// +// instead, these compilers will refuse to compile +// +// COMPILE_ASSERT(5 > 0, some_message); +// +// (They seem to think the ">" in "5 > 0" marks the end of the +// template argument list.) +// +// - The array size is (bool(expr) ? 1 : -1), instead of simply +// +// ((expr) ? 1 : -1). +// +// This is to avoid running into a bug in MS VC 7.1, which +// causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1. + +#endif + +// bit_cast<Dest,Source> is a template function that implements the +// equivalent of "*reinterpret_cast<Dest*>(&source)". We need this in +// very low-level functions like the protobuf library and fast math +// support. +// +// float f = 3.14159265358979; +// int i = bit_cast<int32>(f); +// // i = 0x40490fdb +// +// The classical address-casting method is: +// +// // WRONG +// float f = 3.14159265358979; // WRONG +// int i = * reinterpret_cast<int*>(&f); // WRONG +// +// The address-casting method actually produces undefined behavior +// according to ISO C++ specification section 3.10 -15 -. Roughly, this +// section says: if an object in memory has one type, and a program +// accesses it with a different type, then the result is undefined +// behavior for most values of "different type". +// +// This is true for any cast syntax, either *(int*)&f or +// *reinterpret_cast<int*>(&f). And it is particularly true for +// conversions between integral lvalues and floating-point lvalues. +// +// The purpose of 3.10 -15- is to allow optimizing compilers to assume +// that expressions with different types refer to different memory. gcc +// 4.0.1 has an optimizer that takes advantage of this. So a +// non-conforming program quietly produces wildly incorrect output. +// +// The problem is not the use of reinterpret_cast. The problem is type +// punning: holding an object in memory of one type and reading its bits +// back using a different type. +// +// The C++ standard is more subtle and complex than this, but that +// is the basic idea. +// +// Anyways ... +// +// bit_cast<> calls memcpy() which is blessed by the standard, +// especially by the example in section 3.9 . Also, of course, +// bit_cast<> wraps up the nasty logic in one place. +// +// Fortunately memcpy() is very fast. In optimized mode, with a +// constant size, gcc 2.95.3, gcc 4.0.1, and msvc 7.1 produce inline +// code with the minimal amount of data movement. On a 32-bit system, +// memcpy(d,s,4) compiles to one load and one store, and memcpy(d,s,8) +// compiles to two loads and two stores. +// +// I tested this code with gcc 2.95.3, gcc 4.0.1, icc 8.1, and msvc 7.1. +// +// WARNING: if Dest or Source is a non-POD type, the result of the memcpy +// is likely to surprise you. + +template <class Dest, class Source> +inline Dest bit_cast(const Source& source) { + COMPILE_ASSERT(sizeof(Dest) == sizeof(Source), VerifySizesAreEqual); + + Dest dest; + memcpy(&dest, &source, sizeof(dest)); + return dest; +} + +// Used to explicitly mark the return value of a function as unused. If you are +// really sure you don't want to do anything with the return value of a function +// that has been marked WARN_UNUSED_RESULT, wrap it with this. Example: +// +// scoped_ptr<MyType> my_var = ...; +// if (TakeOwnership(my_var.get()) == SUCCESS) +// ignore_result(my_var.release()); +// +template<typename T> +inline void ignore_result(const T&) { +} + +// The following enum should be used only as a constructor argument to indicate +// that the variable has static storage class, and that the constructor should +// do nothing to its state. It indicates to the reader that it is legal to +// declare a static instance of the class, provided the constructor is given +// the base::LINKER_INITIALIZED argument. Normally, it is unsafe to declare a +// static variable that has a constructor or a destructor because invocation +// order is undefined. However, IF the type can be initialized by filling with +// zeroes (which the loader does for static variables), AND the destructor also +// does nothing to the storage, AND there are no virtual methods, then a +// constructor declared as +// explicit MyClass(base::LinkerInitialized x) {} +// and invoked as +// static MyClass my_variable_name(base::LINKER_INITIALIZED); +namespace base { +enum LinkerInitialized { LINKER_INITIALIZED }; + +// Use these to declare and define a static local variable (static T;) so that +// it is leaked so that its destructors are not called at exit. If you need +// thread-safe initialization, use base/lazy_instance.h instead. +#define CR_DEFINE_STATIC_LOCAL(type, name, arguments) \ + static type& name = *new type arguments + +} // base + +#endif // BASE_MACROS_H_ diff --git a/chromium/base/md5.cc b/chromium/base/md5.cc index 754994c0e56..6227ee66042 100644 --- a/chromium/base/md5.cc +++ b/chromium/base/md5.cc @@ -251,8 +251,12 @@ void MD5Final(MD5Digest* digest, MD5Context* context) { byteReverse(ctx->in, 14); /* Append length in bits and transform */ - ((uint32 *)ctx->in)[ 14 ] = ctx->bits[0]; - ((uint32 *)ctx->in)[ 15 ] = ctx->bits[1]; + 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); @@ -260,6 +264,14 @@ void MD5Final(MD5Digest* digest, MD5Context* context) { memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ } +void MD5IntermediateFinal(MD5Digest* digest, const MD5Context* context) { + /* MD5Final mutates the MD5Context*. Make a copy for generating the + intermediate value. */ + MD5Context context_copy; + memcpy(&context_copy, context, sizeof(context_copy)); + MD5Final(digest, &context_copy); +} + std::string MD5DigestToBase16(const MD5Digest& digest) { static char const zEncode[] = "0123456789abcdef"; diff --git a/chromium/base/md5.h b/chromium/base/md5.h index fba02bd1163..0a87fcf7c4a 100644 --- a/chromium/base/md5.h +++ b/chromium/base/md5.h @@ -58,6 +58,12 @@ BASE_EXPORT void MD5Update(MD5Context* context, const StringPiece& data); // Finalizes the MD5 operation and fills the buffer with the digest. BASE_EXPORT void MD5Final(MD5Digest* digest, MD5Context* context); +// MD5IntermediateFinal() generates a digest without finalizing the MD5 +// operation. Can be used to generate digests for the input seen thus far, +// without affecting the digest generated for the entire input. +BASE_EXPORT void MD5IntermediateFinal(MD5Digest* digest, + const MD5Context* context); + // Converts a digest into human-readable hexadecimal. BASE_EXPORT std::string MD5DigestToBase16(const MD5Digest& digest); diff --git a/chromium/base/md5_unittest.cc b/chromium/base/md5_unittest.cc index 1112c4b4258..3e7f2ad587d 100644 --- a/chromium/base/md5_unittest.cc +++ b/chromium/base/md5_unittest.cc @@ -204,4 +204,49 @@ TEST(MD5, ContextWithStringData) { EXPECT_EQ(expected, actual); } +// Test that a digest generated by MD5IntermediateFinal() gives the same results +// as an independently-calculated digest, and also does not modify the context. +TEST(MD5, IntermediateFinal) { + // Independent context over the header. + MD5Context check_header_context; + MD5Init(&check_header_context); + + // Independent context over entire input. + MD5Context check_full_context; + MD5Init(&check_full_context); + + // Context intermediate digest will be calculated from. + MD5Context context; + MD5Init(&context); + + static const char kHeader[] = "header data"; + static const char kBody[] = "payload data"; + + MD5Update(&context, kHeader); + MD5Update(&check_header_context, kHeader); + MD5Update(&check_full_context, kHeader); + + MD5Digest check_header_digest; + MD5Final(&check_header_digest, &check_header_context); + + MD5Digest header_digest; + MD5IntermediateFinal(&header_digest, &context); + + MD5Update(&context, kBody); + MD5Update(&check_full_context, kBody); + + MD5Digest check_full_digest; + MD5Final(&check_full_digest, &check_full_context); + + MD5Digest digest; + MD5Final(&digest, &context); + + // The header and full digest pairs are the same, and they aren't the same as + // each other. + 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))); +} + } // namespace base diff --git a/chromium/base/memory/aligned_memory.h b/chromium/base/memory/aligned_memory.h index 6719599dc54..1a4cba9ad84 100644 --- a/chromium/base/memory/aligned_memory.h +++ b/chromium/base/memory/aligned_memory.h @@ -26,9 +26,9 @@ // // ... later, to release the memory: // AlignedFree(my_array); // -// Or using scoped_ptr_malloc: +// Or using scoped_ptr: // -// scoped_ptr_malloc<float, ScopedPtrAlignedFree> my_array( +// scoped_ptr<float, AlignedFreeDeleter> my_array( // static_cast<float*>(AlignedAlloc(size, alignment))); #ifndef BASE_MEMORY_ALIGNED_MEMORY_H_ @@ -101,9 +101,9 @@ inline void AlignedFree(void* ptr) { #endif } -// Helper class for use with scoped_ptr_malloc. -class BASE_EXPORT ScopedPtrAlignedFree { - public: +// Deleter for use with scoped_ptr. E.g., use as +// scoped_ptr<Foo, base::AlignedFreeDeleter> foo; +struct AlignedFreeDeleter { inline void operator()(void* ptr) const { AlignedFree(ptr); } diff --git a/chromium/base/memory/aligned_memory_unittest.cc b/chromium/base/memory/aligned_memory_unittest.cc index 6942249f5a4..5d681f9a388 100644 --- a/chromium/base/memory/aligned_memory_unittest.cc +++ b/chromium/base/memory/aligned_memory_unittest.cc @@ -41,6 +41,10 @@ TEST(AlignedMemoryTest, StackAlignment) { EXPECT_ALIGNED(raw8.void_data(), 8); EXPECT_ALIGNED(raw16.void_data(), 16); + + // TODO(ios): __attribute__((aligned(X))) with X >= 128 does not works on + // the stack when building for arm64 on iOS, http://crbug.com/349003 +#if !(defined(OS_IOS) && defined(ARCH_CPU_ARM64)) EXPECT_ALIGNED(raw128.void_data(), 128); // NaCl x86-64 compiler emits non-validating instructions for >128 @@ -60,7 +64,8 @@ TEST(AlignedMemoryTest, StackAlignment) { EXPECT_EQ(4096u, ALIGNOF(raw4096)); EXPECT_ALIGNED(raw4096.void_data(), 4096); #endif // !(defined(OS_IOS) && defined(ARCH_CPU_ARM_FAMILY)) -#endif +#endif // !(defined(OS_NACL) && defined(ARCH_CPU_X86_64)) +#endif // !(defined(OS_IOS) && defined(ARCH_CPU_ARM64)) } TEST(AlignedMemoryTest, DynamicAllocation) { @@ -86,7 +91,7 @@ TEST(AlignedMemoryTest, DynamicAllocation) { } TEST(AlignedMemoryTest, ScopedDynamicAllocation) { - scoped_ptr_malloc<float, base::ScopedPtrAlignedFree> p( + scoped_ptr<float, base::AlignedFreeDeleter> p( static_cast<float*>(base::AlignedAlloc(8, 8))); EXPECT_TRUE(p.get()); EXPECT_ALIGNED(p.get(), 8); diff --git a/chromium/base/memory/discardable_memory.cc b/chromium/base/memory/discardable_memory.cc new file mode 100644 index 00000000000..9ba47aa6bfb --- /dev/null +++ b/chromium/base/memory/discardable_memory.cc @@ -0,0 +1,86 @@ +// 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.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_MAC, "mac" }, + { DISCARDABLE_MEMORY_TYPE_EMULATED, "emulated" }, + { DISCARDABLE_MEMORY_TYPE_MALLOC, "malloc" } +}; + +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; +} + +// static +scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemory( + size_t size) { + return CreateLockedMemoryWithType(GetPreferredType(), size); +} + +} // namespace base diff --git a/chromium/base/memory/discardable_memory.h b/chromium/base/memory/discardable_memory.h index cbc2db630a8..d16ed3e8480 100644 --- a/chromium/base/memory/discardable_memory.h +++ b/chromium/base/memory/discardable_memory.h @@ -5,6 +5,9 @@ #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" @@ -12,10 +15,18 @@ namespace base { -enum LockDiscardableMemoryStatus { - DISCARDABLE_MEMORY_FAILED = -1, - DISCARDABLE_MEMORY_PURGED = 0, - DISCARDABLE_MEMORY_SUCCESS = 1 +enum DiscardableMemoryType { + DISCARDABLE_MEMORY_TYPE_NONE, + DISCARDABLE_MEMORY_TYPE_ASHMEM, + DISCARDABLE_MEMORY_TYPE_MAC, + DISCARDABLE_MEMORY_TYPE_EMULATED, + DISCARDABLE_MEMORY_TYPE_MALLOC +}; + +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 @@ -53,19 +64,56 @@ class BASE_EXPORT DiscardableMemory { public: virtual ~DiscardableMemory() {} - // Check whether the system supports discardable memory natively. Returns - // false if the support is emulated. - static bool SupportedNatively(); + // Call this on a thread with a MessageLoop current to allow discardable + // memory implementations to respond to memory pressure signals. + static void RegisterMemoryPressureListeners(); + + // Call this to prevent discardable memory implementations from responding + // to memory pressure signals. + static void UnregisterMemoryPressureListeners(); + + // 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 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(); + // Locks the memory so that it will not be purged by the system. Returns - // DISCARDABLE_MEMORY_SUCCESS on success. If the return value is - // DISCARDABLE_MEMORY_FAILED then this object should be discarded and - // a new one should be created. If the return value is - // DISCARDABLE_MEMORY_PURGED then the memory is present but any data that - // was in it is gone. - virtual LockDiscardableMemoryStatus Lock() WARN_UNUSED_RESULT = 0; + // 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; // Unlocks the memory so that it can be purged by the system. Must be called // after every successful lock call. @@ -77,10 +125,6 @@ class BASE_EXPORT DiscardableMemory { // Testing utility calls. - // Check whether a purge of all discardable memory in the system is supported. - // Use only for testing! - static bool PurgeForTestingSupported(); - // 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(); diff --git a/chromium/base/memory/discardable_memory_allocator_android.h b/chromium/base/memory/discardable_memory_allocator_android.h deleted file mode 100644 index 7991656cff6..00000000000 --- a/chromium/base/memory/discardable_memory_allocator_android.h +++ /dev/null @@ -1,65 +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_ALLOCATOR_H_ -#define BASE_MEMORY_DISCARDABLE_MEMORY_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" -#include "base/threading/thread_checker.h" - -namespace base { - -class DiscardableMemory; - -namespace internal { - -// On Android ashmem is used to implement discardable memory. It is backed by a -// file (descriptor) thus is 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. -// -// Threading: The allocator must be deleted on the thread it was constructed on -// although its Allocate() method can be invoked on any thread. See -// discardable_memory.h for DiscardableMemory's threading guarantees. -class BASE_EXPORT_PRIVATE DiscardableMemoryAllocator { - public: - // Exposed for testing. - enum { - kMinAshmemRegionSize = 32 * 1024 * 1024, - }; - - // Note that |name| is only used for debugging/measurement purposes. - explicit DiscardableMemoryAllocator(const std::string& name); - ~DiscardableMemoryAllocator(); - - // Note that the allocator must outlive the returned DiscardableMemory - // instance. - scoped_ptr<DiscardableMemory> Allocate(size_t size); - - private: - class AshmemRegion; - class DiscardableAshmemChunk; - - void DeleteAshmemRegion_Locked(AshmemRegion* region); - - base::ThreadChecker thread_checker_; - const std::string name_; - base::Lock lock_; - ScopedVector<AshmemRegion> ashmem_regions_; - - DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryAllocator); -}; - -} // namespace internal -} // namespace base - -#endif // BASE_MEMORY_DISCARDABLE_MEMORY_ALLOCATOR_H_ diff --git a/chromium/base/memory/discardable_memory_allocator_android_unittest.cc b/chromium/base/memory/discardable_memory_allocator_android_unittest.cc deleted file mode 100644 index 97cf5d45f72..00000000000 --- a/chromium/base/memory/discardable_memory_allocator_android_unittest.cc +++ /dev/null @@ -1,232 +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_allocator_android.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 kPageSize = 4096; -const size_t kMinAshmemRegionSize = - DiscardableMemoryAllocator::kMinAshmemRegionSize; - -class DiscardableMemoryAllocatorTest : public testing::Test { - protected: - DiscardableMemoryAllocatorTest() : allocator_(kAllocatorName) {} - - DiscardableMemoryAllocator allocator_; -}; - -void WriteToDiscardableMemory(DiscardableMemory* 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(DiscardableMemoryAllocatorTest, Basic) { - const size_t size = 128; - scoped_ptr<DiscardableMemory> memory(allocator_.Allocate(size)); - ASSERT_TRUE(memory); - WriteToDiscardableMemory(memory.get(), size); -} - -TEST_F(DiscardableMemoryAllocatorTest, LargeAllocation) { - // Note that large allocations should just use DiscardableMemoryAndroidSimple - // instead. - const size_t size = 64 * 1024 * 1024; - scoped_ptr<DiscardableMemory> memory(allocator_.Allocate(size)); - ASSERT_TRUE(memory); - WriteToDiscardableMemory(memory.get(), size); -} - -TEST_F(DiscardableMemoryAllocatorTest, ChunksArePageAligned) { - scoped_ptr<DiscardableMemory> memory(allocator_.Allocate(kPageSize)); - ASSERT_TRUE(memory); - EXPECT_EQ(0U, reinterpret_cast<uint64_t>(memory->Memory()) % kPageSize); - WriteToDiscardableMemory(memory.get(), kPageSize); -} - -TEST_F(DiscardableMemoryAllocatorTest, AllocateFreeAllocate) { - scoped_ptr<DiscardableMemory> memory(allocator_.Allocate(kPageSize)); - // Extra allocation that prevents the region from being deleted when |memory| - // gets deleted. - scoped_ptr<DiscardableMemory> 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()); - WriteToDiscardableMemory(memory.get(), kPageSize); -} - -TEST_F(DiscardableMemoryAllocatorTest, FreeingWholeAshmemRegionClosesAshmem) { - scoped_ptr<DiscardableMemory> 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(DiscardableMemoryAllocatorTest, AllocateUsesBestFitAlgorithm) { - scoped_ptr<DiscardableMemory> memory1(allocator_.Allocate(3 * kPageSize)); - ASSERT_TRUE(memory1); - scoped_ptr<DiscardableMemory> memory2(allocator_.Allocate(2 * kPageSize)); - ASSERT_TRUE(memory2); - scoped_ptr<DiscardableMemory> 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()); - WriteToDiscardableMemory(memory1.get(), kPageSize); -} - -TEST_F(DiscardableMemoryAllocatorTest, MergeFreeChunks) { - scoped_ptr<DiscardableMemory> memory1(allocator_.Allocate(kPageSize)); - ASSERT_TRUE(memory1); - scoped_ptr<DiscardableMemory> memory2(allocator_.Allocate(kPageSize)); - ASSERT_TRUE(memory2); - scoped_ptr<DiscardableMemory> memory3(allocator_.Allocate(kPageSize)); - ASSERT_TRUE(memory3); - scoped_ptr<DiscardableMemory> 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(DiscardableMemoryAllocatorTest, MergeFreeChunksAdvanced) { - scoped_ptr<DiscardableMemory> memory1(allocator_.Allocate(4 * kPageSize)); - ASSERT_TRUE(memory1); - scoped_ptr<DiscardableMemory> 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(DiscardableMemoryAllocatorTest, MergeFreeChunksAdvanced2) { - scoped_ptr<DiscardableMemory> memory1(allocator_.Allocate(4 * kPageSize)); - ASSERT_TRUE(memory1); - scoped_ptr<DiscardableMemory> memory2(allocator_.Allocate(4 * kPageSize)); - ASSERT_TRUE(memory2); - void* const memory1_address = memory1->Memory(); - memory1.reset(); - memory1 = allocator_.Allocate(2 * kPageSize); - scoped_ptr<DiscardableMemory> 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(DiscardableMemoryAllocatorTest, MergeFreeChunksAndDeleteAshmemRegion) { - scoped_ptr<DiscardableMemory> memory1(allocator_.Allocate(4 * kPageSize)); - ASSERT_TRUE(memory1); - scoped_ptr<DiscardableMemory> memory2(allocator_.Allocate(4 * kPageSize)); - ASSERT_TRUE(memory2); - memory1.reset(); - memory1 = allocator_.Allocate(2 * kPageSize); - scoped_ptr<DiscardableMemory> 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(DiscardableMemoryAllocatorTest, - TooLargeFreeChunksDontCauseTooMuchFragmentationWhenRecycled) { - // Keep |memory_1| below allocated so that the ashmem region doesn't get - // closed when |memory_2| is deleted. - scoped_ptr<DiscardableMemory> memory_1(allocator_.Allocate(64 * 1024)); - ASSERT_TRUE(memory_1); - scoped_ptr<DiscardableMemory> 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()); - WriteToDiscardableMemory(memory_2.get(), size); - scoped_ptr<DiscardableMemory> 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()); - WriteToDiscardableMemory(memory_3.get(), size); -} - -TEST_F(DiscardableMemoryAllocatorTest, UseMultipleAshmemRegions) { - // Leave one page untouched at the end of the ashmem region. - const size_t size = kMinAshmemRegionSize - kPageSize; - scoped_ptr<DiscardableMemory> memory1(allocator_.Allocate(size)); - ASSERT_TRUE(memory1); - WriteToDiscardableMemory(memory1.get(), size); - - scoped_ptr<DiscardableMemory> memory2( - allocator_.Allocate(kMinAshmemRegionSize)); - ASSERT_TRUE(memory2); - WriteToDiscardableMemory(memory2.get(), kMinAshmemRegionSize); - // The last page of the first ashmem region should be used for this - // allocation. - scoped_ptr<DiscardableMemory> memory3(allocator_.Allocate(kPageSize)); - ASSERT_TRUE(memory3); - WriteToDiscardableMemory(memory3.get(), kPageSize); - EXPECT_EQ(memory3->Memory(), static_cast<char*>(memory1->Memory()) + size); -} - -} // namespace internal -} // namespace base diff --git a/chromium/base/memory/discardable_memory_android.cc b/chromium/base/memory/discardable_memory_android.cc index 7e84967055f..33d38085716 100644 --- a/chromium/base/memory/discardable_memory_android.cc +++ b/chromium/base/memory/discardable_memory_android.cc @@ -2,258 +2,119 @@ // 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_android.h" - -#include <sys/mman.h> -#include <sys/resource.h> -#include <sys/time.h> -#include <unistd.h> - -#include <limits> +#include "base/memory/discardable_memory.h" +#include "base/android/sys_utils.h" #include "base/basictypes.h" #include "base/compiler_specific.h" -#include "base/file_util.h" #include "base/lazy_instance.h" #include "base/logging.h" -#include "base/memory/discardable_memory.h" -#include "base/memory/discardable_memory_allocator_android.h" -#include "base/synchronization/lock.h" -#include "third_party/ashmem/ashmem.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_malloc.h" namespace base { namespace { -const size_t kPageSize = 4096; - -const char kAshmemAllocatorName[] = "DiscardableMemoryAllocator"; - -struct GlobalContext { - GlobalContext() - : ashmem_fd_limit(GetSoftFDLimit()), - allocator(kAshmemAllocatorName), - ashmem_fd_count_(0) { - } - - const int ashmem_fd_limit; - internal::DiscardableMemoryAllocator allocator; - Lock lock; - - int ashmem_fd_count() const { - lock.AssertAcquired(); - return ashmem_fd_count_; - } - - void decrement_ashmem_fd_count() { - lock.AssertAcquired(); - --ashmem_fd_count_; - } - - void increment_ashmem_fd_count() { - lock.AssertAcquired(); - ++ashmem_fd_count_; - } - - private: - static int GetSoftFDLimit() { - struct rlimit limit_info; - if (getrlimit(RLIMIT_NOFILE, &limit_info) != 0) - return 128; - // Allow 25% of file descriptor capacity for ashmem. - return limit_info.rlim_cur / 4; - } - - int ashmem_fd_count_; -}; - -LazyInstance<GlobalContext>::Leaky g_context = LAZY_INSTANCE_INITIALIZER; - -// This is the default implementation of DiscardableMemory on Android which is -// used when file descriptor usage is under the soft limit. When file descriptor -// usage gets too high the discardable memory allocator is used instead. See -// ShouldUseAllocator() below for more details. -class DiscardableMemoryAndroidSimple : public DiscardableMemory { - public: - DiscardableMemoryAndroidSimple(int fd, void* address, size_t size) - : fd_(fd), - memory_(address), - size_(size) { - DCHECK_GE(fd_, 0); - DCHECK(memory_); - } - - virtual ~DiscardableMemoryAndroidSimple() { - internal::CloseAshmemRegion(fd_, size_, memory_); - } - - // DiscardableMemory: - virtual LockDiscardableMemoryStatus Lock() OVERRIDE { - return internal::LockAshmemRegion(fd_, 0, size_, memory_); - } +const char kAshmemAllocatorName[] = "DiscardableMemoryAshmemAllocator"; - virtual void Unlock() OVERRIDE { - internal::UnlockAshmemRegion(fd_, 0, size_, memory_); - } - - virtual void* Memory() const OVERRIDE { - return memory_; - } +// 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; - private: - const int fd_; - void* const memory_; - const size_t size_; - - DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryAndroidSimple); -}; - -int GetCurrentNumberOfAshmemFDs() { - AutoLock lock(g_context.Get().lock); - return g_context.Get().ashmem_fd_count(); +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::android::SysUtils::AmountOfPhysicalMemoryKB() * 1024 / 8; } -// Returns whether the provided size can be safely page-aligned (without causing -// an overflow). -bool CheckSizeCanBeAlignedToNextPage(size_t size) { - return size <= std::numeric_limits<size_t>::max() - kPageSize + 1; -} +// Holds the shared state used for allocations. +struct SharedState { + SharedState() + : manager(kAshmemMemoryLimit, + kAshmemMemoryLimit, + kAshmemMemoryLimit, + TimeDelta::Max()), + allocator(kAshmemAllocatorName, + GetOptimalAshmemRegionSizeForAllocator()) {} + + internal::DiscardableMemoryManager manager; + internal::DiscardableMemoryAshmemAllocator allocator; +}; +LazyInstance<SharedState>::Leaky g_shared_state = LAZY_INSTANCE_INITIALIZER; } // namespace -namespace internal { - -size_t AlignToNextPage(size_t size) { - DCHECK_EQ(static_cast<int>(kPageSize), getpagesize()); - DCHECK(CheckSizeCanBeAlignedToNextPage(size)); - const size_t mask = ~(kPageSize - 1); - return (size + kPageSize - 1) & mask; -} - -bool CreateAshmemRegion(const char* name, - size_t size, - int* out_fd, - void** out_address) { - AutoLock lock(g_context.Get().lock); - if (g_context.Get().ashmem_fd_count() + 1 > g_context.Get().ashmem_fd_limit) - return false; - int fd = ashmem_create_region(name, size); - if (fd < 0) { - DLOG(ERROR) << "ashmem_create_region() failed"; - return false; - } - file_util::ScopedFD fd_closer(&fd); - - const int err = ashmem_set_prot_region(fd, 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, 0); - if (address == MAP_FAILED) { - DPLOG(ERROR) << "Failed to map memory."; - return false; - } - - ignore_result(fd_closer.release()); - g_context.Get().increment_ashmem_fd_count(); - *out_fd = fd; - *out_address = address; - return true; -} - -bool CloseAshmemRegion(int fd, size_t size, void* address) { - AutoLock lock(g_context.Get().lock); - g_context.Get().decrement_ashmem_fd_count(); - if (munmap(address, size) == -1) { - DPLOG(ERROR) << "Failed to unmap memory."; - close(fd); - return false; - } - return close(fd) == 0; +// static +void DiscardableMemory::RegisterMemoryPressureListeners() { + internal::DiscardableMemoryEmulated::RegisterMemoryPressureListeners(); } -LockDiscardableMemoryStatus LockAshmemRegion(int fd, - size_t off, - size_t size, - const void* address) { - const int result = ashmem_pin_region(fd, off, size); - DCHECK_EQ(0, mprotect(address, size, PROT_READ | PROT_WRITE)); - return result == ASHMEM_WAS_PURGED ? - DISCARDABLE_MEMORY_PURGED : DISCARDABLE_MEMORY_SUCCESS; +// static +void DiscardableMemory::UnregisterMemoryPressureListeners() { + internal::DiscardableMemoryEmulated::UnregisterMemoryPressureListeners(); } -bool UnlockAshmemRegion(int fd, size_t off, size_t size, const void* address) { - const int failed = ashmem_unpin_region(fd, off, size); - if (failed) - DLOG(ERROR) << "Failed to unpin memory."; - // This allows us to catch accesses to unlocked memory. - DCHECK_EQ(0, mprotect(address, size, PROT_NONE)); - return !failed; +// static +bool DiscardableMemory::ReduceMemoryUsage() { + return internal::DiscardableMemoryEmulated::ReduceMemoryUsage(); } -} // namespace internal - // static -bool DiscardableMemory::SupportedNatively() { - return true; +void DiscardableMemory::GetSupportedTypes( + std::vector<DiscardableMemoryType>* types) { + const DiscardableMemoryType supported_types[] = { + DISCARDABLE_MEMORY_TYPE_ASHMEM, + DISCARDABLE_MEMORY_TYPE_EMULATED, + DISCARDABLE_MEMORY_TYPE_MALLOC + }; + types->assign(supported_types, supported_types + arraysize(supported_types)); } -// Allocation can happen in two ways: -// - Each client-requested allocation is backed by an individual ashmem region. -// This allows deleting ashmem regions individually by closing the ashmem file -// descriptor. This is the default path that is taken when file descriptor usage -// allows us to do so or when the allocation size would require and entire -// ashmem region. -// - Allocations are performed by the global allocator when file descriptor -// usage gets too high. This still allows unpinning but does not allow deleting -// (i.e. releasing the physical pages backing) individual regions. -// -// TODO(pliard): consider tuning the size threshold used below. For instance we -// might want to make it a fraction of kMinAshmemRegionSize and also -// systematically have small allocations go through the allocator to let big -// allocations systematically go through individual ashmem regions. -// // static -scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemory( - size_t size) { - if (!CheckSizeCanBeAlignedToNextPage(size)) - return scoped_ptr<DiscardableMemory>(); - // Pinning & unpinning works with page granularity therefore align the size - // upfront. - const size_t aligned_size = internal::AlignToNextPage(size); - // Note that the following code is slightly racy. The worst that can happen in - // practice though is taking the wrong decision (e.g. using the allocator - // rather than DiscardableMemoryAndroidSimple). Moreover keeping the lock - // acquired for the whole allocation would cause a deadlock when the allocator - // tries to create an ashmem region. - const size_t kAllocatorRegionSize = - internal::DiscardableMemoryAllocator::kMinAshmemRegionSize; - GlobalContext* const global_context = g_context.Pointer(); - if (aligned_size >= kAllocatorRegionSize || - GetCurrentNumberOfAshmemFDs() < 0.9 * global_context->ashmem_fd_limit) { - int fd; - void* address; - if (internal::CreateAshmemRegion("", aligned_size, &fd, &address)) { - return scoped_ptr<DiscardableMemory>( - new DiscardableMemoryAndroidSimple(fd, address, aligned_size)); +scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemoryWithType( + DiscardableMemoryType type, size_t size) { + switch (type) { + case DISCARDABLE_MEMORY_TYPE_NONE: + case DISCARDABLE_MEMORY_TYPE_MAC: + return scoped_ptr<DiscardableMemory>(); + 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 scoped_ptr<DiscardableMemory>(); + + return memory.PassAs<DiscardableMemory>(); + } + case DISCARDABLE_MEMORY_TYPE_EMULATED: { + scoped_ptr<internal::DiscardableMemoryEmulated> memory( + new internal::DiscardableMemoryEmulated(size)); + if (!memory->Initialize()) + return scoped_ptr<DiscardableMemory>(); + + return memory.PassAs<DiscardableMemory>(); + } + case DISCARDABLE_MEMORY_TYPE_MALLOC: { + scoped_ptr<internal::DiscardableMemoryMalloc> memory( + new internal::DiscardableMemoryMalloc(size)); + if (!memory->Initialize()) + return scoped_ptr<DiscardableMemory>(); + + return memory.PassAs<DiscardableMemory>(); } } - return global_context->allocator.Allocate(size); -} -// static -bool DiscardableMemory::PurgeForTestingSupported() { - return false; + NOTREACHED(); + return scoped_ptr<DiscardableMemory>(); } // static void DiscardableMemory::PurgeForTesting() { - NOTIMPLEMENTED(); + g_shared_state.Pointer()->manager.PurgeAll(); + internal::DiscardableMemoryEmulated::PurgeForTesting(); } } // namespace base diff --git a/chromium/base/memory/discardable_memory_android.h b/chromium/base/memory/discardable_memory_android.h deleted file mode 100644 index 9db78b33853..00000000000 --- a/chromium/base/memory/discardable_memory_android.h +++ /dev/null @@ -1,37 +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. - -// Please use discardable_memory.h since this is just an internal file providing -// utility functions used both by discardable_memory_android.cc and -// discardable_memory_allocator_android.cc. - -#ifndef BASE_MEMORY_DISCARDABLE_MEMORY_ANDROID_H_ -#define BASE_MEMORY_DISCARDABLE_MEMORY_ANDROID_H_ - -#include "base/basictypes.h" -#include "base/memory/discardable_memory.h" - -namespace base { -namespace internal { - -size_t AlignToNextPage(size_t size); - -bool CreateAshmemRegion(const char* name, size_t size, int* fd, void** address); - -bool CloseAshmemRegion(int fd, size_t size, void* address); - -LockDiscardableMemoryStatus LockAshmemRegion(int fd, - size_t offset, - size_t size, - const void* address); - -bool UnlockAshmemRegion(int fd, - size_t offset, - size_t size, - const void* address); - -} // namespace internal -} // namespace base - -#endif // BASE_MEMORY_DISCARDABLE_MEMORY_ANDROID_H_ diff --git a/chromium/base/memory/discardable_memory_ashmem.cc b/chromium/base/memory/discardable_memory_ashmem.cc new file mode 100644 index 00000000000..a590e531b7a --- /dev/null +++ b/chromium/base/memory/discardable_memory_ashmem.cc @@ -0,0 +1,75 @@ +// 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(); +} + +} // namespace internal +} // namespace base diff --git a/chromium/base/memory/discardable_memory_ashmem.h b/chromium/base/memory/discardable_memory_ashmem.h new file mode 100644 index 00000000000..8436f5d8ece --- /dev/null +++ b/chromium/base/memory/discardable_memory_ashmem.h @@ -0,0 +1,55 @@ +// 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; + + 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_allocator_android.cc b/chromium/base/memory/discardable_memory_ashmem_allocator.cc index 5e108176fe0..bc8c9b90617 100644 --- a/chromium/base/memory/discardable_memory_allocator_android.cc +++ b/chromium/base/memory/discardable_memory_ashmem_allocator.cc @@ -1,33 +1,34 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. +// 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_allocator_android.h" +#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/file_util.h" +#include "base/files/scoped_file.h" #include "base/logging.h" -#include "base/memory/discardable_memory.h" -#include "base/memory/discardable_memory_android.h" #include "base/memory/scoped_vector.h" -#include "base/synchronization/lock.h" -#include "base/threading/thread_checker.h" +#include "third_party/ashmem/ashmem.h" // The allocator consists of three parts (classes): -// - DiscardableMemoryAllocator: entry point of all allocations (through its -// Allocate() method) that are dispatched to the AshmemRegion instances (which -// it owns). +// - 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 implementing the DiscardableMemory interface -// whose instances are returned to the client. DiscardableAshmemChunk lets the -// client seamlessly operate on a subrange of the ashmem region managed by -// AshmemRegion. +// - DiscardableAshmemChunk: class mimicking the DiscardableMemory interface +// whose instances are returned to the client. namespace base { namespace { @@ -44,80 +45,96 @@ namespace { // issues. const size_t kMaxChunkFragmentationBytes = 4096 - 1; -} // namespace +const size_t kMinAshmemRegionSize = 32 * 1024 * 1024; -namespace internal { +// 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; +} -class DiscardableMemoryAllocator::DiscardableAshmemChunk - : public DiscardableMemory { - public: - // Note that |ashmem_region| must outlive |this|. - 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) { +bool CreateAshmemRegion(const char* name, + size_t size, + int* out_fd, + void** out_address) { + base::ScopedFD fd(ashmem_create_region(name, size)); + if (!fd.is_valid()) { + DLOG(ERROR) << "ashmem_create_region() failed"; + return false; } - // Implemented below AshmemRegion since this requires the full definition of - // AshmemRegion. - virtual ~DiscardableAshmemChunk(); - - // DiscardableMemory: - virtual LockDiscardableMemoryStatus Lock() OVERRIDE { - DCHECK(!locked_); - locked_ = true; - return internal::LockAshmemRegion(fd_, offset_, size_, address_); + 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; } - virtual void Unlock() OVERRIDE { - DCHECK(locked_); - locked_ = false; - internal::UnlockAshmemRegion(fd_, offset_, size_, address_); + // 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; } - virtual void* Memory() const OVERRIDE { - return address_; + *out_fd = fd.release(); + *out_address = 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; +} - private: - AshmemRegion* const ashmem_region_; - const int fd_; - void* const address_; - const size_t offset_; - const size_t size_; - bool locked_; +bool LockAshmemRegion(int fd, size_t off, size_t size) { + return ashmem_pin_region(fd, off, size) != ASHMEM_WAS_PURGED; +} - DISALLOW_COPY_AND_ASSIGN(DiscardableAshmemChunk); -}; +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 DiscardableMemoryAllocator::AshmemRegion { +class AshmemRegion { public: // Note that |allocator| must outlive |this|. static scoped_ptr<AshmemRegion> Create( size_t size, const std::string& name, - DiscardableMemoryAllocator* allocator) { + DiscardableMemoryAshmemAllocator* allocator) { + DCHECK_EQ(size, AlignToNextPage(size)); int fd; void* base; - if (!internal::CreateAshmemRegion(name.c_str(), size, &fd, &base)) + if (!CreateAshmemRegion(name.c_str(), size, &fd, &base)) return scoped_ptr<AshmemRegion>(); return make_scoped_ptr(new AshmemRegion(fd, size, base, allocator)); } - virtual ~AshmemRegion() { - const bool result = internal::CloseAshmemRegion(fd_, size_, base_); + ~AshmemRegion() { + const bool result = CloseAshmemRegion(fd_, size_, base_); DCHECK(result); + DCHECK(!highest_allocated_chunk_); } - // Returns a new instance of DiscardableMemory whose size is greater or equal - // than |actual_size| (which is expected to be greater or equal than + // 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 @@ -127,21 +144,34 @@ class DiscardableMemoryAllocator::AshmemRegion { // 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<DiscardableMemory> Allocate_Locked(size_t client_requested_size, - size_t actual_size) { + scoped_ptr<DiscardableAshmemChunk> Allocate_Locked( + size_t client_requested_size, + size_t actual_size) { DCHECK_LE(client_requested_size, actual_size); allocator_->lock_.AssertAcquired(); - scoped_ptr<DiscardableMemory> memory = ReuseFreeChunk_Locked( + + // 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<DiscardableMemory>(); + return scoped_ptr<DiscardableAshmemChunk>(); } + void* const address = static_cast<char*>(base_) + offset_; memory.reset( new DiscardableAshmemChunk(this, fd_, address, offset_, actual_size)); + used_to_previous_chunk_map_.insert( std::make_pair(address, highest_allocated_chunk_)); highest_allocated_chunk_ = address; @@ -158,10 +188,19 @@ class DiscardableMemoryAllocator::AshmemRegion { private: struct FreeChunk { + FreeChunk() : previous_chunk(NULL), start(NULL), size(0) {} + + explicit FreeChunk(size_t size) + : previous_chunk(NULL), + start(NULL), + size(size) { + } + FreeChunk(void* previous_chunk, void* start, size_t size) : previous_chunk(previous_chunk), start(start), size(size) { + DCHECK_LT(previous_chunk, start); } void* const previous_chunk; @@ -179,7 +218,7 @@ class DiscardableMemoryAllocator::AshmemRegion { AshmemRegion(int fd, size_t size, void* base, - DiscardableMemoryAllocator* allocator) + DiscardableMemoryAshmemAllocator* allocator) : fd_(fd), size_(size), base_(base), @@ -193,14 +232,14 @@ class DiscardableMemoryAllocator::AshmemRegion { } // Tries to reuse a previously freed chunk by doing a closest size match. - scoped_ptr<DiscardableMemory> ReuseFreeChunk_Locked( + 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(NULL, NULL, actual_size))); + free_chunks_.lower_bound(FreeChunk(actual_size))); if (reused_chunk.is_null()) - return scoped_ptr<DiscardableMemory>(); + return scoped_ptr<DiscardableAshmemChunk>(); used_to_previous_chunk_map_.insert( std::make_pair(reused_chunk.start, reused_chunk.previous_chunk)); @@ -212,6 +251,7 @@ class DiscardableMemoryAllocator::AshmemRegion { 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 @@ -219,6 +259,11 @@ class DiscardableMemoryAllocator::AshmemRegion { reused_chunk_size = actual_size; void* const new_chunk_start = static_cast<char*>(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 @@ -226,13 +271,13 @@ class DiscardableMemoryAllocator::AshmemRegion { AddFreeChunk_Locked( FreeChunk(reused_chunk.start, new_chunk_start, new_chunk_size)); } + const size_t offset = static_cast<char*>(reused_chunk.start) - static_cast<char*>(base_); - internal::LockAshmemRegion( - fd_, offset, reused_chunk_size, reused_chunk.start); - scoped_ptr<DiscardableMemory> memory( - new DiscardableAshmemChunk(this, fd_, reused_chunk.start, offset, - reused_chunk_size)); + LockAshmemRegion(fd_, offset, reused_chunk_size); + scoped_ptr<DiscardableAshmemChunk> memory( + new DiscardableAshmemChunk( + this, fd_, reused_chunk.start, offset, reused_chunk_size)); return memory.Pass(); } @@ -259,24 +304,34 @@ class DiscardableMemoryAllocator::AshmemRegion { DCHECK(previous_chunk_it != used_to_previous_chunk_map_.end()); void* 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. - DCHECK(!address_to_free_chunk_map_.count(free_chunk.previous_chunk)); + previous_chunk = free_chunk.previous_chunk; + DCHECK(!address_to_free_chunk_map_.count(previous_chunk)); } } + // Merge with the next chunk if free and present. void* next_chunk = static_cast<char*>(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(static_cast<char*>(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) { @@ -284,11 +339,14 @@ class DiscardableMemoryAllocator::AshmemRegion { 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_ = NULL; allocator_->DeleteAshmemRegion_Locked(this); // Deletes |this|. } @@ -316,7 +374,7 @@ class DiscardableMemoryAllocator::AshmemRegion { void*, std::multiset<FreeChunk>::iterator>::iterator it = address_to_free_chunk_map_.find(chunk_start); if (it == address_to_free_chunk_map_.end()) - return FreeChunk(NULL, NULL, 0U); + return FreeChunk(); return RemoveFreeChunkFromIterator_Locked(it->second); } @@ -325,7 +383,7 @@ class DiscardableMemoryAllocator::AshmemRegion { std::multiset<FreeChunk>::iterator free_chunk_it) { allocator_->lock_.AssertAcquired(); if (free_chunk_it == free_chunks_.end()) - return FreeChunk(NULL, NULL, 0U); + 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); @@ -336,7 +394,9 @@ class DiscardableMemoryAllocator::AshmemRegion { const int fd_; const size_t size_; void* const base_; - DiscardableMemoryAllocator* const allocator_; + 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. void* highest_allocated_chunk_; // Points to the end of |highest_allocated_chunk_|. size_t offset_; @@ -358,24 +418,61 @@ class DiscardableMemoryAllocator::AshmemRegion { DISALLOW_COPY_AND_ASSIGN(AshmemRegion); }; -DiscardableMemoryAllocator::DiscardableAshmemChunk::~DiscardableAshmemChunk() { +DiscardableAshmemChunk::~DiscardableAshmemChunk() { if (locked_) - internal::UnlockAshmemRegion(fd_, offset_, size_, address_); + UnlockAshmemRegion(fd_, offset_, size_); ashmem_region_->OnChunkDeletion(address_, size_); } -DiscardableMemoryAllocator::DiscardableMemoryAllocator(const std::string& name) - : name_(name) { +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); } -DiscardableMemoryAllocator::~DiscardableMemoryAllocator() { - DCHECK(thread_checker_.CalledOnValidThread()); +DiscardableMemoryAshmemAllocator::~DiscardableMemoryAshmemAllocator() { DCHECK(ashmem_regions_.empty()); } -scoped_ptr<DiscardableMemory> DiscardableMemoryAllocator::Allocate( +scoped_ptr<DiscardableAshmemChunk> DiscardableMemoryAshmemAllocator::Allocate( size_t size) { - const size_t aligned_size = internal::AlignToNextPage(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 @@ -384,24 +481,36 @@ scoped_ptr<DiscardableMemory> DiscardableMemoryAllocator::Allocate( DCHECK_LE(ashmem_regions_.size(), 5U); for (ScopedVector<AshmemRegion>::iterator it = ashmem_regions_.begin(); it != ashmem_regions_.end(); ++it) { - scoped_ptr<DiscardableMemory> memory( + scoped_ptr<DiscardableAshmemChunk> memory( (*it)->Allocate_Locked(size, aligned_size)); if (memory) return memory.Pass(); } - scoped_ptr<AshmemRegion> new_region( - AshmemRegion::Create( - std::max(static_cast<size_t>(kMinAshmemRegionSize), aligned_size), - name_.c_str(), this)); - if (!new_region) { - // TODO(pliard): consider adding an histogram to see how often this happens. - return scoped_ptr<DiscardableMemory>(); + // 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); } - 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 DiscardableMemoryAllocator::DeleteAshmemRegion_Locked( +void DiscardableMemoryAshmemAllocator::DeleteAshmemRegion_Locked( AshmemRegion* region) { lock_.AssertAcquired(); // Note that there should not be more than a couple of ashmem region instances diff --git a/chromium/base/memory/discardable_memory_ashmem_allocator.h b/chromium/base/memory/discardable_memory_ashmem_allocator.h new file mode 100644 index 00000000000..996dde92496 --- /dev/null +++ b/chromium/base/memory/discardable_memory_ashmem_allocator.h @@ -0,0 +1,93 @@ +// 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 new file mode 100644 index 00000000000..e9f63ba3439 --- /dev/null +++ b/chromium/base/memory/discardable_memory_ashmem_allocator_unittest.cc @@ -0,0 +1,319 @@ +// 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 index ed7c42c750f..340a181834a 100644 --- a/chromium/base/memory/discardable_memory_emulated.cc +++ b/chromium/base/memory/discardable_memory_emulated.cc @@ -5,60 +5,103 @@ #include "base/memory/discardable_memory_emulated.h" #include "base/lazy_instance.h" -#include "base/memory/discardable_memory_provider.h" +#include "base/memory/discardable_memory_manager.h" namespace base { - namespace { -base::LazyInstance<internal::DiscardableMemoryProvider>::Leaky g_provider = - LAZY_INSTANCE_INITIALIZER; +// This is admittedly pretty magical. +const size_t kEmulatedMemoryLimit = 512 * 1024 * 1024; +const size_t kEmulatedSoftMemoryLimit = 32 * 1024 * 1024; +const size_t kEmulatedBytesToKeepUnderModeratePressure = 4 * 1024 * 1024; +const size_t kEmulatedHardMemoryLimitExpirationTimeMs = 1000; + +struct SharedState { + SharedState() + : manager(kEmulatedMemoryLimit, + kEmulatedSoftMemoryLimit, + kEmulatedBytesToKeepUnderModeratePressure, + TimeDelta::FromMilliseconds( + kEmulatedHardMemoryLimitExpirationTimeMs)) {} + + internal::DiscardableMemoryManager manager; +}; +LazyInstance<SharedState>::Leaky g_shared_state = LAZY_INSTANCE_INITIALIZER; } // namespace namespace internal { -DiscardableMemoryEmulated::DiscardableMemoryEmulated(size_t size) - : is_locked_(false) { - g_provider.Pointer()->Register(this, size); +DiscardableMemoryEmulated::DiscardableMemoryEmulated(size_t bytes) + : bytes_(bytes), + is_locked_(false) { + g_shared_state.Pointer()->manager.Register(this, bytes); } DiscardableMemoryEmulated::~DiscardableMemoryEmulated() { if (is_locked_) Unlock(); - g_provider.Pointer()->Unregister(this); + g_shared_state.Pointer()->manager.Unregister(this); +} + +// static +void DiscardableMemoryEmulated::RegisterMemoryPressureListeners() { + g_shared_state.Pointer()->manager.RegisterMemoryPressureListener(); +} + +// static +void DiscardableMemoryEmulated::UnregisterMemoryPressureListeners() { + g_shared_state.Pointer()->manager.UnregisterMemoryPressureListener(); +} + +// static +bool DiscardableMemoryEmulated::ReduceMemoryUsage() { + return g_shared_state.Pointer()->manager.ReduceMemoryUsage(); +} + +// static +void DiscardableMemoryEmulated::PurgeForTesting() { + g_shared_state.Pointer()->manager.PurgeAll(); } bool DiscardableMemoryEmulated::Initialize() { - return Lock() == DISCARDABLE_MEMORY_PURGED; + return Lock() != DISCARDABLE_MEMORY_LOCK_STATUS_FAILED; } -LockDiscardableMemoryStatus DiscardableMemoryEmulated::Lock() { +DiscardableMemoryLockStatus DiscardableMemoryEmulated::Lock() { DCHECK(!is_locked_); bool purged = false; - memory_ = g_provider.Pointer()->Acquire(this, &purged); - if (!memory_) - return DISCARDABLE_MEMORY_FAILED; + if (!g_shared_state.Pointer()->manager.AcquireLock(this, &purged)) + return DISCARDABLE_MEMORY_LOCK_STATUS_FAILED; is_locked_ = true; - return purged ? DISCARDABLE_MEMORY_PURGED : DISCARDABLE_MEMORY_SUCCESS; + return purged ? DISCARDABLE_MEMORY_LOCK_STATUS_PURGED + : DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS; } void DiscardableMemoryEmulated::Unlock() { DCHECK(is_locked_); - g_provider.Pointer()->Release(this, memory_.Pass()); + g_shared_state.Pointer()->manager.ReleaseLock(this); is_locked_ = false; } void* DiscardableMemoryEmulated::Memory() const { + DCHECK(is_locked_); DCHECK(memory_); return memory_.get(); } -// static -void DiscardableMemoryEmulated::PurgeForTesting() { - g_provider.Pointer()->PurgeAll(); +bool DiscardableMemoryEmulated::AllocateAndAcquireLock() { + if (memory_) + return true; + + memory_.reset(new uint8[bytes_]); + return false; +} + +void DiscardableMemoryEmulated::Purge() { + memory_.reset(); } } // namespace internal diff --git a/chromium/base/memory/discardable_memory_emulated.h b/chromium/base/memory/discardable_memory_emulated.h index bd0e834ed06..64e99511b7c 100644 --- a/chromium/base/memory/discardable_memory_emulated.h +++ b/chromium/base/memory/discardable_memory_emulated.h @@ -7,25 +7,39 @@ #include "base/memory/discardable_memory.h" +#include "base/memory/discardable_memory_manager.h" + namespace base { namespace internal { -class DiscardableMemoryEmulated : public DiscardableMemory { +class DiscardableMemoryEmulated + : public DiscardableMemory, + public internal::DiscardableMemoryManagerAllocation { public: - explicit DiscardableMemoryEmulated(size_t size); + explicit DiscardableMemoryEmulated(size_t bytes); virtual ~DiscardableMemoryEmulated(); + static void RegisterMemoryPressureListeners(); + static void UnregisterMemoryPressureListeners(); + static bool ReduceMemoryUsage(); + static void PurgeForTesting(); bool Initialize(); // Overridden from DiscardableMemory: - virtual LockDiscardableMemoryStatus Lock() OVERRIDE; + 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; + private: - scoped_ptr<uint8, FreeDeleter> memory_; + const size_t bytes_; + scoped_ptr<uint8[]> memory_; bool is_locked_; DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryEmulated); diff --git a/chromium/base/memory/discardable_memory_linux.cc b/chromium/base/memory/discardable_memory_linux.cc index 92e39e5e7d4..b9342e92043 100644 --- a/chromium/base/memory/discardable_memory_linux.cc +++ b/chromium/base/memory/discardable_memory_linux.cc @@ -2,29 +2,67 @@ // 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_malloc.h" namespace base { // static -bool DiscardableMemory::SupportedNatively() { - return false; +void DiscardableMemory::RegisterMemoryPressureListeners() { + internal::DiscardableMemoryEmulated::RegisterMemoryPressureListeners(); +} + +// static +void DiscardableMemory::UnregisterMemoryPressureListeners() { + internal::DiscardableMemoryEmulated::UnregisterMemoryPressureListeners(); +} + +// static +bool DiscardableMemory::ReduceMemoryUsage() { + return internal::DiscardableMemoryEmulated::ReduceMemoryUsage(); } // static -scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemory( - size_t size) { - scoped_ptr<internal::DiscardableMemoryEmulated> memory( - new internal::DiscardableMemoryEmulated(size)); - if (!memory->Initialize()) - return scoped_ptr<DiscardableMemory>(); - - return memory.PassAs<DiscardableMemory>(); +void DiscardableMemory::GetSupportedTypes( + std::vector<DiscardableMemoryType>* types) { + const DiscardableMemoryType supported_types[] = { + DISCARDABLE_MEMORY_TYPE_EMULATED, + DISCARDABLE_MEMORY_TYPE_MALLOC + }; + types->assign(supported_types, supported_types + arraysize(supported_types)); } // static -bool DiscardableMemory::PurgeForTestingSupported() { - return true; +scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemoryWithType( + DiscardableMemoryType type, size_t size) { + switch (type) { + case DISCARDABLE_MEMORY_TYPE_NONE: + case DISCARDABLE_MEMORY_TYPE_ASHMEM: + case DISCARDABLE_MEMORY_TYPE_MAC: + return scoped_ptr<DiscardableMemory>(); + case DISCARDABLE_MEMORY_TYPE_EMULATED: { + scoped_ptr<internal::DiscardableMemoryEmulated> memory( + new internal::DiscardableMemoryEmulated(size)); + if (!memory->Initialize()) + return scoped_ptr<DiscardableMemory>(); + + return memory.PassAs<DiscardableMemory>(); + } + case DISCARDABLE_MEMORY_TYPE_MALLOC: { + scoped_ptr<internal::DiscardableMemoryMalloc> memory( + new internal::DiscardableMemoryMalloc(size)); + if (!memory->Initialize()) + return scoped_ptr<DiscardableMemory>(); + + return memory.PassAs<DiscardableMemory>(); + } + } + + NOTREACHED(); + return scoped_ptr<DiscardableMemory>(); } // static diff --git a/chromium/base/memory/discardable_memory_mac.cc b/chromium/base/memory/discardable_memory_mac.cc index aa6823509d5..b2184e7d589 100644 --- a/chromium/base/memory/discardable_memory_mac.cc +++ b/chromium/base/memory/discardable_memory_mac.cc @@ -5,69 +5,154 @@ #include "base/memory/discardable_memory.h" #include <mach/mach.h> -#include <sys/mman.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" +#include "base/mac/scoped_mach_vm.h" +#include "base/memory/discardable_memory_emulated.h" +#include "base/memory/discardable_memory_malloc.h" +#include "base/memory/discardable_memory_manager.h" #include "base/memory/scoped_ptr.h" namespace base { namespace { +// For Mac, have the DiscardableMemoryManager trigger userspace eviction when +// address space usage gets too high (e.g. 512 MBytes). +const size_t kMacMemoryLimit = 512 * 1024 * 1024; + +struct SharedState { + SharedState() + : manager(kMacMemoryLimit, + kMacMemoryLimit, + kMacMemoryLimit, + TimeDelta::Max()) {} + + internal::DiscardableMemoryManager manager; +}; +LazyInstance<SharedState>::Leaky g_shared_state = 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); -class DiscardableMemoryMac : public DiscardableMemory { +class DiscardableMemoryMac + : public DiscardableMemory, + public internal::DiscardableMemoryManagerAllocation { public: - DiscardableMemoryMac(void* memory, size_t size) - : memory_(memory), - size_(size) { - DCHECK(memory_); + explicit DiscardableMemoryMac(size_t bytes) + : memory_(0, 0), + bytes_(mach_vm_round_page(bytes)), + is_locked_(false) { + g_shared_state.Pointer()->manager.Register(this, bytes); } + bool Initialize() { return Lock() != DISCARDABLE_MEMORY_LOCK_STATUS_FAILED; } + virtual ~DiscardableMemoryMac() { - vm_deallocate(mach_task_self(), - reinterpret_cast<vm_address_t>(memory_), - size_); + if (is_locked_) + Unlock(); + g_shared_state.Pointer()->manager.Unregister(this); } - virtual LockDiscardableMemoryStatus Lock() OVERRIDE { - DCHECK_EQ(0, mprotect(memory_, size_, PROT_READ | PROT_WRITE)); - int state = VM_PURGABLE_NONVOLATILE; - kern_return_t ret = vm_purgable_control( - mach_task_self(), - reinterpret_cast<vm_address_t>(memory_), - VM_PURGABLE_SET_STATE, - &state); - if (ret != KERN_SUCCESS) - return DISCARDABLE_MEMORY_FAILED; - - return state & VM_PURGABLE_EMPTY ? DISCARDABLE_MEMORY_PURGED - : DISCARDABLE_MEMORY_SUCCESS; + // Overridden from DiscardableMemory: + virtual DiscardableMemoryLockStatus Lock() OVERRIDE { + DCHECK(!is_locked_); + + bool purged = false; + if (!g_shared_state.Pointer()->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; } virtual void Unlock() OVERRIDE { - int state = VM_PURGABLE_VOLATILE | VM_VOLATILE_GROUP_DEFAULT; - kern_return_t ret = vm_purgable_control( - mach_task_self(), - reinterpret_cast<vm_address_t>(memory_), - VM_PURGABLE_SET_STATE, - &state); - DCHECK_EQ(0, mprotect(memory_, size_, PROT_NONE)); - if (ret != KERN_SUCCESS) - DLOG(ERROR) << "Failed to unlock memory."; + DCHECK(is_locked_); + g_shared_state.Pointer()->manager.ReleaseLock(this); + is_locked_ = false; } virtual void* Memory() const OVERRIDE { - return memory_; + DCHECK(is_locked_); + return reinterpret_cast<void*>(memory_.address()); + } + + // Overridden from internal::DiscardableMemoryManagerAllocation: + virtual bool AllocateAndAcquireLock() OVERRIDE { + 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; + } + + virtual void ReleaseLock() OVERRIDE { + 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 + } + + virtual void Purge() OVERRIDE { + memory_.reset(); } private: - void* const memory_; - const size_t size_; + mac::ScopedMachVM memory_; + const size_t bytes_; + bool is_locked_; DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryMac); }; @@ -75,37 +160,72 @@ class DiscardableMemoryMac : public DiscardableMemory { } // namespace // static -bool DiscardableMemory::SupportedNatively() { - return true; +void DiscardableMemory::RegisterMemoryPressureListeners() { + internal::DiscardableMemoryEmulated::RegisterMemoryPressureListeners(); } // static -scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemory( - size_t size) { - vm_address_t buffer = 0; - kern_return_t ret = vm_allocate(mach_task_self(), - &buffer, - size, - VM_FLAGS_PURGABLE | - VM_FLAGS_ANYWHERE | - kDiscardableMemoryTag); - if (ret != KERN_SUCCESS) { - DLOG(ERROR) << "vm_allocate() failed"; - return scoped_ptr<DiscardableMemory>(); - } - return scoped_ptr<DiscardableMemory>( - new DiscardableMemoryMac(reinterpret_cast<void*>(buffer), size)); +void DiscardableMemory::UnregisterMemoryPressureListeners() { + internal::DiscardableMemoryEmulated::UnregisterMemoryPressureListeners(); } // static -bool DiscardableMemory::PurgeForTestingSupported() { - return true; +bool DiscardableMemory::ReduceMemoryUsage() { + return internal::DiscardableMemoryEmulated::ReduceMemoryUsage(); +} + +// static +void DiscardableMemory::GetSupportedTypes( + std::vector<DiscardableMemoryType>* types) { + const DiscardableMemoryType supported_types[] = { + DISCARDABLE_MEMORY_TYPE_MAC, + DISCARDABLE_MEMORY_TYPE_EMULATED, + DISCARDABLE_MEMORY_TYPE_MALLOC + }; + 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_NONE: + case DISCARDABLE_MEMORY_TYPE_ASHMEM: + return scoped_ptr<DiscardableMemory>(); + case DISCARDABLE_MEMORY_TYPE_MAC: { + scoped_ptr<DiscardableMemoryMac> memory(new DiscardableMemoryMac(size)); + if (!memory->Initialize()) + return scoped_ptr<DiscardableMemory>(); + + return memory.PassAs<DiscardableMemory>(); + } + case DISCARDABLE_MEMORY_TYPE_EMULATED: { + scoped_ptr<internal::DiscardableMemoryEmulated> memory( + new internal::DiscardableMemoryEmulated(size)); + if (!memory->Initialize()) + return scoped_ptr<DiscardableMemory>(); + + return memory.PassAs<DiscardableMemory>(); + } + case DISCARDABLE_MEMORY_TYPE_MALLOC: { + scoped_ptr<internal::DiscardableMemoryMalloc> memory( + new internal::DiscardableMemoryMalloc(size)); + if (!memory->Initialize()) + return scoped_ptr<DiscardableMemory>(); + + return memory.PassAs<DiscardableMemory>(); + } + } + + NOTREACHED(); + return scoped_ptr<DiscardableMemory>(); } // static void DiscardableMemory::PurgeForTesting() { int state = 0; vm_purgable_control(mach_task_self(), 0, VM_PURGABLE_PURGE_ALL, &state); + internal::DiscardableMemoryEmulated::PurgeForTesting(); } } // namespace base diff --git a/chromium/base/memory/discardable_memory_malloc.cc b/chromium/base/memory/discardable_memory_malloc.cc new file mode 100644 index 00000000000..a72f9112d39 --- /dev/null +++ b/chromium/base/memory/discardable_memory_malloc.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/memory/discardable_memory_malloc.h" + +#include "base/logging.h" + +namespace base { +namespace internal { + +DiscardableMemoryMalloc::DiscardableMemoryMalloc(size_t size) : size_(size) { +} + +DiscardableMemoryMalloc::~DiscardableMemoryMalloc() { +} + +bool DiscardableMemoryMalloc::Initialize() { + return Lock() != DISCARDABLE_MEMORY_LOCK_STATUS_FAILED; +} + +DiscardableMemoryLockStatus DiscardableMemoryMalloc::Lock() { + DCHECK(!memory_); + + memory_.reset(static_cast<uint8*>(malloc(size_))); + if (!memory_) + return DISCARDABLE_MEMORY_LOCK_STATUS_FAILED; + + return DISCARDABLE_MEMORY_LOCK_STATUS_PURGED; +} + +void DiscardableMemoryMalloc::Unlock() { + DCHECK(memory_); + memory_.reset(); +} + +void* DiscardableMemoryMalloc::Memory() const { + DCHECK(memory_); + return memory_.get(); +} + +} // namespace internal +} // namespace base diff --git a/chromium/base/memory/discardable_memory_malloc.h b/chromium/base/memory/discardable_memory_malloc.h new file mode 100644 index 00000000000..7729ce5c49b --- /dev/null +++ b/chromium/base/memory/discardable_memory_malloc.h @@ -0,0 +1,35 @@ +// 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_MALLOC_H_ +#define BASE_MEMORY_DISCARDABLE_MEMORY_MALLOC_H_ + +#include "base/memory/discardable_memory.h" + +namespace base { +namespace internal { + +class DiscardableMemoryMalloc : public DiscardableMemory { + public: + explicit DiscardableMemoryMalloc(size_t size); + virtual ~DiscardableMemoryMalloc(); + + bool Initialize(); + + // Overridden from DiscardableMemory: + virtual DiscardableMemoryLockStatus Lock() OVERRIDE; + virtual void Unlock() OVERRIDE; + virtual void* Memory() const OVERRIDE; + + private: + scoped_ptr<uint8, FreeDeleter> memory_; + const size_t size_; + + DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryMalloc); +}; + +} // namespace internal +} // namespace base + +#endif // BASE_MEMORY_DISCARDABLE_MEMORY_MALLOC_H_ diff --git a/chromium/base/memory/discardable_memory_manager.cc b/chromium/base/memory/discardable_memory_manager.cc new file mode 100644 index 00000000000..d976da203c4 --- /dev/null +++ b/chromium/base/memory/discardable_memory_manager.cc @@ -0,0 +1,263 @@ +// 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/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, + size_t bytes_to_keep_under_moderate_pressure, + TimeDelta hard_memory_limit_expiration_time) + : allocations_(AllocationMap::NO_AUTO_EVICT), + bytes_allocated_(0u), + memory_limit_(memory_limit), + soft_memory_limit_(soft_memory_limit), + bytes_to_keep_under_moderate_pressure_( + bytes_to_keep_under_moderate_pressure), + 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::RegisterMemoryPressureListener() { + AutoLock lock(lock_); + DCHECK(base::MessageLoop::current()); + DCHECK(!memory_pressure_listener_); + memory_pressure_listener_.reset(new MemoryPressureListener(base::Bind( + &DiscardableMemoryManager::OnMemoryPressure, Unretained(this)))); +} + +void DiscardableMemoryManager::UnregisterMemoryPressureListener() { + AutoLock lock(lock_); + DCHECK(memory_pressure_listener_); + memory_pressure_listener_.reset(); +} + +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::SetBytesToKeepUnderModeratePressure( + size_t bytes) { + AutoLock lock(lock_); + bytes_to_keep_under_moderate_pressure_ = bytes; +} + +void DiscardableMemoryManager::SetHardMemoryLimitExpirationTime( + TimeDelta hard_memory_limit_expiration_time) { + AutoLock lock(lock_); + hard_memory_limit_expiration_time_ = hard_memory_limit_expiration_time; +} + +bool DiscardableMemoryManager::ReduceMemoryUsage() { + return PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit(); +} + +void DiscardableMemoryManager::Register(Allocation* allocation, size_t bytes) { + AutoLock lock(lock_); + // A registered memory listener is currently required. This DCHECK can be + // moved or removed if we decide that it's useful to relax this condition. + // TODO(reveman): Enable this DCHECK when skia and blink are able to + // register memory pressure listeners. crbug.com/333907 + // DCHECK(memory_pressure_listener_); + 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_; +} + +void DiscardableMemoryManager::OnMemoryPressure( + MemoryPressureListener::MemoryPressureLevel pressure_level) { + switch (pressure_level) { + case MemoryPressureListener::MEMORY_PRESSURE_MODERATE: + PurgeUntilWithinBytesToKeepUnderModeratePressure(); + return; + case MemoryPressureListener::MEMORY_PRESSURE_CRITICAL: + PurgeAll(); + return; + } + + NOTREACHED(); +} + +void +DiscardableMemoryManager::PurgeUntilWithinBytesToKeepUnderModeratePressure() { + AutoLock lock(lock_); + + PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired( + Now(), bytes_to_keep_under_moderate_pressure_); +} + +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 (AllocationMap::reverse_iterator it = allocations_.rbegin(); + it != allocations_.rend(); + ++it) { + Allocation* allocation = it->first; + AllocationInfo* info = &it->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 new file mode 100644 index 00000000000..a61f141c430 --- /dev/null +++ b/chromium/base/memory/discardable_memory_manager.h @@ -0,0 +1,206 @@ +// 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/memory/memory_pressure_listener.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; + + protected: + virtual ~DiscardableMemoryManagerAllocation() {} +}; + +} // namespace internal +} // namespace base + +#if defined(COMPILER_GCC) +namespace BASE_HASH_NAMESPACE { +template <> +struct hash<base::internal::DiscardableMemoryManagerAllocation*> { + size_t operator()( + base::internal::DiscardableMemoryManagerAllocation* ptr) const { + return hash<size_t>()(reinterpret_cast<size_t>(ptr)); + } +}; +} // namespace BASE_HASH_NAMESPACE +#endif // COMPILER + +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. +// +// When notified of memory pressure, the manager either purges the LRU memory -- +// if the pressure is moderate -- or all discardable memory if the pressure is +// critical. +class BASE_EXPORT_PRIVATE DiscardableMemoryManager { + public: + typedef DiscardableMemoryManagerAllocation Allocation; + + DiscardableMemoryManager(size_t memory_limit, + size_t soft_memory_limit, + size_t bytes_to_keep_under_moderate_pressure, + TimeDelta hard_memory_limit_expiration_time); + virtual ~DiscardableMemoryManager(); + + // Call this to register memory pressure listener. Must be called on a thread + // with a MessageLoop current. + void RegisterMemoryPressureListener(); + + // Call this to unregister memory pressure listener. + void UnregisterMemoryPressureListener(); + + // 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 amount of memory to keep when we're under moderate pressure. + void SetBytesToKeepUnderModeratePressure(size_t bytes); + + // Sets the memory usage cutoff time for hard memory limit. + void SetHardMemoryLimitExpirationTime( + TimeDelta hard_memory_limit_expiration_time); + + // 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(); + + // 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; + + // This can be called as a hint that the system is under memory pressure. + void OnMemoryPressure( + MemoryPressureListener::MemoryPressureLevel pressure_level); + + // Purges memory until usage is less or equal to + // |bytes_to_keep_under_moderate_pressure_|. + void PurgeUntilWithinBytesToKeepUnderModeratePressure(); + + // 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_; + + // Under moderate memory pressure, we will purge memory until usage is within + // this limit. + size_t bytes_to_keep_under_moderate_pressure_; + + // Allows us to be respond when the system reports that it is under memory + // pressure. + scoped_ptr<MemoryPressureListener> memory_pressure_listener_; + + // 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 new file mode 100644 index 00000000000..ef5739a6526 --- /dev/null +++ b/chromium/base/memory/discardable_memory_manager_unittest.cc @@ -0,0 +1,505 @@ +// 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/run_loop.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) {} + virtual ~TestAllocationImpl() { DCHECK(!is_locked_); } + + // Overridden from internal::DiscardableMemoryManagerAllocation: + virtual bool AllocateAndAcquireLock() OVERRIDE { + bool was_allocated = is_allocated_; + is_allocated_ = true; + DCHECK(!is_locked_); + is_locked_ = true; + return was_allocated; + } + virtual void ReleaseLock() OVERRIDE { + DCHECK(is_locked_); + is_locked_ = false; + } + virtual void Purge() OVERRIDE { + DCHECK(is_allocated_); + is_allocated_ = false; + } + + 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; +const size_t kDefaultBytesToKeepUnderModeratePressure = kDefaultMemoryLimit; + +class TestDiscardableMemoryManagerImpl + : public internal::DiscardableMemoryManager { + public: + TestDiscardableMemoryManagerImpl() + : DiscardableMemoryManager(kDefaultMemoryLimit, + kDefaultSoftMemoryLimit, + kDefaultBytesToKeepUnderModeratePressure, + TimeDelta::Max()) {} + + void SetNow(TimeTicks now) { now_ = now; } + + private: + // Overriden from internal::DiscardableMemoryManager: + virtual TimeTicks Now() const OVERRIDE { return now_; } + + TimeTicks now_; +}; + +class DiscardableMemoryManagerTestBase { + public: + DiscardableMemoryManagerTestBase() { + manager_.RegisterMemoryPressureListener(); + } + + 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 SetBytesToKeepUnderModeratePressure(size_t bytes) { + manager_.SetBytesToKeepUnderModeratePressure(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); } + + bool ReduceMemoryUsage() { return manager_.ReduceMemoryUsage(); } + + private: + MessageLoopForIO message_loop_; + 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. + MemoryPressureListener::NotifyMemoryPressure( + MemoryPressureListener::MEMORY_PRESSURE_CRITICAL); + + // Required because ObserverListThreadSafe notifies via PostTask. + RunLoop().RunUntilIdle(); + + 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 applying +// memory pressure. +TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscardedModeratePressure) { + RegisterAndUseAllocations(); + + SetBytesToKeepUnderModeratePressure(1024); + SetMemoryLimit(2048); + + MemoryPressureListener::NotifyMemoryPressure( + MemoryPressureListener::MEMORY_PRESSURE_MODERATE); + RunLoop().RunUntilIdle(); + + 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(); + + SetBytesToKeepUnderModeratePressure(1024); + 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) { + SetBytesToKeepUnderModeratePressure(2048); + 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(); + + MemoryPressureListener::NotifyMemoryPressure( + MemoryPressureListener::MEMORY_PRESSURE_CRITICAL); + RunLoop().RunUntilIdle(); + + 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_provider.cc b/chromium/base/memory/discardable_memory_provider.cc deleted file mode 100644 index 1c84339eba7..00000000000 --- a/chromium/base/memory/discardable_memory_provider.cc +++ /dev/null @@ -1,215 +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_provider.h" - -#include "base/bind.h" -#include "base/containers/hash_tables.h" -#include "base/containers/mru_cache.h" -#include "base/debug/trace_event.h" -#include "base/synchronization/lock.h" -#include "base/sys_info.h" - -namespace base { -namespace internal { - -namespace { - -// This is admittedly pretty magical. It's approximately enough memory for two -// 2560x1600 images. -static const size_t kDefaultDiscardableMemoryLimit = 32 * 1024 * 1024; -static const size_t kDefaultBytesToReclaimUnderModeratePressure = - kDefaultDiscardableMemoryLimit / 2; - -} // namespace - -DiscardableMemoryProvider::DiscardableMemoryProvider() - : allocations_(AllocationMap::NO_AUTO_EVICT), - bytes_allocated_(0), - discardable_memory_limit_(kDefaultDiscardableMemoryLimit), - bytes_to_reclaim_under_moderate_pressure_( - kDefaultBytesToReclaimUnderModeratePressure), - memory_pressure_listener_( - base::Bind(&DiscardableMemoryProvider::NotifyMemoryPressure, - Unretained(this))) { -} - -DiscardableMemoryProvider::~DiscardableMemoryProvider() { - DCHECK(allocations_.empty()); - DCHECK_EQ(0u, bytes_allocated_); -} - -void DiscardableMemoryProvider::NotifyMemoryPressure( - MemoryPressureListener::MemoryPressureLevel pressure_level) { - switch (pressure_level) { - case MemoryPressureListener::MEMORY_PRESSURE_MODERATE: - Purge(); - return; - case MemoryPressureListener::MEMORY_PRESSURE_CRITICAL: - PurgeAll(); - return; - } - - NOTREACHED(); -} - -void DiscardableMemoryProvider::SetDiscardableMemoryLimit(size_t bytes) { - AutoLock lock(lock_); - discardable_memory_limit_ = bytes; - EnforcePolicyWithLockAcquired(); -} - -void DiscardableMemoryProvider::SetBytesToReclaimUnderModeratePressure( - size_t bytes) { - AutoLock lock(lock_); - bytes_to_reclaim_under_moderate_pressure_ = bytes; -} - -void DiscardableMemoryProvider::Register( - const DiscardableMemory* discardable, size_t bytes) { - AutoLock lock(lock_); - DCHECK(allocations_.Peek(discardable) == allocations_.end()); - allocations_.Put(discardable, Allocation(bytes)); -} - -void DiscardableMemoryProvider::Unregister( - const DiscardableMemory* discardable) { - AutoLock lock(lock_); - AllocationMap::iterator it = allocations_.Peek(discardable); - if (it == allocations_.end()) - return; - - if (it->second.memory) { - size_t bytes = it->second.bytes; - DCHECK_LE(bytes, bytes_allocated_); - bytes_allocated_ -= bytes; - free(it->second.memory); - } - allocations_.Erase(it); -} - -scoped_ptr<uint8, FreeDeleter> DiscardableMemoryProvider::Acquire( - const DiscardableMemory* discardable, - bool* purged) { - AutoLock lock(lock_); - // NB: |allocations_| is an MRU cache, and use of |Get| here updates that - // cache. - AllocationMap::iterator it = allocations_.Get(discardable); - CHECK(it != allocations_.end()); - - if (it->second.memory) { - scoped_ptr<uint8, FreeDeleter> memory(it->second.memory); - it->second.memory = NULL; - *purged = false; - return memory.Pass(); - } - - size_t bytes = it->second.bytes; - if (!bytes) - return scoped_ptr<uint8, FreeDeleter>(); - - if (discardable_memory_limit_) { - size_t limit = 0; - if (bytes < discardable_memory_limit_) - limit = discardable_memory_limit_ - bytes; - - PurgeLRUWithLockAcquiredUntilUsageIsWithin(limit); - } - - // Check for overflow. - if (std::numeric_limits<size_t>::max() - bytes < bytes_allocated_) - return scoped_ptr<uint8, FreeDeleter>(); - - scoped_ptr<uint8, FreeDeleter> memory(static_cast<uint8*>(malloc(bytes))); - if (!memory) - return scoped_ptr<uint8, FreeDeleter>(); - - bytes_allocated_ += bytes; - *purged = true; - return memory.Pass(); -} - -void DiscardableMemoryProvider::Release( - const DiscardableMemory* discardable, - scoped_ptr<uint8, FreeDeleter> memory) { - AutoLock lock(lock_); - // NB: |allocations_| is an MRU cache, and use of |Get| here updates that - // cache. - AllocationMap::iterator it = allocations_.Get(discardable); - CHECK(it != allocations_.end()); - - DCHECK(!it->second.memory); - it->second.memory = memory.release(); - - EnforcePolicyWithLockAcquired(); -} - -void DiscardableMemoryProvider::PurgeAll() { - AutoLock lock(lock_); - PurgeLRUWithLockAcquiredUntilUsageIsWithin(0); -} - -bool DiscardableMemoryProvider::IsRegisteredForTest( - const DiscardableMemory* discardable) const { - AutoLock lock(lock_); - AllocationMap::const_iterator it = allocations_.Peek(discardable); - return it != allocations_.end(); -} - -bool DiscardableMemoryProvider::CanBePurgedForTest( - const DiscardableMemory* discardable) const { - AutoLock lock(lock_); - AllocationMap::const_iterator it = allocations_.Peek(discardable); - return it != allocations_.end() && it->second.memory; -} - -size_t DiscardableMemoryProvider::GetBytesAllocatedForTest() const { - AutoLock lock(lock_); - return bytes_allocated_; -} - -void DiscardableMemoryProvider::Purge() { - AutoLock lock(lock_); - - if (bytes_to_reclaim_under_moderate_pressure_ == 0) - return; - - size_t limit = 0; - if (bytes_to_reclaim_under_moderate_pressure_ < bytes_allocated_) - limit = bytes_allocated_ - bytes_to_reclaim_under_moderate_pressure_; - - PurgeLRUWithLockAcquiredUntilUsageIsWithin(limit); -} - -void DiscardableMemoryProvider::PurgeLRUWithLockAcquiredUntilUsageIsWithin( - size_t limit) { - TRACE_EVENT1( - "base", - "DiscardableMemoryProvider::PurgeLRUWithLockAcquiredUntilUsageIsWithin", - "limit", limit); - - lock_.AssertAcquired(); - - for (AllocationMap::reverse_iterator it = allocations_.rbegin(); - it != allocations_.rend(); - ++it) { - if (bytes_allocated_ <= limit) - break; - if (!it->second.memory) - continue; - - size_t bytes = it->second.bytes; - DCHECK_LE(bytes, bytes_allocated_); - bytes_allocated_ -= bytes; - free(it->second.memory); - it->second.memory = NULL; - } -} - -void DiscardableMemoryProvider::EnforcePolicyWithLockAcquired() { - PurgeLRUWithLockAcquiredUntilUsageIsWithin(discardable_memory_limit_); -} - -} // namespace internal -} // namespace base diff --git a/chromium/base/memory/discardable_memory_provider.h b/chromium/base/memory/discardable_memory_provider.h deleted file mode 100644 index 6c343c0d929..00000000000 --- a/chromium/base/memory/discardable_memory_provider.h +++ /dev/null @@ -1,143 +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_PROVIDER_H_ -#define BASE_MEMORY_DISCARDABLE_MEMORY_PROVIDER_H_ - -#include "base/base_export.h" -#include "base/containers/hash_tables.h" -#include "base/containers/mru_cache.h" -#include "base/memory/memory_pressure_listener.h" -#include "base/synchronization/lock.h" - -namespace base { -class DiscardableMemory; -} // namespace base - -#if defined(COMPILER_GCC) -namespace BASE_HASH_NAMESPACE { -template <> -struct hash<const base::DiscardableMemory*> { - size_t operator()(const base::DiscardableMemory* ptr) const { - return hash<size_t>()(reinterpret_cast<size_t>(ptr)); - } -}; -} // namespace BASE_HASH_NAMESPACE -#endif // COMPILER - -namespace base { -namespace internal { - -// The DiscardableMemoryProvider manages a collection of emulated -// DiscardableMemory instances. It is used on platforms that do not support -// discardable memory natively. It keeps track of all DiscardableMemory -// instances (in case they need to be purged), and the total amount of -// allocated memory (in case this forces a purge). -// -// When notified of memory pressure, the provider either purges the LRU -// memory -- if the pressure is moderate -- or all discardable memory -// if the pressure is critical. -// -// NB - this class is an implementation detail. It has been exposed for testing -// purposes. You should not need to use this class directly. -class BASE_EXPORT_PRIVATE DiscardableMemoryProvider { - public: - DiscardableMemoryProvider(); - ~DiscardableMemoryProvider(); - - // The maximum number of bytes of discardable memory that may be allocated - // before we force a purge. If this amount is zero, it is interpreted as - // having no limit at all. - void SetDiscardableMemoryLimit(size_t bytes); - - // Sets the amount of memory to reclaim when we're under moderate pressure. - void SetBytesToReclaimUnderModeratePressure(size_t bytes); - - // Adds the given discardable memory to the provider's collection. - void Register(const DiscardableMemory* discardable, size_t bytes); - - // Removes the given discardable memory from the provider's collection. - void Unregister(const DiscardableMemory* discardable); - - // Returns NULL if an error occurred. Otherwise, returns the backing buffer - // and sets |purged| to indicate whether or not the backing buffer has been - // purged since last use. - scoped_ptr<uint8, FreeDeleter> Acquire( - const DiscardableMemory* discardable, bool* purged); - - // Release a previously acquired backing buffer. This gives the buffer back - // to the provider where it can be purged if necessary. - void Release(const DiscardableMemory* discardable, - scoped_ptr<uint8, FreeDeleter> memory); - - // Purges all discardable memory. - void PurgeAll(); - - // Returns true if discardable memory has been added to the provider's - // collection. This should only be used by tests. - bool IsRegisteredForTest(const DiscardableMemory* discardable) const; - - // Returns true if discardable memory can be purged. This should only - // be used by tests. - bool CanBePurgedForTest(const DiscardableMemory* discardable) const; - - // Returns total amount of allocated discardable memory. This should only - // be used by tests. - size_t GetBytesAllocatedForTest() const; - - private: - struct Allocation { - explicit Allocation(size_t bytes) - : bytes(bytes), - memory(NULL) { - } - - size_t bytes; - uint8* memory; - }; - typedef HashingMRUCache<const DiscardableMemory*, Allocation> AllocationMap; - - // This can be called as a hint that the system is under memory pressure. - void NotifyMemoryPressure( - MemoryPressureListener::MemoryPressureLevel pressure_level); - - // Purges |bytes_to_reclaim_under_moderate_pressure_| bytes of - // discardable memory. - void Purge(); - - // Purges least recently used memory until usage is less or equal to |limit|. - // Caller must acquire |lock_| prior to calling this function. - void PurgeLRUWithLockAcquiredUntilUsageIsWithin(size_t limit); - - // Ensures that we don't allocate beyond our memory limit. - // Caller must acquire |lock_| prior to calling this function. - void EnforcePolicyWithLockAcquired(); - - // Needs to be held when accessing members. - mutable Lock lock_; - - // A MRU cache of all allocated bits of discardable memory. Used for purging. - AllocationMap allocations_; - - // The total amount of allocated discardable memory. - size_t bytes_allocated_; - - // The maximum number of bytes of discardable memory that may be allocated - // before we assume moderate memory pressure. - size_t discardable_memory_limit_; - - // Under moderate memory pressure, we will purge this amount of memory. - size_t bytes_to_reclaim_under_moderate_pressure_; - - // Allows us to be respond when the system reports that it is under memory - // pressure. - MemoryPressureListener memory_pressure_listener_; - - DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryProvider); -}; - -} // namespace internal -} // namespace base - -#endif // BASE_MEMORY_DISCARDABLE_MEMORY_PROVIDER_H_ diff --git a/chromium/base/memory/discardable_memory_provider_unittest.cc b/chromium/base/memory/discardable_memory_provider_unittest.cc deleted file mode 100644 index 1559654c789..00000000000 --- a/chromium/base/memory/discardable_memory_provider_unittest.cc +++ /dev/null @@ -1,406 +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_provider.h" - -#include "base/bind.h" -#include "base/memory/discardable_memory.h" -#include "base/run_loop.h" -#include "base/synchronization/waitable_event.h" -#include "base/threading/thread.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -class DiscardableMemoryProviderTestBase { - public: - class TestDiscardableMemory : public DiscardableMemory { - public: - TestDiscardableMemory( - internal::DiscardableMemoryProvider* provider, size_t size) - : provider_(provider), - is_locked_(false) { - provider_->Register(this, size); - } - - virtual ~TestDiscardableMemory() { - if (is_locked_) - Unlock(); - provider_->Unregister(this); - } - - // Overridden from DiscardableMemory: - virtual LockDiscardableMemoryStatus Lock() OVERRIDE { - DCHECK(!is_locked_); - - bool purged = false; - memory_ = provider_->Acquire(this, &purged); - if (!memory_) - return DISCARDABLE_MEMORY_FAILED; - - is_locked_ = true; - return purged ? DISCARDABLE_MEMORY_PURGED : DISCARDABLE_MEMORY_SUCCESS; - } - virtual void Unlock() OVERRIDE { - DCHECK(is_locked_); - provider_->Release(this, memory_.Pass()); - is_locked_ = false; - } - virtual void* Memory() const OVERRIDE { - DCHECK(memory_); - return memory_.get(); - } - - private: - internal::DiscardableMemoryProvider* provider_; - scoped_ptr<uint8, FreeDeleter> memory_; - bool is_locked_; - - DISALLOW_COPY_AND_ASSIGN(TestDiscardableMemory); - }; - - DiscardableMemoryProviderTestBase() - : message_loop_(MessageLoop::TYPE_IO), - provider_(new internal::DiscardableMemoryProvider) { - } - - protected: - bool IsRegistered(const DiscardableMemory* discardable) { - return provider_->IsRegisteredForTest(discardable); - } - - bool CanBePurged(const DiscardableMemory* discardable) { - return provider_->CanBePurgedForTest(discardable); - } - - size_t BytesAllocated() const { - return provider_->GetBytesAllocatedForTest(); - } - - void* Memory(const DiscardableMemory* discardable) const { - return discardable->Memory(); - } - - void SetDiscardableMemoryLimit(size_t bytes) { - provider_->SetDiscardableMemoryLimit(bytes); - } - - void SetBytesToReclaimUnderModeratePressure(size_t bytes) { - provider_->SetBytesToReclaimUnderModeratePressure(bytes); - } - - scoped_ptr<DiscardableMemory> CreateLockedMemory(size_t size) { - scoped_ptr<TestDiscardableMemory> memory( - new TestDiscardableMemory(provider_.get(), size)); - if (memory->Lock() != DISCARDABLE_MEMORY_PURGED) - return scoped_ptr<DiscardableMemory>(); - return memory.PassAs<DiscardableMemory>(); - } - - private: - MessageLoop message_loop_; - scoped_ptr<internal::DiscardableMemoryProvider> provider_; -}; - -class DiscardableMemoryProviderTest - : public DiscardableMemoryProviderTestBase, - public testing::Test { - public: - DiscardableMemoryProviderTest() {} -}; - -TEST_F(DiscardableMemoryProviderTest, CreateLockedMemory) { - size_t size = 1024; - const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size)); - EXPECT_TRUE(IsRegistered(discardable.get())); - EXPECT_NE(static_cast<void*>(NULL), Memory(discardable.get())); - EXPECT_EQ(1024u, BytesAllocated()); - EXPECT_FALSE(CanBePurged(discardable.get())); -} - -TEST_F(DiscardableMemoryProviderTest, CreateLockedMemoryZeroSize) { - size_t size = 0; - const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size)); - EXPECT_FALSE(discardable); - EXPECT_FALSE(IsRegistered(discardable.get())); - EXPECT_EQ(0u, BytesAllocated()); -} - -TEST_F(DiscardableMemoryProviderTest, LockAfterUnlock) { - size_t size = 1024; - const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size)); - EXPECT_TRUE(IsRegistered(discardable.get())); - EXPECT_NE(static_cast<void*>(NULL), Memory(discardable.get())); - EXPECT_EQ(1024u, BytesAllocated()); - EXPECT_FALSE(CanBePurged(discardable.get())); - - // Now unlock so we can lock later. - discardable->Unlock(); - EXPECT_TRUE(CanBePurged(discardable.get())); - - EXPECT_EQ(DISCARDABLE_MEMORY_SUCCESS, discardable->Lock()); - EXPECT_FALSE(CanBePurged(discardable.get())); -} - -TEST_F(DiscardableMemoryProviderTest, LockAfterPurge) { - size_t size = 1024; - const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size)); - EXPECT_TRUE(IsRegistered(discardable.get())); - EXPECT_NE(static_cast<void*>(NULL), Memory(discardable.get())); - EXPECT_EQ(1024u, BytesAllocated()); - EXPECT_FALSE(CanBePurged(discardable.get())); - - // Now unlock so we can lock later. - discardable->Unlock(); - EXPECT_TRUE(CanBePurged(discardable.get())); - - // Force the system to purge. - MemoryPressureListener::NotifyMemoryPressure( - MemoryPressureListener::MEMORY_PRESSURE_CRITICAL); - - // Required because ObserverListThreadSafe notifies via PostTask. - RunLoop().RunUntilIdle(); - - EXPECT_EQ(DISCARDABLE_MEMORY_PURGED, discardable->Lock()); - EXPECT_FALSE(CanBePurged(discardable.get())); -} - -TEST_F(DiscardableMemoryProviderTest, LockAfterPurgeAndCannotReallocate) { - size_t size = 1024; - const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size)); - EXPECT_TRUE(IsRegistered(discardable.get())); - EXPECT_NE(static_cast<void*>(NULL), Memory(discardable.get())); - EXPECT_EQ(1024u, BytesAllocated()); - EXPECT_FALSE(CanBePurged(discardable.get())); - - // Now unlock so we can lock later. - discardable->Unlock(); - EXPECT_TRUE(CanBePurged(discardable.get())); - - // Set max allowed allocation to 1 byte. This will make cause the memory - // to be purged. - SetDiscardableMemoryLimit(1); - - EXPECT_EQ(DISCARDABLE_MEMORY_PURGED, discardable->Lock()); - EXPECT_FALSE(CanBePurged(discardable.get())); -} - -TEST_F(DiscardableMemoryProviderTest, Overflow) { - { - size_t size = 1024; - const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size)); - EXPECT_TRUE(IsRegistered(discardable.get())); - EXPECT_NE(static_cast<void*>(NULL), Memory(discardable.get())); - EXPECT_EQ(1024u, BytesAllocated()); - - size_t massive_size = std::numeric_limits<size_t>::max(); - const scoped_ptr<DiscardableMemory> massive_discardable( - CreateLockedMemory(massive_size)); - EXPECT_FALSE(massive_discardable); - EXPECT_EQ(1024u, BytesAllocated()); - } - 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 DiscardableMemoryProviderPermutationTest - : public DiscardableMemoryProviderTestBase, - public testing::TestWithParam<PermutationTestData> { - public: - DiscardableMemoryProviderPermutationTest() {} - - protected: - // Use discardable memory in order specified by ordering parameter. - void CreateAndUseDiscardableMemory() { - for (int i = 0; i < 3; ++i) { - discardables_[i] = CreateLockedMemory(1024); - EXPECT_TRUE(discardables_[i]); - EXPECT_NE(static_cast<void*>(NULL), Memory(discardables_[i].get())); - discardables_[i]->Unlock(); - } - for (int i = 0; i < 3; ++i) { - int index = GetParam().ordering()[i]; - EXPECT_NE(DISCARDABLE_MEMORY_FAILED, discardables_[index]->Lock()); - // Leave i == 0 locked. - if (i > 0) - discardables_[index]->Unlock(); - } - } - - DiscardableMemory* discardable(unsigned position) { - return discardables_[GetParam().ordering()[position]].get(); - } - - private: - scoped_ptr<DiscardableMemory> discardables_[3]; -}; - -// Verify that memory was discarded in the correct order after applying -// memory pressure. -TEST_P(DiscardableMemoryProviderPermutationTest, LRUDiscardedModeratePressure) { - CreateAndUseDiscardableMemory(); - - SetBytesToReclaimUnderModeratePressure(1024); - MemoryPressureListener::NotifyMemoryPressure( - MemoryPressureListener::MEMORY_PRESSURE_MODERATE); - RunLoop().RunUntilIdle(); - - EXPECT_NE(DISCARDABLE_MEMORY_FAILED, discardable(2)->Lock()); - EXPECT_NE(DISCARDABLE_MEMORY_SUCCESS, discardable(1)->Lock()); - // 0 should still be locked. - EXPECT_NE(static_cast<void*>(NULL), Memory(discardable(0))); -} - -// Verify that memory was discarded in the correct order after changing -// memory limit. -TEST_P(DiscardableMemoryProviderPermutationTest, LRUDiscardedExceedLimit) { - CreateAndUseDiscardableMemory(); - - SetBytesToReclaimUnderModeratePressure(1024); - SetDiscardableMemoryLimit(2048); - - EXPECT_NE(DISCARDABLE_MEMORY_FAILED, discardable(2)->Lock()); - EXPECT_NE(DISCARDABLE_MEMORY_SUCCESS, discardable(1)->Lock()); - // 0 should still be locked. - EXPECT_NE(static_cast<void*>(NULL), Memory(discardable(0))); -} - -// Verify that no more memory than necessary was discarded after changing -// memory limit. -TEST_P(DiscardableMemoryProviderPermutationTest, LRUDiscardedAmount) { - SetBytesToReclaimUnderModeratePressure(2048); - SetDiscardableMemoryLimit(4096); - - CreateAndUseDiscardableMemory(); - - SetDiscardableMemoryLimit(2048); - - EXPECT_EQ(DISCARDABLE_MEMORY_SUCCESS, discardable(2)->Lock()); - EXPECT_EQ(DISCARDABLE_MEMORY_PURGED, discardable(1)->Lock()); - // 0 should still be locked. - EXPECT_NE(static_cast<void*>(NULL), Memory(discardable(0))); -} - -TEST_P(DiscardableMemoryProviderPermutationTest, - CriticalPressureFreesAllUnlocked) { - CreateAndUseDiscardableMemory(); - - MemoryPressureListener::NotifyMemoryPressure( - MemoryPressureListener::MEMORY_PRESSURE_CRITICAL); - RunLoop().RunUntilIdle(); - - for (int i = 0; i < 3; ++i) { - if (i == 0) - EXPECT_NE(static_cast<void*>(NULL), Memory(discardable(i))); - else - EXPECT_EQ(DISCARDABLE_MEMORY_PURGED, discardable(i)->Lock()); - } -} - -INSTANTIATE_TEST_CASE_P(DiscardableMemoryProviderPermutationTests, - DiscardableMemoryProviderPermutationTest, - ::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(DiscardableMemoryProviderTest, NormalDestruction) { - { - size_t size = 1024; - const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size)); - EXPECT_TRUE(IsRegistered(discardable.get())); - EXPECT_EQ(1024u, BytesAllocated()); - } - EXPECT_EQ(0u, BytesAllocated()); -} - -TEST_F(DiscardableMemoryProviderTest, DestructionWhileLocked) { - { - size_t size = 1024; - const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size)); - EXPECT_TRUE(IsRegistered(discardable.get())); - EXPECT_NE(static_cast<void*>(NULL), Memory(discardable.get())); - EXPECT_EQ(1024u, BytesAllocated()); - EXPECT_FALSE(CanBePurged(discardable.get())); - } - // Should have ignored the "locked" status and freed the discardable memory. - EXPECT_EQ(0u, BytesAllocated()); -} - -#if !defined(NDEBUG) && !defined(OS_ANDROID) && !defined(OS_IOS) -// Death tests are not supported with Android APKs. -TEST_F(DiscardableMemoryProviderTest, UnlockedMemoryAccessCrashesInDebugMode) { - size_t size = 1024; - const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size)); - EXPECT_TRUE(IsRegistered(discardable.get())); - EXPECT_NE(static_cast<void*>(NULL), Memory(discardable.get())); - EXPECT_EQ(1024u, BytesAllocated()); - EXPECT_FALSE(CanBePurged(discardable.get())); - discardable->Unlock(); - EXPECT_TRUE(CanBePurged(discardable.get())); - // We *must* die if we are asked to vend a pointer to unlocked memory. - EXPECT_DEATH(discardable->Memory(), ".*Check failed.*"); -} -#endif - -class ThreadedDiscardableMemoryProviderTest - : public DiscardableMemoryProviderTest { - public: - ThreadedDiscardableMemoryProviderTest() - : 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; - const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size)); - EXPECT_TRUE(IsRegistered(discardable.get())); - EXPECT_NE(static_cast<void*>(NULL), Memory(discardable.get())); - discardable->Unlock(); - } - - void SignalHelper() { - thread_sync_.Signal(); - } - - Thread memory_usage_thread_; - WaitableEvent thread_sync_; -}; - -TEST_F(ThreadedDiscardableMemoryProviderTest, UseMemoryOnThread) { - memory_usage_thread_.message_loop()->PostTask( - FROM_HERE, - Bind(&ThreadedDiscardableMemoryProviderTest::UseMemoryHelper, - Unretained(this))); - memory_usage_thread_.message_loop()->PostTask( - FROM_HERE, - Bind(&ThreadedDiscardableMemoryProviderTest::SignalHelper, - Unretained(this))); - thread_sync_.Wait(); -} - -} // namespace base diff --git a/chromium/base/memory/discardable_memory_unittest.cc b/chromium/base/memory/discardable_memory_unittest.cc index c9f67b2556f..dc0e2cd2126 100644 --- a/chromium/base/memory/discardable_memory_unittest.cc +++ b/chromium/base/memory/discardable_memory_unittest.cc @@ -4,52 +4,81 @@ #include "base/memory/discardable_memory.h" -#include <limits> +#include <algorithm> +#include "base/run_loop.h" #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() : message_loop_(MessageLoop::TYPE_IO) { + // Register memory pressure listeners now that we have a message loop. + DiscardableMemory::RegisterMemoryPressureListeners(); + } + virtual ~DiscardableMemoryTest() { + DiscardableMemory::UnregisterMemoryPressureListeners(); + } + + protected: + scoped_ptr<DiscardableMemory> CreateLockedMemory(size_t size) { + return DiscardableMemory::CreateLockedMemoryWithType( + GetParam(), size).Pass(); + } + + private: + MessageLoop message_loop_; +}; const size_t kSize = 1024; -#if defined(OS_ANDROID) -TEST(DiscardableMemoryTest, TooLargeAllocationFails) { - const size_t kPageSize = 4096; - const size_t max_allowed_allocation_size = - std::numeric_limits<size_t>::max() - kPageSize + 1; - scoped_ptr<DiscardableMemory> memory( - DiscardableMemory::CreateLockedMemory(max_allowed_allocation_size + 1)); - // On certain platforms (e.g. Android), page-alignment would have caused an - // overflow resulting in a small allocation if the input size wasn't checked - // correctly. - ASSERT_FALSE(memory); +TEST_P(DiscardableMemoryTest, IsNamed) { + std::string type_name(DiscardableMemory::GetTypeName(GetParam())); + EXPECT_NE("unknown", type_name); + EXPECT_EQ(GetParam(), DiscardableMemory::GetNamedType(type_name)); } -#endif -TEST(DiscardableMemoryTest, SupportedNatively) { +bool IsNativeType(DiscardableMemoryType type) { + return + type == DISCARDABLE_MEMORY_TYPE_ASHMEM || + type == DISCARDABLE_MEMORY_TYPE_MAC; +} + +TEST_P(DiscardableMemoryTest, SupportedNatively) { + std::vector<DiscardableMemoryType> supported_types; + DiscardableMemory::GetSupportedTypes(&supported_types); #if defined(DISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY) - ASSERT_TRUE(DiscardableMemory::SupportedNatively()); + 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. - ASSERT_FALSE(DiscardableMemory::SupportedNatively()); + EXPECT_EQ(0, std::count_if(supported_types.begin(), + supported_types.end(), + IsNativeType)); #endif } // Test Lock() and Unlock() functionalities. -TEST(DiscardableMemoryTest, LockAndUnLock) { - const scoped_ptr<DiscardableMemory> memory( - DiscardableMemory::CreateLockedMemory(kSize)); +TEST_P(DiscardableMemoryTest, LockAndUnLock) { + const scoped_ptr<DiscardableMemory> memory(CreateLockedMemory(kSize)); ASSERT_TRUE(memory); void* addr = memory->Memory(); ASSERT_NE(static_cast<void*>(NULL), addr); memory->Unlock(); - // The system should have no reason to purge discardable blocks in this brief - // interval, though technically speaking this might flake. - EXPECT_EQ(DISCARDABLE_MEMORY_SUCCESS, memory->Lock()); + + EXPECT_NE(DISCARDABLE_MEMORY_LOCK_STATUS_FAILED, memory->Lock()); addr = memory->Memory(); ASSERT_NE(static_cast<void*>(NULL), addr); @@ -57,32 +86,25 @@ TEST(DiscardableMemoryTest, LockAndUnLock) { } // Test delete a discardable memory while it is locked. -TEST(DiscardableMemoryTest, DeleteWhileLocked) { - const scoped_ptr<DiscardableMemory> memory( - DiscardableMemory::CreateLockedMemory(kSize)); +TEST_P(DiscardableMemoryTest, DeleteWhileLocked) { + const scoped_ptr<DiscardableMemory> memory(CreateLockedMemory(kSize)); ASSERT_TRUE(memory); } -#if !defined(OS_ANDROID) // Test forced purging. -TEST(DiscardableMemoryTest, Purge) { - ASSERT_TRUE(DiscardableMemory::PurgeForTestingSupported()); - - const scoped_ptr<DiscardableMemory> memory( - DiscardableMemory::CreateLockedMemory(kSize)); +TEST_P(DiscardableMemoryTest, Purge) { + const scoped_ptr<DiscardableMemory> memory(CreateLockedMemory(kSize)); ASSERT_TRUE(memory); memory->Unlock(); DiscardableMemory::PurgeForTesting(); - EXPECT_EQ(DISCARDABLE_MEMORY_PURGED, memory->Lock()); + EXPECT_EQ(DISCARDABLE_MEMORY_LOCK_STATUS_PURGED, memory->Lock()); } -#endif // !OS_ANDROID #if !defined(NDEBUG) && !defined(OS_ANDROID) // Death tests are not supported with Android APKs. -TEST(DiscardableMemoryTest, UnlockedMemoryAccessCrashesInDebugMode) { - const scoped_ptr<DiscardableMemory> memory( - DiscardableMemory::CreateLockedMemory(kSize)); +TEST_P(DiscardableMemoryTest, UnlockedMemoryAccessCrashesInDebugMode) { + const scoped_ptr<DiscardableMemory> memory(CreateLockedMemory(kSize)); ASSERT_TRUE(memory); memory->Unlock(); ASSERT_DEATH_IF_SUPPORTED( @@ -90,4 +112,16 @@ TEST(DiscardableMemoryTest, UnlockedMemoryAccessCrashesInDebugMode) { } #endif +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 index 92e39e5e7d4..b9342e92043 100644 --- a/chromium/base/memory/discardable_memory_win.cc +++ b/chromium/base/memory/discardable_memory_win.cc @@ -2,29 +2,67 @@ // 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_malloc.h" namespace base { // static -bool DiscardableMemory::SupportedNatively() { - return false; +void DiscardableMemory::RegisterMemoryPressureListeners() { + internal::DiscardableMemoryEmulated::RegisterMemoryPressureListeners(); +} + +// static +void DiscardableMemory::UnregisterMemoryPressureListeners() { + internal::DiscardableMemoryEmulated::UnregisterMemoryPressureListeners(); +} + +// static +bool DiscardableMemory::ReduceMemoryUsage() { + return internal::DiscardableMemoryEmulated::ReduceMemoryUsage(); } // static -scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemory( - size_t size) { - scoped_ptr<internal::DiscardableMemoryEmulated> memory( - new internal::DiscardableMemoryEmulated(size)); - if (!memory->Initialize()) - return scoped_ptr<DiscardableMemory>(); - - return memory.PassAs<DiscardableMemory>(); +void DiscardableMemory::GetSupportedTypes( + std::vector<DiscardableMemoryType>* types) { + const DiscardableMemoryType supported_types[] = { + DISCARDABLE_MEMORY_TYPE_EMULATED, + DISCARDABLE_MEMORY_TYPE_MALLOC + }; + types->assign(supported_types, supported_types + arraysize(supported_types)); } // static -bool DiscardableMemory::PurgeForTestingSupported() { - return true; +scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemoryWithType( + DiscardableMemoryType type, size_t size) { + switch (type) { + case DISCARDABLE_MEMORY_TYPE_NONE: + case DISCARDABLE_MEMORY_TYPE_ASHMEM: + case DISCARDABLE_MEMORY_TYPE_MAC: + return scoped_ptr<DiscardableMemory>(); + case DISCARDABLE_MEMORY_TYPE_EMULATED: { + scoped_ptr<internal::DiscardableMemoryEmulated> memory( + new internal::DiscardableMemoryEmulated(size)); + if (!memory->Initialize()) + return scoped_ptr<DiscardableMemory>(); + + return memory.PassAs<DiscardableMemory>(); + } + case DISCARDABLE_MEMORY_TYPE_MALLOC: { + scoped_ptr<internal::DiscardableMemoryMalloc> memory( + new internal::DiscardableMemoryMalloc(size)); + if (!memory->Initialize()) + return scoped_ptr<DiscardableMemory>(); + + return memory.PassAs<DiscardableMemory>(); + } + } + + NOTREACHED(); + return scoped_ptr<DiscardableMemory>(); } // static diff --git a/chromium/base/memory/ref_counted.cc b/chromium/base/memory/ref_counted.cc index 31ad5098cd0..f5924d0fe76 100644 --- a/chromium/base/memory/ref_counted.cc +++ b/chromium/base/memory/ref_counted.cc @@ -3,54 +3,12 @@ // found in the LICENSE file. #include "base/memory/ref_counted.h" - -#include "base/logging.h" #include "base/threading/thread_collision_warner.h" namespace base { namespace subtle { -RefCountedBase::RefCountedBase() - : ref_count_(0) -#ifndef NDEBUG - , in_dtor_(false) -#endif - { -} - -RefCountedBase::~RefCountedBase() { -#ifndef NDEBUG - DCHECK(in_dtor_) << "RefCounted object deleted without calling Release()"; -#endif -} - -void RefCountedBase::AddRef() const { - // TODO(maruel): Add back once it doesn't assert 500 times/sec. - // Current thread books the critical section "AddRelease" without release it. - // DFAKE_SCOPED_LOCK_THREAD_LOCKED(add_release_); -#ifndef NDEBUG - DCHECK(!in_dtor_); -#endif - ++ref_count_; -} - -bool RefCountedBase::Release() const { - // TODO(maruel): Add back once it doesn't assert 500 times/sec. - // Current thread books the critical section "AddRelease" without release it. - // DFAKE_SCOPED_LOCK_THREAD_LOCKED(add_release_); -#ifndef NDEBUG - DCHECK(!in_dtor_); -#endif - if (--ref_count_ == 0) { -#ifndef NDEBUG - in_dtor_ = true; -#endif - return true; - } - return false; -} - bool RefCountedThreadSafeBase::HasOneRef() const { return AtomicRefCountIsOne( &const_cast<RefCountedThreadSafeBase*>(this)->ref_count_); diff --git a/chromium/base/memory/ref_counted.h b/chromium/base/memory/ref_counted.h index aae36b17c2f..be5eda8e083 100644 --- a/chromium/base/memory/ref_counted.h +++ b/chromium/base/memory/ref_counted.h @@ -10,6 +10,9 @@ #include "base/atomic_ref_count.h" #include "base/base_export.h" #include "base/compiler_specific.h" +#ifndef NDEBUG +#include "base/logging.h" +#endif #include "base/threading/thread_collision_warner.h" namespace base { @@ -21,13 +24,49 @@ class BASE_EXPORT RefCountedBase { bool HasOneRef() const { return ref_count_ == 1; } protected: - RefCountedBase(); - ~RefCountedBase(); + RefCountedBase() + : ref_count_(0) + #ifndef NDEBUG + , in_dtor_(false) + #endif + { + } + + ~RefCountedBase() { + #ifndef NDEBUG + DCHECK(in_dtor_) << "RefCounted object deleted without calling Release()"; + #endif + } - void AddRef() const; + + void AddRef() const { + // TODO(maruel): Add back once it doesn't assert 500 times/sec. + // Current thread books the critical section "AddRelease" + // without release it. + // DFAKE_SCOPED_LOCK_THREAD_LOCKED(add_release_); + #ifndef NDEBUG + DCHECK(!in_dtor_); + #endif + ++ref_count_; + } // Returns true if the object should self-delete. - bool Release() const; + bool Release() const { + // TODO(maruel): Add back once it doesn't assert 500 times/sec. + // Current thread books the critical section "AddRelease" + // without release it. + // DFAKE_SCOPED_LOCK_THREAD_LOCKED(add_release_); + #ifndef NDEBUG + DCHECK(!in_dtor_); + #endif + if (--ref_count_ == 0) { + #ifndef NDEBUG + in_dtor_ = true; + #endif + return true; + } + return false; + } private: mutable int ref_count_; diff --git a/chromium/base/memory/ref_counted_memory.cc b/chromium/base/memory/ref_counted_memory.cc index b1deee11201..477c941355c 100644 --- a/chromium/base/memory/ref_counted_memory.cc +++ b/chromium/base/memory/ref_counted_memory.cc @@ -37,6 +37,9 @@ RefCountedBytes::RefCountedBytes(const std::vector<unsigned char>& initializer) : data_(initializer) { } +RefCountedBytes::RefCountedBytes(const unsigned char* p, size_t size) + : data_(p, p + size) {} + RefCountedBytes* RefCountedBytes::TakeVector( std::vector<unsigned char>* to_destroy) { RefCountedBytes* bytes = new RefCountedBytes; diff --git a/chromium/base/memory/ref_counted_memory.h b/chromium/base/memory/ref_counted_memory.h index d2987c5d21b..a238c3a30fe 100644 --- a/chromium/base/memory/ref_counted_memory.h +++ b/chromium/base/memory/ref_counted_memory.h @@ -30,6 +30,11 @@ class BASE_EXPORT RefCountedMemory // Returns true if |other| is byte for byte equal. bool Equals(const scoped_refptr<RefCountedMemory>& other) const; + // Handy method to simplify calling front() with a reinterpret_cast. + template<typename T> const T* front_as() const { + return reinterpret_cast<const T*>(front()); + } + protected: friend class base::RefCountedThreadSafe<RefCountedMemory>; RefCountedMemory(); @@ -42,8 +47,9 @@ class BASE_EXPORT RefCountedStaticMemory : public RefCountedMemory { public: RefCountedStaticMemory() : data_(NULL), length_(0) {} - RefCountedStaticMemory(const unsigned char* data, size_t length) - : data_(length ? data : NULL), length_(length) {} + RefCountedStaticMemory(const void* data, size_t length) + : data_(static_cast<const unsigned char*>(length ? data : NULL)), + length_(length) {} // Overridden from RefCountedMemory: virtual const unsigned char* front() const OVERRIDE; @@ -58,8 +64,7 @@ class BASE_EXPORT RefCountedStaticMemory : public RefCountedMemory { DISALLOW_COPY_AND_ASSIGN(RefCountedStaticMemory); }; -// An implementation of RefCountedMemory, where we own our the data in a -// vector. +// An implementation of RefCountedMemory, where we own the data in a vector. class BASE_EXPORT RefCountedBytes : public RefCountedMemory { public: RefCountedBytes(); @@ -67,6 +72,9 @@ class BASE_EXPORT RefCountedBytes : public RefCountedMemory { // Constructs a RefCountedBytes object by _copying_ from |initializer|. explicit RefCountedBytes(const std::vector<unsigned char>& initializer); + // Constructs a RefCountedBytes object by copying |size| bytes from |p|. + RefCountedBytes(const unsigned char* p, size_t size); + // Constructs a RefCountedBytes object by performing a swap. (To non // destructively build a RefCountedBytes, use the constructor that takes a // vector.) diff --git a/chromium/base/memory/ref_counted_memory_unittest.cc b/chromium/base/memory/ref_counted_memory_unittest.cc index 88c5b534851..5bfc1c79a69 100644 --- a/chromium/base/memory/ref_counted_memory_unittest.cc +++ b/chromium/base/memory/ref_counted_memory_unittest.cc @@ -10,12 +10,10 @@ namespace base { TEST(RefCountedMemoryUnitTest, RefCountedStaticMemory) { scoped_refptr<RefCountedMemory> mem = new RefCountedStaticMemory( - reinterpret_cast<const uint8*>("static mem00"), 10); + "static mem00", 10); EXPECT_EQ(10U, mem->size()); - EXPECT_EQ("static mem", - std::string(reinterpret_cast<const char*>(mem->front()), - mem->size())); + EXPECT_EQ("static mem", std::string(mem->front_as<char>(), mem->size())); } TEST(RefCountedMemoryUnitTest, RefCountedBytes) { @@ -29,6 +27,16 @@ TEST(RefCountedMemoryUnitTest, RefCountedBytes) { EXPECT_EQ(2U, mem->size()); EXPECT_EQ(45U, mem->front()[0]); EXPECT_EQ(99U, mem->front()[1]); + + scoped_refptr<RefCountedMemory> mem2; + { + unsigned char data2[] = { 12, 11, 99 }; + mem2 = new RefCountedBytes(data2, 3); + } + EXPECT_EQ(3U, mem2->size()); + EXPECT_EQ(12U, mem2->front()[0]); + EXPECT_EQ(11U, mem2->front()[1]); + EXPECT_EQ(99U, mem2->front()[2]); } TEST(RefCountedMemoryUnitTest, RefCountedString) { diff --git a/chromium/base/memory/scoped_handle.h b/chromium/base/memory/scoped_handle.h deleted file mode 100644 index b95559dcd83..00000000000 --- a/chromium/base/memory/scoped_handle.h +++ /dev/null @@ -1,50 +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_HANDLE_H_ -#define BASE_MEMORY_SCOPED_HANDLE_H_ - -#include <stdio.h> - -#include "base/basictypes.h" - -class ScopedStdioHandle { - public: - ScopedStdioHandle() - : handle_(NULL) { } - - explicit ScopedStdioHandle(FILE* handle) - : handle_(handle) { } - - ~ScopedStdioHandle() { - Close(); - } - - void Close() { - if (handle_) { - fclose(handle_); - handle_ = NULL; - } - } - - FILE* get() const { return handle_; } - - FILE* Take() { - FILE* temp = handle_; - handle_ = NULL; - return temp; - } - - void Set(FILE* newhandle) { - Close(); - handle_ = newhandle; - } - - private: - FILE* handle_; - - DISALLOW_COPY_AND_ASSIGN(ScopedStdioHandle); -}; - -#endif // BASE_MEMORY_SCOPED_HANDLE_H_ diff --git a/chromium/base/memory/scoped_ptr.h b/chromium/base/memory/scoped_ptr.h index 790fecce71e..bf2e0b6f068 100644 --- a/chromium/base/memory/scoped_ptr.h +++ b/chromium/base/memory/scoped_ptr.h @@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Scopers help you manage ownership of a pointer, helping you easily manage the -// a pointer within a scope, and automatically destroying the pointer at the -// end of a scope. There are two main classes you will use, which correspond -// to the operators new/delete and new[]/delete[]. +// Scopers help you manage ownership of a pointer, helping you easily manage a +// pointer within a scope, and automatically destroying the pointer at the end +// of a scope. There are two main classes you will use, which correspond to the +// operators new/delete and new[]/delete[]. // // Example usage (scoped_ptr<T>): // { @@ -88,7 +88,7 @@ #define BASE_MEMORY_SCOPED_PTR_H_ // This is an implementation designed to match the anticipated future TR2 -// implementation of the scoped_ptr class and scoped_ptr_malloc (deprecated). +// implementation of the scoped_ptr class. #include <assert.h> #include <stddef.h> @@ -227,7 +227,7 @@ class scoped_ptr_impl { abort(); // Note that running data_.ptr = p can lead to undefined behavior if - // get_deleter()(get()) deletes this. In order to pevent this, reset() + // get_deleter()(get()) deletes this. In order to prevent this, reset() // should update the stored pointer before deleting its old value. // // However, changing reset() to use that behavior may cause current code to @@ -570,134 +570,6 @@ bool operator!=(T* p1, const scoped_ptr<T, D>& p2) { return p1 != p2.get(); } -// DEPRECATED: Use scoped_ptr<C, base::FreeDeleter> instead. -// -// scoped_ptr_malloc<> is similar to scoped_ptr<>, but it accepts a -// second template argument, the functor used to free the object. - -template<class C, class FreeProc = base::FreeDeleter> -class scoped_ptr_malloc { - MOVE_ONLY_TYPE_FOR_CPP_03(scoped_ptr_malloc, RValue) - - public: - - // The element type - typedef C element_type; - - // Constructor. Defaults to initializing with NULL. - // There is no way to create an uninitialized scoped_ptr. - // The input parameter must be allocated with an allocator that matches the - // Free functor. For the default Free functor, this is malloc, calloc, or - // realloc. - explicit scoped_ptr_malloc(C* p = NULL): ptr_(p) {} - - // Constructor. Move constructor for C++03 move emulation of this type. - scoped_ptr_malloc(RValue rvalue) - : ptr_(rvalue.object->release()) { - } - - // Destructor. If there is a C object, call the Free functor. - ~scoped_ptr_malloc() { - reset(); - } - - // operator=. Move operator= for C++03 move emulation of this type. - scoped_ptr_malloc& operator=(RValue rhs) { - reset(rhs.object->release()); - return *this; - } - - // Reset. Calls the Free functor on the current owned object, if any. - // Then takes ownership of a new object, if given. - // this->reset(this->get()) works. - void reset(C* p = NULL) { - if (ptr_ != p) { - if (ptr_ != NULL) { - FreeProc free_proc; - free_proc(ptr_); - } - ptr_ = p; - } - } - - // Get the current object. - // operator* and operator-> will cause an assert() failure if there is - // no current object. - C& operator*() const { - assert(ptr_ != NULL); - return *ptr_; - } - - C* operator->() const { - assert(ptr_ != NULL); - return ptr_; - } - - C* get() const { - return ptr_; - } - - // Allow scoped_ptr_malloc<C> to be used in boolean expressions, but not - // implicitly convertible to a real bool (which is dangerous). - typedef C* scoped_ptr_malloc::*Testable; - operator Testable() const { return ptr_ ? &scoped_ptr_malloc::ptr_ : NULL; } - - // Comparison operators. - // These return whether a scoped_ptr_malloc and a plain pointer refer - // to the same object, not just to two different but equal objects. - // For compatibility with the boost-derived implementation, these - // take non-const arguments. - bool operator==(C* p) const { - return ptr_ == p; - } - - bool operator!=(C* p) const { - return ptr_ != p; - } - - // Swap two scoped pointers. - void swap(scoped_ptr_malloc & b) { - C* tmp = b.ptr_; - b.ptr_ = ptr_; - ptr_ = tmp; - } - - // Release a pointer. - // The return value is the current pointer held by this object. - // If this object holds a NULL pointer, the return value is NULL. - // After this operation, this object will hold a NULL pointer, - // and will not own the object any more. - C* release() WARN_UNUSED_RESULT { - C* tmp = ptr_; - ptr_ = NULL; - return tmp; - } - - private: - C* ptr_; - - // no reason to use these: each scoped_ptr_malloc should have its own object - template <class C2, class GP> - bool operator==(scoped_ptr_malloc<C2, GP> const& p) const; - template <class C2, class GP> - bool operator!=(scoped_ptr_malloc<C2, GP> const& p) const; -}; - -template<class C, class FP> inline -void swap(scoped_ptr_malloc<C, FP>& a, scoped_ptr_malloc<C, FP>& b) { - a.swap(b); -} - -template<class C, class FP> inline -bool operator==(C* p, const scoped_ptr_malloc<C, FP>& b) { - return p == b.get(); -} - -template<class C, class FP> inline -bool operator!=(C* p, const scoped_ptr_malloc<C, FP>& b) { - return p != b.get(); -} - // A function to convert T* into scoped_ptr<T> // Doing e.g. make_scoped_ptr(new FooBarBaz<type>(arg)) is a shorter notation // for scoped_ptr<FooBarBaz<type> >(new FooBarBaz<type>(arg)) diff --git a/chromium/base/memory/scoped_ptr_unittest.cc b/chromium/base/memory/scoped_ptr_unittest.cc index 22da53d3511..e0c15484e08 100644 --- a/chromium/base/memory/scoped_ptr_unittest.cc +++ b/chromium/base/memory/scoped_ptr_unittest.cc @@ -602,5 +602,3 @@ TEST(ScopedPtrTest, OverloadedNewAndDelete) { EXPECT_EQ(1, OverloadedNewAndDelete::delete_count()); EXPECT_EQ(1, OverloadedNewAndDelete::new_count()); } - -// TODO scoped_ptr_malloc diff --git a/chromium/base/memory/shared_memory.h b/chromium/base/memory/shared_memory.h index 9007aede109..d48071e2f73 100644 --- a/chromium/base/memory/shared_memory.h +++ b/chromium/base/memory/shared_memory.h @@ -22,6 +22,7 @@ #if defined(OS_POSIX) #include "base/file_descriptor_posix.h" #include "base/file_util.h" +#include "base/files/scoped_file.h" #endif namespace base { @@ -32,38 +33,43 @@ class FilePath; // the underlying OS handle to a shared memory segment. #if defined(OS_WIN) typedef HANDLE SharedMemoryHandle; -typedef HANDLE SharedMemoryLock; #elif defined(OS_POSIX) // A SharedMemoryId is sufficient to identify a given shared memory segment on a // system, but insufficient to map it. typedef FileDescriptor SharedMemoryHandle; typedef ino_t SharedMemoryId; -// On POSIX, the lock is implemented as a lockf() on the mapped file, -// so no additional member (or definition of SharedMemoryLock) is -// needed. #endif // Options for creating a shared memory object. struct SharedMemoryCreateOptions { - SharedMemoryCreateOptions() : name(NULL), size(0), open_existing(false), - executable(false) {} - + SharedMemoryCreateOptions() + : name_deprecated(NULL), + size(0), + open_existing_deprecated(false), + executable(false), + share_read_only(false) {} + + // DEPRECATED (crbug.com/345734): // If NULL, the object is anonymous. This pointer is owned by the caller // and must live through the call to Create(). - const std::string* name; + const std::string* name_deprecated; // Size of the shared memory object to be created. // When opening an existing object, this has no effect. size_t size; + // DEPRECATED (crbug.com/345734): // If true, and the shared memory already exists, Create() will open the // existing shared memory and ignore the size parameter. If false, - // shared memory must not exist. This flag is meaningless unless name is - // non-NULL. - bool open_existing; + // shared memory must not exist. This flag is meaningless unless + // name_deprecated is non-NULL. + bool open_existing_deprecated; // If true, mappings might need to be made executable later. bool executable; + + // If true, the file can be shared read-only to a process. + bool share_read_only; }; // Platform abstraction for shared memory. Provides a C++ wrapper @@ -74,8 +80,8 @@ class BASE_EXPORT SharedMemory { #if defined(OS_WIN) // Similar to the default constructor, except that this allows for - // calling Lock() to acquire the named mutex before either Create or Open - // are called on Windows. + // calling LockDeprecated() to acquire the named mutex before either Create or + // Open are called on Windows. explicit SharedMemory(const std::wstring& name); #endif @@ -126,16 +132,18 @@ class BASE_EXPORT SharedMemory { return Create(options); } + // DEPRECATED (crbug.com/345734): // Creates or opens a shared memory segment based on a name. // If open_existing is true, and the shared memory already exists, // opens the existing shared memory and ignores the size parameter. // If open_existing is false, shared memory must not exist. // size is the size of the block to be created. // Returns true on success, false on failure. - bool CreateNamed(const std::string& name, bool open_existing, size_t size) { + bool CreateNamedDeprecated( + const std::string& name, bool open_existing, size_t size) { SharedMemoryCreateOptions options; - options.name = &name; - options.open_existing = open_existing; + options.name_deprecated = &name; + options.open_existing_deprecated = open_existing; options.size = size; return Create(options); } @@ -152,7 +160,8 @@ class BASE_EXPORT SharedMemory { // Maps the shared memory into the caller's address space. // Returns true on success, false otherwise. The memory address // is accessed via the memory() accessor. The mapped address is guaranteed to - // have an alignment of at least MAP_MINIMUM_ALIGNMENT. + // have an alignment of at least MAP_MINIMUM_ALIGNMENT. This method will fail + // if this object is currently mapped. bool Map(size_t bytes) { return MapAt(0, bytes); } @@ -201,8 +210,8 @@ class BASE_EXPORT SharedMemory { // handle for use in the remote process. // // |*this| must have been initialized using one of the Create*() or Open() - // methods. If it was constructed from a SharedMemoryHandle, this call will - // CHECK-fail. + // methods with share_read_only=true. If it was constructed from a + // SharedMemoryHandle, this call will CHECK-fail. // // Returns true on success, false otherwise. bool ShareReadOnlyToProcess(ProcessHandle process, @@ -243,31 +252,27 @@ class BASE_EXPORT SharedMemory { return ShareToProcessCommon(process, new_handle, true, SHARE_CURRENT_MODE); } + // DEPRECATED (crbug.com/345734): // Locks the shared memory. // // WARNING: on POSIX the memory locking primitive only works across - // processes, not across threads. The Lock method is not currently + // processes, not across threads. The LockDeprecated method is not currently // used in inner loops, so we protect against multiple threads in a // critical section using a class global lock. - void Lock(); - -#if defined(OS_WIN) - // A Lock() implementation with a timeout that also allows setting - // security attributes on the mutex. sec_attr may be NULL. - // Returns true if the Lock() has been acquired, false if the timeout was - // reached. - bool Lock(uint32 timeout_ms, SECURITY_ATTRIBUTES* sec_attr); -#endif + void LockDeprecated(); + // DEPRECATED (crbug.com/345734): // Releases the shared memory lock. - void Unlock(); + void UnlockDeprecated(); private: #if defined(OS_POSIX) && !defined(OS_NACL) - bool PrepareMapFile(file_util::ScopedFILE fp, file_util::ScopedFD readonly); +#if !defined(OS_ANDROID) + bool PrepareMapFile(ScopedFILE fp, ScopedFD readonly); bool FilePathForMemoryName(const std::string& mem_name, FilePath* path); - void LockOrUnlockCommon(int function); #endif + void LockOrUnlockCommon(int function); +#endif // defined(OS_POSIX) && !defined(OS_NACL) enum ShareMode { SHARE_READONLY, SHARE_CURRENT_MODE, @@ -290,28 +295,29 @@ class BASE_EXPORT SharedMemory { bool read_only_; size_t requested_size_; #if !defined(OS_POSIX) - SharedMemoryLock lock_; + HANDLE lock_; #endif DISALLOW_COPY_AND_ASSIGN(SharedMemory); }; +// DEPRECATED (crbug.com/345734): // A helper class that acquires the shared memory lock while -// the SharedMemoryAutoLock is in scope. -class SharedMemoryAutoLock { +// the SharedMemoryAutoLockDeprecated is in scope. +class SharedMemoryAutoLockDeprecated { public: - explicit SharedMemoryAutoLock(SharedMemory* shared_memory) + explicit SharedMemoryAutoLockDeprecated(SharedMemory* shared_memory) : shared_memory_(shared_memory) { - shared_memory_->Lock(); + shared_memory_->LockDeprecated(); } - ~SharedMemoryAutoLock() { - shared_memory_->Unlock(); + ~SharedMemoryAutoLockDeprecated() { + shared_memory_->UnlockDeprecated(); } private: SharedMemory* shared_memory_; - DISALLOW_COPY_AND_ASSIGN(SharedMemoryAutoLock); + DISALLOW_COPY_AND_ASSIGN(SharedMemoryAutoLockDeprecated); }; } // namespace base diff --git a/chromium/base/memory/shared_memory_android.cc b/chromium/base/memory/shared_memory_android.cc index 25a65730cb7..5ba1bd6a101 100644 --- a/chromium/base/memory/shared_memory_android.cc +++ b/chromium/base/memory/shared_memory_android.cc @@ -24,7 +24,7 @@ bool SharedMemory::Create(const SharedMemoryCreateOptions& options) { // "name" is just a label in ashmem. It is visible in /proc/pid/maps. mapped_file_ = ashmem_create_region( - options.name == NULL ? "" : options.name->c_str(), + options.name_deprecated == NULL ? "" : options.name_deprecated->c_str(), options.size); if (-1 == mapped_file_) { DLOG(ERROR) << "Shared memory creation failed"; diff --git a/chromium/base/memory/shared_memory_nacl.cc b/chromium/base/memory/shared_memory_nacl.cc index 93c10026191..39625ee65b5 100644 --- a/chromium/base/memory/shared_memory_nacl.cc +++ b/chromium/base/memory/shared_memory_nacl.cc @@ -91,6 +91,9 @@ bool SharedMemory::MapAt(off_t offset, size_t bytes) { if (bytes > static_cast<size_t>(std::numeric_limits<int>::max())) return false; + if (memory_) + return false; + memory_ = mmap(NULL, bytes, PROT_READ | (read_only_ ? 0 : PROT_WRITE), MAP_SHARED, mapped_file_, offset); @@ -130,11 +133,11 @@ void SharedMemory::Close() { } } -void SharedMemory::Lock() { +void SharedMemory::LockDeprecated() { NOTIMPLEMENTED(); } -void SharedMemory::Unlock() { +void SharedMemory::UnlockDeprecated() { NOTIMPLEMENTED(); } diff --git a/chromium/base/memory/shared_memory_posix.cc b/chromium/base/memory/shared_memory_posix.cc index 35d8c7d35df..f0a28ba0625 100644 --- a/chromium/base/memory/shared_memory_posix.cc +++ b/chromium/base/memory/shared_memory_posix.cc @@ -12,6 +12,7 @@ #include <unistd.h> #include "base/file_util.h" +#include "base/files/scoped_file.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "base/process/process_metrics.h" @@ -30,9 +31,6 @@ #include "third_party/ashmem/ashmem.h" #endif -using file_util::ScopedFD; -using file_util::ScopedFILE; - namespace base { namespace { @@ -132,23 +130,27 @@ bool SharedMemory::Create(const SharedMemoryCreateOptions& options) { ScopedFILE fp; bool fix_size = true; - int readonly_fd_storage = -1; - ScopedFD readonly_fd(&readonly_fd_storage); + ScopedFD readonly_fd; FilePath path; - if (options.name == NULL || options.name->empty()) { + if (options.name_deprecated == NULL || options.name_deprecated->empty()) { // It doesn't make sense to have a open-existing private piece of shmem - DCHECK(!options.open_existing); + DCHECK(!options.open_existing_deprecated); // Q: Why not use the shm_open() etc. APIs? // A: Because they're limited to 4mb on OS X. FFFFFFFUUUUUUUUUUU - fp.reset(base::CreateAndOpenTemporaryShmemFile(&path, options.executable)); + FilePath directory; + if (GetShmemTempDir(options.executable, &directory)) + fp.reset(CreateAndOpenTemporaryFileInDir(directory, &path)); if (fp) { - // Also open as readonly so that we can ShareReadOnlyToProcess. - *readonly_fd = HANDLE_EINTR(open(path.value().c_str(), O_RDONLY)); - if (*readonly_fd < 0) { - DPLOG(ERROR) << "open(\"" << path.value() << "\", O_RDONLY) failed"; - fp.reset(); + if (options.share_read_only) { + // Also open as readonly so that we can ShareReadOnlyToProcess. + readonly_fd.reset(HANDLE_EINTR(open(path.value().c_str(), O_RDONLY))); + if (!readonly_fd.is_valid()) { + DPLOG(ERROR) << "open(\"" << path.value() << "\", O_RDONLY) failed"; + fp.reset(); + return false; + } } // Deleting the file prevents anyone else from mapping it in (making it // private), and prevents the need for cleanup (once the last fd is @@ -157,7 +159,7 @@ bool SharedMemory::Create(const SharedMemoryCreateOptions& options) { PLOG(WARNING) << "unlink"; } } else { - if (!FilePathForMemoryName(*options.name, &path)) + if (!FilePathForMemoryName(*options.name_deprecated, &path)) return false; // Make sure that the file is opened without any permission @@ -167,7 +169,7 @@ bool SharedMemory::Create(const SharedMemoryCreateOptions& options) { // First, try to create the file. int fd = HANDLE_EINTR( open(path.value().c_str(), O_RDWR | O_CREAT | O_EXCL, kOwnerOnly)); - if (fd == -1 && options.open_existing) { + if (fd == -1 && options.open_existing_deprecated) { // If this doesn't work, try and open an existing file in append mode. // Opening an existing file in a world writable directory has two main // security implications: @@ -197,12 +199,15 @@ bool SharedMemory::Create(const SharedMemoryCreateOptions& options) { fix_size = false; } - // Also open as readonly so that we can ShareReadOnlyToProcess. - *readonly_fd = HANDLE_EINTR(open(path.value().c_str(), O_RDONLY)); - if (*readonly_fd < 0) { - DPLOG(ERROR) << "open(\"" << path.value() << "\", O_RDONLY) failed"; - close(fd); - fd = -1; + if (options.share_read_only) { + // Also open as readonly so that we can ShareReadOnlyToProcess. + readonly_fd.reset(HANDLE_EINTR(open(path.value().c_str(), O_RDONLY))); + if (!readonly_fd.is_valid()) { + DPLOG(ERROR) << "open(\"" << path.value() << "\", O_RDONLY) failed"; + close(fd); + fd = -1; + return false; + } } if (fd >= 0) { // "a+" is always appropriate: if it's a new file, a+ is similar to w+. @@ -265,15 +270,13 @@ bool SharedMemory::Open(const std::string& name, bool read_only) { const char *mode = read_only ? "r" : "r+"; ScopedFILE fp(base::OpenFile(path, mode)); - int readonly_fd_storage = -1; - ScopedFD readonly_fd(&readonly_fd_storage); - *readonly_fd = HANDLE_EINTR(open(path.value().c_str(), O_RDONLY)); - if (*readonly_fd < 0) { + ScopedFD readonly_fd(HANDLE_EINTR(open(path.value().c_str(), O_RDONLY))); + if (!readonly_fd.is_valid()) { DPLOG(ERROR) << "open(\"" << path.value() << "\", O_RDONLY) failed"; + return false; } return PrepareMapFile(fp.Pass(), readonly_fd.Pass()); } - #endif // !defined(OS_ANDROID) bool SharedMemory::MapAt(off_t offset, size_t bytes) { @@ -283,6 +286,9 @@ bool SharedMemory::MapAt(off_t offset, size_t bytes) { if (bytes > static_cast<size_t>(std::numeric_limits<int>::max())) return false; + if (memory_) + return false; + #if defined(OS_ANDROID) // On Android, Map can be called with a size and offset of zero to use the // ashmem-determined size. @@ -339,12 +345,12 @@ void SharedMemory::Close() { } } -void SharedMemory::Lock() { +void SharedMemory::LockDeprecated() { g_thread_lock_.Get().Acquire(); LockOrUnlockCommon(F_LOCK); } -void SharedMemory::Unlock() { +void SharedMemory::UnlockDeprecated() { LockOrUnlockCommon(F_ULOCK); g_thread_lock_.Get().Release(); } @@ -353,7 +359,8 @@ void SharedMemory::Unlock() { bool SharedMemory::PrepareMapFile(ScopedFILE fp, ScopedFD readonly_fd) { DCHECK_EQ(-1, mapped_file_); DCHECK_EQ(-1, readonly_mapped_file_); - if (fp == NULL || *readonly_fd < 0) return false; + if (fp == NULL) + return false; // This function theoretically can block on the disk, but realistically // the temporary files we create will just go into the buffer cache @@ -361,14 +368,16 @@ bool SharedMemory::PrepareMapFile(ScopedFILE fp, ScopedFD readonly_fd) { base::ThreadRestrictions::ScopedAllowIO allow_io; struct stat st = {}; - struct stat readonly_st = {}; if (fstat(fileno(fp.get()), &st)) NOTREACHED(); - if (fstat(*readonly_fd, &readonly_st)) - NOTREACHED(); - if (st.st_dev != readonly_st.st_dev || st.st_ino != readonly_st.st_ino) { - LOG(ERROR) << "writable and read-only inodes don't match; bailing"; - return false; + if (readonly_fd.is_valid()) { + struct stat readonly_st = {}; + if (fstat(readonly_fd.get(), &readonly_st)) + NOTREACHED(); + if (st.st_dev != readonly_st.st_dev || st.st_ino != readonly_st.st_ino) { + LOG(ERROR) << "writable and read-only inodes don't match; bailing"; + return false; + } } mapped_file_ = dup(fileno(fp.get())); @@ -381,11 +390,10 @@ bool SharedMemory::PrepareMapFile(ScopedFILE fp, ScopedFD readonly_fd) { } } inode_ = st.st_ino; - readonly_mapped_file_ = *readonly_fd.release(); + readonly_mapped_file_ = readonly_fd.release(); return true; } -#endif // For the given shmem named |mem_name|, return a filename to mmap() // (and possibly create). Modifies |filename|. Return false on @@ -413,6 +421,7 @@ bool SharedMemory::FilePathForMemoryName(const std::string& mem_name, *path = temp_dir.AppendASCII(name_base + ".shmem." + mem_name); return true; } +#endif // !defined(OS_ANDROID) void SharedMemory::LockOrUnlockCommon(int function) { DCHECK_GE(mapped_file_, 0); diff --git a/chromium/base/memory/shared_memory_unittest.cc b/chromium/base/memory/shared_memory_unittest.cc index f3c612f803d..4d49c36f136 100644 --- a/chromium/base/memory/shared_memory_unittest.cc +++ b/chromium/base/memory/shared_memory_unittest.cc @@ -61,7 +61,7 @@ class MultipleThreadMain : public PlatformThread::Delegate { #endif const uint32 kDataSize = 1024; SharedMemory memory; - bool rv = memory.CreateNamed(s_test_name_, true, kDataSize); + bool rv = memory.CreateNamedDeprecated(s_test_name_, true, kDataSize); EXPECT_TRUE(rv); rv = memory.Map(kDataSize); EXPECT_TRUE(rv); @@ -109,8 +109,8 @@ class MultipleLockThread : public PlatformThread::Delegate { SharedMemoryHandle handle = NULL; { SharedMemory memory1; - EXPECT_TRUE(memory1.CreateNamed("SharedMemoryMultipleLockThreadTest", - true, kDataSize)); + EXPECT_TRUE(memory1.CreateNamedDeprecated( + "SharedMemoryMultipleLockThreadTest", true, kDataSize)); EXPECT_TRUE(memory1.ShareToProcess(GetCurrentProcess(), &handle)); // TODO(paulg): Implement this once we have a posix version of // SharedMemory::ShareToProcess. @@ -122,12 +122,12 @@ class MultipleLockThread : public PlatformThread::Delegate { volatile int* const ptr = static_cast<int*>(memory2.memory()); for (int idx = 0; idx < 20; idx++) { - memory2.Lock(); + memory2.LockDeprecated(); int i = (id_ << 16) + idx; *ptr = i; PlatformThread::Sleep(TimeDelta::FromMilliseconds(1)); EXPECT_EQ(*ptr, i); - memory2.Unlock(); + memory2.UnlockDeprecated(); } memory2.Close(); @@ -143,7 +143,7 @@ class MultipleLockThread : public PlatformThread::Delegate { } // namespace // Android doesn't support SharedMemory::Open/Delete/ -// CreateNamed(openExisting=true) +// CreateNamedDeprecated(openExisting=true) #if !defined(OS_ANDROID) TEST(SharedMemoryTest, OpenClose) { const uint32 kDataSize = 1024; @@ -158,7 +158,7 @@ TEST(SharedMemoryTest, OpenClose) { EXPECT_TRUE(rv); rv = memory1.Open(test_name, false); EXPECT_FALSE(rv); - rv = memory1.CreateNamed(test_name, false, kDataSize); + rv = memory1.CreateNamedDeprecated(test_name, false, kDataSize); EXPECT_TRUE(rv); rv = memory1.Map(kDataSize); EXPECT_TRUE(rv); @@ -201,10 +201,10 @@ TEST(SharedMemoryTest, OpenExclusive) { << Time::Now().ToDoubleT(); std::string test_name = test_name_stream.str(); - // Open two handles to a memory segment and check that open_existing works - // as expected. + // Open two handles to a memory segment and check that + // open_existing_deprecated works as expected. SharedMemory memory1; - bool rv = memory1.CreateNamed(test_name, false, kDataSize); + bool rv = memory1.CreateNamedDeprecated(test_name, false, kDataSize); EXPECT_TRUE(rv); // Memory1 knows it's size because it created it. @@ -224,11 +224,11 @@ TEST(SharedMemoryTest, OpenExclusive) { SharedMemory memory2; // Should not be able to create if openExisting is false. - rv = memory2.CreateNamed(test_name, false, kDataSize2); + rv = memory2.CreateNamedDeprecated(test_name, false, kDataSize2); EXPECT_FALSE(rv); // Should be able to create with openExisting true. - rv = memory2.CreateNamed(test_name, true, kDataSize2); + rv = memory2.CreateNamedDeprecated(test_name, true, kDataSize2); EXPECT_TRUE(rv); // Memory2 shouldn't know the size because we didn't create it. @@ -373,7 +373,11 @@ TEST(SharedMemoryTest, ShareReadOnly) { StringPiece contents = "Hello World"; SharedMemory writable_shmem; - ASSERT_TRUE(writable_shmem.CreateAndMapAnonymous(contents.size())); + SharedMemoryCreateOptions options; + options.size = contents.size(); + options.share_read_only = true; + ASSERT_TRUE(writable_shmem.Create(options)); + ASSERT_TRUE(writable_shmem.Map(options.size)); memcpy(writable_shmem.memory(), contents.data(), contents.size()); EXPECT_TRUE(writable_shmem.Unmap()); @@ -477,7 +481,6 @@ TEST(SharedMemoryTest, MapAt) { SharedMemory memory; ASSERT_TRUE(memory.CreateAndMapAnonymous(kDataSize)); - ASSERT_TRUE(memory.Map(kDataSize)); uint32* ptr = static_cast<uint32*>(memory.memory()); ASSERT_NE(ptr, static_cast<void*>(NULL)); @@ -497,6 +500,19 @@ TEST(SharedMemoryTest, MapAt) { } } +TEST(SharedMemoryTest, MapTwice) { + const uint32 kDataSize = 1024; + SharedMemory memory; + bool rv = memory.CreateAndMapAnonymous(kDataSize); + EXPECT_TRUE(rv); + + void* old_address = memory.memory(); + + rv = memory.Map(kDataSize); + EXPECT_FALSE(rv); + EXPECT_EQ(old_address, memory.memory()); +} + #if defined(OS_POSIX) // Create a shared memory object, mmap it, and mprotect it to PROT_EXEC. TEST(SharedMemoryTest, AnonymousExecutable) { @@ -561,7 +577,7 @@ TEST(SharedMemoryTest, FilePermissionsNamed) { options.size = kTestSize; std::string shared_mem_name = "shared_perm_test-" + IntToString(getpid()) + "-" + Uint64ToString(RandUint64()); - options.name = &shared_mem_name; + options.name_deprecated = &shared_mem_name; // Set a file mode creation mask that gives all permissions. ScopedUmaskSetter permissive_mask(S_IWGRP | S_IWOTH); @@ -613,7 +629,7 @@ class SharedMemoryProcessTest : public MultiProcessTest { #endif const uint32 kDataSize = 1024; SharedMemory memory; - bool rv = memory.CreateNamed(s_test_name_, true, kDataSize); + bool rv = memory.CreateNamedDeprecated(s_test_name_, true, kDataSize); EXPECT_TRUE(rv); if (rv != true) errors++; @@ -624,13 +640,13 @@ class SharedMemoryProcessTest : public MultiProcessTest { int *ptr = static_cast<int*>(memory.memory()); for (int idx = 0; idx < 20; idx++) { - memory.Lock(); + memory.LockDeprecated(); int i = (1 << 16) + idx; *ptr = i; PlatformThread::Sleep(TimeDelta::FromMilliseconds(10)); if (*ptr != i) errors++; - memory.Unlock(); + memory.UnlockDeprecated(); } memory.Close(); @@ -648,7 +664,7 @@ TEST_F(SharedMemoryProcessTest, Tasks) { ProcessHandle handles[kNumTasks]; for (int index = 0; index < kNumTasks; ++index) { - handles[index] = SpawnChild("SharedMemoryTestMain", false); + handles[index] = SpawnChild("SharedMemoryTestMain"); ASSERT_TRUE(handles[index]); } diff --git a/chromium/base/memory/shared_memory_win.cc b/chromium/base/memory/shared_memory_win.cc index 77741bc0d9d..cc177ab3f21 100644 --- a/chromium/base/memory/shared_memory_win.cc +++ b/chromium/base/memory/shared_memory_win.cc @@ -113,7 +113,8 @@ bool SharedMemory::Create(const SharedMemoryCreateOptions& options) { return false; size_t rounded_size = (options.size + kSectionMask) & ~kSectionMask; - name_ = ASCIIToWide(options.name == NULL ? "" : *options.name); + name_ = ASCIIToWide(options.name_deprecated == NULL ? "" : + *options.name_deprecated); mapped_file_ = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, static_cast<DWORD>(rounded_size), name_.empty() ? NULL : name_.c_str()); @@ -127,7 +128,7 @@ bool SharedMemory::Create(const SharedMemoryCreateOptions& options) { // If the file already existed, set requested_size_ to 0 to show that // we don't know the size. requested_size_ = 0; - if (!options.open_existing) { + if (!options.open_existing_deprecated) { Close(); return false; } @@ -163,6 +164,9 @@ bool SharedMemory::MapAt(off_t offset, size_t bytes) { if (bytes > static_cast<size_t>(std::numeric_limits<int>::max())) return false; + if (memory_) + return false; + memory_ = MapViewOfFile(mapped_file_, read_only_ ? FILE_MAP_READ : FILE_MAP_READ | FILE_MAP_WRITE, @@ -230,27 +234,22 @@ void SharedMemory::Close() { } } -void SharedMemory::Lock() { - Lock(INFINITE, NULL); -} - -bool SharedMemory::Lock(uint32 timeout_ms, SECURITY_ATTRIBUTES* sec_attr) { +void SharedMemory::LockDeprecated() { if (lock_ == NULL) { std::wstring name = name_; name.append(L"lock"); - lock_ = CreateMutex(sec_attr, FALSE, name.c_str()); + lock_ = CreateMutex(NULL, FALSE, name.c_str()); if (lock_ == NULL) { DPLOG(ERROR) << "Could not create mutex."; - return false; // there is nothing good we can do here. + NOTREACHED(); + return; // There is nothing good we can do here. } } - DWORD result = WaitForSingleObject(lock_, timeout_ms); - - // Return false for WAIT_ABANDONED, WAIT_TIMEOUT or WAIT_FAILED. - return (result == WAIT_OBJECT_0); + DWORD result = WaitForSingleObject(lock_, INFINITE); + DCHECK_EQ(result, WAIT_OBJECT_0); } -void SharedMemory::Unlock() { +void SharedMemory::UnlockDeprecated() { DCHECK(lock_ != NULL); ReleaseMutex(lock_); } diff --git a/chromium/base/memory/singleton.cc b/chromium/base/memory/singleton.cc index ee5e58d0057..f68ecaa8da9 100644 --- a/chromium/base/memory/singleton.cc +++ b/chromium/base/memory/singleton.cc @@ -18,7 +18,10 @@ subtle::AtomicWord WaitForInstance(subtle::AtomicWord* instance) { // the object has been created. subtle::AtomicWord value; while (true) { - value = subtle::NoBarrier_Load(instance); + // The load has acquire memory ordering as the thread which reads the + // instance pointer must acquire visibility over the associated data. + // The pairing Release_Store operation is in Singleton::get(). + value = subtle::Acquire_Load(instance); if (value != kBeingCreatedMarker) break; PlatformThread::YieldCurrentThread(); diff --git a/chromium/base/memory/singleton.h b/chromium/base/memory/singleton.h index 0d4fc8990c4..e5e2e3efed0 100644 --- a/chromium/base/memory/singleton.h +++ b/chromium/base/memory/singleton.h @@ -63,10 +63,12 @@ struct DefaultSingletonTraits { // exit. See below for the required call that makes this happen. static const bool kRegisterAtExit = true; +#ifndef NDEBUG // Set to false to disallow access on a non-joinable thread. This is // different from kRegisterAtExit because StaticMemorySingletonTraits allows // access on non-joinable threads, and gracefully handles this. static const bool kAllowedToAccessOnNonjoinableThread = false; +#endif }; @@ -76,7 +78,9 @@ struct DefaultSingletonTraits { template<typename Type> struct LeakySingletonTraits : public DefaultSingletonTraits<Type> { static const bool kRegisterAtExit = false; +#ifndef NDEBUG static const bool kAllowedToAccessOnNonjoinableThread = true; +#endif }; @@ -229,7 +233,9 @@ class Singleton { base::ThreadRestrictions::AssertSingletonAllowed(); #endif - base::subtle::AtomicWord value = base::subtle::NoBarrier_Load(&instance_); + // The load has acquire memory ordering as the thread which reads the + // 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_); @@ -248,6 +254,7 @@ class Singleton { // 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)); diff --git a/chromium/base/memory/weak_ptr.h b/chromium/base/memory/weak_ptr.h index 1675889e229..f6001e2a169 100644 --- a/chromium/base/memory/weak_ptr.h +++ b/chromium/base/memory/weak_ptr.h @@ -83,7 +83,7 @@ class BASE_EXPORT WeakReference { public: // Although Flag is bound to a specific thread, it may be deleted from another // via base::WeakPtr::~WeakPtr(). - class Flag : public RefCountedThreadSafe<Flag> { + class BASE_EXPORT Flag : public RefCountedThreadSafe<Flag> { public: Flag(); diff --git a/chromium/base/memory/weak_ptr_unittest.cc b/chromium/base/memory/weak_ptr_unittest.cc index e7e12a272d3..dde408b756a 100644 --- a/chromium/base/memory/weak_ptr_unittest.cc +++ b/chromium/base/memory/weak_ptr_unittest.cc @@ -44,7 +44,9 @@ struct Base { struct Derived : public Base {}; struct TargetBase {}; -struct Target : public TargetBase, public SupportsWeakPtr<Target> {}; +struct Target : public TargetBase, public SupportsWeakPtr<Target> { + virtual ~Target() {} +}; struct DerivedTarget : public Target {}; struct Arrow { WeakPtr<Target> target; diff --git a/chromium/base/message_loop/incoming_task_queue.cc b/chromium/base/message_loop/incoming_task_queue.cc index db99d8750c6..bcc712b4a5d 100644 --- a/chromium/base/message_loop/incoming_task_queue.cc +++ b/chromium/base/message_loop/incoming_task_queue.cc @@ -28,21 +28,6 @@ bool IncomingTaskQueue::AddToIncomingQueue( return PostPendingTask(&pending_task); } -bool IncomingTaskQueue::TryAddToIncomingQueue( - const tracked_objects::Location& from_here, - const Closure& task) { - if (!incoming_queue_lock_.Try()) { - // Reset |task|. - Closure local_task = task; - return false; - } - - AutoLock locked(incoming_queue_lock_, AutoLock::AlreadyAcquired()); - PendingTask pending_task( - from_here, task, CalculateDelayedRuntime(TimeDelta()), true); - return PostPendingTask(&pending_task); -} - bool IncomingTaskQueue::IsHighResolutionTimerEnabledForTesting() { #if defined(OS_WIN) return !high_resolution_timer_expiration_.is_null(); @@ -56,13 +41,6 @@ bool IncomingTaskQueue::IsIdleForTesting() { return incoming_queue_.empty(); } -void IncomingTaskQueue::LockWaitUnLockForTesting(WaitableEvent* caller_wait, - WaitableEvent* caller_signal) { - AutoLock lock(incoming_queue_lock_); - caller_wait->Signal(); - caller_signal->Wait(); -} - void IncomingTaskQueue::ReloadWorkQueue(TaskQueue* work_queue) { // Make sure no tasks are lost. DCHECK(work_queue->empty()); @@ -152,7 +130,8 @@ bool IncomingTaskQueue::PostPendingTask(PendingTask* pending_task) { // delayed_run_time value) and for identifying the task in about:tracing. pending_task->sequence_num = next_sequence_num_++; - TRACE_EVENT_FLOW_BEGIN0("task", "MessageLoop::PostTask", + TRACE_EVENT_FLOW_BEGIN0(TRACE_DISABLED_BY_DEFAULT("toplevel.flow"), + "MessageLoop::PostTask", TRACE_ID_MANGLE(message_loop_->GetTaskTraceID(*pending_task))); bool was_empty = incoming_queue_.empty(); diff --git a/chromium/base/message_loop/incoming_task_queue.h b/chromium/base/message_loop/incoming_task_queue.h index d831a71a3f7..56c5638295b 100644 --- a/chromium/base/message_loop/incoming_task_queue.h +++ b/chromium/base/message_loop/incoming_task_queue.h @@ -38,12 +38,6 @@ class BASE_EXPORT IncomingTaskQueue TimeDelta delay, bool nestable); - // Same as AddToIncomingQueue() except that it will avoid blocking if the lock - // is already held, and will in that case (when the lock is contended) fail to - // add the task, and will return false. - bool TryAddToIncomingQueue(const tracked_objects::Location& from_here, - const Closure& task); - // Returns true if the message loop has high resolution timers enabled. // Provided for testing. bool IsHighResolutionTimerEnabledForTesting(); @@ -51,11 +45,6 @@ class BASE_EXPORT IncomingTaskQueue // Returns true if the message loop is "idle". Provided for testing. bool IsIdleForTesting(); - // Takes the incoming queue lock, signals |caller_wait| and waits until - // |caller_signal| is signalled. - void LockWaitUnLockForTesting(WaitableEvent* caller_wait, - WaitableEvent* caller_signal); - // Loads tasks from the |incoming_queue_| into |*work_queue|. Must be called // from the thread that is running the loop. void ReloadWorkQueue(TaskQueue* work_queue); diff --git a/chromium/base/message_loop/message_loop.cc b/chromium/base/message_loop/message_loop.cc index 3f9d01c8620..dd1a393ab08 100644 --- a/chromium/base/message_loop/message_loop.cc +++ b/chromium/base/message_loop/message_loop.cc @@ -32,10 +32,8 @@ #if defined(OS_ANDROID) #include "base/message_loop/message_pump_android.h" #endif - -#if defined(TOOLKIT_GTK) -#include <gdk/gdk.h> -#include <gdk/gdkx.h> +#if defined(USE_GLIB) +#include "base/message_loop/message_pump_glib.h" #endif namespace base { @@ -50,6 +48,7 @@ LazyInstance<base::ThreadLocalPointer<MessageLoop> >::Leaky lazy_tls_ptr = // Logical events for Histogram profiling. Run with -message-loop-histogrammer // to get an accounting of messages and actions taken on each thread. const int kTaskRunEvent = 0x1; +#if !defined(OS_NACL) const int kTimerEvent = 0x2; // Provide range of message IDs for use in histogramming and debug display. @@ -83,6 +82,7 @@ const LinearHistogram::DescriptionPair event_descriptions_[] = { {-1, NULL} // The list must be null terminated, per API to histogram. }; +#endif // !defined(OS_NACL) bool enable_histogrammer_ = false; @@ -98,29 +98,19 @@ bool AlwaysNotifyPump(MessageLoop::Type type) { #endif } -} // namespace - -//------------------------------------------------------------------------------ - -#if defined(OS_WIN) - -// Upon a SEH exception in this thread, it restores the original unhandled -// exception filter. -static int SEHFilter(LPTOP_LEVEL_EXCEPTION_FILTER old_filter) { - ::SetUnhandledExceptionFilter(old_filter); - return EXCEPTION_CONTINUE_SEARCH; -} +#if defined(OS_IOS) +typedef MessagePumpIOSForIO MessagePumpForIO; +#elif defined(OS_NACL) +typedef MessagePumpDefault MessagePumpForIO; +#elif defined(OS_POSIX) +typedef MessagePumpLibevent MessagePumpForIO; +#endif -// Retrieves a pointer to the current unhandled exception filter. There -// is no standalone getter method. -static LPTOP_LEVEL_EXCEPTION_FILTER GetTopSEHFilter() { - LPTOP_LEVEL_EXCEPTION_FILTER top_filter = NULL; - top_filter = ::SetUnhandledExceptionFilter(0); - ::SetUnhandledExceptionFilter(top_filter); - return top_filter; +MessagePumpForIO* ToPumpIO(MessagePump* pump) { + return static_cast<MessagePumpForIO*>(pump); } -#endif // defined(OS_WIN) +} // namespace //------------------------------------------------------------------------------ @@ -137,7 +127,6 @@ MessageLoop::DestructionObserver::~DestructionObserver() { MessageLoop::MessageLoop(Type type) : type_(type), - exception_restoration_(false), nestable_tasks_allowed_(true), #if defined(OS_WIN) os_modal_loop_(false), @@ -146,13 +135,12 @@ MessageLoop::MessageLoop(Type type) run_loop_(NULL) { Init(); - pump_.reset(CreateMessagePumpForType(type)); + pump_ = CreateMessagePumpForType(type).Pass(); } MessageLoop::MessageLoop(scoped_ptr<MessagePump> pump) : pump_(pump.Pass()), type_(TYPE_CUSTOM), - exception_restoration_(false), nestable_tasks_allowed_(true), #if defined(OS_WIN) os_modal_loop_(false), @@ -223,29 +211,22 @@ bool MessageLoop::InitMessagePumpForUIFactory(MessagePumpFactory* factory) { } // static -MessagePump* MessageLoop::CreateMessagePumpForType(Type type) { +scoped_ptr<MessagePump> MessageLoop::CreateMessagePumpForType(Type type) { // TODO(rvargas): Get rid of the OS guards. -#if defined(OS_WIN) -#define MESSAGE_PUMP_UI new MessagePumpForUI() -#define MESSAGE_PUMP_IO new MessagePumpForIO() -#elif defined(OS_IOS) -#define MESSAGE_PUMP_UI MessagePumpMac::Create() -#define MESSAGE_PUMP_IO new MessagePumpIOSForIO() -#elif defined(OS_MACOSX) -#define MESSAGE_PUMP_UI MessagePumpMac::Create() -#define MESSAGE_PUMP_IO new MessagePumpLibevent() +#if defined(USE_GLIB) && !defined(OS_NACL) + typedef MessagePumpGlib MessagePumpForUI; +#elif defined(OS_LINUX) && !defined(OS_NACL) + typedef MessagePumpLibevent MessagePumpForUI; +#endif + +#if defined(OS_IOS) || defined(OS_MACOSX) +#define MESSAGE_PUMP_UI scoped_ptr<MessagePump>(MessagePumpMac::Create()) #elif defined(OS_NACL) // Currently NaCl doesn't have a UI MessageLoop. // TODO(abarth): Figure out if we need this. -#define MESSAGE_PUMP_UI NULL -// ipc_channel_nacl.cc uses a worker thread to do socket reads currently, and -// doesn't require extra support for watching file descriptors. -#define MESSAGE_PUMP_IO new MessagePumpDefault() -#elif defined(OS_POSIX) // POSIX but not MACOSX. -#define MESSAGE_PUMP_UI new MessagePumpForUI() -#define MESSAGE_PUMP_IO new MessagePumpLibevent() +#define MESSAGE_PUMP_UI scoped_ptr<MessagePump>() #else -#error Not implemented +#define MESSAGE_PUMP_UI scoped_ptr<MessagePump>(new MessagePumpForUI()) #endif if (type == MessageLoop::TYPE_UI) { @@ -254,17 +235,15 @@ MessagePump* MessageLoop::CreateMessagePumpForType(Type type) { return MESSAGE_PUMP_UI; } if (type == MessageLoop::TYPE_IO) - return MESSAGE_PUMP_IO; -#if defined(TOOLKIT_GTK) - if (type == MessageLoop::TYPE_GPU) - return new MessagePumpX11(); -#endif + return scoped_ptr<MessagePump>(new MessagePumpForIO()); + #if defined(OS_ANDROID) if (type == MessageLoop::TYPE_JAVA) - return MESSAGE_PUMP_UI; + return scoped_ptr<MessagePump>(new MessagePumpForUI()); #endif + DCHECK_EQ(MessageLoop::TYPE_DEFAULT, type); - return new MessagePumpDefault(); + return scoped_ptr<MessagePump>(new MessagePumpDefault()); } void MessageLoop::AddDestructionObserver( @@ -286,13 +265,6 @@ void MessageLoop::PostTask( incoming_task_queue_->AddToIncomingQueue(from_here, task, TimeDelta(), true); } -bool MessageLoop::TryPostTask( - const tracked_objects::Location& from_here, - const Closure& task) { - DCHECK(!task.is_null()) << from_here.ToString(); - return incoming_task_queue_->TryAddToIncomingQueue(from_here, task); -} - void MessageLoop::PostDelayedTask( const tracked_objects::Location& from_here, const Closure& task, @@ -399,11 +371,6 @@ bool MessageLoop::IsIdleForTesting() { return incoming_task_queue_->IsIdleForTesting(); } -void MessageLoop::LockWaitUnLockForTesting(WaitableEvent* caller_wait, - WaitableEvent* caller_signal) { - incoming_task_queue_->LockWaitUnLockForTesting(caller_wait, caller_signal); -} - //------------------------------------------------------------------------------ void MessageLoop::Init() { @@ -417,40 +384,12 @@ void MessageLoop::Init() { new ThreadTaskRunnerHandle(message_loop_proxy_)); } -// Runs the loop in two different SEH modes: -// enable_SEH_restoration_ = false : any unhandled exception goes to the last -// one that calls SetUnhandledExceptionFilter(). -// enable_SEH_restoration_ = true : any unhandled exception goes to the filter -// that was existed before the loop was run. void MessageLoop::RunHandler() { -#if defined(OS_WIN) - if (exception_restoration_) { - RunInternalInSEHFrame(); - return; - } -#endif - - RunInternal(); -} - -#if defined(OS_WIN) -__declspec(noinline) void MessageLoop::RunInternalInSEHFrame() { - LPTOP_LEVEL_EXCEPTION_FILTER current_filter = GetTopSEHFilter(); - __try { - RunInternal(); - } __except(SEHFilter(current_filter)) { - } - return; -} -#endif - -void MessageLoop::RunInternal() { DCHECK_EQ(this, current()); StartHistogrammer(); -#if !defined(OS_MACOSX) && !defined(OS_ANDROID) && \ - !defined(USE_GTK_MESSAGE_PUMP) +#if defined(OS_WIN) if (run_loop_->dispatcher_ && type() == TYPE_UI) { static_cast<MessagePumpForUI*>(pump_.get())-> RunWithDispatcher(this, run_loop_->dispatcher_); @@ -479,14 +418,14 @@ void MessageLoop::RunTask(const PendingTask& pending_task) { tracked_objects::TrackedTime start_time = tracked_objects::ThreadData::NowForStartOfRun(pending_task.birth_tally); - TRACE_EVENT_FLOW_END1("task", "MessageLoop::PostTask", - TRACE_ID_MANGLE(GetTaskTraceID(pending_task)), + TRACE_EVENT_FLOW_END1(TRACE_DISABLED_BY_DEFAULT("toplevel.flow"), + "MessageLoop::PostTask", TRACE_ID_MANGLE(GetTaskTraceID(pending_task)), "queue_duration", (start_time - pending_task.EffectiveTimePosted()).InMilliseconds()); // When tracing memory for posted tasks it's more valuable to attribute the // memory allocations to the source function than generically to "RunTask". TRACE_EVENT_WITH_MEMORY_TAG2( - "task", "MessageLoop::RunTask", + "toplevel", "MessageLoop::RunTask", pending_task.posted_from.function_name(), // Name for memory tracking. "src_file", pending_task.posted_from.file_name(), "src_func", pending_task.posted_from.function_name()); @@ -712,6 +651,7 @@ void MessageLoop::ReleaseSoonInternal( PostNonNestableTask(from_here, Bind(releaser, object)); } +#if !defined(OS_NACL) //------------------------------------------------------------------------------ // MessageLoopForUI @@ -728,64 +668,75 @@ void MessageLoopForUI::Attach() { } #endif -#if !defined(OS_MACOSX) && !defined(OS_NACL) && !defined(OS_ANDROID) +#if defined(OS_WIN) void MessageLoopForUI::AddObserver(Observer* observer) { - pump_ui()->AddObserver(observer); + static_cast<MessagePumpWin*>(pump_.get())->AddObserver(observer); } void MessageLoopForUI::RemoveObserver(Observer* observer) { - pump_ui()->RemoveObserver(observer); + static_cast<MessagePumpWin*>(pump_.get())->RemoveObserver(observer); } +#endif // defined(OS_WIN) -#endif // !defined(OS_MACOSX) && !defined(OS_NACL) && !defined(OS_ANDROID) +#if defined(USE_OZONE) || (defined(OS_CHROMEOS) && !defined(USE_GLIB)) +bool MessageLoopForUI::WatchFileDescriptor( + int fd, + bool persistent, + MessagePumpLibevent::Mode mode, + MessagePumpLibevent::FileDescriptorWatcher *controller, + MessagePumpLibevent::Watcher *delegate) { + return static_cast<MessagePumpLibevent*>(pump_.get())->WatchFileDescriptor( + fd, + persistent, + mode, + controller, + delegate); +} +#endif + +#endif // !defined(OS_NACL) //------------------------------------------------------------------------------ // MessageLoopForIO -#if defined(OS_WIN) +#if !defined(OS_NACL) +void MessageLoopForIO::AddIOObserver( + MessageLoopForIO::IOObserver* io_observer) { + ToPumpIO(pump_.get())->AddIOObserver(io_observer); +} + +void MessageLoopForIO::RemoveIOObserver( + MessageLoopForIO::IOObserver* io_observer) { + ToPumpIO(pump_.get())->RemoveIOObserver(io_observer); +} +#if defined(OS_WIN) void MessageLoopForIO::RegisterIOHandler(HANDLE file, IOHandler* handler) { - pump_io()->RegisterIOHandler(file, handler); + ToPumpIO(pump_.get())->RegisterIOHandler(file, handler); } bool MessageLoopForIO::RegisterJobObject(HANDLE job, IOHandler* handler) { - return pump_io()->RegisterJobObject(job, handler); + return ToPumpIO(pump_.get())->RegisterJobObject(job, handler); } bool MessageLoopForIO::WaitForIOCompletion(DWORD timeout, IOHandler* filter) { - return pump_io()->WaitForIOCompletion(timeout, filter); -} - -#elif defined(OS_IOS) - -bool MessageLoopForIO::WatchFileDescriptor(int fd, - bool persistent, - Mode mode, - FileDescriptorWatcher *controller, - Watcher *delegate) { - return pump_io()->WatchFileDescriptor( - fd, - persistent, - mode, - controller, - delegate); + return ToPumpIO(pump_.get())->WaitForIOCompletion(timeout, filter); } - -#elif defined(OS_POSIX) && !defined(OS_NACL) - +#elif defined(OS_POSIX) bool MessageLoopForIO::WatchFileDescriptor(int fd, bool persistent, Mode mode, FileDescriptorWatcher *controller, Watcher *delegate) { - return pump_libevent()->WatchFileDescriptor( + return ToPumpIO(pump_.get())->WatchFileDescriptor( fd, persistent, mode, controller, delegate); } - #endif +#endif // !defined(OS_NACL) + } // namespace base diff --git a/chromium/base/message_loop/message_loop.h b/chromium/base/message_loop/message_loop.h index e307d365a00..22d4f7395e4 100644 --- a/chromium/base/message_loop/message_loop.h +++ b/chromium/base/message_loop/message_loop.h @@ -18,6 +18,7 @@ #include "base/message_loop/message_loop_proxy.h" #include "base/message_loop/message_loop_proxy_impl.h" #include "base/message_loop/message_pump.h" +#include "base/message_loop/timer_slack.h" #include "base/observer_list.h" #include "base/pending_task.h" #include "base/sequenced_task_runner_helpers.h" @@ -25,41 +26,21 @@ #include "base/time/time.h" #include "base/tracking_info.h" +// TODO(sky): these includes should not be necessary. Nuke them. #if defined(OS_WIN) -// We need this to declare base::MessagePumpWin::Dispatcher, which we should -// really just eliminate. #include "base/message_loop/message_pump_win.h" #elif defined(OS_IOS) #include "base/message_loop/message_pump_io_ios.h" #elif defined(OS_POSIX) #include "base/message_loop/message_pump_libevent.h" -#if !defined(OS_MACOSX) && !defined(OS_ANDROID) - -#if defined(USE_AURA) && defined(USE_X11) && !defined(OS_NACL) -#include "base/message_loop/message_pump_x11.h" -#elif defined(USE_OZONE) && !defined(OS_NACL) -#include "base/message_loop/message_pump_ozone.h" -#else -#define USE_GTK_MESSAGE_PUMP -#include "base/message_loop/message_pump_gtk.h" -#if defined(TOOLKIT_GTK) -#include "base/message_loop/message_pump_x11.h" -#endif -#endif - -#endif #endif namespace base { class HistogramBase; -class MessagePumpDispatcher; class MessagePumpObserver; class RunLoop; class ThreadTaskRunnerHandle; -#if defined(OS_ANDROID) -class MessagePumpForUI; -#endif class WaitableEvent; // A MessageLoop is used to process events for a particular thread. There is @@ -95,14 +76,6 @@ class WaitableEvent; // class BASE_EXPORT MessageLoop : public MessagePump::Delegate { public: - -#if defined(USE_GTK_MESSAGE_PUMP) - typedef MessagePumpGdkObserver Observer; -#elif !defined(OS_MACOSX) && !defined(OS_ANDROID) - typedef MessagePumpDispatcher Dispatcher; - typedef MessagePumpObserver Observer; -#endif - // A MessageLoop has a particular type, which indicates the set of // asynchronous events it may process in addition to tasks and timers. // @@ -113,11 +86,6 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate { // This type of ML also supports native UI events (e.g., Windows messages). // See also MessageLoopForUI. // - // TYPE_GPU - // This type of ML also supports native UI events for use in the GPU - // process. On Linux this will always be an X11 ML (as compared with the - // sometimes-GTK ML in the browser process). - // // TYPE_IO // This type of ML also supports asynchronous IO. See also // MessageLoopForIO. @@ -135,9 +103,6 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate { TYPE_DEFAULT, TYPE_UI, TYPE_CUSTOM, -#if defined(TOOLKIT_GTK) - TYPE_GPU, -#endif TYPE_IO, #if defined(OS_ANDROID) TYPE_JAVA, @@ -157,7 +122,7 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate { static void EnableHistogrammer(bool enable_histogrammer); - typedef MessagePump* (MessagePumpFactory)(); + typedef scoped_ptr<MessagePump> (MessagePumpFactory)(); // Uses the given base::MessagePumpForUIFactory to override the default // MessagePump implementation for 'TYPE_UI'. Returns true if the factory // was successfully registered. @@ -165,10 +130,7 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate { // Creates the default MessagePump based on |type|. Caller owns return // value. - // TODO(sky): convert this and InitMessagePumpForUIFactory() to return a - // scoped_ptr. - static MessagePump* CreateMessagePumpForType(Type type); - + static scoped_ptr<MessagePump> CreateMessagePumpForType(Type type); // A DestructionObserver is notified when the current MessageLoop is being // destroyed. These observers are notified prior to MessageLoop::current() // being changed to return NULL. This gives interested parties the chance to @@ -210,18 +172,11 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate { // PostTask(from_here, task) is equivalent to // PostDelayedTask(from_here, task, 0). // - // The TryPostTask is meant for the cases where the calling thread cannot - // block. If posting the task will block, the call returns false, the task - // is not posted but the task is consumed anyways. - // // NOTE: These methods may be called on any thread. The Task will be invoked // on the thread that executes MessageLoop::Run(). void PostTask(const tracked_objects::Location& from_here, const Closure& task); - bool TryPostTask(const tracked_objects::Location& from_here, - const Closure& task); - void PostDelayedTask(const tracked_objects::Location& from_here, const Closure& task, TimeDelta delay); @@ -253,6 +208,18 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate { // live until the next run of the MessageLoop, or if the object needs to be // released on a particular thread. // + // A common pattern is to manually increment the object's reference count + // (AddRef), clear the pointer, then issue a ReleaseSoon. The reference count + // is incremented manually to ensure clearing the pointer does not trigger a + // delete and to account for the upcoming decrement (ReleaseSoon). For + // example: + // + // scoped_refptr<Foo> foo = ... + // foo->AddRef(); + // Foo* raw_foo = foo.get(); + // foo = NULL; + // message_loop->ReleaseSoon(raw_foo); + // // NOTE: This method may be called on any thread. The object will be // released (and thus possibly deleted) on the thread that executes // MessageLoop::Run(). If this is not the same as the thread that calls @@ -308,6 +275,11 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate { // arbitrary MessageLoop to QuitWhenIdle. static Closure QuitWhenIdleClosure(); + // Set the timer slack for this message loop. + void SetTimerSlack(TimerSlack timer_slack) { + pump_->SetTimerSlack(timer_slack); + } + // Returns true if this loop is |type|. This allows subclasses (especially // those in tests) to specialize how they are identified. virtual bool IsType(Type type) const; @@ -366,14 +338,6 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate { bool old_state_; }; - // Enables or disables the restoration during an exception of the unhandled - // exception filter that was active when Run() was called. This can happen - // if some third party code call SetUnhandledExceptionFilter() and never - // restores the previous filter. - void set_exception_restoration(bool restore) { - exception_restoration_ = restore; - } - // Returns true if we are currently running a nested message loop. bool IsNested(); @@ -424,31 +388,8 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate { // Returns true if the message loop is "idle". Provided for testing. bool IsIdleForTesting(); - // Takes the incoming queue lock, signals |caller_wait| and waits until - // |caller_signal| is signalled. - void LockWaitUnLockForTesting(WaitableEvent* caller_wait, - WaitableEvent* caller_signal); - //---------------------------------------------------------------------------- protected: - -#if defined(OS_WIN) - MessagePumpWin* pump_win() { - return static_cast<MessagePumpWin*>(pump_.get()); - } -#elif defined(OS_POSIX) && !defined(OS_IOS) - MessagePumpLibevent* pump_libevent() { - return static_cast<MessagePumpLibevent*>(pump_.get()); - } -#if defined(TOOLKIT_GTK) - friend class MessagePumpX11; - MessagePumpX11* pump_gpu() { - DCHECK_EQ(TYPE_GPU, type()); - return static_cast<MessagePumpX11*>(pump_.get()); - } -#endif -#endif - scoped_ptr<MessagePump> pump_; private: @@ -458,21 +399,9 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate { // Configures various members for the two constructors. void Init(); - // A function to encapsulate all the exception handling capability in the - // stacks around the running of a main message loop. It will run the message - // loop in a SEH try block or not depending on the set_SEH_restoration() - // flag invoking respectively RunInternalInSEHFrame() or RunInternal(). + // Invokes the actual run loop using the message pump. void RunHandler(); -#if defined(OS_WIN) - __declspec(noinline) void RunInternalInSEHFrame(); -#endif - - // A surrounding stack frame around the running of the message loop that - // supports all saving and restoring of state, as is needed for any/all (ugly) - // recursive calls. - void RunInternal(); - // Called to process any delayed non-nestable tasks. bool ProcessNextDelayedNonNestableTask(); @@ -539,8 +468,6 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate { ObserverList<DestructionObserver> destruction_observers_; - bool exception_restoration_; - // A recursion block that prevents accidentally running additional tasks when // insider a (accidentally induced?) nested message pump. bool nestable_tasks_allowed_; @@ -578,6 +505,8 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate { DISALLOW_COPY_AND_ASSIGN(MessageLoop); }; +#if !defined(OS_NACL) + //----------------------------------------------------------------------------- // MessageLoopForUI extends MessageLoop with methods that are particular to a // MessageLoop instantiated with TYPE_UI. @@ -587,10 +516,6 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate { // class BASE_EXPORT MessageLoopForUI : public MessageLoop { public: -#if defined(OS_WIN) - typedef MessagePumpForUI::MessageFilter MessageFilter; -#endif - MessageLoopForUI() : MessageLoop(TYPE_UI) { } @@ -602,6 +527,11 @@ class BASE_EXPORT MessageLoopForUI : public MessageLoop { return static_cast<MessageLoopForUI*>(loop); } + static bool IsCurrent() { + MessageLoop* loop = MessageLoop::current(); + return loop && loop->type() == MessageLoop::TYPE_UI; + } + #if defined(OS_IOS) // On iOS, the main message loop cannot be Run(). Instead call Attach(), // which connects this MessageLoop to the UI thread's CFRunLoop and allows @@ -614,33 +544,25 @@ class BASE_EXPORT MessageLoopForUI : public MessageLoop { // never be called. Instead use Start(), which will forward all the native UI // events to the Java message loop. void Start(); -#elif !defined(OS_MACOSX) +#endif + +#if defined(OS_WIN) + typedef MessagePumpObserver Observer; - // Please see message_pump_win/message_pump_glib for definitions of these - // methods. + // Please see message_pump_win for definitions of these methods. void AddObserver(Observer* observer); void RemoveObserver(Observer* observer); - -#if defined(OS_WIN) - // Plese see MessagePumpForUI for definitions of this method. - void SetMessageFilter(scoped_ptr<MessageFilter> message_filter) { - pump_ui()->SetMessageFilter(message_filter.Pass()); - } #endif - protected: -#if defined(USE_X11) - friend class MessagePumpX11; -#endif -#if defined(USE_OZONE) && !defined(OS_NACL) - friend class MessagePumpOzone; +#if defined(USE_OZONE) || (defined(OS_CHROMEOS) && !defined(USE_GLIB)) + // Please see MessagePumpLibevent for definition. + bool WatchFileDescriptor( + int fd, + bool persistent, + MessagePumpLibevent::Mode mode, + MessagePumpLibevent::FileDescriptorWatcher* controller, + MessagePumpLibevent::Watcher* delegate); #endif - - // TODO(rvargas): Make this platform independent. - MessagePumpForUI* pump_ui() { - return static_cast<MessagePumpForUI*>(pump_.get()); - } -#endif // !defined(OS_MACOSX) }; // Do not add any member variables to MessageLoopForUI! This is important b/c @@ -649,6 +571,8 @@ class BASE_EXPORT MessageLoopForUI : public MessageLoop { COMPILE_ASSERT(sizeof(MessageLoop) == sizeof(MessageLoopForUI), MessageLoopForUI_should_not_have_extra_member_variables); +#endif // !defined(OS_NACL) + //----------------------------------------------------------------------------- // MessageLoopForIO extends MessageLoop with methods that are particular to a // MessageLoop instantiated with TYPE_IO. @@ -658,6 +582,23 @@ COMPILE_ASSERT(sizeof(MessageLoop) == sizeof(MessageLoopForUI), // class BASE_EXPORT MessageLoopForIO : public MessageLoop { public: + MessageLoopForIO() : MessageLoop(TYPE_IO) { + } + + // Returns the MessageLoopForIO of the current thread. + static MessageLoopForIO* current() { + MessageLoop* loop = MessageLoop::current(); + DCHECK_EQ(MessageLoop::TYPE_IO, loop->type()); + return static_cast<MessageLoopForIO*>(loop); + } + + static bool IsCurrent() { + MessageLoop* loop = MessageLoop::current(); + return loop && loop->type() == MessageLoop::TYPE_IO; + } + +#if !defined(OS_NACL) + #if defined(OS_WIN) typedef MessagePumpForIO::IOHandler IOHandler; typedef MessagePumpForIO::IOContext IOContext; @@ -684,65 +625,25 @@ class BASE_EXPORT MessageLoopForIO : public MessageLoop { WATCH_WRITE = MessagePumpLibevent::WATCH_WRITE, WATCH_READ_WRITE = MessagePumpLibevent::WATCH_READ_WRITE }; - #endif - MessageLoopForIO() : MessageLoop(TYPE_IO) { - } - - // Returns the MessageLoopForIO of the current thread. - static MessageLoopForIO* current() { - MessageLoop* loop = MessageLoop::current(); - DCHECK_EQ(MessageLoop::TYPE_IO, loop->type()); - return static_cast<MessageLoopForIO*>(loop); - } - - void AddIOObserver(IOObserver* io_observer) { - pump_io()->AddIOObserver(io_observer); - } - - void RemoveIOObserver(IOObserver* io_observer) { - pump_io()->RemoveIOObserver(io_observer); - } + void AddIOObserver(IOObserver* io_observer); + void RemoveIOObserver(IOObserver* io_observer); #if defined(OS_WIN) // Please see MessagePumpWin for definitions of these methods. void RegisterIOHandler(HANDLE file, IOHandler* handler); bool RegisterJobObject(HANDLE job, IOHandler* handler); bool WaitForIOCompletion(DWORD timeout, IOHandler* filter); - - protected: - // TODO(rvargas): Make this platform independent. - MessagePumpForIO* pump_io() { - return static_cast<MessagePumpForIO*>(pump_.get()); - } - -#elif defined(OS_IOS) - // Please see MessagePumpIOSForIO for definition. +#elif defined(OS_POSIX) + // Please see MessagePumpIOSForIO/MessagePumpLibevent for definition. bool WatchFileDescriptor(int fd, bool persistent, Mode mode, FileDescriptorWatcher *controller, Watcher *delegate); - - private: - MessagePumpIOSForIO* pump_io() { - return static_cast<MessagePumpIOSForIO*>(pump_.get()); - } - -#elif defined(OS_POSIX) - // Please see MessagePumpLibevent for definition. - bool WatchFileDescriptor(int fd, - bool persistent, - Mode mode, - FileDescriptorWatcher* controller, - Watcher* delegate); - - private: - MessagePumpLibevent* pump_io() { - return static_cast<MessagePumpLibevent*>(pump_.get()); - } -#endif // defined(OS_POSIX) +#endif // defined(OS_IOS) || defined(OS_POSIX) +#endif // !defined(OS_NACL) }; // Do not add any member variables to MessageLoopForIO! This is important b/c diff --git a/chromium/base/message_loop/message_loop_test.cc b/chromium/base/message_loop/message_loop_test.cc index 9ac5b72d374..eca6c8f2452 100644 --- a/chromium/base/message_loop/message_loop_test.cc +++ b/chromium/base/message_loop/message_loop_test.cc @@ -101,69 +101,14 @@ void RunTest_PostTask(MessagePumpFactory factory) { &Foo::Test1Int, foo.get(), 100)); MessageLoop::current()->PostTask(FROM_HERE, Bind( &Foo::Test2Ptr, foo.get(), &a, &c)); - - // TryPost with no contention. It must succeed. - EXPECT_TRUE(MessageLoop::current()->TryPostTask(FROM_HERE, Bind( - &Foo::Test2Mixed, foo.get(), a, &d))); - - // TryPost with simulated contention. It must fail. We wait for a helper - // thread to lock the queue, we TryPost on this thread and finally we - // signal the helper to unlock and exit. - WaitableEvent wait(true, false); - WaitableEvent signal(true, false); - Thread thread("RunTest_PostTask_helper"); - thread.Start(); - thread.message_loop()->PostTask( - FROM_HERE, - Bind(&MessageLoop::LockWaitUnLockForTesting, - base::Unretained(MessageLoop::current()), - &wait, - &signal)); - - wait.Wait(); - EXPECT_FALSE(MessageLoop::current()->TryPostTask(FROM_HERE, Bind( - &Foo::Test2Mixed, foo.get(), a, &d))); - signal.Signal(); - - // After all tests, post a message that will shut down the message loop - MessageLoop::current()->PostTask(FROM_HERE, Bind( - &MessageLoop::Quit, Unretained(MessageLoop::current()))); - - // Now kick things off - MessageLoop::current()->Run(); - - EXPECT_EQ(foo->test_count(), 105); - EXPECT_EQ(foo->result(), "abacad"); -} - -void RunTest_PostTask_SEH(MessagePumpFactory factory) { - scoped_ptr<MessagePump> pump(factory()); - MessageLoop loop(pump.Pass()); - - // Add tests to message loop - scoped_refptr<Foo> foo(new Foo()); - std::string a("a"), b("b"), c("c"), d("d"); - MessageLoop::current()->PostTask(FROM_HERE, Bind( - &Foo::Test0, foo.get())); - MessageLoop::current()->PostTask(FROM_HERE, Bind( - &Foo::Test1ConstRef, foo.get(), a)); - MessageLoop::current()->PostTask(FROM_HERE, Bind( - &Foo::Test1Ptr, foo.get(), &b)); - MessageLoop::current()->PostTask(FROM_HERE, Bind( - &Foo::Test1Int, foo.get(), 100)); - MessageLoop::current()->PostTask(FROM_HERE, Bind( - &Foo::Test2Ptr, foo.get(), &a, &c)); MessageLoop::current()->PostTask(FROM_HERE, Bind( &Foo::Test2Mixed, foo.get(), a, &d)); - // After all tests, post a message that will shut down the message loop MessageLoop::current()->PostTask(FROM_HERE, Bind( &MessageLoop::Quit, Unretained(MessageLoop::current()))); - // Now kick things off with the SEH block active. - MessageLoop::current()->set_exception_restoration(true); + // Now kick things off MessageLoop::current()->Run(); - MessageLoop::current()->set_exception_restoration(false); EXPECT_EQ(foo->test_count(), 105); EXPECT_EQ(foo->result(), "abacad"); diff --git a/chromium/base/message_loop/message_loop_test.h b/chromium/base/message_loop/message_loop_test.h index 5d1a4f5ac27..3d9889cd58d 100644 --- a/chromium/base/message_loop/message_loop_test.h +++ b/chromium/base/message_loop/message_loop_test.h @@ -19,7 +19,6 @@ namespace test { typedef MessageLoop::MessagePumpFactory MessagePumpFactory; void RunTest_PostTask(MessagePumpFactory factory); -void RunTest_PostTask_SEH(MessagePumpFactory factory); void RunTest_PostDelayedTask_Basic(MessagePumpFactory factory); void RunTest_PostDelayedTask_InDelayOrder(MessagePumpFactory factory); void RunTest_PostDelayedTask_InPostOrder(MessagePumpFactory factory); @@ -52,9 +51,6 @@ void RunTest_RecursivePosts(MessagePumpFactory factory); TEST(MessageLoopTestType##id, PostTask) { \ base::test::RunTest_PostTask(factory); \ } \ - TEST(MessageLoopTestType##id, PostTask_SEH) { \ - base::test::RunTest_PostTask_SEH(factory); \ - } \ TEST(MessageLoopTestType##id, PostDelayedTask_Basic) { \ base::test::RunTest_PostDelayedTask_Basic(factory); \ } \ diff --git a/chromium/base/message_loop/message_loop_unittest.cc b/chromium/base/message_loop/message_loop_unittest.cc index e6d25ecef82..1b09eb070fa 100644 --- a/chromium/base/message_loop/message_loop_unittest.cc +++ b/chromium/base/message_loop/message_loop_unittest.cc @@ -22,6 +22,7 @@ #include "testing/gtest/include/gtest/gtest.h" #if defined(OS_WIN) +#include "base/message_loop/message_pump_dispatcher.h" #include "base/message_loop/message_pump_win.h" #include "base/process/memory.h" #include "base/strings/string16.h" @@ -35,15 +36,15 @@ namespace base { namespace { -MessagePump* TypeDefaultMessagePumpFactory() { +scoped_ptr<MessagePump> TypeDefaultMessagePumpFactory() { return MessageLoop::CreateMessagePumpForType(MessageLoop::TYPE_DEFAULT); } -MessagePump* TypeIOMessagePumpFactory() { +scoped_ptr<MessagePump> TypeIOMessagePumpFactory() { return MessageLoop::CreateMessagePumpForType(MessageLoop::TYPE_IO); } -MessagePump* TypeUIMessagePumpFactory() { +scoped_ptr<MessagePump> TypeUIMessagePumpFactory() { return MessageLoop::CreateMessagePumpForType(MessageLoop::TYPE_UI); } @@ -52,36 +53,11 @@ class Foo : public RefCounted<Foo> { Foo() : test_count_(0) { } - void Test0() { - ++test_count_; - } - void Test1ConstRef(const std::string& a) { ++test_count_; result_.append(a); } - void Test1Ptr(std::string* a) { - ++test_count_; - result_.append(*a); - } - - void Test1Int(int a) { - test_count_ += a; - } - - void Test2Ptr(std::string* a, std::string* b) { - ++test_count_; - result_.append(*a); - result_.append(*b); - } - - void Test2Mixed(const std::string& a, std::string* b) { - ++test_count_; - result_.append(a); - result_.append(*b); - } - int test_count() const { return test_count_; } const std::string& result() const { return result_; } @@ -167,120 +143,6 @@ void RunTest_PostDelayedTask_SharedTimer_SubPump() { EXPECT_TRUE(run_time.is_null()); } -LONG WINAPI BadExceptionHandler(EXCEPTION_POINTERS *ex_info) { - ADD_FAILURE() << "bad exception handler"; - ::ExitProcess(ex_info->ExceptionRecord->ExceptionCode); - return EXCEPTION_EXECUTE_HANDLER; -} - -// This task throws an SEH exception: initially write to an invalid address. -// If the right SEH filter is installed, it will fix the error. -class Crasher : public RefCounted<Crasher> { - public: - // Ctor. If trash_SEH_handler is true, the task will override the unhandled - // exception handler with one sure to crash this test. - explicit Crasher(bool trash_SEH_handler) - : trash_SEH_handler_(trash_SEH_handler) { - } - - void Run() { - PlatformThread::Sleep(TimeDelta::FromMilliseconds(1)); - if (trash_SEH_handler_) - ::SetUnhandledExceptionFilter(&BadExceptionHandler); - // Generate a SEH fault. We do it in asm to make sure we know how to undo - // the damage. - -#if defined(_M_IX86) - - __asm { - mov eax, dword ptr [Crasher::bad_array_] - mov byte ptr [eax], 66 - } - -#elif defined(_M_X64) - - bad_array_[0] = 66; - -#else -#error "needs architecture support" -#endif - - MessageLoop::current()->QuitWhenIdle(); - } - // Points the bad array to a valid memory location. - static void FixError() { - bad_array_ = &valid_store_; - } - - private: - bool trash_SEH_handler_; - static volatile char* bad_array_; - static char valid_store_; -}; - -volatile char* Crasher::bad_array_ = 0; -char Crasher::valid_store_ = 0; - -// This SEH filter fixes the problem and retries execution. Fixing requires -// that the last instruction: mov eax, [Crasher::bad_array_] to be retried -// so we move the instruction pointer 5 bytes back. -LONG WINAPI HandleCrasherException(EXCEPTION_POINTERS *ex_info) { - if (ex_info->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION) - return EXCEPTION_EXECUTE_HANDLER; - - Crasher::FixError(); - -#if defined(_M_IX86) - - ex_info->ContextRecord->Eip -= 5; - -#elif defined(_M_X64) - - ex_info->ContextRecord->Rip -= 5; - -#endif - - return EXCEPTION_CONTINUE_EXECUTION; -} - -void RunTest_Crasher(MessageLoop::Type message_loop_type) { - MessageLoop loop(message_loop_type); - - if (::IsDebuggerPresent()) - return; - - LPTOP_LEVEL_EXCEPTION_FILTER old_SEH_filter = - ::SetUnhandledExceptionFilter(&HandleCrasherException); - - MessageLoop::current()->PostTask( - FROM_HERE, - Bind(&Crasher::Run, new Crasher(false))); - MessageLoop::current()->set_exception_restoration(true); - MessageLoop::current()->Run(); - MessageLoop::current()->set_exception_restoration(false); - - ::SetUnhandledExceptionFilter(old_SEH_filter); -} - -void RunTest_CrasherNasty(MessageLoop::Type message_loop_type) { - MessageLoop loop(message_loop_type); - - if (::IsDebuggerPresent()) - return; - - LPTOP_LEVEL_EXCEPTION_FILTER old_SEH_filter = - ::SetUnhandledExceptionFilter(&HandleCrasherException); - - MessageLoop::current()->PostTask( - FROM_HERE, - Bind(&Crasher::Run, new Crasher(true))); - MessageLoop::current()->set_exception_restoration(true); - MessageLoop::current()->Run(); - MessageLoop::current()->set_exception_restoration(false); - - ::SetUnhandledExceptionFilter(old_SEH_filter); -} - const wchar_t kMessageBoxTitle[] = L"MessageLoop Unit Test"; enum TaskType { @@ -559,11 +421,11 @@ void PostNTasksThenQuit(int posts_remaining) { #if defined(OS_WIN) -class DispatcherImpl : public MessageLoopForUI::Dispatcher { +class DispatcherImpl : public MessagePumpDispatcher { public: DispatcherImpl() : dispatch_count_(0) {} - virtual bool Dispatch(const NativeEvent& msg) OVERRIDE { + virtual 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 @@ -571,7 +433,8 @@ class DispatcherImpl : public MessageLoopForUI::Dispatcher { if (msg.message != WM_TIMER) ++dispatch_count_; // We treat WM_LBUTTONUP as the last message. - return msg.message != WM_LBUTTONUP; + return msg.message == WM_LBUTTONUP ? POST_DISPATCH_QUIT_LOOP + : POST_DISPATCH_NONE; } int dispatch_count_; @@ -781,18 +644,6 @@ TEST(MessageLoopTest, PostDelayedTask_SharedTimer_SubPump) { RunTest_PostDelayedTask_SharedTimer_SubPump(); } -TEST(MessageLoopTest, Crasher) { - RunTest_Crasher(MessageLoop::TYPE_DEFAULT); - RunTest_Crasher(MessageLoop::TYPE_UI); - RunTest_Crasher(MessageLoop::TYPE_IO); -} - -TEST(MessageLoopTest, CrasherNasty) { - RunTest_CrasherNasty(MessageLoop::TYPE_DEFAULT); - RunTest_CrasherNasty(MessageLoop::TYPE_UI); - RunTest_CrasherNasty(MessageLoop::TYPE_IO); -} - // This test occasionally hangs http://crbug.com/44567 TEST(MessageLoopTest, DISABLED_RecursiveDenial2) { RunTest_RecursiveDenial2(MessageLoop::TYPE_DEFAULT); diff --git a/chromium/base/message_loop/message_pump.cc b/chromium/base/message_loop/message_pump.cc index 7ffc2b170de..3d85b9b5643 100644 --- a/chromium/base/message_loop/message_pump.cc +++ b/chromium/base/message_loop/message_pump.cc @@ -12,4 +12,7 @@ MessagePump::MessagePump() { MessagePump::~MessagePump() { } +void MessagePump::SetTimerSlack(TimerSlack) { +} + } // namespace base diff --git a/chromium/base/message_loop/message_pump.h b/chromium/base/message_loop/message_pump.h index 816bb3c3d39..a795f31b67c 100644 --- a/chromium/base/message_loop/message_pump.h +++ b/chromium/base/message_loop/message_pump.h @@ -7,6 +7,7 @@ #include "base/base_export.h" #include "base/basictypes.h" +#include "base/message_loop/timer_slack.h" #include "base/threading/non_thread_safe.h" namespace base { @@ -134,6 +135,9 @@ class BASE_EXPORT MessagePump : public NonThreadSafe { // cancelling any pending DoDelayedWork callback. This method may only be // used on the thread that called Run. virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time) = 0; + + // Sets the timer slack to the specified value. + virtual void SetTimerSlack(TimerSlack timer_slack); }; } // namespace base diff --git a/chromium/base/message_loop/message_pump_android.cc b/chromium/base/message_loop/message_pump_android.cc index e756fdd3f52..babd17b577e 100644 --- a/chromium/base/message_loop/message_pump_android.cc +++ b/chromium/base/message_loop/message_pump_android.cc @@ -21,7 +21,8 @@ using base::android::ScopedJavaLocalRef; // ---------------------------------------------------------------------------- // This method can not move to anonymous namespace as it has been declared as // 'static' in system_message_handler_jni.h. -static void DoRunLoopOnce(JNIEnv* env, jobject obj, jlong native_delegate) { +static void DoRunLoopOnce(JNIEnv* env, jobject obj, jlong native_delegate, + jlong delayed_scheduled_time_ticks) { base::MessagePump::Delegate* delegate = reinterpret_cast<base::MessagePump::Delegate*>(native_delegate); DCHECK(delegate); @@ -33,16 +34,42 @@ static void DoRunLoopOnce(JNIEnv* env, jobject obj, jlong native_delegate) { // that will be processed before calling here again. bool did_work = delegate->DoWork(); - // This is the time when we need to do delayed work. - base::TimeTicks delayed_work_time; - did_work |= delegate->DoDelayedWork(&delayed_work_time); - - // Always call this if there is a delayed message waiting in the queue - // since is at most one delayed message in the Java message handler, and this - // function call may be the result of that message being handled. - if (!delayed_work_time.is_null()) { - Java_SystemMessageHandler_setDelayedTimer(env, obj, - (delayed_work_time - base::TimeTicks::Now()).InMillisecondsRoundedUp()); + // In the java side, |SystemMessageHandler| keeps a single "delayed" message. + // It's an expensive operation to |removeMessage| there, so this is optimized + // to avoid those calls. + // + // At this stage, |next_delayed_work_time| can be: + // 1) The same as previously scheduled: nothing to be done, move along. This + // is the typical case, since this method is called for every single message. + // + // 2) Not previously scheduled: just post a new message in java. + // + // 3) Shorter than previously scheduled: far less common. In this case, + // |removeMessage| and post a new one. + // + // 4) Longer than previously scheduled (or null): nothing to be done, move + // along. + // + // Side note: base::TimeTicks is a C++ representation and can't be + // compared in java. When calling |scheduleDelayedWork|, pass the + // |InternalValue()| to java and then back to C++ so the comparisons can be + // done here. + // This roundtrip allows comparing TimeTicks directly (cheap) and + // avoid comparisons with TimeDelta / Now() (expensive). + base::TimeTicks next_delayed_work_time; + did_work |= delegate->DoDelayedWork(&next_delayed_work_time); + + if (!next_delayed_work_time.is_null()) { + // Schedule a new message if there's nothing already scheduled or there's a + // shorter delay than previously scheduled (see (2) and (3) above). + if (delayed_scheduled_time_ticks == 0 || + next_delayed_work_time < base::TimeTicks::FromInternalValue( + delayed_scheduled_time_ticks)) { + Java_SystemMessageHandler_scheduleDelayedWork(env, obj, + next_delayed_work_time.ToInternalValue(), + (next_delayed_work_time - + base::TimeTicks::Now()).InMillisecondsRoundedUp()); + } } // This is a major difference between android and other platforms: since we @@ -90,7 +117,7 @@ void MessagePumpForUI::Quit() { JNIEnv* env = base::android::AttachCurrentThread(); DCHECK(env); - Java_SystemMessageHandler_removeTimer(env, + Java_SystemMessageHandler_removeAllPendingMessages(env, system_message_handler_obj_.obj()); system_message_handler_obj_.Reset(); } @@ -108,7 +135,7 @@ void MessagePumpForUI::ScheduleWork() { JNIEnv* env = base::android::AttachCurrentThread(); DCHECK(env); - Java_SystemMessageHandler_setTimer(env, + Java_SystemMessageHandler_scheduleWork(env, system_message_handler_obj_.obj()); } @@ -122,8 +149,9 @@ void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { (delayed_work_time - TimeTicks::Now()).InMillisecondsRoundedUp(); // Note that we're truncating to milliseconds as required by the java side, // even though delayed_work_time is microseconds resolution. - Java_SystemMessageHandler_setDelayedTimer(env, - system_message_handler_obj_.obj(), millis); + Java_SystemMessageHandler_scheduleDelayedWork(env, + system_message_handler_obj_.obj(), + delayed_work_time.ToInternalValue(), millis); } // static diff --git a/chromium/base/message_loop/message_pump_dispatcher.h b/chromium/base/message_loop/message_pump_dispatcher.h index e49fa4f15df..0dea22662f9 100644 --- a/chromium/base/message_loop/message_pump_dispatcher.h +++ b/chromium/base/message_loop/message_pump_dispatcher.h @@ -5,6 +5,8 @@ #ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_DISPATCHER_H #define BASE_MESSAGE_LOOP_MESSAGE_PUMP_DISPATCHER_H +#include <stdint.h> + #include "base/base_export.h" #include "base/event_types.h" @@ -16,15 +18,24 @@ namespace base { // every message is passed to Dispatcher's Dispatch method for dispatch. It is // up to the Dispatcher whether or not to dispatch the event. // -// The nested loop is exited by either posting a quit, or returning false -// from Dispatch. +// The nested loop is exited by either posting a quit, or setting the +// POST_DISPATCH_QUIT_LOOP flag on the return value from Dispatch. class BASE_EXPORT MessagePumpDispatcher { public: + enum PostDispatchAction { + POST_DISPATCH_NONE = 0x0, + POST_DISPATCH_QUIT_LOOP = 0x1, + POST_DISPATCH_PERFORM_DEFAULT = 0x2, + }; + virtual ~MessagePumpDispatcher() {} - // Dispatches the event. If true is returned processing continues as - // normal. If false is returned, the nested loop exits immediately. - virtual bool Dispatch(const NativeEvent& event) = 0; + // Dispatches the event. The return value can have more than one + // PostDispatchAction flags OR'ed together. If POST_DISPATCH_PERFORM_DEFAULT + // is set in the returned value, then the message-pump performs the default + // action. If POST_DISPATCH_QUIT_LOOP is set, in the return value, then the + // nested loop exits immediately. + virtual uint32_t Dispatch(const NativeEvent& event) = 0; }; } // namespace base diff --git a/chromium/base/message_loop/message_pump_glib.cc b/chromium/base/message_loop/message_pump_glib.cc index 13d8b2b9914..f06f60d8cf5 100644 --- a/chromium/base/message_loop/message_pump_glib.cc +++ b/chromium/base/message_loop/message_pump_glib.cc @@ -9,8 +9,10 @@ #include <glib.h> +#include "base/lazy_instance.h" #include "base/logging.h" #include "base/posix/eintr_wrapper.h" +#include "base/synchronization/lock.h" #include "base/threading/platform_thread.h" namespace base { @@ -117,11 +119,53 @@ GSourceFuncs WorkSourceFuncs = { NULL }; +// The following is used to make sure we only run the MessagePumpGlib on one +// thread. X only has one message pump so we can only have one UI loop per +// process. +#ifndef NDEBUG + +// Tracks the pump the most recent pump that has been run. +struct ThreadInfo { + // The pump. + MessagePumpGlib* pump; + + // ID of the thread the pump was run on. + PlatformThreadId thread_id; +}; + +// Used for accesing |thread_info|. +static LazyInstance<Lock>::Leaky thread_info_lock = LAZY_INSTANCE_INITIALIZER; + +// If non-NULL it means a MessagePumpGlib exists and has been Run. This is +// destroyed when the MessagePump is destroyed. +ThreadInfo* thread_info = NULL; + +void CheckThread(MessagePumpGlib* pump) { + AutoLock auto_lock(thread_info_lock.Get()); + if (!thread_info) { + thread_info = new ThreadInfo; + thread_info->pump = pump; + thread_info->thread_id = PlatformThread::CurrentId(); + } + DCHECK(thread_info->thread_id == PlatformThread::CurrentId()) << + "Running MessagePumpGlib on two different threads; " + "this is unsupported by GLib!"; +} + +void PumpDestroyed(MessagePumpGlib* pump) { + AutoLock auto_lock(thread_info_lock.Get()); + if (thread_info && thread_info->pump == pump) { + delete thread_info; + thread_info = NULL; + } +} + +#endif + } // namespace struct MessagePumpGlib::RunState { Delegate* delegate; - MessagePumpDispatcher* dispatcher; // Used to flag that the current Run() invocation should return ASAP. bool should_quit; @@ -161,71 +205,15 @@ MessagePumpGlib::MessagePumpGlib() } MessagePumpGlib::~MessagePumpGlib() { +#ifndef NDEBUG + PumpDestroyed(this); +#endif g_source_destroy(work_source_); g_source_unref(work_source_); close(wakeup_pipe_read_); close(wakeup_pipe_write_); } -void MessagePumpGlib::RunWithDispatcher(Delegate* delegate, - MessagePumpDispatcher* dispatcher) { -#ifndef NDEBUG - // Make sure we only run this on one thread. X/GTK only has one message pump - // so we can only have one UI loop per process. - static PlatformThreadId thread_id = PlatformThread::CurrentId(); - DCHECK(thread_id == PlatformThread::CurrentId()) << - "Running MessagePumpGlib on two different threads; " - "this is unsupported by GLib!"; -#endif - - RunState state; - state.delegate = delegate; - state.dispatcher = dispatcher; - state.should_quit = false; - state.run_depth = state_ ? state_->run_depth + 1 : 1; - state.has_work = false; - - RunState* previous_state = state_; - state_ = &state; - - // We really only do a single task for each iteration of the loop. If we - // have done something, assume there is likely something more to do. This - // will mean that we don't block on the message pump until there was nothing - // more to do. We also set this to true to make sure not to block on the - // first iteration of the loop, so RunUntilIdle() works correctly. - bool more_work_is_plausible = true; - - // We run our own loop instead of using g_main_loop_quit in one of the - // callbacks. This is so we only quit our own loops, and we don't quit - // nested loops run by others. TODO(deanm): Is this what we want? - for (;;) { - // Don't block if we think we have more work to do. - bool block = !more_work_is_plausible; - - more_work_is_plausible = g_main_context_iteration(context_, block); - if (state_->should_quit) - break; - - more_work_is_plausible |= state_->delegate->DoWork(); - if (state_->should_quit) - break; - - more_work_is_plausible |= - state_->delegate->DoDelayedWork(&delayed_work_time_); - if (state_->should_quit) - break; - - if (more_work_is_plausible) - continue; - - more_work_is_plausible = state_->delegate->DoIdleWork(); - if (state_->should_quit) - break; - } - - state_ = previous_state; -} - // Return the timeout we want passed to poll. int MessagePumpGlib::HandlePrepare() { // We know we have work, but we haven't called HandleDispatch yet. Don't let @@ -291,7 +279,55 @@ void MessagePumpGlib::HandleDispatch() { } void MessagePumpGlib::Run(Delegate* delegate) { - RunWithDispatcher(delegate, NULL); +#ifndef NDEBUG + CheckThread(this); +#endif + + RunState state; + state.delegate = delegate; + state.should_quit = false; + state.run_depth = state_ ? state_->run_depth + 1 : 1; + state.has_work = false; + + RunState* previous_state = state_; + state_ = &state; + + // We really only do a single task for each iteration of the loop. If we + // have done something, assume there is likely something more to do. This + // will mean that we don't block on the message pump until there was nothing + // more to do. We also set this to true to make sure not to block on the + // first iteration of the loop, so RunUntilIdle() works correctly. + bool more_work_is_plausible = true; + + // We run our own loop instead of using g_main_loop_quit in one of the + // callbacks. This is so we only quit our own loops, and we don't quit + // nested loops run by others. TODO(deanm): Is this what we want? + for (;;) { + // Don't block if we think we have more work to do. + bool block = !more_work_is_plausible; + + more_work_is_plausible = g_main_context_iteration(context_, block); + if (state_->should_quit) + break; + + more_work_is_plausible |= state_->delegate->DoWork(); + if (state_->should_quit) + break; + + more_work_is_plausible |= + state_->delegate->DoDelayedWork(&delayed_work_time_); + if (state_->should_quit) + break; + + if (more_work_is_plausible) + continue; + + more_work_is_plausible = state_->delegate->DoIdleWork(); + if (state_->should_quit) + break; + } + + state_ = previous_state; } void MessagePumpGlib::Quit() { @@ -319,8 +355,9 @@ void MessagePumpGlib::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { ScheduleWork(); } -MessagePumpDispatcher* MessagePumpGlib::GetDispatcher() { - return state_ ? state_->dispatcher : NULL; +bool MessagePumpGlib::ShouldQuit() const { + CHECK(state_); + return state_->should_quit; } } // namespace base diff --git a/chromium/base/message_loop/message_pump_glib.h b/chromium/base/message_loop/message_pump_glib.h index 174bd051f57..a13493a739c 100644 --- a/chromium/base/message_loop/message_pump_glib.h +++ b/chromium/base/message_loop/message_pump_glib.h @@ -17,19 +17,6 @@ typedef struct _GSource GSource; namespace base { -// MessagePumpObserver is notified prior to an event being dispatched. As -// Observers are notified of every change, they have to be FAST! The platform -// specific implementation of the class is in message_pump_gtk/message_pump_x. -class MessagePumpObserver; - -// MessagePumpDispatcher is used during a nested invocation of Run to dispatch -// events. If Run is invoked with a non-NULL MessagePumpDispatcher, MessageLoop -// does not dispatch events (or invoke gtk_main_do_event), rather every event is -// passed to Dispatcher's Dispatch method for dispatch. It is up to the -// Dispatcher to dispatch, or not, the event. The platform specific -// implementation of the class is in message_pump_gtk/message_pump_x. -class MessagePumpDispatcher; - // This class implements a base MessagePump needed for TYPE_UI MessageLoops on // platforms using GLib. class BASE_EXPORT MessagePumpGlib : public MessagePump { @@ -37,10 +24,6 @@ class BASE_EXPORT MessagePumpGlib : public MessagePump { MessagePumpGlib(); virtual ~MessagePumpGlib(); - // Like MessagePump::Run, but events are routed through dispatcher. - virtual void RunWithDispatcher(Delegate* delegate, - MessagePumpDispatcher* dispatcher); - // Internal methods used for processing the pump callbacks. They are // public for simplicity but should not be used directly. HandlePrepare // is called during the prepare step of glib, and returns a timeout that @@ -57,11 +40,9 @@ class BASE_EXPORT MessagePumpGlib : public MessagePump { virtual void ScheduleWork() OVERRIDE; virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time) OVERRIDE; - protected: - // Returns the dispatcher for the current run state (|state_->dispatcher|). - MessagePumpDispatcher* GetDispatcher(); - private: + bool ShouldQuit() const; + // We may make recursive calls to Run, so we save state that needs to be // separate between them in this structure type. struct RunState; diff --git a/chromium/base/message_loop/message_pump_glib_unittest.cc b/chromium/base/message_loop/message_pump_glib_unittest.cc index 033e6cde92d..aaf6b4d5d06 100644 --- a/chromium/base/message_loop/message_pump_glib_unittest.cc +++ b/chromium/base/message_loop/message_pump_glib_unittest.cc @@ -19,10 +19,6 @@ #include "base/threading/thread.h" #include "testing/gtest/include/gtest/gtest.h" -#if defined(TOOLKIT_GTK) -#include <gtk/gtk.h> -#endif - namespace base { namespace { @@ -406,42 +402,6 @@ TEST_F(MessagePumpGLibTest, TestDrainingGLib) { EXPECT_EQ(3, injector()->processed_events()); } - -namespace { - -#if defined(TOOLKIT_GTK) -void AddEventsAndDrainGtk(EventInjector* injector) { - // Add a couple of dummy events - injector->AddDummyEvent(0); - injector->AddDummyEvent(0); - // Then add an event that will quit the main loop. - injector->AddEvent(0, MessageLoop::QuitWhenIdleClosure()); - - // Post a couple of dummy tasks - MessageLoop::current()->PostTask(FROM_HERE, Bind(&DoNothing)); - MessageLoop::current()->PostTask(FROM_HERE, Bind(&DoNothing)); - - // Drain the events - while (gtk_events_pending()) { - gtk_main_iteration(); - } -} -#endif - -} // namespace - -#if defined(TOOLKIT_GTK) -TEST_F(MessagePumpGLibTest, TestDrainingGtk) { - // Tests that draining events using Gtk works. - loop()->PostTask( - FROM_HERE, - Bind(&AddEventsAndDrainGtk, Unretained(injector()))); - loop()->Run(); - - EXPECT_EQ(3, injector()->processed_events()); -} -#endif - namespace { // Helper class that lets us run the GLib message loop. @@ -456,15 +416,9 @@ class GLibLoopRunner : public RefCounted<GLibLoopRunner> { } void RunLoop() { -#if defined(TOOLKIT_GTK) - while (!quit_) { - gtk_main_iteration(); - } -#else while (!quit_) { g_main_context_iteration(NULL, TRUE); } -#endif } void Quit() { diff --git a/chromium/base/message_loop/message_pump_gtk.cc b/chromium/base/message_loop/message_pump_gtk.cc deleted file mode 100644 index 86d2415a412..00000000000 --- a/chromium/base/message_loop/message_pump_gtk.cc +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/message_loop/message_pump_gtk.h" - -#include <gtk/gtk.h> -#include <gdk/gdkx.h> - -#include "base/debug/trace_event.h" -#include "base/profiler/scoped_profile.h" - -namespace base { - -namespace { - -const char* EventToTypeString(const GdkEvent* event) { - switch (event->type) { - case GDK_NOTHING: return "GDK_NOTHING"; - case GDK_DELETE: return "GDK_DELETE"; - case GDK_DESTROY: return "GDK_DESTROY"; - case GDK_EXPOSE: return "GDK_EXPOSE"; - case GDK_MOTION_NOTIFY: return "GDK_MOTION_NOTIFY"; - case GDK_BUTTON_PRESS: return "GDK_BUTTON_PRESS"; - case GDK_2BUTTON_PRESS: return "GDK_2BUTTON_PRESS"; - case GDK_3BUTTON_PRESS: return "GDK_3BUTTON_PRESS"; - case GDK_BUTTON_RELEASE: return "GDK_BUTTON_RELEASE"; - case GDK_KEY_PRESS: return "GDK_KEY_PRESS"; - case GDK_KEY_RELEASE: return "GDK_KEY_RELEASE"; - case GDK_ENTER_NOTIFY: return "GDK_ENTER_NOTIFY"; - case GDK_LEAVE_NOTIFY: return "GDK_LEAVE_NOTIFY"; - case GDK_FOCUS_CHANGE: return "GDK_FOCUS_CHANGE"; - case GDK_CONFIGURE: return "GDK_CONFIGURE"; - case GDK_MAP: return "GDK_MAP"; - case GDK_UNMAP: return "GDK_UNMAP"; - case GDK_PROPERTY_NOTIFY: return "GDK_PROPERTY_NOTIFY"; - case GDK_SELECTION_CLEAR: return "GDK_SELECTION_CLEAR"; - case GDK_SELECTION_REQUEST: return "GDK_SELECTION_REQUEST"; - case GDK_SELECTION_NOTIFY: return "GDK_SELECTION_NOTIFY"; - case GDK_PROXIMITY_IN: return "GDK_PROXIMITY_IN"; - case GDK_PROXIMITY_OUT: return "GDK_PROXIMITY_OUT"; - case GDK_DRAG_ENTER: return "GDK_DRAG_ENTER"; - case GDK_DRAG_LEAVE: return "GDK_DRAG_LEAVE"; - case GDK_DRAG_MOTION: return "GDK_DRAG_MOTION"; - case GDK_DRAG_STATUS: return "GDK_DRAG_STATUS"; - case GDK_DROP_START: return "GDK_DROP_START"; - case GDK_DROP_FINISHED: return "GDK_DROP_FINISHED"; - case GDK_CLIENT_EVENT: return "GDK_CLIENT_EVENT"; - case GDK_VISIBILITY_NOTIFY: return "GDK_VISIBILITY_NOTIFY"; - case GDK_NO_EXPOSE: return "GDK_NO_EXPOSE"; - case GDK_SCROLL: return "GDK_SCROLL"; - case GDK_WINDOW_STATE: return "GDK_WINDOW_STATE"; - case GDK_SETTING: return "GDK_SETTING"; - case GDK_OWNER_CHANGE: return "GDK_OWNER_CHANGE"; - case GDK_GRAB_BROKEN: return "GDK_GRAB_BROKEN"; - case GDK_DAMAGE: return "GDK_DAMAGE"; - default: - return "Unknown Gdk Event"; - } -} - -} // namespace - -MessagePumpGtk::MessagePumpGtk() : MessagePumpGlib() { - gdk_event_handler_set(&EventDispatcher, this, NULL); -} - -MessagePumpGtk::~MessagePumpGtk() { - gdk_event_handler_set(reinterpret_cast<GdkEventFunc>(gtk_main_do_event), - this, NULL); -} - -void MessagePumpGtk::DispatchEvents(GdkEvent* event) { - UNSHIPPED_TRACE_EVENT1("task", "MessagePumpGtk::DispatchEvents", - "type", EventToTypeString(event)); - - WillProcessEvent(event); - gtk_main_do_event(event); - DidProcessEvent(event); -} - -// static -Display* MessagePumpGtk::GetDefaultXDisplay() { - static GdkDisplay* display = gdk_display_get_default(); - if (!display) { - // GTK / GDK has not been initialized, which is a decision we wish to - // support, for example for the GPU process. - static Display* xdisplay = XOpenDisplay(NULL); - return xdisplay; - } - return GDK_DISPLAY_XDISPLAY(display); -} - -void MessagePumpGtk::AddObserver(MessagePumpGdkObserver* observer) { - observers_.AddObserver(observer); -} - -void MessagePumpGtk::RemoveObserver(MessagePumpGdkObserver* observer) { - observers_.RemoveObserver(observer); -} - -void MessagePumpGtk::WillProcessEvent(GdkEvent* event) { - FOR_EACH_OBSERVER(MessagePumpGdkObserver, - observers_, - WillProcessEvent(event)); -} - -void MessagePumpGtk::DidProcessEvent(GdkEvent* event) { - FOR_EACH_OBSERVER(MessagePumpGdkObserver, - observers_, - DidProcessEvent(event)); -} - -// static -void MessagePumpGtk::EventDispatcher(GdkEvent* event, gpointer data) { - MessagePumpGtk* message_pump = reinterpret_cast<MessagePumpGtk*>(data); - message_pump->DispatchEvents(event); -} - -} // namespace base diff --git a/chromium/base/message_loop/message_pump_gtk.h b/chromium/base/message_loop/message_pump_gtk.h deleted file mode 100644 index b3c3b8c7415..00000000000 --- a/chromium/base/message_loop/message_pump_gtk.h +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_GTK_H_ -#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_GTK_H_ - -#include "base/message_loop/message_pump_glib.h" - -typedef union _GdkEvent GdkEvent; -typedef struct _XDisplay Display; - -namespace base { - -// The documentation for this class is in message_pump_glib.h -class MessagePumpGdkObserver { - public: - // This method is called before processing a message. - virtual void WillProcessEvent(GdkEvent* event) = 0; - - // This method is called after processing a message. - virtual void DidProcessEvent(GdkEvent* event) = 0; - - protected: - virtual ~MessagePumpGdkObserver() {} -}; - -// This class implements a message-pump for dispatching GTK events. -class BASE_EXPORT MessagePumpGtk : public MessagePumpGlib { - public: - MessagePumpGtk(); - virtual ~MessagePumpGtk(); - - // Dispatch an available GdkEvent. Essentially this allows a subclass to do - // some task before/after calling the default handler (EventDispatcher). - void DispatchEvents(GdkEvent* event); - - // Returns default X Display. - static Display* GetDefaultXDisplay(); - - // Adds an Observer, which will start receiving notifications immediately. - void AddObserver(MessagePumpGdkObserver* observer); - - // Removes an Observer. It is safe to call this method while an Observer is - // receiving a notification callback. - void RemoveObserver(MessagePumpGdkObserver* observer); - - private: - // Invoked from EventDispatcher. Notifies all observers we're about to - // process an event. - void WillProcessEvent(GdkEvent* event); - - // Invoked from EventDispatcher. Notifies all observers we processed an - // event. - void DidProcessEvent(GdkEvent* event); - - // Callback prior to gdk dispatching an event. - static void EventDispatcher(GdkEvent* event, void* data); - - // List of observers. - ObserverList<MessagePumpGdkObserver> observers_; - - DISALLOW_COPY_AND_ASSIGN(MessagePumpGtk); -}; - -typedef MessagePumpGtk MessagePumpForUI; - -} // namespace base - -#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_GTK_H_ 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 f3b598c6ba6..e6dcc33a4bf 100644 --- a/chromium/base/message_loop/message_pump_io_ios_unittest.cc +++ b/chromium/base/message_loop/message_pump_io_ios_unittest.cc @@ -82,7 +82,7 @@ TEST_F(MessagePumpIOSForIOTest, TestWatchingFromBadThread) { ASSERT_DEBUG_DEATH(io_loop()->WatchFileDescriptor( STDOUT_FILENO, false, MessageLoopForIO::WATCH_READ, &watcher, &delegate), "Check failed: " - "watch_file_descriptor_caller_checker_.CalledOnValidThread()"); + "watch_file_descriptor_caller_checker_.CalledOnValidThread\\(\\)"); } #endif // GTEST_HAS_DEATH_TEST && !defined(NDEBUG) diff --git a/chromium/base/message_loop/message_pump_libevent.cc b/chromium/base/message_loop/message_pump_libevent.cc index 26be687c6dd..d52025a1990 100644 --- a/chromium/base/message_loop/message_pump_libevent.cc +++ b/chromium/base/message_loop/message_pump_libevent.cc @@ -217,7 +217,7 @@ static void timer_callback(int fd, short events, void *context) // Reentrant! void MessagePumpLibevent::Run(Delegate* delegate) { - DCHECK(keep_running_) << "Quit must have been called outside of Run!"; + AutoReset<bool> auto_reset_keep_running(&keep_running_, true); AutoReset<bool> auto_reset_in_run(&in_run_, true); // event_base_loopexit() + EVLOOP_ONCE is leaky, see http://crbug.com/25641. @@ -275,12 +275,10 @@ void MessagePumpLibevent::Run(Delegate* delegate) { } } } - - keep_running_ = true; } void MessagePumpLibevent::Quit() { - DCHECK(in_run_); + DCHECK(in_run_) << "Quit was called outside of Run!"; // Tell both libevent and Run that they should break out of their loops. keep_running_ = false; ScheduleWork(); diff --git a/chromium/base/message_loop/message_pump_libevent_unittest.cc b/chromium/base/message_loop/message_pump_libevent_unittest.cc index bf6d21c3280..9298d687609 100644 --- a/chromium/base/message_loop/message_pump_libevent_unittest.cc +++ b/chromium/base/message_loop/message_pump_libevent_unittest.cc @@ -6,8 +6,10 @@ #include <unistd.h> +#include "base/bind.h" #include "base/message_loop/message_loop.h" #include "base/posix/eintr_wrapper.h" +#include "base/run_loop.h" #include "base/threading/thread.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/libevent/event.h" @@ -78,7 +80,13 @@ TEST_F(MessagePumpLibeventTest, TestWatchingFromBadThread) { ASSERT_DEATH(io_loop()->WatchFileDescriptor( STDOUT_FILENO, false, MessageLoopForIO::WATCH_READ, &watcher, &delegate), "Check failed: " - "watch_file_descriptor_caller_checker_.CalledOnValidThread()"); + "watch_file_descriptor_caller_checker_.CalledOnValidThread\\(\\)"); +} + +TEST_F(MessagePumpLibeventTest, QuitOutsideOfRun) { + scoped_ptr<MessagePumpLibevent> pump(new MessagePumpLibevent); + ASSERT_DEATH(pump->Quit(), "Check failed: in_run_. " + "Quit was called outside of Run!"); } #endif // GTEST_HAS_DEATH_TEST && !defined(NDEBUG) @@ -157,6 +165,41 @@ TEST_F(MessagePumpLibeventTest, StopWatcher) { OnLibeventNotification(pump.get(), &watcher); } +void QuitMessageLoopAndStart(const Closure& quit_closure) { + quit_closure.Run(); + + MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current()); + RunLoop runloop; + MessageLoop::current()->PostTask(FROM_HERE, runloop.QuitClosure()); + runloop.Run(); +} + +class NestedPumpWatcher : public MessagePumpLibevent::Watcher { + public: + NestedPumpWatcher() {} + virtual ~NestedPumpWatcher() {} + + virtual void OnFileCanReadWithoutBlocking(int /* fd */) OVERRIDE { + RunLoop runloop; + MessageLoop::current()->PostTask(FROM_HERE, Bind(&QuitMessageLoopAndStart, + runloop.QuitClosure())); + runloop.Run(); + } + + virtual void OnFileCanWriteWithoutBlocking(int /* fd */) OVERRIDE {} +}; + +TEST_F(MessagePumpLibeventTest, NestedPumpWatcher) { + scoped_ptr<MessagePumpLibevent> pump(new MessagePumpLibevent); + MessagePumpLibevent::FileDescriptorWatcher watcher; + NestedPumpWatcher delegate; + pump->WatchFileDescriptor(pipefds_[1], + false, MessagePumpLibevent::WATCH_READ, &watcher, &delegate); + + // Spoof a libevent notification. + OnLibeventNotification(pump.get(), &watcher); +} + } // namespace } // namespace base diff --git a/chromium/base/message_loop/message_pump_mac.h b/chromium/base/message_loop/message_pump_mac.h index 424cb70938d..6e63d2b5516 100644 --- a/chromium/base/message_loop/message_pump_mac.h +++ b/chromium/base/message_loop/message_pump_mac.h @@ -37,10 +37,9 @@ #include <CoreFoundation/CoreFoundation.h> #include "base/memory/weak_ptr.h" +#include "base/message_loop/timer_slack.h" -#if !defined(__OBJC__) -class NSAutoreleasePool; -#else // !defined(__OBJC__) +#if defined(__OBJC__) #if defined(OS_IOS) #import <Foundation/Foundation.h> #else @@ -55,7 +54,7 @@ class NSAutoreleasePool; - (BOOL)isHandlingSendEvent; @end #endif // !defined(OS_IOS) -#endif // !defined(__OBJC__) +#endif // defined(__OBJC__) namespace base { @@ -63,6 +62,22 @@ class MessagePumpInstrumentation; class RunLoop; class TimeTicks; +// AutoreleasePoolType is a proxy type for autorelease pools. Its definition +// depends on the translation unit (TU) in which this header appears. In pure +// C++ TUs, it is defined as a forward C++ class declaration (that is never +// defined), because autorelease pools are an Objective-C concept. In Automatic +// Reference Counting (ARC) Objective-C TUs, it is similarly defined as a +// forward C++ class declaration, because clang will not allow the type +// "NSAutoreleasePool" in such TUs. Finally, in Manual Retain Release (MRR) +// Objective-C TUs, it is a type alias for NSAutoreleasePool. In all cases, a +// method that takes or returns an NSAutoreleasePool* can use +// AutoreleasePoolType* instead. +#if !defined(__OBJC__) || __has_feature(objc_arc) +class AutoreleasePoolType; +#else // !defined(__OBJC__) || __has_feature(objc_arc) +typedef NSAutoreleasePool AutoreleasePoolType; +#endif // !defined(__OBJC__) || __has_feature(objc_arc) + class MessagePumpCFRunLoopBase : public MessagePump { // Needs access to CreateAutoreleasePool. friend class MessagePumpScopedAutoreleasePool; @@ -79,6 +94,7 @@ class MessagePumpCFRunLoopBase : public MessagePump { virtual void ScheduleWork() OVERRIDE; virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time) OVERRIDE; + virtual void SetTimerSlack(TimerSlack timer_slack) OVERRIDE; protected: // Accessors for private data members to be used by subclasses. @@ -94,7 +110,7 @@ class MessagePumpCFRunLoopBase : public MessagePump { // In some cases, CreateAutoreleasePool may return nil intentionally to // preventing an autorelease pool from being created, allowing any // objects autoreleased by work to fall into the current autorelease pool. - virtual NSAutoreleasePool* CreateAutoreleasePool(); + virtual AutoreleasePoolType* CreateAutoreleasePool(); // Enables instrumentation of the MessagePump. See MessagePumpInstrumentation // in the implementation for details. @@ -181,6 +197,8 @@ class MessagePumpCFRunLoopBase : public MessagePump { // See PowerStateNotification. CFAbsoluteTime delayed_work_fire_time_; + base::TimerSlack timer_slack_; + // The recursion depth of the currently-executing CFRunLoopRun loop on the // run loop's thread. 0 if no run loops are running inside of whatever scope // the object was created in. @@ -205,7 +223,7 @@ class MessagePumpCFRunLoopBase : public MessagePump { DISALLOW_COPY_AND_ASSIGN(MessagePumpCFRunLoopBase); }; -class MessagePumpCFRunLoop : public MessagePumpCFRunLoopBase { +class BASE_EXPORT MessagePumpCFRunLoop : public MessagePumpCFRunLoopBase { public: MessagePumpCFRunLoop(); virtual ~MessagePumpCFRunLoop(); @@ -224,9 +242,9 @@ class MessagePumpCFRunLoop : public MessagePumpCFRunLoopBase { DISALLOW_COPY_AND_ASSIGN(MessagePumpCFRunLoop); }; -class MessagePumpNSRunLoop : public MessagePumpCFRunLoopBase { +class BASE_EXPORT MessagePumpNSRunLoop : public MessagePumpCFRunLoopBase { public: - BASE_EXPORT MessagePumpNSRunLoop(); + MessagePumpNSRunLoop(); virtual ~MessagePumpNSRunLoop(); virtual void DoRun(Delegate* delegate) OVERRIDE; @@ -296,14 +314,14 @@ class MessagePumpCrApplication : public MessagePumpNSApplication { protected: // Returns nil if NSApp is currently in the middle of calling // -sendEvent. Requires NSApp implementing CrAppProtocol. - virtual NSAutoreleasePool* CreateAutoreleasePool() OVERRIDE; + virtual AutoreleasePoolType* CreateAutoreleasePool() OVERRIDE; private: DISALLOW_COPY_AND_ASSIGN(MessagePumpCrApplication); }; #endif // !defined(OS_IOS) -class MessagePumpMac { +class BASE_EXPORT MessagePumpMac { public: // If not on the main thread, returns a new instance of // MessagePumpNSRunLoop. @@ -321,17 +339,21 @@ class MessagePumpMac { // UsingCrApp() returns false if the message pump was created before // NSApp was initialized, or if NSApp does not implement // CrAppProtocol. NSApp must be initialized before calling. - BASE_EXPORT static bool UsingCrApp(); + static bool UsingCrApp(); // Wrapper to query -[NSApp isHandlingSendEvent] from C++ code. // Requires NSApp to implement CrAppProtocol. - BASE_EXPORT static bool IsHandlingSendEvent(); + static bool IsHandlingSendEvent(); #endif // !defined(OS_IOS) private: DISALLOW_IMPLICIT_CONSTRUCTORS(MessagePumpMac); }; +// Tasks posted to the message loop are posted under this mode, as well +// as kCFRunLoopCommonModes. +extern const CFStringRef BASE_EXPORT kMessageLoopExclusiveRunLoopMode; + } // namespace base #endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_MAC_H_ diff --git a/chromium/base/message_loop/message_pump_mac.mm b/chromium/base/message_loop/message_pump_mac.mm index 9a8e95a6e19..0ab9ab7c467 100644 --- a/chromium/base/message_loop/message_pump_mac.mm +++ b/chromium/base/message_loop/message_pump_mac.mm @@ -4,6 +4,7 @@ #import "base/message_loop/message_pump_mac.h" +#include <dlfcn.h> #import <Foundation/Foundation.h> #include <limits> @@ -12,6 +13,7 @@ #include "base/format_macros.h" #include "base/logging.h" #include "base/mac/scoped_cftyperef.h" +#include "base/message_loop/timer_slack.h" #include "base/metrics/histogram.h" #include "base/run_loop.h" #include "base/strings/stringprintf.h" @@ -21,8 +23,44 @@ #import <AppKit/AppKit.h> #endif // !defined(OS_IOS) +namespace base { + namespace { +void CFRunLoopAddSourceToAllModes(CFRunLoopRef rl, CFRunLoopSourceRef source) { + CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes); + CFRunLoopAddSource(rl, source, kMessageLoopExclusiveRunLoopMode); +} + +void CFRunLoopRemoveSourceFromAllModes(CFRunLoopRef rl, + CFRunLoopSourceRef source) { + CFRunLoopRemoveSource(rl, source, kCFRunLoopCommonModes); + CFRunLoopRemoveSource(rl, source, kMessageLoopExclusiveRunLoopMode); +} + +void CFRunLoopAddTimerToAllModes(CFRunLoopRef rl, CFRunLoopTimerRef timer) { + CFRunLoopAddTimer(rl, timer, kCFRunLoopCommonModes); + CFRunLoopAddTimer(rl, timer, kMessageLoopExclusiveRunLoopMode); +} + +void CFRunLoopRemoveTimerFromAllModes(CFRunLoopRef rl, + CFRunLoopTimerRef timer) { + CFRunLoopRemoveTimer(rl, timer, kCFRunLoopCommonModes); + CFRunLoopRemoveTimer(rl, timer, kMessageLoopExclusiveRunLoopMode); +} + +void CFRunLoopAddObserverToAllModes(CFRunLoopRef rl, + CFRunLoopObserverRef observer) { + CFRunLoopAddObserver(rl, observer, kCFRunLoopCommonModes); + CFRunLoopAddObserver(rl, observer, kMessageLoopExclusiveRunLoopMode); +} + +void CFRunLoopRemoveObserverFromAllModes(CFRunLoopRef rl, + CFRunLoopObserverRef observer) { + CFRunLoopRemoveObserver(rl, observer, kCFRunLoopCommonModes); + CFRunLoopRemoveObserver(rl, observer, kMessageLoopExclusiveRunLoopMode); +} + void NoOp(void* info) { } @@ -35,9 +73,38 @@ const CFTimeInterval kCFTimeIntervalMax = bool g_not_using_cr_app = false; #endif +// Call through to CFRunLoopTimerSetTolerance(), which is only available on +// OS X 10.9. +void SetTimerTolerance(CFRunLoopTimerRef timer, CFTimeInterval tolerance) { + typedef void (*CFRunLoopTimerSetTolerancePtr)(CFRunLoopTimerRef timer, + CFTimeInterval tolerance); + + static CFRunLoopTimerSetTolerancePtr settimertolerance_function_ptr; + + static dispatch_once_t get_timer_tolerance_function_ptr_once; + dispatch_once(&get_timer_tolerance_function_ptr_once, ^{ + NSBundle* bundle =[NSBundle + bundleWithPath:@"/System/Library/Frameworks/CoreFoundation.framework"]; + const char* path = [[bundle executablePath] fileSystemRepresentation]; + CHECK(path); + void* library_handle = dlopen(path, RTLD_LAZY | RTLD_LOCAL); + CHECK(library_handle) << dlerror(); + settimertolerance_function_ptr = + reinterpret_cast<CFRunLoopTimerSetTolerancePtr>( + dlsym(library_handle, "CFRunLoopTimerSetTolerance")); + + dlclose(library_handle); + }); + + if (settimertolerance_function_ptr) + settimertolerance_function_ptr(timer, tolerance); +} + } // namespace -namespace base { +// static +const CFStringRef kMessageLoopExclusiveRunLoopMode = + CFSTR("kMessageLoopExclusiveRunLoopMode"); // A scoper for autorelease pools created from message pump run loops. // Avoids dirtying up the ScopedNSAutoreleasePool interface for the rare @@ -95,9 +162,7 @@ class MessagePumpInstrumentation { 0, // order &MessagePumpInstrumentation::TimerFired, &timer_context)); - CFRunLoopAddTimer(CFRunLoopGetCurrent(), - timer_, - kCFRunLoopCommonModes); + CFRunLoopAddTimerToAllModes(CFRunLoopGetCurrent(), timer_); } // Used to track kCFRunLoopEntry. @@ -282,6 +347,7 @@ class MessagePumpInstrumentation { MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() : delegate_(NULL), delayed_work_fire_time_(kCFTimeIntervalMax), + timer_slack_(base::TIMER_SLACK_NONE), nesting_level_(0), run_nesting_level_(0), deepest_nesting_level_(0), @@ -302,7 +368,7 @@ MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() 0, // priority RunDelayedWorkTimer, &timer_context); - CFRunLoopAddTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes); + CFRunLoopAddTimerToAllModes(run_loop_, delayed_work_timer_); CFRunLoopSourceContext source_context = CFRunLoopSourceContext(); source_context.info = this; @@ -310,20 +376,19 @@ MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() work_source_ = CFRunLoopSourceCreate(NULL, // allocator 1, // priority &source_context); - CFRunLoopAddSource(run_loop_, work_source_, kCFRunLoopCommonModes); + CFRunLoopAddSourceToAllModes(run_loop_, work_source_); source_context.perform = RunIdleWorkSource; idle_work_source_ = CFRunLoopSourceCreate(NULL, // allocator 2, // priority &source_context); - CFRunLoopAddSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes); + CFRunLoopAddSourceToAllModes(run_loop_, idle_work_source_); source_context.perform = RunNestingDeferredWorkSource; nesting_deferred_work_source_ = CFRunLoopSourceCreate(NULL, // allocator 0, // priority &source_context); - CFRunLoopAddSource(run_loop_, nesting_deferred_work_source_, - kCFRunLoopCommonModes); + CFRunLoopAddSourceToAllModes(run_loop_, nesting_deferred_work_source_); CFRunLoopObserverContext observer_context = CFRunLoopObserverContext(); observer_context.info = this; @@ -334,7 +399,7 @@ MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() 0, // priority StartOrEndWaitObserver, &observer_context); - CFRunLoopAddObserver(run_loop_, pre_wait_observer_, kCFRunLoopCommonModes); + CFRunLoopAddObserverToAllModes(run_loop_, pre_wait_observer_); pre_source_observer_ = CFRunLoopObserverCreate(NULL, // allocator kCFRunLoopBeforeSources, @@ -342,7 +407,7 @@ MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() 0, // priority PreSourceObserver, &observer_context); - CFRunLoopAddObserver(run_loop_, pre_source_observer_, kCFRunLoopCommonModes); + CFRunLoopAddObserverToAllModes(run_loop_, pre_source_observer_); enter_exit_observer_ = CFRunLoopObserverCreate(NULL, // allocator kCFRunLoopEntry | @@ -351,36 +416,32 @@ MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() 0, // priority EnterExitObserver, &observer_context); - CFRunLoopAddObserver(run_loop_, enter_exit_observer_, kCFRunLoopCommonModes); + CFRunLoopAddObserverToAllModes(run_loop_, enter_exit_observer_); } // Ideally called on the run loop thread. If other run loops were running // lower on the run loop thread's stack when this object was created, the // same number of run loops must be running when this object is destroyed. MessagePumpCFRunLoopBase::~MessagePumpCFRunLoopBase() { - CFRunLoopRemoveObserver(run_loop_, enter_exit_observer_, - kCFRunLoopCommonModes); + CFRunLoopRemoveObserverFromAllModes(run_loop_, enter_exit_observer_); CFRelease(enter_exit_observer_); - CFRunLoopRemoveObserver(run_loop_, pre_source_observer_, - kCFRunLoopCommonModes); + CFRunLoopRemoveObserverFromAllModes(run_loop_, pre_source_observer_); CFRelease(pre_source_observer_); - CFRunLoopRemoveObserver(run_loop_, pre_wait_observer_, - kCFRunLoopCommonModes); + CFRunLoopRemoveObserverFromAllModes(run_loop_, pre_wait_observer_); CFRelease(pre_wait_observer_); - CFRunLoopRemoveSource(run_loop_, nesting_deferred_work_source_, - kCFRunLoopCommonModes); + CFRunLoopRemoveSourceFromAllModes(run_loop_, nesting_deferred_work_source_); CFRelease(nesting_deferred_work_source_); - CFRunLoopRemoveSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes); + CFRunLoopRemoveSourceFromAllModes(run_loop_, idle_work_source_); CFRelease(idle_work_source_); - CFRunLoopRemoveSource(run_loop_, work_source_, kCFRunLoopCommonModes); + CFRunLoopRemoveSourceFromAllModes(run_loop_, work_source_); CFRelease(work_source_); - CFRunLoopRemoveTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes); + CFRunLoopRemoveTimerFromAllModes(run_loop_, delayed_work_timer_); CFRelease(delayed_work_timer_); CFRelease(run_loop_); @@ -438,6 +499,15 @@ void MessagePumpCFRunLoopBase::ScheduleDelayedWork( TimeDelta delta = delayed_work_time - TimeTicks::Now(); delayed_work_fire_time_ = CFAbsoluteTimeGetCurrent() + delta.InSecondsF(); CFRunLoopTimerSetNextFireDate(delayed_work_timer_, delayed_work_fire_time_); + if (timer_slack_ == TIMER_SLACK_MAXIMUM) { + SetTimerTolerance(delayed_work_timer_, delta.InSecondsF() * 0.5); + } else { + SetTimerTolerance(delayed_work_timer_, 0); + } +} + +void MessagePumpCFRunLoopBase::SetTimerSlack(TimerSlack timer_slack) { + timer_slack_ = timer_slack; } // Called from the run loop. @@ -697,7 +767,7 @@ void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) { } // Base version returns a standard NSAutoreleasePool. -NSAutoreleasePool* MessagePumpCFRunLoopBase::CreateAutoreleasePool() { +AutoreleasePoolType* MessagePumpCFRunLoopBase::CreateAutoreleasePool() { return [[NSAutoreleasePool alloc] init]; } @@ -760,11 +830,11 @@ MessagePumpNSRunLoop::MessagePumpNSRunLoop() quit_source_ = CFRunLoopSourceCreate(NULL, // allocator 0, // priority &source_context); - CFRunLoopAddSource(run_loop(), quit_source_, kCFRunLoopCommonModes); + CFRunLoopAddSourceToAllModes(run_loop(), quit_source_); } MessagePumpNSRunLoop::~MessagePumpNSRunLoop() { - CFRunLoopRemoveSource(run_loop(), quit_source_, kCFRunLoopCommonModes); + CFRunLoopRemoveSourceFromAllModes(run_loop(), quit_source_); CFRelease(quit_source_); } @@ -909,7 +979,7 @@ MessagePumpCrApplication::~MessagePumpCrApplication() { // CrApplication is responsible for setting handlingSendEvent to true just // before it sends the event through the event handling mechanism, and // returning it to its previous value once the event has been sent. -NSAutoreleasePool* MessagePumpCrApplication::CreateAutoreleasePool() { +AutoreleasePoolType* MessagePumpCrApplication::CreateAutoreleasePool() { if (MessagePumpMac::IsHandlingSendEvent()) return nil; return MessagePumpNSApplication::CreateAutoreleasePool(); diff --git a/chromium/base/message_loop/message_pump_observer.h b/chromium/base/message_loop/message_pump_observer.h index 333a75f1cbb..d6e7abf786a 100644 --- a/chromium/base/message_loop/message_pump_observer.h +++ b/chromium/base/message_loop/message_pump_observer.h @@ -8,34 +8,22 @@ #include "base/base_export.h" #include "base/event_types.h" -namespace base { - -enum EventStatus { - EVENT_CONTINUE, // The event should be dispatched as normal. -#if defined(USE_X11) - EVENT_HANDLED // The event should not be processed any farther. +#if !defined(OS_WIN) +#error Should not be here. #endif -}; + +namespace base { // A MessagePumpObserver is an object that receives global -// notifications from the UI MessageLoop with MessagePumpWin or -// MessagePumpX11. +// notifications from the UI MessageLoop with MessagePumpWin. // // NOTE: An Observer implementation should be extremely fast! -// -// For use with MessagePumpX11, please see message_pump_glib.h for more -// info about how this is invoked in this environment. class BASE_EXPORT MessagePumpObserver { public: - // This method is called before processing a NativeEvent. If the - // method returns EVENT_HANDLED, it indicates the event has already - // been handled, so the event is not processed any farther. If the - // method returns EVENT_CONTINUE, the event dispatching proceeds as - // normal. - virtual EventStatus WillProcessEvent(const NativeEvent& event) = 0; - - // This method is called after processing a message. This method - // will not be called if WillProcessEvent returns EVENT_HANDLED. + // This method is called before processing a NativeEvent. + virtual void WillProcessEvent(const NativeEvent& event) = 0; + + // This method is called after processing a message. virtual void DidProcessEvent(const NativeEvent& event) = 0; protected: diff --git a/chromium/base/message_loop/message_pump_ozone.cc b/chromium/base/message_loop/message_pump_ozone.cc deleted file mode 100644 index f7bff6d3ace..00000000000 --- a/chromium/base/message_loop/message_pump_ozone.cc +++ /dev/null @@ -1,61 +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/message_loop/message_pump_ozone.h" - -#include "base/logging.h" -#include "base/message_loop/message_loop.h" - -namespace base { - -MessagePumpOzone::MessagePumpOzone() - : MessagePumpLibevent() { -} - -MessagePumpOzone::~MessagePumpOzone() { -} - -void MessagePumpOzone::AddObserver(MessagePumpObserver* /* observer */) { - NOTIMPLEMENTED(); -} - -void MessagePumpOzone::RemoveObserver(MessagePumpObserver* /* observer */) { - NOTIMPLEMENTED(); -} - -// static -MessagePumpOzone* MessagePumpOzone::Current() { - MessageLoopForUI* loop = MessageLoopForUI::current(); - return static_cast<MessagePumpOzone*>(loop->pump_ui()); -} - -void MessagePumpOzone::AddDispatcherForRootWindow( - MessagePumpDispatcher* dispatcher) { - // Only one root window is supported. - DCHECK(dispatcher_.size() == 0); - dispatcher_.insert(dispatcher_.begin(),dispatcher); -} - -void MessagePumpOzone::RemoveDispatcherForRootWindow( - MessagePumpDispatcher* dispatcher) { - DCHECK(dispatcher_.size() == 1); - dispatcher_.pop_back(); -} - -bool MessagePumpOzone::Dispatch(const NativeEvent& dev) { - if (dispatcher_.size() > 0) - return dispatcher_[0]->Dispatch(dev); - else - return true; -} - -// This code assumes that the caller tracks the lifetime of the |dispatcher|. -void MessagePumpOzone::RunWithDispatcher( - Delegate* delegate, MessagePumpDispatcher* dispatcher) { - dispatcher_.push_back(dispatcher); - Run(delegate); - dispatcher_.pop_back(); -} - -} // namespace base diff --git a/chromium/base/message_loop/message_pump_ozone.h b/chromium/base/message_loop/message_pump_ozone.h deleted file mode 100644 index edcdf2e3a85..00000000000 --- a/chromium/base/message_loop/message_pump_ozone.h +++ /dev/null @@ -1,52 +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. - -#ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_OZONE_H_ -#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_OZONE_H_ - -#include "base/memory/scoped_ptr.h" -#include "base/memory/scoped_vector.h" -#include "base/message_loop/message_pump_dispatcher.h" -#include "base/message_loop/message_pump_libevent.h" -#include "base/message_loop/message_pump_observer.h" -#include "base/observer_list.h" - -namespace base { - -// This class implements a message-pump for processing events from input devices -// Refer to MessagePump for further documentation. -class BASE_EXPORT MessagePumpOzone : public MessagePumpLibevent, - public MessagePumpDispatcher { - public: - MessagePumpOzone(); - virtual ~MessagePumpOzone(); - - // Returns the UI message pump. - static MessagePumpOzone* Current(); - - // Add/Remove the root window dispatcher. - void AddDispatcherForRootWindow(MessagePumpDispatcher* dispatcher); - void RemoveDispatcherForRootWindow(MessagePumpDispatcher* dispatcher); - - void RunWithDispatcher(Delegate* delegate, MessagePumpDispatcher* dispatcher); - - // Add / remove an Observer, which will start receiving notifications - // immediately. - void AddObserver(MessagePumpObserver* observer); - void RemoveObserver(MessagePumpObserver* observer); - - // Overridden from MessagePumpDispatcher. - virtual bool Dispatch(const NativeEvent& event) OVERRIDE; - - private: - std::vector<MessagePumpDispatcher*> dispatcher_; - - DISALLOW_COPY_AND_ASSIGN(MessagePumpOzone); -}; - -typedef MessagePumpOzone MessagePumpForUI; - -} // namespace base - -#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_OZONE_H_ diff --git a/chromium/base/message_loop/message_pump_win.cc b/chromium/base/message_loop/message_pump_win.cc index 1927473b1eb..ae022bf0957 100644 --- a/chromium/base/message_loop/message_pump_win.cc +++ b/chromium/base/message_loop/message_pump_win.cc @@ -97,8 +97,7 @@ int MessagePumpWin::GetCurrentDelay() const { // MessagePumpForUI public: MessagePumpForUI::MessagePumpForUI() - : atom_(0), - message_filter_(new MessageFilter) { + : atom_(0) { InitMessageWnd(); } @@ -346,7 +345,7 @@ bool MessagePumpForUI::ProcessNextWindowsMessage() { sent_messages_in_queue = true; MSG msg; - if (message_filter_->DoPeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != FALSE) return ProcessMessageHelper(msg); return sent_messages_in_queue; @@ -372,14 +371,14 @@ bool MessagePumpForUI::ProcessMessageHelper(const MSG& msg) { WillProcessMessage(msg); - if (!message_filter_->ProcessMessage(msg)) { - if (state_->dispatcher) { - if (!state_->dispatcher->Dispatch(msg)) - state_->should_quit = true; - } else { - TranslateMessage(&msg); - DispatchMessage(&msg); - } + uint32_t action = MessagePumpDispatcher::POST_DISPATCH_PERFORM_DEFAULT; + if (state_->dispatcher) + action = state_->dispatcher->Dispatch(msg); + if (action & MessagePumpDispatcher::POST_DISPATCH_QUIT_LOOP) + state_->should_quit = true; + if (action & MessagePumpDispatcher::POST_DISPATCH_PERFORM_DEFAULT) { + TranslateMessage(&msg); + DispatchMessage(&msg); } DidProcessMessage(msg); @@ -406,8 +405,7 @@ bool MessagePumpForUI::ProcessPumpReplacementMessage() { have_message = PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE) || PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE); } else { - have_message = !!message_filter_->DoPeekMessage(&msg, NULL, 0, 0, - PM_REMOVE); + have_message = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != FALSE; } DCHECK(!have_message || kMsgHaveWork != msg.message || @@ -429,11 +427,6 @@ bool MessagePumpForUI::ProcessPumpReplacementMessage() { return ProcessMessageHelper(msg); } -void MessagePumpForUI::SetMessageFilter( - scoped_ptr<MessageFilter> message_filter) { - message_filter_ = message_filter.Pass(); -} - //----------------------------------------------------------------------------- // MessagePumpForIO public: diff --git a/chromium/base/message_loop/message_pump_win.h b/chromium/base/message_loop/message_pump_win.h index 9184058a6c0..535a21320e0 100644 --- a/chromium/base/message_loop/message_pump_win.h +++ b/chromium/base/message_loop/message_pump_win.h @@ -11,7 +11,6 @@ #include "base/base_export.h" #include "base/basictypes.h" -#include "base/memory/scoped_ptr.h" #include "base/message_loop/message_pump.h" #include "base/message_loop/message_pump_dispatcher.h" #include "base/message_loop/message_pump_observer.h" @@ -127,44 +126,12 @@ class BASE_EXPORT MessagePumpWin : public MessagePump { // class BASE_EXPORT MessagePumpForUI : public MessagePumpWin { public: - // A MessageFilter implements the common Peek/Translate/Dispatch code to deal - // with windows messages. - // This abstraction is used to inject TSF message peeking. See - // TextServicesMessageFilter. - class BASE_EXPORT MessageFilter { - public: - virtual ~MessageFilter() {} - // Implements the functionality exposed by the OS through PeekMessage. - virtual BOOL DoPeekMessage(MSG* msg, - HWND window_handle, - UINT msg_filter_min, - UINT msg_filter_max, - UINT remove_msg) { - return PeekMessage(msg, window_handle, msg_filter_min, msg_filter_max, - remove_msg); - } - // Returns true if |message| was consumed by the filter and no extra - // processing is required. If this method returns false, it is the - // responsibility of the caller to ensure that normal processing takes - // place. - // The priority to consume messages is the following: - // - Native Windows' message filter (CallMsgFilter). - // - MessageFilter::ProcessMessage. - // - MessagePumpDispatcher. - // - TranslateMessage / DispatchMessage. - virtual bool ProcessMessage(const MSG& msg) { return false;} - }; // The application-defined code passed to the hook procedure. static const int kMessageFilterCode = 0x5001; MessagePumpForUI(); virtual ~MessagePumpForUI(); - // Sets a new MessageFilter. MessagePumpForUI takes ownership of - // |message_filter|. When SetMessageFilter is called, old MessageFilter is - // deleted. - void SetMessageFilter(scoped_ptr<MessageFilter> message_filter); - // MessagePump methods: virtual void ScheduleWork(); virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time); @@ -188,8 +155,6 @@ class BASE_EXPORT MessagePumpForUI : public MessagePumpWin { // A hidden message-only window. HWND message_hwnd_; - - scoped_ptr<MessageFilter> message_filter_; }; //----------------------------------------------------------------------------- diff --git a/chromium/base/message_loop/message_pump_x11.cc b/chromium/base/message_loop/message_pump_x11.cc deleted file mode 100644 index 35dcc040348..00000000000 --- a/chromium/base/message_loop/message_pump_x11.cc +++ /dev/null @@ -1,313 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/message_loop/message_pump_x11.h" - -#include <glib.h> -#include <X11/X.h> -#include <X11/extensions/XInput2.h> -#include <X11/XKBlib.h> - -#include "base/basictypes.h" -#include "base/message_loop/message_loop.h" - -namespace base { - -namespace { - -gboolean XSourcePrepare(GSource* source, gint* timeout_ms) { - if (XPending(MessagePumpX11::GetDefaultXDisplay())) - *timeout_ms = 0; - else - *timeout_ms = -1; - return FALSE; -} - -gboolean XSourceCheck(GSource* source) { - return XPending(MessagePumpX11::GetDefaultXDisplay()); -} - -gboolean XSourceDispatch(GSource* source, - GSourceFunc unused_func, - gpointer data) { - MessagePumpX11* pump = static_cast<MessagePumpX11*>(data); - return pump->DispatchXEvents(); -} - -GSourceFuncs XSourceFuncs = { - XSourcePrepare, - XSourceCheck, - XSourceDispatch, - NULL -}; - -// The connection is essentially a global that's accessed through a static -// method and destroyed whenever ~MessagePumpX11() is called. We do this -// for historical reasons so user code can call -// MessagePumpForUI::GetDefaultXDisplay() where MessagePumpForUI is a typedef -// to whatever type in the current build. -// -// TODO(erg): This can be changed to something more sane like -// MessagePumpX11::Current()->display() once MessagePumpGtk goes away. -Display* g_xdisplay = NULL; -int g_xinput_opcode = -1; - -bool InitializeXInput2() { - Display* display = MessagePumpX11::GetDefaultXDisplay(); - if (!display) - return false; - - int event, err; - - int xiopcode; - if (!XQueryExtension(display, "XInputExtension", &xiopcode, &event, &err)) { - DVLOG(1) << "X Input extension not available."; - return false; - } - g_xinput_opcode = xiopcode; - -#if defined(USE_XI2_MT) - // USE_XI2_MT also defines the required XI2 minor minimum version. - int major = 2, minor = USE_XI2_MT; -#else - int major = 2, minor = 0; -#endif - if (XIQueryVersion(display, &major, &minor) == BadRequest) { - DVLOG(1) << "XInput2 not supported in the server."; - return false; - } -#if defined(USE_XI2_MT) - if (major < 2 || (major == 2 && minor < USE_XI2_MT)) { - DVLOG(1) << "XI version on server is " << major << "." << minor << ". " - << "But 2." << USE_XI2_MT << " is required."; - return false; - } -#endif - - return true; -} - -Window FindEventTarget(const NativeEvent& xev) { - Window target = xev->xany.window; - if (xev->type == GenericEvent && - static_cast<XIEvent*>(xev->xcookie.data)->extension == g_xinput_opcode) { - target = static_cast<XIDeviceEvent*>(xev->xcookie.data)->event; - } - return target; -} - -bool InitializeXkb() { - Display* display = MessagePumpX11::GetDefaultXDisplay(); - if (!display) - return false; - - int opcode, event, error; - int major = XkbMajorVersion; - int minor = XkbMinorVersion; - if (!XkbQueryExtension(display, &opcode, &event, &error, &major, &minor)) { - DVLOG(1) << "Xkb extension not available."; - return false; - } - - // Ask the server not to send KeyRelease event when the user holds down a key. - // crbug.com/138092 - Bool supported_return; - if (!XkbSetDetectableAutoRepeat(display, True, &supported_return)) { - DVLOG(1) << "XKB not supported in the server."; - return false; - } - - return true; -} - -} // namespace - -MessagePumpX11::MessagePumpX11() : MessagePumpGlib(), - x_source_(NULL) { - InitializeXInput2(); - InitializeXkb(); - InitXSource(); - - // Can't put this in the initializer list because g_xdisplay may not exist - // until after InitXSource(). - x_root_window_ = DefaultRootWindow(g_xdisplay); -} - -MessagePumpX11::~MessagePumpX11() { - g_source_destroy(x_source_); - g_source_unref(x_source_); - XCloseDisplay(g_xdisplay); - g_xdisplay = NULL; -} - -// static -Display* MessagePumpX11::GetDefaultXDisplay() { - if (!g_xdisplay) - g_xdisplay = XOpenDisplay(NULL); - return g_xdisplay; -} - -#if defined(TOOLKIT_GTK) -// static -MessagePumpX11* MessagePumpX11::Current() { - MessageLoop* loop = MessageLoop::current(); - return static_cast<MessagePumpX11*>(loop->pump_gpu()); -} -#else -// static -MessagePumpX11* MessagePumpX11::Current() { - MessageLoopForUI* loop = MessageLoopForUI::current(); - return static_cast<MessagePumpX11*>(loop->pump_ui()); -} -#endif - -void MessagePumpX11::AddDispatcherForWindow( - MessagePumpDispatcher* dispatcher, - unsigned long xid) { - dispatchers_.insert(std::make_pair(xid, dispatcher)); -} - -void MessagePumpX11::RemoveDispatcherForWindow(unsigned long xid) { - dispatchers_.erase(xid); -} - -void MessagePumpX11::AddDispatcherForRootWindow( - MessagePumpDispatcher* dispatcher) { - root_window_dispatchers_.AddObserver(dispatcher); -} - -void MessagePumpX11::RemoveDispatcherForRootWindow( - MessagePumpDispatcher* dispatcher) { - root_window_dispatchers_.RemoveObserver(dispatcher); -} - -void MessagePumpX11::AddObserver(MessagePumpObserver* observer) { - observers_.AddObserver(observer); -} - -void MessagePumpX11::RemoveObserver(MessagePumpObserver* observer) { - observers_.RemoveObserver(observer); -} - -bool MessagePumpX11::DispatchXEvents() { - Display* display = GetDefaultXDisplay(); - DCHECK(display); - MessagePumpDispatcher* dispatcher = - GetDispatcher() ? GetDispatcher() : this; - - // In the general case, we want to handle all pending events before running - // the tasks. This is what happens in the message_pump_glib case. - while (XPending(display)) { - XEvent xev; - XNextEvent(display, &xev); - if (dispatcher && ProcessXEvent(dispatcher, &xev)) - return TRUE; - } - return TRUE; -} - -void MessagePumpX11::BlockUntilWindowMapped(unsigned long xid) { - XEvent event; - - Display* display = GetDefaultXDisplay(); - DCHECK(display); - - MessagePumpDispatcher* dispatcher = - GetDispatcher() ? GetDispatcher() : this; - - do { - // Block until there's a message of |event_mask| type on |w|. Then remove - // it from the queue and stuff it in |event|. - XWindowEvent(display, xid, StructureNotifyMask, &event); - ProcessXEvent(dispatcher, &event); - } while (event.type != MapNotify); -} - -void MessagePumpX11::InitXSource() { - // CHECKs are to help track down crbug.com/113106. - CHECK(!x_source_); - Display* display = GetDefaultXDisplay(); - CHECK(display) << "Unable to get connection to X server"; - x_poll_.reset(new GPollFD()); - CHECK(x_poll_.get()); - x_poll_->fd = ConnectionNumber(display); - x_poll_->events = G_IO_IN; - - x_source_ = g_source_new(&XSourceFuncs, sizeof(GSource)); - g_source_add_poll(x_source_, x_poll_.get()); - g_source_set_can_recurse(x_source_, TRUE); - g_source_set_callback(x_source_, NULL, this, NULL); - g_source_attach(x_source_, g_main_context_default()); -} - -bool MessagePumpX11::ProcessXEvent(MessagePumpDispatcher* dispatcher, - XEvent* xev) { - bool should_quit = false; - - bool have_cookie = false; - if (xev->type == GenericEvent && - XGetEventData(xev->xgeneric.display, &xev->xcookie)) { - have_cookie = true; - } - - if (!WillProcessXEvent(xev)) { - if (!dispatcher->Dispatch(xev)) { - should_quit = true; - Quit(); - } - DidProcessXEvent(xev); - } - - if (have_cookie) { - XFreeEventData(xev->xgeneric.display, &xev->xcookie); - } - - return should_quit; -} - -bool MessagePumpX11::WillProcessXEvent(XEvent* xevent) { - if (!observers().might_have_observers()) - return false; - ObserverListBase<MessagePumpObserver>::Iterator it(observers()); - MessagePumpObserver* obs; - while ((obs = it.GetNext()) != NULL) { - if (obs->WillProcessEvent(xevent)) - return true; - } - return false; -} - -void MessagePumpX11::DidProcessXEvent(XEvent* xevent) { - FOR_EACH_OBSERVER(MessagePumpObserver, observers(), DidProcessEvent(xevent)); -} - -MessagePumpDispatcher* MessagePumpX11::GetDispatcherForXEvent( - const NativeEvent& xev) const { - ::Window x_window = FindEventTarget(xev); - DispatchersMap::const_iterator it = dispatchers_.find(x_window); - return it != dispatchers_.end() ? it->second : NULL; -} - -bool MessagePumpX11::Dispatch(const NativeEvent& xev) { - // MappingNotify events (meaning that the keyboard or pointer buttons have - // been remapped) aren't associated with a window; send them to all - // dispatchers. - if (xev->type == MappingNotify) { - for (DispatchersMap::const_iterator it = dispatchers_.begin(); - it != dispatchers_.end(); ++it) { - it->second->Dispatch(xev); - } - return true; - } - - if (FindEventTarget(xev) == x_root_window_) { - FOR_EACH_OBSERVER(MessagePumpDispatcher, root_window_dispatchers_, - Dispatch(xev)); - return true; - } - MessagePumpDispatcher* dispatcher = GetDispatcherForXEvent(xev); - return dispatcher ? dispatcher->Dispatch(xev) : true; -} - -} // namespace base diff --git a/chromium/base/message_loop/message_pump_x11.h b/chromium/base/message_loop/message_pump_x11.h deleted file mode 100644 index 015c230a700..00000000000 --- a/chromium/base/message_loop/message_pump_x11.h +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_X11_H -#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_X11_H - -#include <bitset> -#include <map> - -#include "base/memory/scoped_ptr.h" -#include "base/message_loop/message_pump.h" -#include "base/message_loop/message_pump_dispatcher.h" -#include "base/message_loop/message_pump_glib.h" -#include "base/message_loop/message_pump_observer.h" -#include "base/observer_list.h" - -// It would be nice to include the X11 headers here so that we use Window -// instead of its typedef of unsigned long, but we can't because everything in -// chrome includes us through base/message_loop/message_loop.h, and X11's crappy -// #define heavy headers muck up half of chrome. - -typedef struct _GPollFD GPollFD; -typedef struct _GSource GSource; -typedef struct _XDisplay Display; - -namespace base { - -// This class implements a message-pump for dispatching X events. -// -// If there's a current dispatcher given through RunWithDispatcher(), that -// dispatcher receives events. Otherwise, we route to messages to dispatchers -// who have subscribed to messages from a specific X11 window. -class BASE_EXPORT MessagePumpX11 : public MessagePumpGlib, - public MessagePumpDispatcher { - public: - MessagePumpX11(); - virtual ~MessagePumpX11(); - - // Returns default X Display. - static Display* GetDefaultXDisplay(); - - // Returns the UI or GPU message pump. - static MessagePumpX11* Current(); - - // Adds/Removes |dispatcher| for the |xid|. This will route all messages from - // the window |xid| to |dispatcher. - void AddDispatcherForWindow(MessagePumpDispatcher* dispatcher, - unsigned long xid); - void RemoveDispatcherForWindow(unsigned long xid); - - // Adds/Removes |dispatcher| to receive all events sent to the X root - // window. A root window can have multiple dispatchers, and events on root - // windows will be dispatched to all. - void AddDispatcherForRootWindow(MessagePumpDispatcher* dispatcher); - void RemoveDispatcherForRootWindow(MessagePumpDispatcher* dispatcher); - - // Adds an Observer, which will start receiving notifications immediately. - void AddObserver(MessagePumpObserver* observer); - - // Removes an Observer. It is safe to call this method while an Observer is - // receiving a notification callback. - void RemoveObserver(MessagePumpObserver* observer); - - // Internal function. Called by the glib source dispatch function. Processes - // all available X events. - bool DispatchXEvents(); - - // Blocks on the X11 event queue until we receive notification from the - // xserver that |w| has been mapped; StructureNotifyMask events on |w| are - // pulled out from the queue and dispatched out of order. - // - // For those that know X11, this is really a wrapper around XWindowEvent - // which still makes sure the preempted event is dispatched instead of - // dropped on the floor. This method exists because mapping a window is - // asynchronous (and we receive an XEvent when mapped), while there are also - // functions which require a mapped window. - void BlockUntilWindowMapped(unsigned long xid); - - private: - typedef std::map<unsigned long, MessagePumpDispatcher*> DispatchersMap; - - // Initializes the glib event source for X. - void InitXSource(); - - // Dispatches the XEvent and returns true if we should exit the current loop - // of message processing. - bool ProcessXEvent(MessagePumpDispatcher* dispatcher, XEvent* event); - - // Sends the event to the observers. If an observer returns true, then it does - // not send the event to any other observers and returns true. Returns false - // if no observer returns true. - bool WillProcessXEvent(XEvent* xevent); - void DidProcessXEvent(XEvent* xevent); - - // Returns the Dispatcher based on the event's target window. - MessagePumpDispatcher* GetDispatcherForXEvent(const NativeEvent& xev) const; - - ObserverList<MessagePumpObserver>& observers() { return observers_; } - - // Overridden from MessagePumpDispatcher: - virtual bool Dispatch(const NativeEvent& event) OVERRIDE; - - // The event source for X events. - GSource* x_source_; - - // The poll attached to |x_source_|. - scoped_ptr<GPollFD> x_poll_; - - DispatchersMap dispatchers_; - - // Dispatch calls can cause addition of new dispatchers as we iterate - // through them. Use ObserverList to ensure the iterator remains valid across - // additions. - ObserverList<MessagePumpDispatcher> root_window_dispatchers_; - - // List of observers. - ObserverList<MessagePumpObserver> observers_; - - unsigned long x_root_window_; - - DISALLOW_COPY_AND_ASSIGN(MessagePumpX11); -}; - -#if !defined(TOOLKIT_GTK) -typedef MessagePumpX11 MessagePumpForUI; -#endif - -} // namespace base - -#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_X11_H diff --git a/chromium/base/message_loop/timer_slack.h b/chromium/base/message_loop/timer_slack.h new file mode 100644 index 00000000000..1ad6ca94a1b --- /dev/null +++ b/chromium/base/message_loop/timer_slack.h @@ -0,0 +1,22 @@ +// 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_MESSAGE_LOOP_TIMER_SLACK_H_ +#define BASE_MESSAGE_LOOP_TIMER_SLACK_H_ + +namespace base { + +// Amount of timer slack to use for delayed timers. Increasing timer slack +// allows the OS to coalesce timers more effectively. +enum TimerSlack { + // Lowest value for timer slack allowed by OS. + TIMER_SLACK_NONE, + + // Maximal value for timer slack allowed by OS. + TIMER_SLACK_MAXIMUM +}; + +} // namespace base + +#endif // BASE_MESSAGE_LOOP_TIMER_SLACK_H_ diff --git a/chromium/base/metrics/OWNERS b/chromium/base/metrics/OWNERS index 17a19451a29..3fd7c0dbc2b 100644 --- a/chromium/base/metrics/OWNERS +++ b/chromium/base/metrics/OWNERS @@ -1,8 +1,3 @@ -# Primary OWNER -jar@chromium.org - -# Secondary OWNER; can review simpler changes +asvitkine@chromium.org isherman@chromium.org - -# Note that all members of the parent file base/OWNERS can also stamp trivial -# changes, but will probably defer to Jim for meatier changes. +jar@chromium.org diff --git a/chromium/base/metrics/field_trial.cc b/chromium/base/metrics/field_trial.cc index b99f3148f96..54b95211470 100644 --- a/chromium/base/metrics/field_trial.cc +++ b/chromium/base/metrics/field_trial.cc @@ -252,7 +252,7 @@ FieldTrialList::FieldTrialList( FieldTrialList::~FieldTrialList() { AutoLock auto_lock(lock_); while (!registered_.empty()) { - RegistrationList::iterator it = registered_.begin(); + RegistrationMap::iterator it = registered_.begin(); it->second->Release(); registered_.erase(it->first); } @@ -390,7 +390,7 @@ void FieldTrialList::GetActiveFieldTrialGroups( return; AutoLock auto_lock(global_->lock_); - for (RegistrationList::iterator it = global_->registered_.begin(); + for (RegistrationMap::iterator it = global_->registered_.begin(); it != global_->registered_.end(); ++it) { FieldTrial::ActiveGroup active_group; if (it->second->GetActiveGroup(&active_group)) @@ -399,8 +399,10 @@ void FieldTrialList::GetActiveFieldTrialGroups( } // static -bool FieldTrialList::CreateTrialsFromString(const std::string& trials_string, - FieldTrialActivationMode mode) { +bool FieldTrialList::CreateTrialsFromString( + const std::string& trials_string, + FieldTrialActivationMode mode, + const std::set<std::string>& ignored_trial_names) { DCHECK(global_); if (trials_string.empty() || !global_) return true; @@ -419,6 +421,9 @@ bool FieldTrialList::CreateTrialsFromString(const std::string& trials_string, group_name_end - name_end - 1); next_item = group_name_end + 1; + if (ignored_trial_names.find(name) != ignored_trial_names.end()) + continue; + FieldTrial* trial = CreateFieldTrial(name, group_name); if (!trial) return false; @@ -513,7 +518,7 @@ const FieldTrial::EntropyProvider* } FieldTrial* FieldTrialList::PreLockedFind(const std::string& name) { - RegistrationList::iterator it = registered_.find(name); + RegistrationMap::iterator it = registered_.find(name); if (registered_.end() == it) return NULL; return it->second; diff --git a/chromium/base/metrics/field_trial.h b/chromium/base/metrics/field_trial.h index 70ce2f9c45d..51177eb93f7 100644 --- a/chromium/base/metrics/field_trial.h +++ b/chromium/base/metrics/field_trial.h @@ -55,6 +55,7 @@ #define BASE_METRICS_FIELD_TRIAL_H_ #include <map> +#include <set> #include <string> #include <vector> @@ -412,9 +413,12 @@ class BASE_EXPORT FieldTrialList { // browser process into this non-browser process, but could also be invoked // through a command line argument to the browser process. The created field // trials are marked as "used" for the purposes of active trial reporting if - // |mode| is ACTIVATE_TRIALS. - static bool CreateTrialsFromString(const std::string& prior_trials, - FieldTrialActivationMode mode); + // |mode| is ACTIVATE_TRIALS. Trial names in |ignored_trial_names| are ignored + // when parsing |prior_trials|. + static bool CreateTrialsFromString( + const std::string& prior_trials, + FieldTrialActivationMode mode, + const std::set<std::string>& ignored_trial_names); // Create a FieldTrial with the given |name| and using 100% probability for // the FieldTrial, force FieldTrial to have the same group string as @@ -441,7 +445,7 @@ class BASE_EXPORT FieldTrialList { private: // A map from FieldTrial names to the actual instances. - typedef std::map<std::string, FieldTrial*> RegistrationList; + typedef std::map<std::string, FieldTrial*> RegistrationMap; // If one-time randomization is enabled, returns a weak pointer to the // corresponding EntropyProvider. Otherwise, returns NULL. @@ -466,7 +470,7 @@ class BASE_EXPORT FieldTrialList { // Lock for access to registered_. base::Lock lock_; - RegistrationList registered_; + RegistrationMap registered_; // Entropy provider to be used for one-time randomized field trials. If NULL, // one-time randomization is not supported. diff --git a/chromium/base/metrics/field_trial_unittest.cc b/chromium/base/metrics/field_trial_unittest.cc index a77633e8f09..866095ca66e 100644 --- a/chromium/base/metrics/field_trial_unittest.cc +++ b/chromium/base/metrics/field_trial_unittest.cc @@ -385,7 +385,8 @@ TEST_F(FieldTrialTest, Restore) { ASSERT_FALSE(FieldTrialList::TrialExists("xxx")); FieldTrialList::CreateTrialsFromString("Some_name/Winner/xxx/yyyy/", - FieldTrialList::DONT_ACTIVATE_TRIALS); + FieldTrialList::DONT_ACTIVATE_TRIALS, + std::set<std::string>()); FieldTrial* trial = FieldTrialList::Find("Some_name"); ASSERT_NE(static_cast<FieldTrial*>(NULL), trial); @@ -400,13 +401,17 @@ TEST_F(FieldTrialTest, Restore) { TEST_F(FieldTrialTest, BogusRestore) { EXPECT_FALSE(FieldTrialList::CreateTrialsFromString( - "MissingSlash", FieldTrialList::DONT_ACTIVATE_TRIALS)); + "MissingSlash", FieldTrialList::DONT_ACTIVATE_TRIALS, + std::set<std::string>())); EXPECT_FALSE(FieldTrialList::CreateTrialsFromString( - "MissingGroupName/", FieldTrialList::DONT_ACTIVATE_TRIALS)); + "MissingGroupName/", FieldTrialList::DONT_ACTIVATE_TRIALS, + std::set<std::string>())); EXPECT_FALSE(FieldTrialList::CreateTrialsFromString( - "MissingFinalSlash/gname", FieldTrialList::DONT_ACTIVATE_TRIALS)); + "MissingFinalSlash/gname", FieldTrialList::DONT_ACTIVATE_TRIALS, + std::set<std::string>())); EXPECT_FALSE(FieldTrialList::CreateTrialsFromString( - "noname, only group/", FieldTrialList::DONT_ACTIVATE_TRIALS)); + "noname, only group/", FieldTrialList::DONT_ACTIVATE_TRIALS, + std::set<std::string>())); } TEST_F(FieldTrialTest, DuplicateRestore) { @@ -420,18 +425,21 @@ TEST_F(FieldTrialTest, DuplicateRestore) { // It is OK if we redundantly specify a winner. EXPECT_TRUE(FieldTrialList::CreateTrialsFromString( - save_string, FieldTrialList::DONT_ACTIVATE_TRIALS)); + save_string, FieldTrialList::DONT_ACTIVATE_TRIALS, + std::set<std::string>())); // But it is an error to try to change to a different winner. EXPECT_FALSE(FieldTrialList::CreateTrialsFromString( - "Some name/Loser/", FieldTrialList::DONT_ACTIVATE_TRIALS)); + "Some name/Loser/", FieldTrialList::DONT_ACTIVATE_TRIALS, + std::set<std::string>())); } TEST_F(FieldTrialTest, CreateTrialsFromStringActive) { ASSERT_FALSE(FieldTrialList::TrialExists("Abc")); ASSERT_FALSE(FieldTrialList::TrialExists("Xyz")); ASSERT_TRUE(FieldTrialList::CreateTrialsFromString( - "Abc/def/Xyz/zyx/", FieldTrialList::ACTIVATE_TRIALS)); + "Abc/def/Xyz/zyx/", FieldTrialList::ACTIVATE_TRIALS, + std::set<std::string>())); FieldTrial::ActiveGroups active_groups; FieldTrialList::GetActiveFieldTrialGroups(&active_groups); @@ -446,7 +454,8 @@ TEST_F(FieldTrialTest, CreateTrialsFromStringNotActive) { ASSERT_FALSE(FieldTrialList::TrialExists("Abc")); ASSERT_FALSE(FieldTrialList::TrialExists("Xyz")); ASSERT_TRUE(FieldTrialList::CreateTrialsFromString( - "Abc/def/Xyz/zyx/", FieldTrialList::DONT_ACTIVATE_TRIALS)); + "Abc/def/Xyz/zyx/", FieldTrialList::DONT_ACTIVATE_TRIALS, + std::set<std::string>())); FieldTrial::ActiveGroups active_groups; FieldTrialList::GetActiveFieldTrialGroups(&active_groups); @@ -469,7 +478,7 @@ TEST_F(FieldTrialTest, CreateTrialsFromStringActiveObserver) { TestFieldTrialObserver observer; ASSERT_TRUE(FieldTrialList::CreateTrialsFromString( - "Abc/def/", FieldTrialList::ACTIVATE_TRIALS)); + "Abc/def/", FieldTrialList::ACTIVATE_TRIALS, std::set<std::string>())); RunLoop().RunUntilIdle(); EXPECT_EQ("Abc", observer.trial_name()); @@ -481,7 +490,8 @@ TEST_F(FieldTrialTest, CreateTrialsFromStringNotActiveObserver) { TestFieldTrialObserver observer; ASSERT_TRUE(FieldTrialList::CreateTrialsFromString( - "Abc/def/", FieldTrialList::DONT_ACTIVATE_TRIALS)); + "Abc/def/", FieldTrialList::DONT_ACTIVATE_TRIALS, + std::set<std::string>())); RunLoop().RunUntilIdle(); // Observer shouldn't be notified. EXPECT_TRUE(observer.trial_name().empty()); @@ -494,6 +504,48 @@ TEST_F(FieldTrialTest, CreateTrialsFromStringNotActiveObserver) { EXPECT_EQ("def", observer.group_name()); } +TEST_F(FieldTrialTest, CreateTrialsFromStringWithIgnoredFieldTrials) { + ASSERT_FALSE(FieldTrialList::TrialExists("Unaccepted1")); + ASSERT_FALSE(FieldTrialList::TrialExists("Foo")); + ASSERT_FALSE(FieldTrialList::TrialExists("Unaccepted2")); + ASSERT_FALSE(FieldTrialList::TrialExists("Bar")); + ASSERT_FALSE(FieldTrialList::TrialExists("Unaccepted3")); + + std::set<std::string> ignored_trial_names; + ignored_trial_names.insert("Unaccepted1"); + ignored_trial_names.insert("Unaccepted2"); + ignored_trial_names.insert("Unaccepted3"); + + FieldTrialList::CreateTrialsFromString( + "Unaccepted1/Unaccepted1_name/" + "Foo/Foo_name/" + "Unaccepted2/Unaccepted2_name/" + "Bar/Bar_name/" + "Unaccepted3/Unaccepted3_name/", + FieldTrialList::DONT_ACTIVATE_TRIALS, + ignored_trial_names); + + EXPECT_FALSE(FieldTrialList::TrialExists("Unaccepted1")); + EXPECT_TRUE(FieldTrialList::TrialExists("Foo")); + EXPECT_FALSE(FieldTrialList::TrialExists("Unaccepted2")); + EXPECT_TRUE(FieldTrialList::TrialExists("Bar")); + EXPECT_FALSE(FieldTrialList::TrialExists("Unaccepted3")); + + FieldTrial::ActiveGroups active_groups; + FieldTrialList::GetActiveFieldTrialGroups(&active_groups); + EXPECT_TRUE(active_groups.empty()); + + FieldTrial* trial = FieldTrialList::Find("Foo"); + ASSERT_NE(static_cast<FieldTrial*>(NULL), trial); + EXPECT_EQ("Foo", trial->trial_name()); + EXPECT_EQ("Foo_name", trial->group_name()); + + trial = FieldTrialList::Find("Bar"); + ASSERT_NE(static_cast<FieldTrial*>(NULL), trial); + EXPECT_EQ("Bar", trial->trial_name()); + EXPECT_EQ("Bar_name", trial->group_name()); +} + TEST_F(FieldTrialTest, CreateFieldTrial) { ASSERT_FALSE(FieldTrialList::TrialExists("Some_name")); diff --git a/chromium/base/metrics/histogram.cc b/chromium/base/metrics/histogram.cc index fbe66d05d29..beb9b9e8898 100644 --- a/chromium/base/metrics/histogram.cc +++ b/chromium/base/metrics/histogram.cc @@ -110,7 +110,16 @@ HistogramBase* Histogram::FactoryGet(const string& name, } DCHECK_EQ(HISTOGRAM, histogram->GetHistogramType()); - CHECK(histogram->HasConstructionArguments(minimum, maximum, bucket_count)); + if (!histogram->HasConstructionArguments(minimum, maximum, bucket_count)) { + // The construction arguments do not match the existing histogram. This can + // come about if an extension updates in the middle of a chrome run and has + // changed one of them, or simply by bad code within Chrome itself. We + // return NULL here with the expectation that bad code in Chrome will crash + // on dereference, but extension/Pepper APIs will guard against NULL and not + // crash. + DLOG(ERROR) << "Histogram " << name << " has bad construction arguments"; + return NULL; + } return histogram; } @@ -567,7 +576,16 @@ HistogramBase* LinearHistogram::FactoryGetWithRangeDescription( } DCHECK_EQ(LINEAR_HISTOGRAM, histogram->GetHistogramType()); - CHECK(histogram->HasConstructionArguments(minimum, maximum, bucket_count)); + if (!histogram->HasConstructionArguments(minimum, maximum, bucket_count)) { + // The construction arguments do not match the existing histogram. This can + // come about if an extension updates in the middle of a chrome run and has + // changed one of them, or simply by bad code within Chrome itself. We + // return NULL here with the expectation that bad code in Chrome will crash + // on dereference, but extension/Pepper APIs will guard against NULL and not + // crash. + DLOG(ERROR) << "Histogram " << name << " has bad construction arguments"; + return NULL; + } return histogram; } diff --git a/chromium/base/metrics/histogram.h b/chromium/base/metrics/histogram.h index 9845362d98a..b795a12dbd1 100644 --- a/chromium/base/metrics/histogram.h +++ b/chromium/base/metrics/histogram.h @@ -155,8 +155,8 @@ class Lock; base::subtle::Release_Store(&atomic_histogram_pointer, \ reinterpret_cast<base::subtle::AtomicWord>(histogram_pointer)); \ } \ - DCHECK_EQ(histogram_pointer->histogram_name(), \ - std::string(constant_histogram_name)); \ + if (DCHECK_IS_ON) \ + histogram_pointer->CheckName(constant_histogram_name); \ histogram_pointer->histogram_add_method_invocation; \ } while (0) @@ -190,6 +190,12 @@ class Lock; 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 HISTOGRAM_PERCENTAGE(name, under_one_hundred) \ HISTOGRAM_ENUMERATION(name, under_one_hundred, 101) @@ -349,9 +355,15 @@ class Lock; // 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) \ - STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \ - base::LinearHistogram::FactoryGet(name, 1, boundary_value, \ - boundary_value + 1, base::HistogramBase::kUmaTargetedHistogramFlag)) + 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), \ diff --git a/chromium/base/metrics/histogram_base.cc b/chromium/base/metrics/histogram_base.cc index 5c6e2d271bb..6e7e69e061c 100644 --- a/chromium/base/metrics/histogram_base.cc +++ b/chromium/base/metrics/histogram_base.cc @@ -20,7 +20,7 @@ namespace base { std::string HistogramTypeToString(HistogramType type) { - switch(type) { + switch (type) { case HISTOGRAM: return "HISTOGRAM"; case LINEAR_HISTOGRAM: @@ -66,6 +66,10 @@ HistogramBase::HistogramBase(const std::string& name) HistogramBase::~HistogramBase() {} +void HistogramBase::CheckName(const StringPiece& name) const { + DCHECK_EQ(histogram_name(), name); +} + void HistogramBase::SetFlags(int32 flags) { flags_ |= flags; } diff --git a/chromium/base/metrics/histogram_base.h b/chromium/base/metrics/histogram_base.h index 4248bd84a4c..be07fc6d419 100644 --- a/chromium/base/metrics/histogram_base.h +++ b/chromium/base/metrics/histogram_base.h @@ -12,6 +12,7 @@ #include "base/base_export.h" #include "base/basictypes.h" #include "base/memory/scoped_ptr.h" +#include "base/strings/string_piece.h" #include "base/time/time.h" class Pickle; @@ -48,20 +49,28 @@ BASE_EXPORT_PRIVATE HistogramBase* DeserializeHistogramInfo( class BASE_EXPORT HistogramBase { public: - typedef int Sample; // Used for samples. - typedef subtle::Atomic32 Count; // Used to count samples. + typedef int Sample; // Used for samples. + typedef subtle::Atomic32 AtomicCount; // Used to count samples. + typedef int32 Count; // Used to manipulate counts in temporaries. static const Sample kSampleType_MAX; // INT_MAX enum Flags { kNoFlags = 0, - kUmaTargetedHistogramFlag = 0x1, // Histogram should be UMA uploaded. - // Indicate that the histogram was pickled to be sent across an IPC Channel. - // If we observe this flag on a histogram being aggregated into after IPC, - // then we are running in a single process mode, and the aggregation should - // not take place (as we would be aggregating back into the source - // histogram!). + // Histogram should be UMA uploaded. + kUmaTargetedHistogramFlag = 0x1, + + // Indicates that this is a stability histogram. This flag exists to specify + // which histograms should be included in the initial stability log. Please + // refer to |MetricsService::PrepareInitialStabilityLog|. + kUmaStabilityHistogramFlag = kUmaTargetedHistogramFlag | 0x2, + + // Indicates that the histogram was pickled to be sent across an IPC + // Channel. If we observe this flag on a histogram being aggregated into + // after IPC, then we are running in a single process mode, and the + // aggregation should not take place (as we would be aggregating back into + // the source histogram!). kIPCSerializationSourceFlag = 0x10, // Only for Histogram and its sub classes: fancy bucket-naming support. @@ -84,6 +93,11 @@ class BASE_EXPORT HistogramBase { std::string histogram_name() const { return histogram_name_; } + // Comapres |name| to the histogram name and triggers a DCHECK if they do not + // match. This is a helper function used by histogram macros, which results in + // in more compact machine code being generated by the macros. + void CheckName(const StringPiece& name) const; + // Operations with Flags enum. int32 flags() const { return flags_; } void SetFlags(int32 flags); @@ -129,7 +143,7 @@ class BASE_EXPORT HistogramBase { // customize the output. void WriteJSON(std::string* output) const; -protected: + protected: // Subclasses should implement this function to make SerializeInfo work. virtual bool SerializeInfoImpl(Pickle* pickle) const = 0; diff --git a/chromium/base/metrics/histogram_delta_serialization.cc b/chromium/base/metrics/histogram_delta_serialization.cc index 924916db76e..e4aad13ac22 100644 --- a/chromium/base/metrics/histogram_delta_serialization.cc +++ b/chromium/base/metrics/histogram_delta_serialization.cc @@ -7,8 +7,8 @@ #include "base/logging.h" #include "base/metrics/histogram_base.h" #include "base/metrics/histogram_snapshot_manager.h" +#include "base/numerics/safe_conversions.h" #include "base/pickle.h" -#include "base/safe_numerics.h" #include "base/values.h" namespace base { @@ -66,7 +66,7 @@ void HistogramDeltaSerialization::PrepareAndSerializeDeltas( // the histograms, so that the receiving process can distinguish them from the // local histograms. histogram_snapshot_manager_.PrepareDeltas( - Histogram::kIPCSerializationSourceFlag, false); + Histogram::kIPCSerializationSourceFlag, Histogram::kNoFlags); serialized_deltas_ = NULL; } @@ -75,7 +75,7 @@ void HistogramDeltaSerialization::DeserializeAndAddSamples( const std::vector<std::string>& serialized_deltas) { for (std::vector<std::string>::const_iterator it = serialized_deltas.begin(); it != serialized_deltas.end(); ++it) { - Pickle pickle(it->data(), checked_numeric_cast<int>(it->size())); + Pickle pickle(it->data(), checked_cast<int>(it->size())); PickleIterator iter(pickle); DeserializeHistogramAndAddSamples(&iter); } diff --git a/chromium/base/metrics/histogram_samples.cc b/chromium/base/metrics/histogram_samples.cc index 9f3dd6a40e0..2319ed1572f 100644 --- a/chromium/base/metrics/histogram_samples.cc +++ b/chromium/base/metrics/histogram_samples.cc @@ -64,7 +64,10 @@ HistogramSamples::~HistogramSamples() {} void HistogramSamples::Add(const HistogramSamples& other) { sum_ += other.sum(); - redundant_count_ += other.redundant_count(); + HistogramBase::Count old_redundant_count = + subtle::NoBarrier_Load(&redundant_count_); + subtle::NoBarrier_Store(&redundant_count_, + old_redundant_count + other.redundant_count()); bool success = AddSubtractImpl(other.Iterator().get(), ADD); DCHECK(success); } @@ -76,7 +79,10 @@ bool HistogramSamples::AddFromPickle(PickleIterator* iter) { if (!iter->ReadInt64(&sum) || !iter->ReadInt(&redundant_count)) return false; sum_ += sum; - redundant_count_ += redundant_count; + HistogramBase::Count old_redundant_count = + subtle::NoBarrier_Load(&redundant_count_); + subtle::NoBarrier_Store(&redundant_count_, + old_redundant_count + redundant_count); SampleCountPickleIterator pickle_iter(iter); return AddSubtractImpl(&pickle_iter, ADD); @@ -84,13 +90,17 @@ bool HistogramSamples::AddFromPickle(PickleIterator* iter) { void HistogramSamples::Subtract(const HistogramSamples& other) { sum_ -= other.sum(); - redundant_count_ -= other.redundant_count(); + HistogramBase::Count old_redundant_count = + subtle::NoBarrier_Load(&redundant_count_); + subtle::NoBarrier_Store(&redundant_count_, + old_redundant_count - other.redundant_count()); bool success = AddSubtractImpl(other.Iterator().get(), SUBTRACT); DCHECK(success); } bool HistogramSamples::Serialize(Pickle* pickle) const { - if (!pickle->WriteInt64(sum_) || !pickle->WriteInt(redundant_count_)) + if (!pickle->WriteInt64(sum_) || + !pickle->WriteInt(subtle::NoBarrier_Load(&redundant_count_))) return false; HistogramBase::Sample min; @@ -113,8 +123,8 @@ void HistogramSamples::IncreaseSum(int64 diff) { } void HistogramSamples::IncreaseRedundantCount(HistogramBase::Count diff) { - base::subtle::NoBarrier_Store(&redundant_count_, - base::subtle::NoBarrier_Load(&redundant_count_) + diff); + subtle::NoBarrier_Store(&redundant_count_, + subtle::NoBarrier_Load(&redundant_count_) + diff); } SampleCountIterator::~SampleCountIterator() {} diff --git a/chromium/base/metrics/histogram_samples.h b/chromium/base/metrics/histogram_samples.h index c55b58442c5..c4c0a9842b7 100644 --- a/chromium/base/metrics/histogram_samples.h +++ b/chromium/base/metrics/histogram_samples.h @@ -39,7 +39,9 @@ class BASE_EXPORT HistogramSamples { // Accessor fuctions. int64 sum() const { return sum_; } - HistogramBase::Count redundant_count() const { return redundant_count_; } + HistogramBase::Count redundant_count() const { + return subtle::NoBarrier_Load(&redundant_count_); + } protected: // Based on |op| type, add or subtract sample counts data from the iterator. @@ -59,7 +61,7 @@ class BASE_EXPORT HistogramSamples { // types, there might be races during histogram accumulation and snapshotting // that we choose to accept. In this case, the tallies might mismatch even // when no memory corruption has happened. - HistogramBase::Count redundant_count_; + HistogramBase::AtomicCount redundant_count_; }; class BASE_EXPORT SampleCountIterator { diff --git a/chromium/base/metrics/histogram_snapshot_manager.cc b/chromium/base/metrics/histogram_snapshot_manager.cc index cb594bd96d9..b1e26da59dc 100644 --- a/chromium/base/metrics/histogram_snapshot_manager.cc +++ b/chromium/base/metrics/histogram_snapshot_manager.cc @@ -25,18 +25,17 @@ HistogramSnapshotManager::~HistogramSnapshotManager() { STLDeleteValues(&logged_samples_); } -void HistogramSnapshotManager::PrepareDeltas(HistogramBase::Flags flag_to_set, - bool record_only_uma) { +void HistogramSnapshotManager::PrepareDeltas( + HistogramBase::Flags flag_to_set, + HistogramBase::Flags required_flags) { StatisticsRecorder::Histograms histograms; StatisticsRecorder::GetHistograms(&histograms); for (StatisticsRecorder::Histograms::const_iterator it = histograms.begin(); histograms.end() != it; ++it) { (*it)->SetFlags(flag_to_set); - if (record_only_uma && - 0 == ((*it)->flags() & Histogram::kUmaTargetedHistogramFlag)) - continue; - PrepareDelta(**it); + if (((*it)->flags() & required_flags) == required_flags) + PrepareDelta(**it); } } diff --git a/chromium/base/metrics/histogram_snapshot_manager.h b/chromium/base/metrics/histogram_snapshot_manager.h index 3c7050859e3..5a5f2e93e5e 100644 --- a/chromium/base/metrics/histogram_snapshot_manager.h +++ b/chromium/base/metrics/histogram_snapshot_manager.h @@ -29,9 +29,13 @@ class BASE_EXPORT HistogramSnapshotManager { virtual ~HistogramSnapshotManager(); // Snapshot all histograms, and ask |histogram_flattener_| to record the - // delta. The arguments allow selecting only a subset of histograms for - // recording, or to set a flag in each recorded histogram. - void PrepareDeltas(HistogramBase::Flags flags_to_set, bool record_only_uma); + // delta. |flags_to_set| is used to set flags for each histogram. + // |required_flags| is used to select histograms to be recorded. + // Only histograms that have all the flags specified by the argument will be + // chosen. If all histograms should be recorded, set it to + // |Histogram::kNoFlags|. + void PrepareDeltas(HistogramBase::Flags flags_to_set, + HistogramBase::Flags required_flags); private: // Snapshot this histogram, and record the delta. diff --git a/chromium/base/metrics/histogram_snapshot_manager_unittest.cc b/chromium/base/metrics/histogram_snapshot_manager_unittest.cc new file mode 100644 index 00000000000..e6672ea7eb2 --- /dev/null +++ b/chromium/base/metrics/histogram_snapshot_manager_unittest.cc @@ -0,0 +1,106 @@ +// 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/metrics/histogram_snapshot_manager.h" + +#include <string> +#include <vector> + +#include "base/metrics/histogram.h" +#include "base/metrics/histogram_delta_serialization.h" +#include "base/metrics/statistics_recorder.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { + +class HistogramFlattenerDeltaRecorder : public HistogramFlattener { + public: + HistogramFlattenerDeltaRecorder() {} + + virtual void RecordDelta(const HistogramBase& histogram, + const HistogramSamples& snapshot) OVERRIDE { + recorded_delta_histogram_names_.push_back(histogram.histogram_name()); + } + + virtual void InconsistencyDetected( + HistogramBase::Inconsistency problem) OVERRIDE { + ASSERT_TRUE(false); + } + + virtual void UniqueInconsistencyDetected( + HistogramBase::Inconsistency problem) OVERRIDE { + ASSERT_TRUE(false); + } + + virtual void InconsistencyDetectedInLoggedCount(int amount) OVERRIDE { + ASSERT_TRUE(false); + } + + std::vector<std::string> GetRecordedDeltaHistogramNames() { + return recorded_delta_histogram_names_; + } + + private: + std::vector<std::string> recorded_delta_histogram_names_; + + DISALLOW_COPY_AND_ASSIGN(HistogramFlattenerDeltaRecorder); +}; + +class HistogramSnapshotManagerTest : public testing::Test { + protected: + HistogramSnapshotManagerTest() + : histogram_snapshot_manager_(&histogram_flattener_delta_recorder_) {} + + virtual ~HistogramSnapshotManagerTest() {} + + StatisticsRecorder statistics_recorder_; + HistogramFlattenerDeltaRecorder histogram_flattener_delta_recorder_; + HistogramSnapshotManager histogram_snapshot_manager_; +}; + +TEST_F(HistogramSnapshotManagerTest, PrepareDeltasNoFlagsFilter) { + // kNoFlags filter should record all histograms. + UMA_HISTOGRAM_ENUMERATION("UmaHistogram", 1, 2); + UMA_STABILITY_HISTOGRAM_ENUMERATION("UmaStabilityHistogram", 1, 2); + + histogram_snapshot_manager_.PrepareDeltas(HistogramBase::kNoFlags, + HistogramBase::kNoFlags); + + const std::vector<std::string>& histograms = + histogram_flattener_delta_recorder_.GetRecordedDeltaHistogramNames(); + EXPECT_EQ(2U, histograms.size()); + EXPECT_EQ("UmaHistogram", histograms[0]); + EXPECT_EQ("UmaStabilityHistogram", histograms[1]); +} + +TEST_F(HistogramSnapshotManagerTest, PrepareDeltasUmaHistogramFlagFilter) { + // Note that kUmaStabilityHistogramFlag includes kUmaTargetedHistogramFlag. + UMA_HISTOGRAM_ENUMERATION("UmaHistogram", 1, 2); + UMA_STABILITY_HISTOGRAM_ENUMERATION("UmaStabilityHistogram", 1, 2); + + histogram_snapshot_manager_.PrepareDeltas( + HistogramBase::kNoFlags, HistogramBase::kUmaTargetedHistogramFlag); + + const std::vector<std::string>& histograms = + histogram_flattener_delta_recorder_.GetRecordedDeltaHistogramNames(); + EXPECT_EQ(2U, histograms.size()); + EXPECT_EQ("UmaHistogram", histograms[0]); + EXPECT_EQ("UmaStabilityHistogram", histograms[1]); +} + +TEST_F(HistogramSnapshotManagerTest, + PrepareDeltasUmaStabilityHistogramFlagFilter) { + UMA_HISTOGRAM_ENUMERATION("UmaHistogram", 1, 2); + UMA_STABILITY_HISTOGRAM_ENUMERATION("UmaStabilityHistogram", 1, 2); + + histogram_snapshot_manager_.PrepareDeltas( + HistogramBase::kNoFlags, HistogramBase::kUmaStabilityHistogramFlag); + + const std::vector<std::string>& histograms = + histogram_flattener_delta_recorder_.GetRecordedDeltaHistogramNames(); + EXPECT_EQ(1U, histograms.size()); + EXPECT_EQ("UmaStabilityHistogram", histograms[0]); +} + +} // namespace base diff --git a/chromium/base/metrics/histogram_unittest.cc b/chromium/base/metrics/histogram_unittest.cc index e7a01aa5af3..5fbd28a1069 100644 --- a/chromium/base/metrics/histogram_unittest.cc +++ b/chromium/base/metrics/histogram_unittest.cc @@ -443,6 +443,32 @@ TEST_F(HistogramTest, CustomHistogramSerializeInfo) { EXPECT_FALSE(iter.SkipBytes(1)); } +TEST_F(HistogramTest, BadConstruction) { + HistogramBase* histogram = Histogram::FactoryGet( + "BadConstruction", 0, 100, 8, HistogramBase::kNoFlags); + EXPECT_TRUE(histogram->HasConstructionArguments(1, 100, 8)); + + // Try to get the same histogram name with different arguments. + HistogramBase* bad_histogram = Histogram::FactoryGet( + "BadConstruction", 0, 100, 7, HistogramBase::kNoFlags); + EXPECT_EQ(NULL, bad_histogram); + bad_histogram = Histogram::FactoryGet( + "BadConstruction", 0, 99, 8, HistogramBase::kNoFlags); + EXPECT_EQ(NULL, bad_histogram); + + HistogramBase* linear_histogram = LinearHistogram::FactoryGet( + "BadConstructionLinear", 0, 100, 8, HistogramBase::kNoFlags); + EXPECT_TRUE(linear_histogram->HasConstructionArguments(1, 100, 8)); + + // Try to get the same histogram name with different arguments. + bad_histogram = LinearHistogram::FactoryGet( + "BadConstructionLinear", 0, 100, 7, HistogramBase::kNoFlags); + EXPECT_EQ(NULL, bad_histogram); + bad_histogram = LinearHistogram::FactoryGet( + "BadConstructionLinear", 10, 100, 8, HistogramBase::kNoFlags); + EXPECT_EQ(NULL, bad_histogram); +} + #if GTEST_HAS_DEATH_TEST // For Histogram, LinearHistogram and CustomHistogram, the minimum for a // declared range is 1, while the maximum is (HistogramBase::kSampleType_MAX - diff --git a/chromium/base/metrics/sample_vector.cc b/chromium/base/metrics/sample_vector.cc index 89233c74839..ca3205e9759 100644 --- a/chromium/base/metrics/sample_vector.cc +++ b/chromium/base/metrics/sample_vector.cc @@ -32,20 +32,20 @@ void SampleVector::Accumulate(Sample value, Count count) { Count SampleVector::GetCount(Sample value) const { size_t bucket_index = GetBucketIndex(value); - return counts_[bucket_index]; + return subtle::NoBarrier_Load(&counts_[bucket_index]); } Count SampleVector::TotalCount() const { Count count = 0; for (size_t i = 0; i < counts_.size(); i++) { - count += counts_[i]; + count += subtle::NoBarrier_Load(&counts_[i]); } return count; } Count SampleVector::GetCountAtIndex(size_t bucket_index) const { DCHECK(bucket_index < counts_.size()); - return counts_[bucket_index]; + return subtle::NoBarrier_Load(&counts_[bucket_index]); } scoped_ptr<SampleCountIterator> SampleVector::Iterator() const { @@ -66,7 +66,10 @@ bool SampleVector::AddSubtractImpl(SampleCountIterator* iter, if (min == bucket_ranges_->range(index) && max == bucket_ranges_->range(index + 1)) { // Sample matches this bucket! - counts_[index] += (op == HistogramSamples::ADD) ? count : -count; + HistogramBase::Count old_counts = + subtle::NoBarrier_Load(&counts_[index]); + subtle::NoBarrier_Store(&counts_[index], + old_counts + ((op == HistogramSamples::ADD) ? count : -count)); iter->Next(); } else if (min > bucket_ranges_->range(index)) { // Sample is larger than current bucket range. Try next. @@ -138,7 +141,7 @@ void SampleVectorIterator::Get(HistogramBase::Sample* min, if (max != NULL) *max = bucket_ranges_->range(index_ + 1); if (count != NULL) - *count = (*counts_)[index_]; + *count = subtle::NoBarrier_Load(&(*counts_)[index_]); } bool SampleVectorIterator::GetBucketIndex(size_t* index) const { @@ -153,7 +156,7 @@ void SampleVectorIterator::SkipEmptyBuckets() { return; while (index_ < counts_->size()) { - if ((*counts_)[index_] != 0) + if (subtle::NoBarrier_Load(&(*counts_)[index_]) != 0) return; index_++; } diff --git a/chromium/base/metrics/sample_vector.h b/chromium/base/metrics/sample_vector.h index 67c344a97c3..6b2adcf2094 100644 --- a/chromium/base/metrics/sample_vector.h +++ b/chromium/base/metrics/sample_vector.h @@ -46,7 +46,7 @@ class BASE_EXPORT_PRIVATE SampleVector : public HistogramSamples { private: FRIEND_TEST_ALL_PREFIXES(HistogramTest, CorruptSampleCounts); - std::vector<HistogramBase::Count> counts_; + std::vector<HistogramBase::AtomicCount> counts_; // Shares the same BucketRanges with Histogram object. const BucketRanges* const bucket_ranges_; @@ -56,7 +56,7 @@ class BASE_EXPORT_PRIVATE SampleVector : public HistogramSamples { class BASE_EXPORT_PRIVATE SampleVectorIterator : public SampleCountIterator { public: - SampleVectorIterator(const std::vector<HistogramBase::Count>* counts, + SampleVectorIterator(const std::vector<HistogramBase::AtomicCount>* counts, const BucketRanges* bucket_ranges); virtual ~SampleVectorIterator(); @@ -73,7 +73,7 @@ class BASE_EXPORT_PRIVATE SampleVectorIterator : public SampleCountIterator { private: void SkipEmptyBuckets(); - const std::vector<HistogramBase::Count>* counts_; + const std::vector<HistogramBase::AtomicCount>* counts_; const BucketRanges* bucket_ranges_; size_t index_; diff --git a/chromium/base/metrics/statistics_recorder.h b/chromium/base/metrics/statistics_recorder.h index 0716e80572d..3bef622b76f 100644 --- a/chromium/base/metrics/statistics_recorder.h +++ b/chromium/base/metrics/statistics_recorder.h @@ -30,7 +30,7 @@ class BASE_EXPORT StatisticsRecorder { public: typedef std::vector<HistogramBase*> Histograms; - // Initializes the StatisticsRecorder system. + // Initializes the StatisticsRecorder system. Safe to call multiple times. static void Initialize(); // Find out if histograms can now be registered into our list. @@ -70,9 +70,9 @@ class BASE_EXPORT StatisticsRecorder { static HistogramBase* FindHistogram(const std::string& name); // GetSnapshot copies some of the pointers to registered histograms into the - // caller supplied vector (Histograms). Only histograms with names matching - // query are returned. The query must be a substring of histogram name for its - // pointer to be copied. + // caller supplied vector (Histograms). Only histograms which have |query| as + // a substring are copied (an empty string will process all registered + // histograms). static void GetSnapshot(const std::string& query, Histograms* snapshot); private: @@ -86,6 +86,7 @@ class BASE_EXPORT StatisticsRecorder { friend struct DefaultLazyInstanceTraits<StatisticsRecorder>; friend class HistogramBaseTest; + friend class HistogramSnapshotManagerTest; friend class HistogramTest; friend class SparseHistogramTest; friend class StatisticsRecorderTest; diff --git a/chromium/base/metrics/stats_table.cc b/chromium/base/metrics/stats_table.cc index 716b7cfc91d..03158fdbd0a 100644 --- a/chromium/base/metrics/stats_table.cc +++ b/chromium/base/metrics/stats_table.cc @@ -14,12 +14,6 @@ #include "base/threading/platform_thread.h" #include "base/threading/thread_local_storage.h" -#if defined(OS_POSIX) -#include "base/posix/global_descriptors.h" -#include "errno.h" -#include "ipc/ipc_descriptors.h" -#endif - namespace base { // The StatsTable uses a shared memory segment that is laid out as follows @@ -105,7 +99,7 @@ class StatsTable::Internal { // Construct a new Internal based on expected size parameters, or // return NULL on failure. - static Internal* New(const std::string& name, + static Internal* New(const StatsTable::TableIdentifier& table, int size, int max_threads, int max_counters); @@ -151,8 +145,9 @@ class StatsTable::Internal { } // Create or open the SharedMemory used by the stats table. - static SharedMemory* CreateSharedMemory(const std::string& name, - int size); + static SharedMemory* CreateSharedMemory( + const StatsTable::TableIdentifier& table, + int size); // Initializes the table on first access. Sets header values // appropriately and zeroes all counters. @@ -174,11 +169,12 @@ class StatsTable::Internal { }; // static -StatsTable::Internal* StatsTable::Internal::New(const std::string& name, - int size, - int max_threads, - int max_counters) { - scoped_ptr<SharedMemory> shared_memory(CreateSharedMemory(name, size)); +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)) @@ -200,16 +196,14 @@ StatsTable::Internal* StatsTable::Internal::New(const std::string& name, } // static -SharedMemory* StatsTable::Internal::CreateSharedMemory(const std::string& name, - int size) { +SharedMemory* StatsTable::Internal::CreateSharedMemory( + const StatsTable::TableIdentifier& table, + int size) { #if defined(OS_POSIX) - GlobalDescriptors* global_descriptors = GlobalDescriptors::GetInstance(); - if (global_descriptors->MaybeGet(kStatsTableSharedMemFd) != -1) { - // Open the shared memory file descriptor passed by the browser process. - FileDescriptor file_descriptor( - global_descriptors->Get(kStatsTableSharedMemFd), false); - return new SharedMemory(file_descriptor, false); - } + // 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)) @@ -217,15 +211,22 @@ SharedMemory* StatsTable::Internal::CreateSharedMemory(const std::string& name, return shared_memory.release(); #elif defined(OS_WIN) scoped_ptr<SharedMemory> shared_memory(new SharedMemory()); - if (!shared_memory->CreateNamed(name, true, size)) - return NULL; + 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) { + int max_counters, + int max_threads) { // Zero everything. memset(memory, 0, size); @@ -286,7 +287,8 @@ struct StatsTable::TLSData { // We keep a singleton table which can be easily accessed. StatsTable* global_table = NULL; -StatsTable::StatsTable(const std::string& name, int max_threads, +StatsTable::StatsTable(const TableIdentifier& table, + int max_threads, int max_counters) : internal_(NULL), tls_index_(SlotReturnFunction) { @@ -298,7 +300,7 @@ StatsTable::StatsTable(const std::string& name, int max_threads, AlignedSize(max_threads * sizeof(int)) + AlignedSize((sizeof(int) * (max_counters * max_threads))); - internal_ = Internal::New(name, table_size, max_threads, max_counters); + internal_ = Internal::New(table, table_size, max_threads, max_counters); if (!internal_) DPLOG(ERROR) << "StatsTable did not initialize"; @@ -344,8 +346,9 @@ int StatsTable::RegisterThread(const std::string& name) { // 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. { - SharedMemoryAutoLock lock(internal_->shared_memory()); + SharedMemoryAutoLockDeprecated lock(internal_->shared_memory()); slot = FindEmptyThread(); if (!slot) { return 0; @@ -568,7 +571,7 @@ int StatsTable::AddCounter(const std::string& name) { { // To add a counter to the shared memory, we need the // shared memory lock. - SharedMemoryAutoLock lock(internal_->shared_memory()); + SharedMemoryAutoLockDeprecated lock(internal_->shared_memory()); // We have space, so create a new counter. counter_id = FindCounterOrEmptyRow(name); diff --git a/chromium/base/metrics/stats_table.h b/chromium/base/metrics/stats_table.h index 49ba79f88ba..719e6304813 100644 --- a/chromium/base/metrics/stats_table.h +++ b/chromium/base/metrics/stats_table.h @@ -28,24 +28,45 @@ #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: - // Create a new StatsTable. - // If a StatsTable already exists with the specified name, this StatsTable - // will use the same shared memory segment as the original. Otherwise, - // a new StatsTable is created and all counters are zeroed. + // 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. // - // name is the name of the StatsTable to use. + // 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 std::string& name, int max_threads, int max_counters); + 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. diff --git a/chromium/base/metrics/stats_table_unittest.cc b/chromium/base/metrics/stats_table_unittest.cc index 8fd33971f4f..53a47315e6f 100644 --- a/chromium/base/metrics/stats_table_unittest.cc +++ b/chromium/base/metrics/stats_table_unittest.cc @@ -18,21 +18,14 @@ namespace base { class StatsTableTest : public MultiProcessTest { - public: - void DeleteShmem(const std::string& name) { - SharedMemory mem; - mem.Delete(name); - } }; // Open a StatsTable and verify that we can write to each of the // locations in the table. TEST_F(StatsTableTest, VerifySlots) { - const std::string kTableName = "VerifySlotsStatTable"; const int kMaxThreads = 1; const int kMaxCounter = 5; - DeleteShmem(kTableName); - StatsTable table(kTableName, kMaxThreads, kMaxCounter); + StatsTable table(StatsTable::TableIdentifier(), kMaxThreads, kMaxCounter); // Register a single thread. std::string thread_name = "mainThread"; @@ -55,8 +48,6 @@ TEST_F(StatsTableTest, VerifySlots) { // Try to allocate an additional counter. Verify it fails. int counter_id = table.FindCounter(counter_base_name); EXPECT_EQ(counter_id, 0); - - DeleteShmem(kTableName); } // CounterZero will continually be set to 0. @@ -117,11 +108,9 @@ void StatsTableThread::Run() { #endif TEST_F(StatsTableTest, MAYBE_MultipleThreads) { // Create a stats table. - const std::string kTableName = "MultipleThreadStatTable"; const int kMaxThreads = 20; const int kMaxCounter = 5; - DeleteShmem(kTableName); - StatsTable table(kTableName, kMaxThreads, kMaxCounter); + StatsTable table(StatsTable::TableIdentifier(), kMaxThreads, kMaxCounter); StatsTable::set_current(&table); EXPECT_EQ(0, table.CountThreadsRegistered()); @@ -166,10 +155,11 @@ TEST_F(StatsTableTest, MAYBE_MultipleThreads) { EXPECT_EQ((kMaxThreads % 2) * kThreadLoops, table.GetCounterValue(name)); EXPECT_EQ(0, table.CountThreadsRegistered()); - - DeleteShmem(kTableName); } +// 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) { @@ -199,7 +189,6 @@ TEST_F(StatsTableTest, DISABLED_MultipleProcesses) { // Create a stats table. const int kMaxProcs = 20; const int kMaxCounter = 5; - DeleteShmem(kMPTableName); StatsTable table(kMPTableName, kMaxProcs, kMaxCounter); StatsTable::set_current(&table); EXPECT_EQ(0, table.CountThreadsRegistered()); @@ -211,7 +200,7 @@ TEST_F(StatsTableTest, DISABLED_MultipleProcesses) { // Spawn the processes. for (int16 index = 0; index < kMaxProcs; index++) { - procs[index] = this->SpawnChild("StatsTableMultipleProcessMain", false); + procs[index] = SpawnChild("StatsTableMultipleProcessMain"); EXPECT_NE(kNullProcessHandle, procs[index]); } @@ -241,9 +230,8 @@ TEST_F(StatsTableTest, DISABLED_MultipleProcesses) { EXPECT_EQ(-kMaxProcs * kThreadLoops, table.GetCounterValue(name)); EXPECT_EQ(0, table.CountThreadsRegistered()); - - DeleteShmem(kMPTableName); } +#endif class MockStatsCounter : public StatsCounter { public: @@ -255,11 +243,9 @@ class MockStatsCounter : public StatsCounter { // Test some basic StatsCounter operations TEST_F(StatsTableTest, StatsCounter) { // Create a stats table. - const std::string kTableName = "StatTable"; const int kMaxThreads = 20; const int kMaxCounter = 5; - DeleteShmem(kTableName); - StatsTable table(kTableName, kMaxThreads, kMaxCounter); + StatsTable table(StatsTable::TableIdentifier(), kMaxThreads, kMaxCounter); StatsTable::set_current(&table); MockStatsCounter foo("foo"); @@ -295,8 +281,6 @@ TEST_F(StatsTableTest, StatsCounter) { EXPECT_EQ(-1, table.GetCounterValue("c:foo")); foo.Subtract(-1); EXPECT_EQ(0, table.GetCounterValue("c:foo")); - - DeleteShmem(kTableName); } class MockStatsCounterTimer : public StatsCounterTimer { @@ -311,11 +295,9 @@ class MockStatsCounterTimer : public StatsCounterTimer { // Test some basic StatsCounterTimer operations TEST_F(StatsTableTest, StatsCounterTimer) { // Create a stats table. - const std::string kTableName = "StatTable"; const int kMaxThreads = 20; const int kMaxCounter = 5; - DeleteShmem(kTableName); - StatsTable table(kTableName, kMaxThreads, kMaxCounter); + StatsTable table(StatsTable::TableIdentifier(), kMaxThreads, kMaxCounter); StatsTable::set_current(&table); MockStatsCounterTimer bar("bar"); @@ -340,17 +322,14 @@ TEST_F(StatsTableTest, StatsCounterTimer) { bar.Stop(); EXPECT_GT(table.GetCounterValue("t:bar"), 0); EXPECT_LE(kDuration.InMilliseconds() * 2, table.GetCounterValue("t:bar")); - DeleteShmem(kTableName); } // Test some basic StatsRate operations TEST_F(StatsTableTest, StatsRate) { // Create a stats table. - const std::string kTableName = "StatTable"; const int kMaxThreads = 20; const int kMaxCounter = 5; - DeleteShmem(kTableName); - StatsTable table(kTableName, kMaxThreads, kMaxCounter); + StatsTable table(StatsTable::TableIdentifier(), kMaxThreads, kMaxCounter); StatsTable::set_current(&table); StatsRate baz("baz"); @@ -375,17 +354,14 @@ TEST_F(StatsTableTest, StatsRate) { baz.Stop(); EXPECT_EQ(2, table.GetCounterValue("c:baz")); EXPECT_LE(kDuration.InMilliseconds() * 2, table.GetCounterValue("t:baz")); - DeleteShmem(kTableName); } // Test some basic StatsScope operations TEST_F(StatsTableTest, StatsScope) { // Create a stats table. - const std::string kTableName = "StatTable"; const int kMaxThreads = 20; const int kMaxCounter = 5; - DeleteShmem(kTableName); - StatsTable table(kTableName, kMaxThreads, kMaxCounter); + StatsTable table(StatsTable::TableIdentifier(), kMaxThreads, kMaxCounter); StatsTable::set_current(&table); StatsCounterTimer foo("foo"); @@ -417,8 +393,6 @@ TEST_F(StatsTableTest, StatsScope) { 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")); - - DeleteShmem(kTableName); } } // namespace base diff --git a/chromium/base/metrics/user_metrics.cc b/chromium/base/metrics/user_metrics.cc new file mode 100644 index 00000000000..9db5840ab57 --- /dev/null +++ b/chromium/base/metrics/user_metrics.cc @@ -0,0 +1,74 @@ +// 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/metrics/user_metrics.h" + +#include <vector> + +#include "base/lazy_instance.h" +#include "base/threading/thread_checker.h" + +namespace base { +namespace { + +// A helper class for tracking callbacks and ensuring thread-safety. +class Callbacks { + public: + Callbacks() {} + + // Records the |action|. + void Record(const std::string& action) { + DCHECK(thread_checker_.CalledOnValidThread()); + for (size_t i = 0; i < callbacks_.size(); ++i) { + callbacks_[i].Run(action); + } + } + + // Adds |callback| to the list of |callbacks_|. + void AddCallback(const ActionCallback& callback) { + DCHECK(thread_checker_.CalledOnValidThread()); + callbacks_.push_back(callback); + } + + // Removes the first instance of |callback| from the list of |callbacks_|, if + // there is one. + void RemoveCallback(const ActionCallback& callback) { + DCHECK(thread_checker_.CalledOnValidThread()); + for (size_t i = 0; i < callbacks_.size(); ++i) { + if (callbacks_[i].Equals(callback)) { + callbacks_.erase(callbacks_.begin() + i); + return; + } + } + } + + private: + base::ThreadChecker thread_checker_; + std::vector<ActionCallback> callbacks_; + + DISALLOW_COPY_AND_ASSIGN(Callbacks); +}; + +base::LazyInstance<Callbacks> g_callbacks = LAZY_INSTANCE_INITIALIZER; + +} // namespace + +void RecordAction(const UserMetricsAction& action) { + g_callbacks.Get().Record(action.str_); +} + +void RecordComputedAction(const std::string& action) { + g_callbacks.Get().Record(action); +} + +void AddActionCallback(const ActionCallback& callback) { + g_callbacks.Get().AddCallback(callback); +} + +void RemoveActionCallback(const ActionCallback& callback) { + g_callbacks.Get().RemoveCallback(callback); + +} + +} // namespace base diff --git a/chromium/base/metrics/user_metrics.h b/chromium/base/metrics/user_metrics.h new file mode 100644 index 00000000000..bcfefb80994 --- /dev/null +++ b/chromium/base/metrics/user_metrics.h @@ -0,0 +1,60 @@ +// 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_USER_METRICS_H_ +#define BASE_METRICS_USER_METRICS_H_ + +#include <string> + +#include "base/base_export.h" +#include "base/callback.h" +#include "base/metrics/user_metrics_action.h" + +namespace base { + +// This module provides some helper functions for logging actions tracked by +// the user metrics system. + +// Record that the user performed an action. +// This method *must* be called from the main thread. +// +// "Action" here means a user-generated event: +// good: "Reload", "CloseTab", and "IMEInvoked" +// not good: "SSLDialogShown", "PageLoaded", "DiskFull" +// We use this to gather anonymized information about how users are +// interacting with the browser. +// WARNING: In calls to this function, UserMetricsAction and a +// string literal parameter must be on the same line, e.g. +// RecordAction(UserMetricsAction("my extremely long action name")); +// This ensures that our processing scripts can associate this action's hash +// with its metric name. Therefore, it will be possible to retrieve the metric +// name from the hash later on. +// +// Once a new recorded action is added, run +// tools/metrics/actions/extract_actions.py +// to add the metric to actions.xml, then update the <owner>s and <description> +// sections. Make sure to include the actions.xml file when you upload your code +// for review! +// +// For more complicated situations (like when there are many different +// possible actions), see RecordComputedAction. +BASE_EXPORT void RecordAction(const UserMetricsAction& action); + +// This function has identical input and behavior to RecordAction, but is +// not automatically found by the action-processing scripts. It can be used +// when it's a pain to enumerate all possible actions, but if you use this +// you need to also update the rules for extracting known actions in +// tools/metrics/actions/extract_actions.py. +BASE_EXPORT void RecordComputedAction(const std::string& action); + +// Called with the action string. +typedef base::Callback<void(const std::string&)> ActionCallback; + +// Add/remove action callbacks (see above). +BASE_EXPORT void AddActionCallback(const ActionCallback& callback); +BASE_EXPORT void RemoveActionCallback(const ActionCallback& callback); + +} // namespace base + +#endif // BASE_METRICS_USER_METRICS_H_ diff --git a/chromium/base/metrics/user_metrics_action.h b/chromium/base/metrics/user_metrics_action.h new file mode 100644 index 00000000000..8c195b3e803 --- /dev/null +++ b/chromium/base/metrics/user_metrics_action.h @@ -0,0 +1,28 @@ +// 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_USER_METRICS_ACTION_H_ +#define BASE_METRICS_USER_METRICS_ACTION_H_ + +namespace base { + +// UserMetricsAction exists purely to standardize on the parameters passed to +// UserMetrics. That way, our toolset can scan the source code reliable for +// constructors and extract the associated string constants. +// WARNING: When using UserMetricsAction, UserMetricsAction and a string literal +// parameter must be on the same line, e.g. +// RecordAction(UserMetricsAction("my extremely long action name")); +// or +// RenderThread::Get()->RecordAction( +// UserMetricsAction("my extremely long action name")); +// because otherwise our processing scripts won't pick up on new actions. +// Please see tools/metrics/actions/extract_actions.py for details. +struct UserMetricsAction { + const char* str_; + explicit UserMetricsAction(const char* str) : str_(str) {} +}; + +} // namespace base + +#endif // BASE_METRICS_USER_METRICS_ACTION_H_ diff --git a/chromium/base/native_library.h b/chromium/base/native_library.h index 891f35bd540..1e764da89aa 100644 --- a/chromium/base/native_library.h +++ b/chromium/base/native_library.h @@ -8,7 +8,11 @@ // This file defines a cross-platform "NativeLibrary" type which represents // a loadable module. +#include <string> + #include "base/base_export.h" +#include "base/compiler_specific.h" +#include "base/strings/string16.h" #include "build/build_config.h" #if defined(OS_WIN) @@ -17,15 +21,6 @@ #import <CoreFoundation/CoreFoundation.h> #endif // OS_* -#include "base/strings/string16.h" - -// Macro useful for writing cross-platform function pointers. -#if defined(OS_WIN) && !defined(CDECL) -#define CDECL __cdecl -#else -#define CDECL -#endif - namespace base { class FilePath; @@ -56,12 +51,26 @@ typedef NativeLibraryStruct* NativeLibrary; typedef void* NativeLibrary; #endif // OS_* +struct BASE_EXPORT NativeLibraryLoadError { +#if defined(OS_WIN) + NativeLibraryLoadError() : code(0) {} +#endif // OS_WIN + + // Returns a string representation of the load error. + std::string ToString() const; + +#if defined(OS_WIN) + DWORD code; +#else + std::string message; +#endif // OS_WIN +}; + // Loads a native library from disk. Release it with UnloadNativeLibrary when // you're done. Returns NULL on failure. -// If |err| is not NULL, it may be filled in with an error message on -// error. +// If |error| is not NULL, it may be filled in on load error. BASE_EXPORT NativeLibrary LoadNativeLibrary(const FilePath& library_path, - std::string* error); + NativeLibraryLoadError* error); #if defined(OS_WIN) // Loads a native library from disk. Release it with UnloadNativeLibrary when diff --git a/chromium/base/native_library_mac.mm b/chromium/base/native_library_mac.mm index 6544fcaed99..6696454c6ef 100644 --- a/chromium/base/native_library_mac.mm +++ b/chromium/base/native_library_mac.mm @@ -41,9 +41,14 @@ static NativeLibraryObjCStatus GetObjCStatusForImage( return section == NULL ? OBJC_NOT_PRESENT : OBJC_PRESENT; } +std::string NativeLibraryLoadError::ToString() const { + return message; +} + // static +// TODO(xhwang): Fill |error|. See http://crbug.com/353771 NativeLibrary LoadNativeLibrary(const base::FilePath& library_path, - std::string* error) { + NativeLibraryLoadError* /* error */) { // dlopen() etc. open the file off disk. if (library_path.Extension() == "dylib" || !DirectoryExists(library_path)) { void* dylib = dlopen(library_path.value().c_str(), RTLD_LAZY); diff --git a/chromium/base/native_library_posix.cc b/chromium/base/native_library_posix.cc index dfa20fc01cf..3179a93833c 100644 --- a/chromium/base/native_library_posix.cc +++ b/chromium/base/native_library_posix.cc @@ -13,9 +13,13 @@ namespace base { +std::string NativeLibraryLoadError::ToString() const { + return message; +} + // static NativeLibrary LoadNativeLibrary(const FilePath& library_path, - std::string* error) { + NativeLibraryLoadError* error) { // dlopen() opens the file off disk. base::ThreadRestrictions::AssertIOAllowed(); @@ -25,7 +29,7 @@ NativeLibrary LoadNativeLibrary(const FilePath& library_path, // and http://crbug.com/40794. void* dl = dlopen(library_path.value().c_str(), RTLD_LAZY); if (!dl && error) - *error = dlerror(); + error->message = dlerror(); return dl; } diff --git a/chromium/base/native_library_win.cc b/chromium/base/native_library_win.cc index 2d437fa2a58..43528b977e4 100644 --- a/chromium/base/native_library_win.cc +++ b/chromium/base/native_library_win.cc @@ -7,6 +7,7 @@ #include <windows.h> #include "base/file_util.h" +#include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/threading/thread_restrictions.h" @@ -14,34 +15,48 @@ namespace base { typedef HMODULE (WINAPI* LoadLibraryFunction)(const wchar_t* file_name); +namespace { + NativeLibrary LoadNativeLibraryHelper(const FilePath& library_path, - LoadLibraryFunction load_library_api) { + LoadLibraryFunction load_library_api, + NativeLibraryLoadError* error) { // LoadLibrary() opens the file off disk. - base::ThreadRestrictions::AssertIOAllowed(); + ThreadRestrictions::AssertIOAllowed(); // Switch the current directory to the library directory as the library // may have dependencies on DLLs in this directory. bool restore_directory = false; FilePath current_directory; - if (file_util::GetCurrentDirectory(¤t_directory)) { + if (GetCurrentDirectory(¤t_directory)) { FilePath plugin_path = library_path.DirName(); if (!plugin_path.empty()) { - file_util::SetCurrentDirectory(plugin_path); + SetCurrentDirectory(plugin_path); restore_directory = true; } } HMODULE module = (*load_library_api)(library_path.value().c_str()); + if (!module && error) { + // GetLastError() needs to be called immediately after |load_library_api|. + error->code = GetLastError(); + } + if (restore_directory) - file_util::SetCurrentDirectory(current_directory); + SetCurrentDirectory(current_directory); return module; } +} // namespace + +std::string NativeLibraryLoadError::ToString() const { + return StringPrintf("%u", code); +} + // static NativeLibrary LoadNativeLibrary(const FilePath& library_path, - std::string* error) { - return LoadNativeLibraryHelper(library_path, LoadLibraryW); + NativeLibraryLoadError* error) { + return LoadNativeLibraryHelper(library_path, LoadLibraryW, error); } NativeLibrary LoadNativeLibraryDynamically(const FilePath& library_path) { @@ -51,7 +66,7 @@ NativeLibrary LoadNativeLibraryDynamically(const FilePath& library_path) { load_library = reinterpret_cast<LoadLibraryFunction>( GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW")); - return LoadNativeLibraryHelper(library_path, load_library); + return LoadNativeLibraryHelper(library_path, load_library, NULL); } // static diff --git a/chromium/base/nix/mime_util_xdg.cc b/chromium/base/nix/mime_util_xdg.cc index dd2c7eb663c..f78b6aba558 100644 --- a/chromium/base/nix/mime_util_xdg.cc +++ b/chromium/base/nix/mime_util_xdg.cc @@ -4,572 +4,21 @@ #include "base/nix/mime_util_xdg.h" -#include <cstdlib> -#include <list> -#include <map> -#include <vector> - -#include "base/environment.h" -#include "base/file_util.h" +#include "base/files/file_path.h" #include "base/lazy_instance.h" -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "base/memory/singleton.h" -#include "base/nix/xdg_util.h" -#include "base/strings/string_split.h" -#include "base/strings/string_util.h" #include "base/synchronization/lock.h" #include "base/third_party/xdg_mime/xdgmime.h" #include "base/threading/thread_restrictions.h" -#include "base/time/time.h" namespace base { namespace nix { namespace { -class IconTheme; - // None of the XDG stuff is thread-safe, so serialize all access under // this lock. LazyInstance<Lock>::Leaky g_mime_util_xdg_lock = LAZY_INSTANCE_INITIALIZER; -class MimeUtilConstants { - public: - typedef std::map<std::string, IconTheme*> IconThemeMap; - typedef std::map<FilePath, Time> IconDirMtimeMap; - typedef std::vector<std::string> IconFormats; - - // Specified by XDG icon theme specs. - static const int kUpdateIntervalInSeconds = 5; - - static const size_t kDefaultThemeNum = 4; - - static MimeUtilConstants* GetInstance() { - return Singleton<MimeUtilConstants>::get(); - } - - // Store icon directories and their mtimes. - IconDirMtimeMap icon_dirs_; - - // Store icon formats. - IconFormats icon_formats_; - - // Store loaded icon_theme. - IconThemeMap icon_themes_; - - // The default theme. - IconTheme* default_themes_[kDefaultThemeNum]; - - TimeTicks last_check_time_; - - // The current icon theme, usually set through GTK theme integration. - std::string icon_theme_name_; - - private: - MimeUtilConstants() { - icon_formats_.push_back(".png"); - icon_formats_.push_back(".svg"); - icon_formats_.push_back(".xpm"); - - for (size_t i = 0; i < kDefaultThemeNum; ++i) - default_themes_[i] = NULL; - } - ~MimeUtilConstants(); - - friend struct DefaultSingletonTraits<MimeUtilConstants>; - - DISALLOW_COPY_AND_ASSIGN(MimeUtilConstants); -}; - -// IconTheme represents an icon theme as defined by the xdg icon theme spec. -// Example themes on GNOME include 'Human' and 'Mist'. -// Example themes on KDE include 'crystalsvg' and 'kdeclassic'. -class IconTheme { - public: - // A theme consists of multiple sub-directories, like '32x32' and 'scalable'. - class SubDirInfo { - public: - // See spec for details. - enum Type { - Fixed, - Scalable, - Threshold - }; - SubDirInfo() - : size(0), - type(Threshold), - max_size(0), - min_size(0), - threshold(2) { - } - size_t size; // Nominal size of the icons in this directory. - Type type; // Type of the icon size. - size_t max_size; // Maximum size that the icons can be scaled to. - size_t min_size; // Minimum size that the icons can be scaled to. - size_t threshold; // Maximum difference from desired size. 2 by default. - }; - - explicit IconTheme(const std::string& name); - - ~IconTheme() {} - - // Returns the path to an icon with the name |icon_name| and a size of |size| - // pixels. If the icon does not exist, but |inherits| is true, then look for - // the icon in the parent theme. - FilePath GetIconPath(const std::string& icon_name, int size, bool inherits); - - // Load a theme with the name |theme_name| into memory. Returns null if theme - // is invalid. - static IconTheme* LoadTheme(const std::string& theme_name); - - private: - // Returns the path to an icon with the name |icon_name| in |subdir|. - FilePath GetIconPathUnderSubdir(const std::string& icon_name, - const std::string& subdir); - - // Whether the theme loaded properly. - bool IsValid() { - return index_theme_loaded_; - } - - // Read and parse |file| which is usually named 'index.theme' per theme spec. - bool LoadIndexTheme(const FilePath& file); - - // Checks to see if the icons in |info| matches |size| (in pixels). Returns - // 0 if they match, or the size difference in pixels. - size_t MatchesSize(SubDirInfo* info, size_t size); - - // Yet another function to read a line. - std::string ReadLine(FILE* fp); - - // Set directories to search for icons to the comma-separated list |dirs|. - bool SetDirectories(const std::string& dirs); - - bool index_theme_loaded_; // True if an instance is properly loaded. - // store the scattered directories of this theme. - std::list<FilePath> dirs_; - - // store the subdirs of this theme and array index of |info_array_|. - std::map<std::string, int> subdirs_; - scoped_ptr<SubDirInfo[]> info_array_; // List of sub-directories. - std::string inherits_; // Name of the theme this one inherits from. -}; - -IconTheme::IconTheme(const std::string& name) - : index_theme_loaded_(false) { - ThreadRestrictions::AssertIOAllowed(); - // Iterate on all icon directories to find directories of the specified - // theme and load the first encountered index.theme. - MimeUtilConstants::IconDirMtimeMap::iterator iter; - FilePath theme_path; - MimeUtilConstants::IconDirMtimeMap* icon_dirs = - &MimeUtilConstants::GetInstance()->icon_dirs_; - for (iter = icon_dirs->begin(); iter != icon_dirs->end(); ++iter) { - theme_path = iter->first.Append(name); - if (!DirectoryExists(theme_path)) - continue; - FilePath theme_index = theme_path.Append("index.theme"); - if (!index_theme_loaded_ && PathExists(theme_index)) { - if (!LoadIndexTheme(theme_index)) - return; - index_theme_loaded_ = true; - } - dirs_.push_back(theme_path); - } -} - -FilePath IconTheme::GetIconPath(const std::string& icon_name, int size, - bool inherits) { - std::map<std::string, int>::iterator subdir_iter; - FilePath icon_path; - - for (subdir_iter = subdirs_.begin(); - subdir_iter != subdirs_.end(); - ++subdir_iter) { - SubDirInfo* info = &info_array_[subdir_iter->second]; - if (MatchesSize(info, size) == 0) { - icon_path = GetIconPathUnderSubdir(icon_name, subdir_iter->first); - if (!icon_path.empty()) - return icon_path; - } - } - // Now looking for the mostly matched. - size_t min_delta_seen = 9999; - - for (subdir_iter = subdirs_.begin(); - subdir_iter != subdirs_.end(); - ++subdir_iter) { - SubDirInfo* info = &info_array_[subdir_iter->second]; - size_t delta = MatchesSize(info, size); - if (delta < min_delta_seen) { - FilePath path = GetIconPathUnderSubdir(icon_name, subdir_iter->first); - if (!path.empty()) { - min_delta_seen = delta; - icon_path = path; - } - } - } - - if (!icon_path.empty() || !inherits || inherits_ == "") - return icon_path; - - IconTheme* theme = LoadTheme(inherits_); - // Inheriting from itself means the theme is buggy but we shouldn't crash. - if (theme && theme != this) - return theme->GetIconPath(icon_name, size, inherits); - else - return FilePath(); -} - -IconTheme* IconTheme::LoadTheme(const std::string& theme_name) { - scoped_ptr<IconTheme> theme; - MimeUtilConstants::IconThemeMap* icon_themes = - &MimeUtilConstants::GetInstance()->icon_themes_; - if (icon_themes->find(theme_name) != icon_themes->end()) { - theme.reset((*icon_themes)[theme_name]); - } else { - theme.reset(new IconTheme(theme_name)); - if (!theme->IsValid()) - theme.reset(); - (*icon_themes)[theme_name] = theme.get(); - } - return theme.release(); -} - -FilePath IconTheme::GetIconPathUnderSubdir(const std::string& icon_name, - const std::string& subdir) { - FilePath icon_path; - std::list<FilePath>::iterator dir_iter; - MimeUtilConstants::IconFormats* icon_formats = - &MimeUtilConstants::GetInstance()->icon_formats_; - for (dir_iter = dirs_.begin(); dir_iter != dirs_.end(); ++dir_iter) { - for (size_t i = 0; i < icon_formats->size(); ++i) { - icon_path = dir_iter->Append(subdir); - icon_path = icon_path.Append(icon_name + (*icon_formats)[i]); - if (PathExists(icon_path)) - return icon_path; - } - } - return FilePath(); -} - -bool IconTheme::LoadIndexTheme(const FilePath& file) { - FILE* fp = base::OpenFile(file, "r"); - SubDirInfo* current_info = NULL; - if (!fp) - return false; - - // Read entries. - while (!feof(fp) && !ferror(fp)) { - std::string buf = ReadLine(fp); - if (buf == "") - break; - - std::string entry; - TrimWhitespaceASCII(buf, TRIM_ALL, &entry); - if (entry.length() == 0 || entry[0] == '#') { - // Blank line or Comment. - continue; - } else if (entry[0] == '[' && info_array_.get()) { - current_info = NULL; - std::string subdir = entry.substr(1, entry.length() - 2); - if (subdirs_.find(subdir) != subdirs_.end()) - current_info = &info_array_[subdirs_[subdir]]; - } - - std::string key, value; - std::vector<std::string> r; - SplitStringDontTrim(entry, '=', &r); - if (r.size() < 2) - continue; - - TrimWhitespaceASCII(r[0], TRIM_ALL, &key); - for (size_t i = 1; i < r.size(); i++) - value.append(r[i]); - TrimWhitespaceASCII(value, TRIM_ALL, &value); - - if (current_info) { - if (key == "Size") { - current_info->size = atoi(value.c_str()); - } else if (key == "Type") { - if (value == "Fixed") - current_info->type = SubDirInfo::Fixed; - else if (value == "Scalable") - current_info->type = SubDirInfo::Scalable; - else if (value == "Threshold") - current_info->type = SubDirInfo::Threshold; - } else if (key == "MaxSize") { - current_info->max_size = atoi(value.c_str()); - } else if (key == "MinSize") { - current_info->min_size = atoi(value.c_str()); - } else if (key == "Threshold") { - current_info->threshold = atoi(value.c_str()); - } - } else { - if (key.compare("Directories") == 0 && !info_array_.get()) { - if (!SetDirectories(value)) break; - } else if (key.compare("Inherits") == 0) { - if (value != "hicolor") - inherits_ = value; - } - } - } - - base::CloseFile(fp); - return info_array_.get() != NULL; -} - -size_t IconTheme::MatchesSize(SubDirInfo* info, size_t size) { - if (info->type == SubDirInfo::Fixed) { - if (size > info->size) - return size - info->size; - else - return info->size - size; - } else if (info->type == SubDirInfo::Scalable) { - if (size < info->min_size) - return info->min_size - size; - if (size > info->max_size) - return size - info->max_size; - return 0; - } else { - if (size + info->threshold < info->size) - return info->size - size - info->threshold; - if (size > info->size + info->threshold) - return size - info->size - info->threshold; - return 0; - } -} - -std::string IconTheme::ReadLine(FILE* fp) { - if (!fp) - return std::string(); - - std::string result; - const size_t kBufferSize = 100; - char buffer[kBufferSize]; - while ((fgets(buffer, kBufferSize - 1, fp)) != NULL) { - result += buffer; - size_t len = result.length(); - if (len == 0) - break; - char end = result[len - 1]; - if (end == '\n' || end == '\0') - break; - } - - return result; -} - -bool IconTheme::SetDirectories(const std::string& dirs) { - int num = 0; - std::string::size_type pos = 0, epos; - std::string dir; - while ((epos = dirs.find(',', pos)) != std::string::npos) { - TrimWhitespaceASCII(dirs.substr(pos, epos - pos), TRIM_ALL, &dir); - if (dir.length() == 0) { - DLOG(WARNING) << "Invalid index.theme: blank subdir"; - return false; - } - subdirs_[dir] = num++; - pos = epos + 1; - } - TrimWhitespaceASCII(dirs.substr(pos), TRIM_ALL, &dir); - if (dir.length() == 0) { - DLOG(WARNING) << "Invalid index.theme: blank subdir"; - return false; - } - subdirs_[dir] = num++; - info_array_.reset(new SubDirInfo[num]); - return true; -} - -bool CheckDirExistsAndGetMtime(const FilePath& dir, Time* last_modified) { - if (!DirectoryExists(dir)) - return false; - PlatformFileInfo file_info; - if (!GetFileInfo(dir, &file_info)) - return false; - *last_modified = file_info.last_modified; - return true; -} - -// Make sure |dir| exists and add it to the list of icon directories. -void TryAddIconDir(const FilePath& dir) { - Time last_modified; - if (!CheckDirExistsAndGetMtime(dir, &last_modified)) - return; - MimeUtilConstants::GetInstance()->icon_dirs_[dir] = last_modified; -} - -// For a xdg directory |dir|, add the appropriate icon sub-directories. -void AddXDGDataDir(const FilePath& dir) { - if (!DirectoryExists(dir)) - return; - TryAddIconDir(dir.Append("icons")); - TryAddIconDir(dir.Append("pixmaps")); -} - -// Add all the xdg icon directories. -void InitIconDir() { - FilePath home = GetHomeDir(); - if (!home.empty()) { - FilePath legacy_data_dir(home); - legacy_data_dir = legacy_data_dir.AppendASCII(".icons"); - if (DirectoryExists(legacy_data_dir)) - TryAddIconDir(legacy_data_dir); - } - const char* env = getenv("XDG_DATA_HOME"); - if (env) { - AddXDGDataDir(FilePath(env)); - } else if (!home.empty()) { - FilePath local_data_dir(home); - local_data_dir = local_data_dir.AppendASCII(".local"); - local_data_dir = local_data_dir.AppendASCII("share"); - AddXDGDataDir(local_data_dir); - } - - env = getenv("XDG_DATA_DIRS"); - if (!env) { - AddXDGDataDir(FilePath("/usr/local/share")); - AddXDGDataDir(FilePath("/usr/share")); - } else { - std::string xdg_data_dirs = env; - std::string::size_type pos = 0, epos; - while ((epos = xdg_data_dirs.find(':', pos)) != std::string::npos) { - AddXDGDataDir(FilePath(xdg_data_dirs.substr(pos, epos - pos))); - pos = epos + 1; - } - AddXDGDataDir(FilePath(xdg_data_dirs.substr(pos))); - } -} - -void EnsureUpdated() { - MimeUtilConstants* constants = MimeUtilConstants::GetInstance(); - if (constants->last_check_time_.is_null()) { - constants->last_check_time_ = TimeTicks::Now(); - InitIconDir(); - return; - } - - // Per xdg theme spec, we should check the icon directories every so often - // for newly added icons. - TimeDelta time_since_last_check = - TimeTicks::Now() - constants->last_check_time_; - if (time_since_last_check.InSeconds() > constants->kUpdateIntervalInSeconds) { - constants->last_check_time_ += time_since_last_check; - - bool rescan_icon_dirs = false; - MimeUtilConstants::IconDirMtimeMap* icon_dirs = &constants->icon_dirs_; - MimeUtilConstants::IconDirMtimeMap::iterator iter; - for (iter = icon_dirs->begin(); iter != icon_dirs->end(); ++iter) { - Time last_modified; - if (!CheckDirExistsAndGetMtime(iter->first, &last_modified) || - last_modified != iter->second) { - rescan_icon_dirs = true; - break; - } - } - - if (rescan_icon_dirs) { - constants->icon_dirs_.clear(); - constants->icon_themes_.clear(); - InitIconDir(); - } - } -} - -// Find a fallback icon if we cannot find it in the default theme. -FilePath LookupFallbackIcon(const std::string& icon_name) { - MimeUtilConstants* constants = MimeUtilConstants::GetInstance(); - MimeUtilConstants::IconDirMtimeMap::iterator iter; - MimeUtilConstants::IconDirMtimeMap* icon_dirs = &constants->icon_dirs_; - MimeUtilConstants::IconFormats* icon_formats = &constants->icon_formats_; - for (iter = icon_dirs->begin(); iter != icon_dirs->end(); ++iter) { - for (size_t i = 0; i < icon_formats->size(); ++i) { - FilePath icon = iter->first.Append(icon_name + (*icon_formats)[i]); - if (PathExists(icon)) - return icon; - } - } - return FilePath(); -} - -// Initialize the list of default themes. -void InitDefaultThemes() { - IconTheme** default_themes = - MimeUtilConstants::GetInstance()->default_themes_; - - scoped_ptr<Environment> env(Environment::Create()); - base::nix::DesktopEnvironment desktop_env = - base::nix::GetDesktopEnvironment(env.get()); - if (desktop_env == base::nix::DESKTOP_ENVIRONMENT_KDE3 || - desktop_env == base::nix::DESKTOP_ENVIRONMENT_KDE4) { - // KDE - std::string kde_default_theme; - std::string kde_fallback_theme; - - // TODO(thestig): Figure out how to get the current icon theme on KDE. - // Setting stored in ~/.kde/share/config/kdeglobals under Icons -> Theme. - default_themes[0] = NULL; - - // Try some reasonable defaults for KDE. - if (desktop_env == base::nix::DESKTOP_ENVIRONMENT_KDE3) { - // KDE 3 - kde_default_theme = "default.kde"; - kde_fallback_theme = "crystalsvg"; - } else { - // KDE 4 - kde_default_theme = "default.kde4"; - kde_fallback_theme = "oxygen"; - } - default_themes[1] = IconTheme::LoadTheme(kde_default_theme); - default_themes[2] = IconTheme::LoadTheme(kde_fallback_theme); - } else { - // Assume it's Gnome and use GTK to figure out the theme. - default_themes[1] = IconTheme::LoadTheme( - MimeUtilConstants::GetInstance()->icon_theme_name_); - default_themes[2] = IconTheme::LoadTheme("gnome"); - } - // hicolor needs to be last per icon theme spec. - default_themes[3] = IconTheme::LoadTheme("hicolor"); - - for (size_t i = 0; i < MimeUtilConstants::kDefaultThemeNum; i++) { - if (default_themes[i] == NULL) - continue; - // NULL out duplicate pointers. - for (size_t j = i + 1; j < MimeUtilConstants::kDefaultThemeNum; j++) { - if (default_themes[j] == default_themes[i]) - default_themes[j] = NULL; - } - } -} - -// Try to find an icon with the name |icon_name| that's |size| pixels. -FilePath LookupIconInDefaultTheme(const std::string& icon_name, int size) { - EnsureUpdated(); - MimeUtilConstants* constants = MimeUtilConstants::GetInstance(); - MimeUtilConstants::IconThemeMap* icon_themes = &constants->icon_themes_; - if (icon_themes->empty()) - InitDefaultThemes(); - - FilePath icon_path; - IconTheme** default_themes = constants->default_themes_; - for (size_t i = 0; i < MimeUtilConstants::kDefaultThemeNum; i++) { - if (default_themes[i]) { - icon_path = default_themes[i]->GetIconPath(icon_name, size, true); - if (!icon_path.empty()) - return icon_path; - } - } - return LookupFallbackIcon(icon_name); -} - -MimeUtilConstants::~MimeUtilConstants() { - for (size_t i = 0; i < kDefaultThemeNum; i++) - delete default_themes_[i]; -} - } // namespace std::string GetFileMimeType(const FilePath& filepath) { @@ -586,68 +35,5 @@ std::string GetDataMimeType(const std::string& data) { return xdg_mime_get_mime_type_for_data(data.data(), data.length(), NULL); } -void SetIconThemeName(const std::string& name) { - // If the theme name is already loaded, do nothing. Chrome doesn't respond - // to changes in the system theme, so we never need to set this more than - // once. - if (!MimeUtilConstants::GetInstance()->icon_theme_name_.empty()) - return; - - MimeUtilConstants::GetInstance()->icon_theme_name_ = name; -} - -FilePath GetMimeIcon(const std::string& mime_type, size_t size) { - ThreadRestrictions::AssertIOAllowed(); - std::vector<std::string> icon_names; - std::string icon_name; - FilePath icon_file; - - if (!mime_type.empty()) { - AutoLock scoped_lock(g_mime_util_xdg_lock.Get()); - const char *icon = xdg_mime_get_icon(mime_type.c_str()); - icon_name = std::string(icon ? icon : ""); - } - - if (icon_name.length()) - icon_names.push_back(icon_name); - - // For text/plain, try text-plain. - icon_name = mime_type; - for (size_t i = icon_name.find('/', 0); i != std::string::npos; - i = icon_name.find('/', i + 1)) { - icon_name[i] = '-'; - } - icon_names.push_back(icon_name); - // Also try gnome-mime-text-plain. - icon_names.push_back("gnome-mime-" + icon_name); - - // Try "deb" for "application/x-deb" in KDE 3. - size_t x_substr_pos = mime_type.find("/x-"); - if (x_substr_pos != std::string::npos) { - icon_name = mime_type.substr(x_substr_pos + 3); - icon_names.push_back(icon_name); - } - - // Try generic name like text-x-generic. - icon_name = mime_type.substr(0, mime_type.find('/')) + "-x-generic"; - icon_names.push_back(icon_name); - - // Last resort - icon_names.push_back("unknown"); - - for (size_t i = 0; i < icon_names.size(); i++) { - if (icon_names[i][0] == '/') { - icon_file = FilePath(icon_names[i]); - if (PathExists(icon_file)) - return icon_file; - } else { - icon_file = LookupIconInDefaultTheme(icon_names[i], size); - if (!icon_file.empty()) - return icon_file; - } - } - return FilePath(); -} - } // namespace nix } // namespace base diff --git a/chromium/base/nix/mime_util_xdg.h b/chromium/base/nix/mime_util_xdg.h index 79eb782e60a..e40415e1c48 100644 --- a/chromium/base/nix/mime_util_xdg.h +++ b/chromium/base/nix/mime_util_xdg.h @@ -25,17 +25,6 @@ BASE_EXPORT std::string GetFileMimeType(const FilePath& filepath); // Get the mime type for a byte vector. BASE_EXPORT std::string GetDataMimeType(const std::string& data); -// Sets the current icon theme that we've detected from the desktop -// environment. Currently only works when we believe we're in a GTK -// environment. -BASE_EXPORT void SetIconThemeName(const std::string& name); - -// Gets the file name for an icon given the mime type and icon pixel size. -// Where an icon is a square image of |size| x |size|. -// This will try to find the closest matching icon. If that's not available, -// then a generic icon, and finally an empty FilePath if all else fails. -BASE_EXPORT FilePath GetMimeIcon(const std::string& mime_type, size_t size); - } // namespace nix } // namespace base diff --git a/chromium/base/nix/xdg_util.cc b/chromium/base/nix/xdg_util.cc index 4c1510f5c11..4086a3d0af1 100644 --- a/chromium/base/nix/xdg_util.cc +++ b/chromium/base/nix/xdg_util.cc @@ -6,9 +6,11 @@ #include <string> +#include "base/base_paths.h" #include "base/environment.h" #include "base/file_util.h" #include "base/files/file_path.h" +#include "base/path_service.h" #include "base/third_party/xdg_user_dirs/xdg_user_dir_lookup.h" namespace { @@ -28,10 +30,12 @@ FilePath GetXDGDirectory(Environment* env, const char* env_name, const char* fallback_dir) { FilePath path; std::string env_value; - if (env->GetVar(env_name, &env_value) && !env_value.empty()) + if (env->GetVar(env_name, &env_value) && !env_value.empty()) { path = FilePath(env_value); - else - path = GetHomeDir().Append(fallback_dir); + } else { + PathService::Get(base::DIR_HOME, &path); + path = path.Append(fallback_dir); + } return path.StripTrailingSeparators(); } @@ -42,7 +46,8 @@ FilePath GetXDGUserDirectory(const char* dir_name, const char* fallback_dir) { path = FilePath(xdg_dir); free(xdg_dir); } else { - path = GetHomeDir().Append(fallback_dir); + PathService::Get(base::DIR_HOME, &path); + path = path.Append(fallback_dir); } return path.StripTrailingSeparators(); } diff --git a/chromium/base/numerics/OWNERS b/chromium/base/numerics/OWNERS new file mode 100644 index 00000000000..41f35fc79b4 --- /dev/null +++ b/chromium/base/numerics/OWNERS @@ -0,0 +1,3 @@ +jschuh@chromium.org +tsepez@chromium.org + diff --git a/chromium/base/numerics/safe_conversions.h b/chromium/base/numerics/safe_conversions.h new file mode 100644 index 00000000000..fe85fc6f52d --- /dev/null +++ b/chromium/base/numerics/safe_conversions.h @@ -0,0 +1,64 @@ +// 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_SAFE_CONVERSIONS_H_ +#define BASE_SAFE_CONVERSIONS_H_ + +#include <limits> + +#include "base/logging.h" +#include "base/numerics/safe_conversions_impl.h" + +namespace base { + +// Convenience function that returns true if the supplied value is in range +// for the destination type. +template <typename Dst, typename Src> +inline bool IsValueInRangeForNumericType(Src value) { + return internal::DstRangeRelationToSrcRange<Dst>(value) == + internal::RANGE_VALID; +} + +// checked_cast<> is analogous to static_cast<> for numeric types, +// except that it CHECKs that the specified numeric conversion will not +// overflow or underflow. NaN source will always trigger a CHECK. +template <typename Dst, typename Src> +inline Dst checked_cast(Src value) { + CHECK(IsValueInRangeForNumericType<Dst>(value)); + return static_cast<Dst>(value); +} + +// saturated_cast<> is analogous to static_cast<> for numeric types, except +// that the specified numeric conversion will saturate rather than overflow or +// underflow. NaN assignment to an integral will trigger a CHECK condition. +template <typename Dst, typename Src> +inline Dst saturated_cast(Src value) { + // Optimization for floating point values, which already saturate. + if (std::numeric_limits<Dst>::is_iec559) + return static_cast<Dst>(value); + + switch (internal::DstRangeRelationToSrcRange<Dst>(value)) { + case internal::RANGE_VALID: + return static_cast<Dst>(value); + + case internal::RANGE_UNDERFLOW: + return std::numeric_limits<Dst>::min(); + + case internal::RANGE_OVERFLOW: + return std::numeric_limits<Dst>::max(); + + // Should fail only on attempting to assign NaN to a saturated integer. + case internal::RANGE_INVALID: + CHECK(false); + return std::numeric_limits<Dst>::max(); + } + + NOTREACHED(); + return static_cast<Dst>(value); +} + +} // namespace base + +#endif // BASE_SAFE_CONVERSIONS_H_ + diff --git a/chromium/base/numerics/safe_conversions_impl.h b/chromium/base/numerics/safe_conversions_impl.h new file mode 100644 index 00000000000..f05d553815e --- /dev/null +++ b/chromium/base/numerics/safe_conversions_impl.h @@ -0,0 +1,217 @@ +// 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_SAFE_CONVERSIONS_IMPL_H_ +#define BASE_SAFE_CONVERSIONS_IMPL_H_ + +#include <limits> + +#include "base/macros.h" +#include "base/template_util.h" + +namespace base { +namespace internal { + +// The std library doesn't provide a binary max_exponent for integers, however +// we can compute one by adding one to the number of non-sign bits. This allows +// for accurate range comparisons between floating point and integer types. +template <typename NumericType> +struct MaxExponent { + static const int value = std::numeric_limits<NumericType>::is_iec559 + ? std::numeric_limits<NumericType>::max_exponent + : (sizeof(NumericType) * 8 + 1 - + std::numeric_limits<NumericType>::is_signed); +}; + +enum IntegerRepresentation { + INTEGER_REPRESENTATION_UNSIGNED, + INTEGER_REPRESENTATION_SIGNED +}; + +// A range for a given nunmeric Src type is contained for a given numeric Dst +// type if both numeric_limits<Src>::max() <= numeric_limits<Dst>::max() and +// numeric_limits<Src>::min() >= numeric_limits<Dst>::min() are true. +// We implement this as template specializations rather than simple static +// comparisons to ensure type correctness in our comparisons. +enum NumericRangeRepresentation { + NUMERIC_RANGE_NOT_CONTAINED, + NUMERIC_RANGE_CONTAINED +}; + +// Helper templates to statically determine if our destination type can contain +// maximum and minimum values represented by the source type. + +template < + typename Dst, + typename Src, + IntegerRepresentation DstSign = std::numeric_limits<Dst>::is_signed + ? INTEGER_REPRESENTATION_SIGNED + : INTEGER_REPRESENTATION_UNSIGNED, + IntegerRepresentation SrcSign = + std::numeric_limits<Src>::is_signed + ? INTEGER_REPRESENTATION_SIGNED + : INTEGER_REPRESENTATION_UNSIGNED > +struct StaticDstRangeRelationToSrcRange; + +// Same sign: Dst is guaranteed to contain Src only if its range is equal or +// larger. +template <typename Dst, typename Src, IntegerRepresentation Sign> +struct StaticDstRangeRelationToSrcRange<Dst, Src, Sign, Sign> { + static const NumericRangeRepresentation value = + MaxExponent<Dst>::value >= MaxExponent<Src>::value + ? NUMERIC_RANGE_CONTAINED + : NUMERIC_RANGE_NOT_CONTAINED; +}; + +// Unsigned to signed: Dst is guaranteed to contain source only if its range is +// larger. +template <typename Dst, typename Src> +struct StaticDstRangeRelationToSrcRange<Dst, + Src, + INTEGER_REPRESENTATION_SIGNED, + INTEGER_REPRESENTATION_UNSIGNED> { + static const NumericRangeRepresentation value = + MaxExponent<Dst>::value > MaxExponent<Src>::value + ? NUMERIC_RANGE_CONTAINED + : NUMERIC_RANGE_NOT_CONTAINED; +}; + +// Signed to unsigned: Dst cannot be statically determined to contain Src. +template <typename Dst, typename Src> +struct StaticDstRangeRelationToSrcRange<Dst, + Src, + INTEGER_REPRESENTATION_UNSIGNED, + INTEGER_REPRESENTATION_SIGNED> { + static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED; +}; + +enum RangeConstraint { + RANGE_VALID = 0x0, // Value can be represented by the destination type. + RANGE_UNDERFLOW = 0x1, // Value would overflow. + RANGE_OVERFLOW = 0x2, // Value would underflow. + RANGE_INVALID = RANGE_UNDERFLOW | RANGE_OVERFLOW // Invalid (i.e. NaN). +}; + +// Helper function for coercing an int back to a RangeContraint. +inline RangeConstraint GetRangeConstraint(int integer_range_constraint) { + DCHECK(integer_range_constraint >= RANGE_VALID && + integer_range_constraint <= RANGE_INVALID); + return static_cast<RangeConstraint>(integer_range_constraint); +} + +// This function creates a RangeConstraint from an upper and lower bound +// check by taking advantage of the fact that only NaN can be out of range in +// both directions at once. +inline RangeConstraint GetRangeConstraint(bool is_in_upper_bound, + bool is_in_lower_bound) { + return GetRangeConstraint((is_in_upper_bound ? 0 : RANGE_OVERFLOW) | + (is_in_lower_bound ? 0 : RANGE_UNDERFLOW)); +} + +template < + typename Dst, + typename Src, + IntegerRepresentation DstSign = std::numeric_limits<Dst>::is_signed + ? INTEGER_REPRESENTATION_SIGNED + : INTEGER_REPRESENTATION_UNSIGNED, + IntegerRepresentation SrcSign = std::numeric_limits<Src>::is_signed + ? INTEGER_REPRESENTATION_SIGNED + : INTEGER_REPRESENTATION_UNSIGNED, + NumericRangeRepresentation DstRange = + StaticDstRangeRelationToSrcRange<Dst, Src>::value > +struct DstRangeRelationToSrcRangeImpl; + +// The following templates are for ranges that must be verified at runtime. We +// split it into checks based on signedness to avoid confusing casts and +// compiler warnings on signed an unsigned comparisons. + +// Dst range is statically determined to contain Src: Nothing to check. +template <typename Dst, + typename Src, + IntegerRepresentation DstSign, + IntegerRepresentation SrcSign> +struct DstRangeRelationToSrcRangeImpl<Dst, + Src, + DstSign, + SrcSign, + NUMERIC_RANGE_CONTAINED> { + static RangeConstraint Check(Src value) { return RANGE_VALID; } +}; + +// Signed to signed narrowing: Both the upper and lower boundaries may be +// exceeded. +template <typename Dst, typename Src> +struct DstRangeRelationToSrcRangeImpl<Dst, + Src, + INTEGER_REPRESENTATION_SIGNED, + INTEGER_REPRESENTATION_SIGNED, + NUMERIC_RANGE_NOT_CONTAINED> { + static RangeConstraint Check(Src value) { + return std::numeric_limits<Dst>::is_iec559 + ? GetRangeConstraint(value <= std::numeric_limits<Dst>::max(), + value >= -std::numeric_limits<Dst>::max()) + : GetRangeConstraint(value <= std::numeric_limits<Dst>::max(), + value >= std::numeric_limits<Dst>::min()); + } +}; + +// Unsigned to unsigned narrowing: Only the upper boundary can be exceeded. +template <typename Dst, typename Src> +struct DstRangeRelationToSrcRangeImpl<Dst, + Src, + INTEGER_REPRESENTATION_UNSIGNED, + INTEGER_REPRESENTATION_UNSIGNED, + NUMERIC_RANGE_NOT_CONTAINED> { + static RangeConstraint Check(Src value) { + return GetRangeConstraint(value <= std::numeric_limits<Dst>::max(), true); + } +}; + +// Unsigned to signed: The upper boundary may be exceeded. +template <typename Dst, typename Src> +struct DstRangeRelationToSrcRangeImpl<Dst, + Src, + INTEGER_REPRESENTATION_SIGNED, + INTEGER_REPRESENTATION_UNSIGNED, + NUMERIC_RANGE_NOT_CONTAINED> { + static RangeConstraint Check(Src value) { + return sizeof(Dst) > sizeof(Src) + ? RANGE_VALID + : GetRangeConstraint( + value <= static_cast<Src>(std::numeric_limits<Dst>::max()), + true); + } +}; + +// Signed to unsigned: The upper boundary may be exceeded for a narrower Dst, +// and any negative value exceeds the lower boundary. +template <typename Dst, typename Src> +struct DstRangeRelationToSrcRangeImpl<Dst, + Src, + INTEGER_REPRESENTATION_UNSIGNED, + INTEGER_REPRESENTATION_SIGNED, + NUMERIC_RANGE_NOT_CONTAINED> { + static RangeConstraint Check(Src value) { + return (MaxExponent<Dst>::value >= MaxExponent<Src>::value) + ? GetRangeConstraint(true, value >= static_cast<Src>(0)) + : GetRangeConstraint( + value <= static_cast<Src>(std::numeric_limits<Dst>::max()), + value >= static_cast<Src>(0)); + } +}; + +template <typename Dst, typename Src> +inline RangeConstraint DstRangeRelationToSrcRange(Src value) { + COMPILE_ASSERT(std::numeric_limits<Src>::is_specialized, + argument_must_be_numeric); + COMPILE_ASSERT(std::numeric_limits<Dst>::is_specialized, + result_must_be_numeric); + return DstRangeRelationToSrcRangeImpl<Dst, Src>::Check(value); +} + +} // namespace internal +} // namespace base + +#endif // BASE_SAFE_CONVERSIONS_IMPL_H_ + diff --git a/chromium/base/numerics/safe_math.h b/chromium/base/numerics/safe_math.h new file mode 100644 index 00000000000..51a534f421c --- /dev/null +++ b/chromium/base/numerics/safe_math.h @@ -0,0 +1,271 @@ +// 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_SAFE_MATH_H_ +#define BASE_SAFE_MATH_H_ + +#include "base/numerics/safe_math_impl.h" + +namespace base { + +namespace internal { + +// CheckedNumeric implements all the logic and operators for detecting integer +// boundary conditions such as overflow, underflow, and invalid conversions. +// The CheckedNumeric type implicitly converts from floating point and integer +// data types, and contains overloads for basic arithmetic operations (i.e.: +, +// -, *, /, %). +// +// The following methods convert from CheckedNumeric to standard numeric values: +// IsValid() - Returns true if the underlying numeric value is valid (i.e. has +// has not wrapped and is not the result of an invalid conversion). +// ValueOrDie() - Returns the underlying value. If the state is not valid this +// call will crash on a CHECK. +// ValueOrDefault() - Returns the current value, or the supplied default if the +// state is not valid. +// ValueFloating() - Returns the underlying floating point value (valid only +// only for floating point CheckedNumeric types). +// +// Bitwise operations are explicitly not supported, because correct +// handling of some cases (e.g. sign manipulation) is ambiguous. Comparison +// operations are explicitly not supported because they could result in a crash +// on a CHECK condition. You should use patterns like the following for these +// operations: +// Bitwise operation: +// CheckedNumeric<int> checked_int = untrusted_input_value; +// int x = checked_int.ValueOrDefault(0) | kFlagValues; +// Comparison: +// CheckedNumeric<size_t> checked_size; +// CheckedNumeric<int> checked_size = untrusted_input_value; +// checked_size = checked_size + HEADER LENGTH; +// if (checked_size.IsValid() && checked_size.ValueOrDie() < buffer_size) +// Do stuff... +template <typename T> +class CheckedNumeric { + public: + typedef T type; + + CheckedNumeric() {} + + // Copy constructor. + template <typename Src> + CheckedNumeric(const CheckedNumeric<Src>& rhs) + : state_(rhs.ValueUnsafe(), rhs.validity()) {} + + template <typename Src> + CheckedNumeric(Src value, RangeConstraint validity) + : state_(value, validity) {} + + // This is not an explicit constructor because we implicitly upgrade regular + // numerics to CheckedNumerics to make them easier to use. + template <typename Src> + CheckedNumeric(Src value) + : state_(value) { + COMPILE_ASSERT(std::numeric_limits<Src>::is_specialized, + argument_must_be_numeric); + } + + // IsValid() is the public API to test if a CheckedNumeric is currently valid. + bool IsValid() const { return validity() == RANGE_VALID; } + + // ValueOrDie() The primary accessor for the underlying value. If the current + // state is not valid it will CHECK and crash. + T ValueOrDie() const { + CHECK(IsValid()); + return state_.value(); + } + + // ValueOrDefault(T default_value) A convenience method that returns the + // current value if the state is valid, and the supplied default_value for + // any other state. + T ValueOrDefault(T default_value) const { + return IsValid() ? state_.value() : default_value; + } + + // ValueFloating() - Since floating point values include their validity state, + // we provide an easy method for extracting them directly, without a risk of + // crashing on a CHECK. + T ValueFloating() const { + COMPILE_ASSERT(std::numeric_limits<T>::is_iec559, argument_must_be_float); + return CheckedNumeric<T>::cast(*this).ValueUnsafe(); + } + + // validity() - DO NOT USE THIS IN EXTERNAL CODE - It is public right now for + // tests and to avoid a big matrix of friend operator overloads. But the + // values it returns are likely to change in the future. + // Returns: current validity state (i.e. valid, overflow, underflow, nan). + // TODO(jschuh): crbug.com/332611 Figure out and implement semantics for + // saturation/wrapping so we can expose this state consistently and implement + // saturated arithmetic. + RangeConstraint validity() const { return state_.validity(); } + + // ValueUnsafe() - DO NOT USE THIS IN EXTERNAL CODE - It is public right now + // for tests and to avoid a big matrix of friend operator overloads. But the + // values it returns are likely to change in the future. + // Returns: the raw numeric value, regardless of the current state. + // TODO(jschuh): crbug.com/332611 Figure out and implement semantics for + // saturation/wrapping so we can expose this state consistently and implement + // saturated arithmetic. + T ValueUnsafe() const { return state_.value(); } + + // Prototypes for the supported arithmetic operator overloads. + template <typename Src> CheckedNumeric& operator+=(Src rhs); + template <typename Src> CheckedNumeric& operator-=(Src rhs); + template <typename Src> CheckedNumeric& operator*=(Src rhs); + template <typename Src> CheckedNumeric& operator/=(Src rhs); + template <typename Src> CheckedNumeric& operator%=(Src rhs); + + CheckedNumeric operator-() const { + RangeConstraint validity; + T value = CheckedNeg(state_.value(), &validity); + // Negation is always valid for floating point. + if (std::numeric_limits<T>::is_iec559) + return CheckedNumeric<T>(value); + + validity = GetRangeConstraint(state_.validity() | validity); + return CheckedNumeric<T>(value, validity); + } + + CheckedNumeric Abs() const { + RangeConstraint validity; + T value = CheckedAbs(state_.value(), &validity); + // Absolute value is always valid for floating point. + if (std::numeric_limits<T>::is_iec559) + return CheckedNumeric<T>(value); + + validity = GetRangeConstraint(state_.validity() | validity); + return CheckedNumeric<T>(value, validity); + } + + CheckedNumeric& operator++() { + *this += 1; + return *this; + } + + CheckedNumeric operator++(int) { + CheckedNumeric value = *this; + *this += 1; + return value; + } + + CheckedNumeric& operator--() { + *this -= 1; + return *this; + } + + CheckedNumeric operator--(int) { + CheckedNumeric value = *this; + *this -= 1; + return value; + } + + // These static methods behave like a convenience cast operator targeting + // the desired CheckedNumeric type. As an optimization, a reference is + // returned when Src is the same type as T. + template <typename Src> + static CheckedNumeric<T> cast( + Src u, + typename enable_if<std::numeric_limits<Src>::is_specialized, int>::type = + 0) { + return u; + } + + template <typename Src> + static CheckedNumeric<T> cast( + const CheckedNumeric<Src>& u, + typename enable_if<!is_same<Src, T>::value, int>::type = 0) { + return u; + } + + static const CheckedNumeric<T>& cast(const CheckedNumeric<T>& u) { return u; } + + private: + CheckedNumericState<T> state_; +}; + +// This is the boilerplate for the standard arithmetic operator overloads. A +// macro isn't the prettiest solution, but it beats rewriting these five times. +// Some details worth noting are: +// * We apply the standard arithmetic promotions. +// * We skip range checks for floating points. +// * We skip range checks for destination integers with sufficient range. +// TODO(jschuh): extract these out into templates. +#define BASE_NUMERIC_ARITHMETIC_OPERATORS(NAME, OP, COMPOUND_OP) \ + /* Binary arithmetic operator for CheckedNumerics of the same type. */ \ + template <typename T> \ + CheckedNumeric<typename ArithmeticPromotion<T>::type> operator OP( \ + const CheckedNumeric<T>& lhs, const CheckedNumeric<T>& rhs) { \ + typedef typename ArithmeticPromotion<T>::type Promotion; \ + /* Floating point always takes the fast path */ \ + if (std::numeric_limits<T>::is_iec559) \ + return CheckedNumeric<T>(lhs.ValueUnsafe() OP rhs.ValueUnsafe()); \ + if (IsIntegerArithmeticSafe<Promotion, T, T>::value) \ + return CheckedNumeric<Promotion>( \ + lhs.ValueUnsafe() OP rhs.ValueUnsafe(), \ + GetRangeConstraint(rhs.validity() | lhs.validity())); \ + RangeConstraint validity = RANGE_VALID; \ + T result = Checked##NAME(static_cast<Promotion>(lhs.ValueUnsafe()), \ + static_cast<Promotion>(rhs.ValueUnsafe()), \ + &validity); \ + return CheckedNumeric<Promotion>( \ + result, \ + GetRangeConstraint(validity | lhs.validity() | rhs.validity())); \ + } \ + /* Assignment arithmetic operator implementation from CheckedNumeric. */ \ + template <typename T> \ + template <typename Src> \ + CheckedNumeric<T>& CheckedNumeric<T>::operator COMPOUND_OP(Src rhs) { \ + *this = CheckedNumeric<T>::cast(*this) OP CheckedNumeric<Src>::cast(rhs); \ + return *this; \ + } \ + /* Binary arithmetic operator for CheckedNumeric of different type. */ \ + template <typename T, typename Src> \ + CheckedNumeric<typename ArithmeticPromotion<T, Src>::type> operator OP( \ + const CheckedNumeric<Src>& lhs, const CheckedNumeric<T>& rhs) { \ + typedef typename ArithmeticPromotion<T, Src>::type Promotion; \ + if (IsIntegerArithmeticSafe<Promotion, T, Src>::value) \ + return CheckedNumeric<Promotion>( \ + lhs.ValueUnsafe() OP rhs.ValueUnsafe(), \ + GetRangeConstraint(rhs.validity() | lhs.validity())); \ + return CheckedNumeric<Promotion>::cast(lhs) \ + OP CheckedNumeric<Promotion>::cast(rhs); \ + } \ + /* Binary arithmetic operator for left CheckedNumeric and right numeric. */ \ + template <typename T, typename Src> \ + CheckedNumeric<typename ArithmeticPromotion<T, Src>::type> operator OP( \ + const CheckedNumeric<T>& lhs, Src rhs) { \ + typedef typename ArithmeticPromotion<T, Src>::type Promotion; \ + if (IsIntegerArithmeticSafe<Promotion, T, Src>::value) \ + return CheckedNumeric<Promotion>(lhs.ValueUnsafe() OP rhs, \ + lhs.validity()); \ + return CheckedNumeric<Promotion>::cast(lhs) \ + OP CheckedNumeric<Promotion>::cast(rhs); \ + } \ + /* Binary arithmetic operator for right numeric and left CheckedNumeric. */ \ + template <typename T, typename Src> \ + CheckedNumeric<typename ArithmeticPromotion<T, Src>::type> operator OP( \ + Src lhs, const CheckedNumeric<T>& rhs) { \ + typedef typename ArithmeticPromotion<T, Src>::type Promotion; \ + if (IsIntegerArithmeticSafe<Promotion, T, Src>::value) \ + return CheckedNumeric<Promotion>(lhs OP rhs.ValueUnsafe(), \ + rhs.validity()); \ + return CheckedNumeric<Promotion>::cast(lhs) \ + OP CheckedNumeric<Promotion>::cast(rhs); \ + } + +BASE_NUMERIC_ARITHMETIC_OPERATORS(Add, +, += ) +BASE_NUMERIC_ARITHMETIC_OPERATORS(Sub, -, -= ) +BASE_NUMERIC_ARITHMETIC_OPERATORS(Mul, *, *= ) +BASE_NUMERIC_ARITHMETIC_OPERATORS(Div, /, /= ) +BASE_NUMERIC_ARITHMETIC_OPERATORS(Mod, %, %= ) + +#undef BASE_NUMERIC_ARITHMETIC_OPERATORS + +} // namespace internal + +using internal::CheckedNumeric; + +} // namespace base + +#endif // BASE_SAFE_MATH_H_ diff --git a/chromium/base/numerics/safe_math_impl.h b/chromium/base/numerics/safe_math_impl.h new file mode 100644 index 00000000000..3b5e64dd5b5 --- /dev/null +++ b/chromium/base/numerics/safe_math_impl.h @@ -0,0 +1,502 @@ +// 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 SAFE_MATH_IMPL_H_ +#define SAFE_MATH_IMPL_H_ + +#include <stdint.h> + +#include <cmath> +#include <cstdlib> +#include <limits> + +#include "base/macros.h" +#include "base/numerics/safe_conversions.h" +#include "base/template_util.h" + +namespace base { +namespace internal { + +// Everything from here up to the floating point operations is portable C++, +// but it may not be fast. This code could be split based on +// platform/architecture and replaced with potentially faster implementations. + +// Integer promotion templates used by the portable checked integer arithmetic. +template <size_t Size, bool IsSigned> +struct IntegerForSizeAndSign; +template <> +struct IntegerForSizeAndSign<1, true> { + typedef int8_t type; +}; +template <> +struct IntegerForSizeAndSign<1, false> { + typedef uint8_t type; +}; +template <> +struct IntegerForSizeAndSign<2, true> { + typedef int16_t type; +}; +template <> +struct IntegerForSizeAndSign<2, false> { + typedef uint16_t type; +}; +template <> +struct IntegerForSizeAndSign<4, true> { + typedef int32_t type; +}; +template <> +struct IntegerForSizeAndSign<4, false> { + typedef uint32_t type; +}; +template <> +struct IntegerForSizeAndSign<8, true> { + typedef int64_t type; +}; +template <> +struct IntegerForSizeAndSign<8, false> { + typedef uint64_t type; +}; + +// WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to +// support 128-bit math, then the ArithmeticPromotion template below will need +// to be updated (or more likely replaced with a decltype expression). + +template <typename Integer> +struct UnsignedIntegerForSize { + typedef typename enable_if< + std::numeric_limits<Integer>::is_integer, + typename IntegerForSizeAndSign<sizeof(Integer), false>::type>::type type; +}; + +template <typename Integer> +struct SignedIntegerForSize { + typedef typename enable_if< + std::numeric_limits<Integer>::is_integer, + typename IntegerForSizeAndSign<sizeof(Integer), true>::type>::type type; +}; + +template <typename Integer> +struct TwiceWiderInteger { + typedef typename enable_if< + std::numeric_limits<Integer>::is_integer, + typename IntegerForSizeAndSign< + sizeof(Integer) * 2, + std::numeric_limits<Integer>::is_signed>::type>::type type; +}; + +template <typename Integer> +struct PositionOfSignBit { + static const typename enable_if<std::numeric_limits<Integer>::is_integer, + size_t>::type value = 8 * sizeof(Integer) - 1; +}; + +// Helper templates for integer manipulations. + +template <typename T> +bool HasSignBit(T x) { + // Cast to unsigned since right shift on signed is undefined. + return !!(static_cast<typename UnsignedIntegerForSize<T>::type>(x) >> + PositionOfSignBit<T>::value); +} + +// This wrapper undoes the standard integer promotions. +template <typename T> +T BinaryComplement(T x) { + return ~x; +} + +// Here are the actual portable checked integer math implementations. +// TODO(jschuh): Break this code out from the enable_if pattern and find a clean +// way to coalesce things into the CheckedNumericState specializations below. + +template <typename T> +typename enable_if<std::numeric_limits<T>::is_integer, T>::type +CheckedAdd(T x, T y, RangeConstraint* validity) { + // Since the value of x+y is undefined if we have a signed type, we compute + // it using the unsigned type of the same size. + typedef typename UnsignedIntegerForSize<T>::type UnsignedDst; + UnsignedDst ux = static_cast<UnsignedDst>(x); + UnsignedDst uy = static_cast<UnsignedDst>(y); + UnsignedDst uresult = ux + uy; + // Addition is valid if the sign of (x + y) is equal to either that of x or + // that of y. + if (std::numeric_limits<T>::is_signed) { + if (HasSignBit(BinaryComplement((uresult ^ ux) & (uresult ^ uy)))) + *validity = RANGE_VALID; + else // Direction of wrap is inverse of result sign. + *validity = HasSignBit(uresult) ? RANGE_OVERFLOW : RANGE_UNDERFLOW; + + } else { // Unsigned is either valid or overflow. + *validity = BinaryComplement(x) >= y ? RANGE_VALID : RANGE_OVERFLOW; + } + return static_cast<T>(uresult); +} + +template <typename T> +typename enable_if<std::numeric_limits<T>::is_integer, T>::type +CheckedSub(T x, T y, RangeConstraint* validity) { + // Since the value of x+y is undefined if we have a signed type, we compute + // it using the unsigned type of the same size. + typedef typename UnsignedIntegerForSize<T>::type UnsignedDst; + UnsignedDst ux = static_cast<UnsignedDst>(x); + UnsignedDst uy = static_cast<UnsignedDst>(y); + UnsignedDst uresult = ux - uy; + // Subtraction is valid if either x and y have same sign, or (x-y) and x have + // the same sign. + if (std::numeric_limits<T>::is_signed) { + if (HasSignBit(BinaryComplement((uresult ^ ux) & (ux ^ uy)))) + *validity = RANGE_VALID; + else // Direction of wrap is inverse of result sign. + *validity = HasSignBit(uresult) ? RANGE_OVERFLOW : RANGE_UNDERFLOW; + + } else { // Unsigned is either valid or underflow. + *validity = x >= y ? RANGE_VALID : RANGE_UNDERFLOW; + } + return static_cast<T>(uresult); +} + +// Integer multiplication is a bit complicated. In the fast case we just +// we just promote to a twice wider type, and range check the result. In the +// slow case we need to manually check that the result won't be truncated by +// checking with division against the appropriate bound. +template <typename T> +typename enable_if< + std::numeric_limits<T>::is_integer && sizeof(T) * 2 <= sizeof(uintmax_t), + T>::type +CheckedMul(T x, T y, RangeConstraint* validity) { + typedef typename TwiceWiderInteger<T>::type IntermediateType; + IntermediateType tmp = + static_cast<IntermediateType>(x) * static_cast<IntermediateType>(y); + *validity = DstRangeRelationToSrcRange<T>(tmp); + return static_cast<T>(tmp); +} + +template <typename T> +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)) { + return RANGE_VALID; + + } else if (x > 0) { + if (y > 0) + *validity = + x <= std::numeric_limits<T>::max() / y ? RANGE_VALID : RANGE_OVERFLOW; + else + *validity = y >= std::numeric_limits<T>::min() / x ? RANGE_VALID + : RANGE_UNDERFLOW; + + } else { + if (y > 0) + *validity = x >= std::numeric_limits<T>::min() / y ? RANGE_VALID + : RANGE_UNDERFLOW; + else + *validity = + y >= std::numeric_limits<T>::max() / x ? RANGE_VALID : RANGE_OVERFLOW; + } + + return x * y; +} + +template <typename T> +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) { + *validity = (y == 0 || x <= std::numeric_limits<T>::max() / y) + ? RANGE_VALID + : RANGE_OVERFLOW; + return x * y; +} + +// Division just requires a check for an invalid negation on signed min/-1. +template <typename T> +T CheckedDiv( + T x, + T y, + RangeConstraint* validity, + typename enable_if<std::numeric_limits<T>::is_integer, int>::type = 0) { + if (std::numeric_limits<T>::is_signed && x == std::numeric_limits<T>::min() && + y == static_cast<T>(-1)) { + *validity = RANGE_OVERFLOW; + return std::numeric_limits<T>::min(); + } + + *validity = RANGE_VALID; + return x / y; +} + +template <typename T> +typename enable_if< + std::numeric_limits<T>::is_integer&& std::numeric_limits<T>::is_signed, + T>::type +CheckedMod(T x, T y, RangeConstraint* validity) { + *validity = y > 0 ? RANGE_VALID : RANGE_INVALID; + return x % y; +} + +template <typename T> +typename enable_if< + std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed, + T>::type +CheckedMod(T x, T y, RangeConstraint* validity) { + *validity = RANGE_VALID; + return x % y; +} + +template <typename T> +typename enable_if< + std::numeric_limits<T>::is_integer&& std::numeric_limits<T>::is_signed, + T>::type +CheckedNeg(T value, RangeConstraint* validity) { + *validity = + value != std::numeric_limits<T>::min() ? RANGE_VALID : RANGE_OVERFLOW; + // The negation of signed min is min, so catch that one. + return -value; +} + +template <typename T> +typename enable_if< + std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed, + T>::type +CheckedNeg(T value, RangeConstraint* validity) { + // The only legal unsigned negation is zero. + *validity = value ? RANGE_UNDERFLOW : RANGE_VALID; + return static_cast<T>( + -static_cast<typename SignedIntegerForSize<T>::type>(value)); +} + +template <typename T> +typename enable_if< + std::numeric_limits<T>::is_integer&& std::numeric_limits<T>::is_signed, + T>::type +CheckedAbs(T value, RangeConstraint* validity) { + *validity = + value != std::numeric_limits<T>::min() ? RANGE_VALID : RANGE_OVERFLOW; + return std::abs(value); +} + +template <typename T> +typename enable_if< + std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed, + T>::type +CheckedAbs(T value, RangeConstraint* validity) { + // Absolute value of a positive is just its identiy. + *validity = RANGE_VALID; + return value; +} + +// These are the floating point stubs that the compiler needs to see. Only the +// negation operation is ever called. +#define BASE_FLOAT_ARITHMETIC_STUBS(NAME) \ + template <typename T> \ + typename enable_if<std::numeric_limits<T>::is_iec559, T>::type \ + Checked##NAME(T, T, RangeConstraint*) { \ + NOTREACHED(); \ + return 0; \ + } + +BASE_FLOAT_ARITHMETIC_STUBS(Add) +BASE_FLOAT_ARITHMETIC_STUBS(Sub) +BASE_FLOAT_ARITHMETIC_STUBS(Mul) +BASE_FLOAT_ARITHMETIC_STUBS(Div) +BASE_FLOAT_ARITHMETIC_STUBS(Mod) + +#undef BASE_FLOAT_ARITHMETIC_STUBS + +template <typename T> +typename enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedNeg( + T value, + RangeConstraint*) { + return -value; +} + +template <typename T> +typename enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedAbs( + T value, + RangeConstraint*) { + return std::abs(value); +} + +// Floats carry around their validity state with them, but integers do not. So, +// we wrap the underlying value in a specialization in order to hide that detail +// and expose an interface via accessors. +enum NumericRepresentation { + NUMERIC_INTEGER, + NUMERIC_FLOATING, + NUMERIC_UNKNOWN +}; + +template <typename NumericType> +struct GetNumericRepresentation { + static const NumericRepresentation value = + std::numeric_limits<NumericType>::is_integer + ? NUMERIC_INTEGER + : (std::numeric_limits<NumericType>::is_iec559 ? NUMERIC_FLOATING + : NUMERIC_UNKNOWN); +}; + +template <typename T, NumericRepresentation type = + GetNumericRepresentation<T>::value> +class CheckedNumericState {}; + +// Integrals require quite a bit of additional housekeeping to manage state. +template <typename T> +class CheckedNumericState<T, NUMERIC_INTEGER> { + private: + T value_; + RangeConstraint validity_; + + public: + template <typename Src, NumericRepresentation type> + friend class CheckedNumericState; + + CheckedNumericState() : value_(0), validity_(RANGE_VALID) {} + + template <typename Src> + CheckedNumericState(Src value, RangeConstraint validity) + : value_(value), + validity_(GetRangeConstraint(validity | + DstRangeRelationToSrcRange<T>(value))) { + COMPILE_ASSERT(std::numeric_limits<Src>::is_specialized, + argument_must_be_numeric); + } + + // Copy constructor. + template <typename Src> + CheckedNumericState(const CheckedNumericState<Src>& rhs) + : value_(static_cast<T>(rhs.value())), + validity_(GetRangeConstraint( + rhs.validity() | DstRangeRelationToSrcRange<T>(rhs.value()))) {} + + template <typename Src> + explicit CheckedNumericState( + Src value, + typename enable_if<std::numeric_limits<Src>::is_specialized, int>::type = + 0) + : value_(static_cast<T>(value)), + validity_(DstRangeRelationToSrcRange<T>(value)) {} + + RangeConstraint validity() const { return validity_; } + T value() const { return value_; } +}; + +// Floating points maintain their own validity, but need translation wrappers. +template <typename T> +class CheckedNumericState<T, NUMERIC_FLOATING> { + private: + T value_; + + public: + template <typename Src, NumericRepresentation type> + friend class CheckedNumericState; + + CheckedNumericState() : value_(0.0) {} + + template <typename Src> + CheckedNumericState( + Src value, + RangeConstraint validity, + typename enable_if<std::numeric_limits<Src>::is_integer, int>::type = 0) { + switch (DstRangeRelationToSrcRange<T>(value)) { + case RANGE_VALID: + value_ = static_cast<T>(value); + break; + + case RANGE_UNDERFLOW: + value_ = -std::numeric_limits<T>::infinity(); + break; + + case RANGE_OVERFLOW: + value_ = std::numeric_limits<T>::infinity(); + break; + + case RANGE_INVALID: + value_ = std::numeric_limits<T>::quiet_NaN(); + break; + + default: + NOTREACHED(); + } + } + + template <typename Src> + explicit CheckedNumericState( + Src value, + typename enable_if<std::numeric_limits<Src>::is_specialized, int>::type = + 0) + : value_(static_cast<T>(value)) {} + + // Copy constructor. + template <typename Src> + CheckedNumericState(const CheckedNumericState<Src>& rhs) + : value_(static_cast<T>(rhs.value())) {} + + RangeConstraint validity() const { + return GetRangeConstraint(value_ <= std::numeric_limits<T>::max(), + value_ >= -std::numeric_limits<T>::max()); + } + T value() const { return value_; } +}; + +// For integers less than 128-bit and floats 32-bit or larger, we can distil +// C/C++ arithmetic promotions down to two simple rules: +// 1. The type with the larger maximum exponent always takes precedence. +// 2. The resulting type must be promoted to at least an int. +// The following template specializations implement that promotion logic. +enum ArithmeticPromotionCategory { + LEFT_PROMOTION, + RIGHT_PROMOTION, + DEFAULT_PROMOTION +}; + +template <typename Lhs, + typename Rhs = Lhs, + ArithmeticPromotionCategory Promotion = + (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value) + ? (MaxExponent<Lhs>::value > MaxExponent<int>::value + ? LEFT_PROMOTION + : DEFAULT_PROMOTION) + : (MaxExponent<Rhs>::value > MaxExponent<int>::value + ? RIGHT_PROMOTION + : DEFAULT_PROMOTION) > +struct ArithmeticPromotion; + +template <typename Lhs, typename Rhs> +struct ArithmeticPromotion<Lhs, Rhs, LEFT_PROMOTION> { + typedef Lhs type; +}; + +template <typename Lhs, typename Rhs> +struct ArithmeticPromotion<Lhs, Rhs, RIGHT_PROMOTION> { + typedef Rhs type; +}; + +template <typename Lhs, typename Rhs> +struct ArithmeticPromotion<Lhs, Rhs, DEFAULT_PROMOTION> { + typedef int type; +}; + +// We can statically check if operations on the provided types can wrap, so we +// can skip the checked operations if they're not needed. So, for an integer we +// care if the destination type preserves the sign and is twice the width of +// the source. +template <typename T, typename Lhs, typename Rhs> +struct IsIntegerArithmeticSafe { + static const bool value = !std::numeric_limits<T>::is_iec559 && + StaticDstRangeRelationToSrcRange<T, Lhs>::value == + NUMERIC_RANGE_CONTAINED && + sizeof(T) >= (2 * sizeof(Lhs)) && + StaticDstRangeRelationToSrcRange<T, Rhs>::value != + NUMERIC_RANGE_CONTAINED && + sizeof(T) >= (2 * sizeof(Rhs)); +}; + +} // namespace internal +} // namespace base + +#endif // SAFE_MATH_IMPL_H_ diff --git a/chromium/base/numerics/safe_numerics_unittest.cc b/chromium/base/numerics/safe_numerics_unittest.cc new file mode 100644 index 00000000000..09ad1305a14 --- /dev/null +++ b/chromium/base/numerics/safe_numerics_unittest.cc @@ -0,0 +1,580 @@ +// 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. + +#if defined(COMPILER_MSVC) && defined(ARCH_CPU_32_BITS) +#include <mmintrin.h> +#endif +#include <stdint.h> + +#include <limits> + +#include "base/compiler_specific.h" +#include "base/numerics/safe_conversions.h" +#include "base/numerics/safe_math.h" +#include "base/template_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +using std::numeric_limits; +using base::CheckedNumeric; +using base::checked_cast; +using base::saturated_cast; +using base::internal::MaxExponent; +using base::internal::RANGE_VALID; +using base::internal::RANGE_INVALID; +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 +} + +// Helper macros to wrap displaying the conversion types and line numbers. +#define TEST_EXPECTED_VALIDITY(expected, actual) \ + EXPECT_EQ(expected, CheckedNumeric<Dst>(actual).validity()) \ + << "Result test: Value " << +(actual).ValueUnsafe() << " as " << dst \ + << " on line " << line; + +#define TEST_EXPECTED_VALUE(expected, actual) \ + EXPECT_EQ(static_cast<Dst>(expected), \ + CheckedNumeric<Dst>(actual).ValueUnsafe()) \ + << "Result test: Value " << +((actual).ValueUnsafe()) << " as " << dst \ + << " on line " << line; + +// Signed integer arithmetic. +template <typename Dst> +static void TestSpecializedArithmetic( + const char* dst, + int line, + typename enable_if< + numeric_limits<Dst>::is_integer&& numeric_limits<Dst>::is_signed, + int>::type = 0) { + typedef numeric_limits<Dst> DstLimits; + TEST_EXPECTED_VALIDITY(RANGE_OVERFLOW, + -CheckedNumeric<Dst>(DstLimits::min())); + TEST_EXPECTED_VALIDITY(RANGE_OVERFLOW, + CheckedNumeric<Dst>(DstLimits::min()).Abs()); + TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(-1).Abs()); + + TEST_EXPECTED_VALIDITY(RANGE_VALID, + CheckedNumeric<Dst>(DstLimits::max()) + -1); + TEST_EXPECTED_VALIDITY(RANGE_UNDERFLOW, + CheckedNumeric<Dst>(DstLimits::min()) + -1); + TEST_EXPECTED_VALIDITY( + RANGE_UNDERFLOW, + CheckedNumeric<Dst>(-DstLimits::max()) + -DstLimits::max()); + + TEST_EXPECTED_VALIDITY(RANGE_UNDERFLOW, + CheckedNumeric<Dst>(DstLimits::min()) - 1); + TEST_EXPECTED_VALIDITY(RANGE_VALID, + CheckedNumeric<Dst>(DstLimits::min()) - -1); + TEST_EXPECTED_VALIDITY( + RANGE_OVERFLOW, + CheckedNumeric<Dst>(DstLimits::max()) - -DstLimits::max()); + TEST_EXPECTED_VALIDITY( + RANGE_UNDERFLOW, + CheckedNumeric<Dst>(-DstLimits::max()) - DstLimits::max()); + + TEST_EXPECTED_VALIDITY(RANGE_UNDERFLOW, + CheckedNumeric<Dst>(DstLimits::min()) * 2); + + TEST_EXPECTED_VALIDITY(RANGE_OVERFLOW, + CheckedNumeric<Dst>(DstLimits::min()) / -1); + TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(-1) / 2); + + // Modulus is legal only for integers. + TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>() % 1); + TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % 1); + TEST_EXPECTED_VALUE(-1, CheckedNumeric<Dst>(-1) % 2); + TEST_EXPECTED_VALIDITY(RANGE_INVALID, CheckedNumeric<Dst>(-1) % -2); + TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(DstLimits::min()) % 2); + TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(DstLimits::max()) % 2); + // Test all the different modulus combinations. + TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % CheckedNumeric<Dst>(1)); + TEST_EXPECTED_VALUE(0, 1 % CheckedNumeric<Dst>(1)); + TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % 1); + CheckedNumeric<Dst> checked_dst = 1; + TEST_EXPECTED_VALUE(0, checked_dst %= 1); +} + +// Unsigned integer arithmetic. +template <typename Dst> +static void TestSpecializedArithmetic( + const char* dst, + int line, + typename enable_if< + numeric_limits<Dst>::is_integer && !numeric_limits<Dst>::is_signed, + int>::type = 0) { + typedef numeric_limits<Dst> DstLimits; + TEST_EXPECTED_VALIDITY(RANGE_VALID, -CheckedNumeric<Dst>(DstLimits::min())); + TEST_EXPECTED_VALIDITY(RANGE_VALID, + CheckedNumeric<Dst>(DstLimits::min()).Abs()); + TEST_EXPECTED_VALIDITY(RANGE_UNDERFLOW, + CheckedNumeric<Dst>(DstLimits::min()) + -1); + TEST_EXPECTED_VALIDITY(RANGE_UNDERFLOW, + CheckedNumeric<Dst>(DstLimits::min()) - 1); + TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(DstLimits::min()) * 2); + TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) / 2); + + // Modulus is legal only for integers. + TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>() % 1); + TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % 1); + TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) % 2); + TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(DstLimits::min()) % 2); + TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(DstLimits::max()) % 2); + // Test all the different modulus combinations. + TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % CheckedNumeric<Dst>(1)); + TEST_EXPECTED_VALUE(0, 1 % CheckedNumeric<Dst>(1)); + TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % 1); + CheckedNumeric<Dst> checked_dst = 1; + TEST_EXPECTED_VALUE(0, checked_dst %= 1); +} + +// Floating point arithmetic. +template <typename Dst> +void TestSpecializedArithmetic( + const char* dst, + int line, + typename enable_if<numeric_limits<Dst>::is_iec559, int>::type = 0) { + typedef numeric_limits<Dst> DstLimits; + TEST_EXPECTED_VALIDITY(RANGE_VALID, -CheckedNumeric<Dst>(DstLimits::min())); + + TEST_EXPECTED_VALIDITY(RANGE_VALID, + CheckedNumeric<Dst>(DstLimits::min()).Abs()); + TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(-1).Abs()); + + TEST_EXPECTED_VALIDITY(RANGE_VALID, + CheckedNumeric<Dst>(DstLimits::min()) + -1); + TEST_EXPECTED_VALIDITY(RANGE_VALID, + CheckedNumeric<Dst>(DstLimits::max()) + 1); + TEST_EXPECTED_VALIDITY( + RANGE_UNDERFLOW, + CheckedNumeric<Dst>(-DstLimits::max()) + -DstLimits::max()); + + TEST_EXPECTED_VALIDITY( + RANGE_OVERFLOW, + CheckedNumeric<Dst>(DstLimits::max()) - -DstLimits::max()); + TEST_EXPECTED_VALIDITY( + RANGE_UNDERFLOW, + CheckedNumeric<Dst>(-DstLimits::max()) - DstLimits::max()); + + TEST_EXPECTED_VALIDITY(RANGE_VALID, + CheckedNumeric<Dst>(DstLimits::min()) * 2); + + TEST_EXPECTED_VALUE(-0.5, CheckedNumeric<Dst>(-1.0) / 2); + EXPECT_EQ(static_cast<Dst>(1.0), CheckedNumeric<Dst>(1.0).ValueFloating()); +} + +// Generic arithmetic tests. +template <typename Dst> +static void TestArithmetic(const char* dst, int line) { + typedef numeric_limits<Dst> DstLimits; + + EXPECT_EQ(true, CheckedNumeric<Dst>().IsValid()); + EXPECT_EQ(false, + CheckedNumeric<Dst>(CheckedNumeric<Dst>(DstLimits::max()) * + DstLimits::max()).IsValid()); + EXPECT_EQ(static_cast<Dst>(0), CheckedNumeric<Dst>().ValueOrDie()); + EXPECT_EQ(static_cast<Dst>(0), CheckedNumeric<Dst>().ValueOrDefault(1)); + EXPECT_EQ(static_cast<Dst>(1), + CheckedNumeric<Dst>(CheckedNumeric<Dst>(DstLimits::max()) * + DstLimits::max()).ValueOrDefault(1)); + + // Test the operator combinations. + TEST_EXPECTED_VALUE(2, CheckedNumeric<Dst>(1) + CheckedNumeric<Dst>(1)); + TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) - CheckedNumeric<Dst>(1)); + TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) * CheckedNumeric<Dst>(1)); + TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) / CheckedNumeric<Dst>(1)); + TEST_EXPECTED_VALUE(2, 1 + CheckedNumeric<Dst>(1)); + TEST_EXPECTED_VALUE(0, 1 - CheckedNumeric<Dst>(1)); + TEST_EXPECTED_VALUE(1, 1 * CheckedNumeric<Dst>(1)); + TEST_EXPECTED_VALUE(1, 1 / CheckedNumeric<Dst>(1)); + TEST_EXPECTED_VALUE(2, CheckedNumeric<Dst>(1) + 1); + TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) - 1); + TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) * 1); + TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) / 1); + CheckedNumeric<Dst> checked_dst = 1; + TEST_EXPECTED_VALUE(2, checked_dst += 1); + checked_dst = 1; + TEST_EXPECTED_VALUE(0, checked_dst -= 1); + checked_dst = 1; + TEST_EXPECTED_VALUE(1, checked_dst *= 1); + checked_dst = 1; + TEST_EXPECTED_VALUE(1, checked_dst /= 1); + + // Generic negation. + TEST_EXPECTED_VALUE(0, -CheckedNumeric<Dst>()); + TEST_EXPECTED_VALUE(-1, -CheckedNumeric<Dst>(1)); + TEST_EXPECTED_VALUE(1, -CheckedNumeric<Dst>(-1)); + TEST_EXPECTED_VALUE(static_cast<Dst>(DstLimits::max() * -1), + -CheckedNumeric<Dst>(DstLimits::max())); + + // Generic absolute value. + TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>().Abs()); + TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1).Abs()); + TEST_EXPECTED_VALUE(DstLimits::max(), + CheckedNumeric<Dst>(DstLimits::max()).Abs()); + + // Generic addition. + TEST_EXPECTED_VALUE(1, (CheckedNumeric<Dst>() + 1)); + TEST_EXPECTED_VALUE(2, (CheckedNumeric<Dst>(1) + 1)); + TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>(-1) + 1)); + TEST_EXPECTED_VALIDITY(RANGE_VALID, + CheckedNumeric<Dst>(DstLimits::min()) + 1); + TEST_EXPECTED_VALIDITY( + RANGE_OVERFLOW, CheckedNumeric<Dst>(DstLimits::max()) + DstLimits::max()); + + // Generic subtraction. + TEST_EXPECTED_VALUE(-1, (CheckedNumeric<Dst>() - 1)); + TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>(1) - 1)); + TEST_EXPECTED_VALUE(-2, (CheckedNumeric<Dst>(-1) - 1)); + TEST_EXPECTED_VALIDITY(RANGE_VALID, + CheckedNumeric<Dst>(DstLimits::max()) - 1); + + // Generic multiplication. + 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_VALIDITY( + RANGE_OVERFLOW, CheckedNumeric<Dst>(DstLimits::max()) * DstLimits::max()); + + // Generic division. + TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>() / 1); + TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) / 1); + TEST_EXPECTED_VALUE(DstLimits::min() / 2, + CheckedNumeric<Dst>(DstLimits::min()) / 2); + TEST_EXPECTED_VALUE(DstLimits::max() / 2, + CheckedNumeric<Dst>(DstLimits::max()) / 2); + + TestSpecializedArithmetic<Dst>(dst, line); +} + +// Helper macro to wrap displaying the conversion types and line numbers. +#define TEST_ARITHMETIC(Dst) TestArithmetic<Dst>(#Dst, __LINE__) + +TEST(SafeNumerics, SignedIntegerMath) { + TEST_ARITHMETIC(int8_t); + TEST_ARITHMETIC(int); + TEST_ARITHMETIC(intptr_t); + TEST_ARITHMETIC(intmax_t); +} + +TEST(SafeNumerics, UnsignedIntegerMath) { + TEST_ARITHMETIC(uint8_t); + TEST_ARITHMETIC(unsigned int); + TEST_ARITHMETIC(uintptr_t); + TEST_ARITHMETIC(uintmax_t); +} + +TEST(SafeNumerics, FloatingPointMath) { + TEST_ARITHMETIC(float); + TEST_ARITHMETIC(double); +} + +// Enumerates the five different conversions types we need to test. +enum NumericConversionType { + SIGN_PRESERVING_VALUE_PRESERVING, + SIGN_PRESERVING_NARROW, + SIGN_TO_UNSIGN_WIDEN_OR_EQUAL, + SIGN_TO_UNSIGN_NARROW, + UNSIGN_TO_SIGN_NARROW_OR_EQUAL, +}; + +// Template covering the different conversion tests. +template <typename Dst, typename Src, NumericConversionType conversion> +struct TestNumericConversion {}; + +// EXPECT_EQ wrappers providing specific detail on test failures. +#define TEST_EXPECTED_RANGE(expected, actual) \ + EXPECT_EQ(expected, base::internal::DstRangeRelationToSrcRange<Dst>(actual)) \ + << "Conversion test: " << src << " value " << actual << " to " << dst \ + << " on line " << line; + +template <typename Dst, typename Src> +struct TestNumericConversion<Dst, Src, SIGN_PRESERVING_VALUE_PRESERVING> { + static void Test(const char *dst, const char *src, int line) { + typedef numeric_limits<Src> SrcLimits; + typedef numeric_limits<Dst> DstLimits; + // Integral to floating. + COMPILE_ASSERT((DstLimits::is_iec559 && SrcLimits::is_integer) || + // Not floating to integral and... + (!(DstLimits::is_integer && SrcLimits::is_iec559) && + // Same sign, same numeric, source is narrower or same. + ((SrcLimits::is_signed == DstLimits::is_signed && + sizeof(Dst) >= sizeof(Src)) || + // Or signed destination and source is smaller + (DstLimits::is_signed && sizeof(Dst) > sizeof(Src)))), + comparison_must_be_sign_preserving_and_value_preserving); + + const CheckedNumeric<Dst> checked_dst = SrcLimits::max(); + ; + TEST_EXPECTED_VALIDITY(RANGE_VALID, checked_dst); + if (MaxExponent<Dst>::value > MaxExponent<Src>::value) { + if (MaxExponent<Dst>::value >= MaxExponent<Src>::value * 2 - 1) { + // At least twice larger type. + TEST_EXPECTED_VALIDITY(RANGE_VALID, SrcLimits::max() * checked_dst); + + } else { // Larger, but not at least twice as large. + TEST_EXPECTED_VALIDITY(RANGE_OVERFLOW, SrcLimits::max() * checked_dst); + TEST_EXPECTED_VALIDITY(RANGE_VALID, checked_dst + 1); + } + } else { // Same width type. + TEST_EXPECTED_VALIDITY(RANGE_OVERFLOW, checked_dst + 1); + } + + TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::max()); + TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1)); + if (SrcLimits::is_iec559) { + TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::max() * static_cast<Src>(-1)); + 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()); + } + } +}; + +template <typename Dst, typename Src> +struct TestNumericConversion<Dst, Src, SIGN_PRESERVING_NARROW> { + static void Test(const char *dst, const char *src, int line) { + typedef numeric_limits<Src> SrcLimits; + typedef numeric_limits<Dst> DstLimits; + COMPILE_ASSERT(SrcLimits::is_signed == DstLimits::is_signed, + destination_and_source_sign_must_be_the_same); + COMPILE_ASSERT(sizeof(Dst) < sizeof(Src) || + (DstLimits::is_integer && SrcLimits::is_iec559), + destination_must_be_narrower_than_source); + + const CheckedNumeric<Dst> checked_dst; + TEST_EXPECTED_VALIDITY(RANGE_OVERFLOW, checked_dst + SrcLimits::max()); + TEST_EXPECTED_VALUE(1, checked_dst + static_cast<Src>(1)); + TEST_EXPECTED_VALIDITY(RANGE_UNDERFLOW, checked_dst - SrcLimits::max()); + + TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::max()); + TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1)); + if (SrcLimits::is_iec559) { + TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::max() * -1); + TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(-1)); + 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()); + TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(-1)); + } else { + TEST_EXPECTED_VALIDITY(RANGE_INVALID, checked_dst - static_cast<Src>(1)); + TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::min()); + } + } +}; + +template <typename Dst, typename Src> +struct TestNumericConversion<Dst, Src, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL> { + static void Test(const char *dst, const char *src, int line) { + typedef numeric_limits<Src> SrcLimits; + typedef numeric_limits<Dst> DstLimits; + COMPILE_ASSERT(sizeof(Dst) >= sizeof(Src), + destination_must_be_equal_or_wider_than_source); + COMPILE_ASSERT(SrcLimits::is_signed, source_must_be_signed); + COMPILE_ASSERT(!DstLimits::is_signed, destination_must_be_unsigned); + + const CheckedNumeric<Dst> checked_dst; + TEST_EXPECTED_VALUE(SrcLimits::max(), checked_dst + SrcLimits::max()); + TEST_EXPECTED_VALIDITY(RANGE_UNDERFLOW, checked_dst + static_cast<Src>(-1)); + TEST_EXPECTED_VALIDITY(RANGE_UNDERFLOW, checked_dst + -SrcLimits::max()); + + TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::min()); + TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::max()); + TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1)); + TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, static_cast<Src>(-1)); + } +}; + +template <typename Dst, typename Src> +struct TestNumericConversion<Dst, Src, SIGN_TO_UNSIGN_NARROW> { + static void Test(const char *dst, const char *src, int line) { + typedef numeric_limits<Src> SrcLimits; + typedef numeric_limits<Dst> DstLimits; + COMPILE_ASSERT((DstLimits::is_integer && SrcLimits::is_iec559) || + (sizeof(Dst) < sizeof(Src)), + destination_must_be_narrower_than_source); + COMPILE_ASSERT(SrcLimits::is_signed, source_must_be_signed); + COMPILE_ASSERT(!DstLimits::is_signed, destination_must_be_unsigned); + + const CheckedNumeric<Dst> checked_dst; + TEST_EXPECTED_VALUE(1, checked_dst + static_cast<Src>(1)); + TEST_EXPECTED_VALIDITY(RANGE_OVERFLOW, checked_dst + SrcLimits::max()); + TEST_EXPECTED_VALIDITY(RANGE_UNDERFLOW, checked_dst + static_cast<Src>(-1)); + TEST_EXPECTED_VALIDITY(RANGE_UNDERFLOW, checked_dst + -SrcLimits::max()); + + TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::max()); + TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1)); + TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, static_cast<Src>(-1)); + if (SrcLimits::is_iec559) { + TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::max() * -1); + 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()); + } + } +}; + +template <typename Dst, typename Src> +struct TestNumericConversion<Dst, Src, UNSIGN_TO_SIGN_NARROW_OR_EQUAL> { + static void Test(const char *dst, const char *src, int line) { + typedef numeric_limits<Src> SrcLimits; + typedef numeric_limits<Dst> DstLimits; + COMPILE_ASSERT(sizeof(Dst) <= sizeof(Src), + destination_must_be_narrower_or_equal_to_source); + COMPILE_ASSERT(!SrcLimits::is_signed, source_must_be_unsigned); + COMPILE_ASSERT(DstLimits::is_signed, destination_must_be_signed); + + const CheckedNumeric<Dst> checked_dst; + TEST_EXPECTED_VALUE(1, checked_dst + static_cast<Src>(1)); + TEST_EXPECTED_VALIDITY(RANGE_OVERFLOW, checked_dst + SrcLimits::max()); + TEST_EXPECTED_VALUE(SrcLimits::min(), checked_dst + SrcLimits::min()); + + TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::min()); + TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::max()); + TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1)); + } +}; + +// Helper macro to wrap displaying the conversion types and line numbers +#define TEST_NUMERIC_CONVERSION(d, s, t) \ + TestNumericConversion<d, s, t>::Test(#d, #s, __LINE__) + +TEST(SafeNumerics, IntMinOperations) { + TEST_NUMERIC_CONVERSION(int8_t, int8_t, SIGN_PRESERVING_VALUE_PRESERVING); + TEST_NUMERIC_CONVERSION(uint8_t, uint8_t, SIGN_PRESERVING_VALUE_PRESERVING); + + TEST_NUMERIC_CONVERSION(int8_t, int, SIGN_PRESERVING_NARROW); + TEST_NUMERIC_CONVERSION(uint8_t, unsigned int, SIGN_PRESERVING_NARROW); + TEST_NUMERIC_CONVERSION(int8_t, float, SIGN_PRESERVING_NARROW); + + TEST_NUMERIC_CONVERSION(uint8_t, int8_t, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL); + + TEST_NUMERIC_CONVERSION(uint8_t, int, SIGN_TO_UNSIGN_NARROW); + TEST_NUMERIC_CONVERSION(uint8_t, intmax_t, SIGN_TO_UNSIGN_NARROW); + TEST_NUMERIC_CONVERSION(uint8_t, float, SIGN_TO_UNSIGN_NARROW); + + TEST_NUMERIC_CONVERSION(int8_t, unsigned int, UNSIGN_TO_SIGN_NARROW_OR_EQUAL); + TEST_NUMERIC_CONVERSION(int8_t, uintmax_t, UNSIGN_TO_SIGN_NARROW_OR_EQUAL); +} + +TEST(SafeNumerics, IntOperations) { + TEST_NUMERIC_CONVERSION(int, int, SIGN_PRESERVING_VALUE_PRESERVING); + TEST_NUMERIC_CONVERSION(unsigned int, unsigned int, + SIGN_PRESERVING_VALUE_PRESERVING); + TEST_NUMERIC_CONVERSION(int, int8_t, SIGN_PRESERVING_VALUE_PRESERVING); + TEST_NUMERIC_CONVERSION(unsigned int, uint8_t, + SIGN_PRESERVING_VALUE_PRESERVING); + TEST_NUMERIC_CONVERSION(int, uint8_t, SIGN_PRESERVING_VALUE_PRESERVING); + + TEST_NUMERIC_CONVERSION(int, intmax_t, SIGN_PRESERVING_NARROW); + TEST_NUMERIC_CONVERSION(unsigned int, uintmax_t, SIGN_PRESERVING_NARROW); + TEST_NUMERIC_CONVERSION(int, float, SIGN_PRESERVING_NARROW); + TEST_NUMERIC_CONVERSION(int, double, SIGN_PRESERVING_NARROW); + + TEST_NUMERIC_CONVERSION(unsigned int, int, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL); + TEST_NUMERIC_CONVERSION(unsigned int, int8_t, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL); + + TEST_NUMERIC_CONVERSION(unsigned int, intmax_t, SIGN_TO_UNSIGN_NARROW); + TEST_NUMERIC_CONVERSION(unsigned int, float, SIGN_TO_UNSIGN_NARROW); + TEST_NUMERIC_CONVERSION(unsigned int, double, SIGN_TO_UNSIGN_NARROW); + + TEST_NUMERIC_CONVERSION(int, unsigned int, UNSIGN_TO_SIGN_NARROW_OR_EQUAL); + TEST_NUMERIC_CONVERSION(int, uintmax_t, UNSIGN_TO_SIGN_NARROW_OR_EQUAL); +} + +TEST(SafeNumerics, IntMaxOperations) { + TEST_NUMERIC_CONVERSION(intmax_t, intmax_t, SIGN_PRESERVING_VALUE_PRESERVING); + TEST_NUMERIC_CONVERSION(uintmax_t, uintmax_t, + SIGN_PRESERVING_VALUE_PRESERVING); + TEST_NUMERIC_CONVERSION(intmax_t, int, SIGN_PRESERVING_VALUE_PRESERVING); + TEST_NUMERIC_CONVERSION(uintmax_t, unsigned int, + SIGN_PRESERVING_VALUE_PRESERVING); + TEST_NUMERIC_CONVERSION(intmax_t, unsigned int, + SIGN_PRESERVING_VALUE_PRESERVING); + TEST_NUMERIC_CONVERSION(intmax_t, uint8_t, SIGN_PRESERVING_VALUE_PRESERVING); + + TEST_NUMERIC_CONVERSION(intmax_t, float, SIGN_PRESERVING_NARROW); + TEST_NUMERIC_CONVERSION(intmax_t, double, SIGN_PRESERVING_NARROW); + + TEST_NUMERIC_CONVERSION(uintmax_t, int, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL); + TEST_NUMERIC_CONVERSION(uintmax_t, int8_t, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL); + + TEST_NUMERIC_CONVERSION(uintmax_t, float, SIGN_TO_UNSIGN_NARROW); + TEST_NUMERIC_CONVERSION(uintmax_t, double, SIGN_TO_UNSIGN_NARROW); + + TEST_NUMERIC_CONVERSION(intmax_t, uintmax_t, UNSIGN_TO_SIGN_NARROW_OR_EQUAL); +} + +TEST(SafeNumerics, FloatOperations) { + TEST_NUMERIC_CONVERSION(float, intmax_t, SIGN_PRESERVING_VALUE_PRESERVING); + TEST_NUMERIC_CONVERSION(float, uintmax_t, + SIGN_PRESERVING_VALUE_PRESERVING); + TEST_NUMERIC_CONVERSION(float, int, SIGN_PRESERVING_VALUE_PRESERVING); + TEST_NUMERIC_CONVERSION(float, unsigned int, + SIGN_PRESERVING_VALUE_PRESERVING); + + TEST_NUMERIC_CONVERSION(float, double, SIGN_PRESERVING_NARROW); +} + +TEST(SafeNumerics, DoubleOperations) { + TEST_NUMERIC_CONVERSION(double, intmax_t, SIGN_PRESERVING_VALUE_PRESERVING); + TEST_NUMERIC_CONVERSION(double, uintmax_t, + SIGN_PRESERVING_VALUE_PRESERVING); + TEST_NUMERIC_CONVERSION(double, int, SIGN_PRESERVING_VALUE_PRESERVING); + TEST_NUMERIC_CONVERSION(double, unsigned int, + SIGN_PRESERVING_VALUE_PRESERVING); +} + +TEST(SafeNumerics, SizeTOperations) { + TEST_NUMERIC_CONVERSION(size_t, int, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL); + TEST_NUMERIC_CONVERSION(int, size_t, UNSIGN_TO_SIGN_NARROW_OR_EQUAL); +} + +TEST(SafeNumerics, CastTests) { +// MSVC catches and warns that we're forcing saturation in these tests. +// Since that's intentional, we need to shut this warning off. +#if defined(COMPILER_MSVC) +#pragma warning(disable : 4756) +#endif + + int small_positive = 1; + int small_negative = -1; + double double_small = 1.0; + double double_large = numeric_limits<double>::max(); + double double_infinity = numeric_limits<float>::infinity(); + + // Just test that the cast compiles, since the other tests cover logic. + EXPECT_EQ(0, checked_cast<int>(static_cast<size_t>(0))); + + // Test various saturation corner cases. + EXPECT_EQ(saturated_cast<int>(small_negative), + static_cast<int>(small_negative)); + EXPECT_EQ(saturated_cast<int>(small_positive), + static_cast<int>(small_positive)); + EXPECT_EQ(saturated_cast<unsigned>(small_negative), + static_cast<unsigned>(0)); + EXPECT_EQ(saturated_cast<int>(double_small), + static_cast<int>(double_small)); + EXPECT_EQ(saturated_cast<int>(double_large), numeric_limits<int>::max()); + EXPECT_EQ(saturated_cast<float>(double_large), double_infinity); + EXPECT_EQ(saturated_cast<float>(-double_large), -double_infinity); +} + diff --git a/chromium/base/observer_list_unittest.cc b/chromium/base/observer_list_unittest.cc index 57843f401fc..1bda3dcadae 100644 --- a/chromium/base/observer_list_unittest.cc +++ b/chromium/base/observer_list_unittest.cc @@ -443,7 +443,7 @@ TEST(ObserverListTest, Existing) { EXPECT_TRUE(b.added); // B's adder should not have been notified because it was added during - // notificaiton. + // notification. EXPECT_EQ(0, b.adder.total); // Notify again to make sure b's adder is notified. @@ -467,7 +467,7 @@ TEST(ObserverListThreadSafeTest, Existing) { EXPECT_TRUE(b.added); // B's adder should not have been notified because it was added during - // notificaiton. + // notification. EXPECT_EQ(0, b.adder.total); // Notify again to make sure b's adder is notified. diff --git a/chromium/base/os_compat_android.cc b/chromium/base/os_compat_android.cc index ec221e480dc..b2756b2b226 100644 --- a/chromium/base/os_compat_android.cc +++ b/chromium/base/os_compat_android.cc @@ -9,7 +9,10 @@ #include <math.h> #include <sys/stat.h> #include <sys/syscall.h> + +#if !defined(__LP64__) #include <time64.h> +#endif #include "base/rand_util.h" #include "base/strings/string_piece.h" @@ -37,17 +40,19 @@ int futimes(int fd, const struct timeval tv[2]) { return syscall(__NR_utimensat, fd, NULL, ts, 0); } -// Android has only timegm64() and no timegm(). +#if !defined(__LP64__) +// 32-bit Android has only timegm64() and not timegm(). // We replicate the behaviour of timegm() when the result overflows time_t. time_t timegm(struct tm* const t) { // time_t is signed on Android. - static const time_t kTimeMax = ~(1 << (sizeof(time_t) * CHAR_BIT - 1)); - static const time_t kTimeMin = (1 << (sizeof(time_t) * CHAR_BIT - 1)); + static const time_t kTimeMax = ~(1L << (sizeof(time_t) * CHAR_BIT - 1)); + static const time_t kTimeMin = (1L << (sizeof(time_t) * CHAR_BIT - 1)); time64_t result = timegm64(t); if (result < kTimeMin || result > kTimeMax) return -1; return result; } +#endif // The following is only needed when building with GCC 4.6 or higher // (i.e. not with Android GCC 4.4.3, nor with Clang). diff --git a/chromium/base/path_service.cc b/chromium/base/path_service.cc index f0a6a844158..75833db9a11 100644 --- a/chromium/base/path_service.cc +++ b/chromium/base/path_service.cc @@ -187,7 +187,7 @@ bool PathService::Get(int key, FilePath* result) { // special case the current directory because it can never be cached if (key == base::DIR_CURRENT) - return file_util::GetCurrentDirectory(result); + return base::GetCurrentDirectory(result); Provider* provider = NULL; { @@ -233,13 +233,15 @@ bool PathService::Get(int key, FilePath* result) { // static bool PathService::Override(int key, const FilePath& path) { - // Just call the full function with true for the value of |create|. - return OverrideAndCreateIfNeeded(key, path, true); + // Just call the full function with true for the value of |create|, and + // assume that |path| may not be absolute yet. + return OverrideAndCreateIfNeeded(key, path, false, true); } // static bool PathService::OverrideAndCreateIfNeeded(int key, const FilePath& path, + bool is_absolute, bool create) { PathData* path_data = GetPathData(); DCHECK(path_data); @@ -259,9 +261,12 @@ bool PathService::OverrideAndCreateIfNeeded(int key, } // We need to have an absolute path. - file_path = MakeAbsoluteFilePath(file_path); - if (file_path.empty()) - return false; + if (!is_absolute) { + file_path = MakeAbsoluteFilePath(file_path); + if (file_path.empty()) + return false; + } + DCHECK(file_path.IsAbsolute()); base::AutoLock scoped_lock(path_data->lock); diff --git a/chromium/base/path_service.h b/chromium/base/path_service.h index 832b92b1581..554eb9e3464 100644 --- a/chromium/base/path_service.h +++ b/chromium/base/path_service.h @@ -41,13 +41,23 @@ class BASE_EXPORT PathService { // // WARNING: Consumers of PathService::Get may expect paths to be constant // over the lifetime of the app, so this method should be used with caution. + // + // Unit tests generally should use ScopedPathOverride instead. Overrides from + // one test should not carry over to another. static bool Override(int key, const base::FilePath& path); - // This function does the same as PathService::Override but it takes an extra - // parameter |create| which guides whether the directory to be overriden must + // This function does the same as PathService::Override but it takes extra + // parameters: + // - |is_absolute| indicates that |path| has already been expanded into an + // absolute path, otherwise MakeAbsoluteFilePath() will be used. This is + // useful to override paths that may not exist yet, since MakeAbsoluteFilePath + // fails for those. Note that MakeAbsoluteFilePath also expands symbolic + // links, even if path.IsAbsolute() is already true. + // - |create| guides whether the directory to be overriden must // be created in case it doesn't exist already. static bool OverrideAndCreateIfNeeded(int key, const base::FilePath& path, + bool is_absolute, bool create); // To extend the set of supported keys, you can register a path provider, diff --git a/chromium/base/path_service_unittest.cc b/chromium/base/path_service_unittest.cc index fbb2d766deb..7a70a89c41a 100644 --- a/chromium/base/path_service_unittest.cc +++ b/chromium/base/path_service_unittest.cc @@ -100,8 +100,9 @@ typedef PlatformTest PathServiceTest; TEST_F(PathServiceTest, Get) { for (int key = base::PATH_START + 1; key < base::PATH_END; ++key) { #if defined(OS_ANDROID) - if (key == base::FILE_MODULE || key == base::DIR_USER_DESKTOP) - continue; // Android doesn't implement FILE_MODULE and DIR_USER_DESKTOP; + if (key == base::FILE_MODULE || key == base::DIR_USER_DESKTOP || + key == base::DIR_HOME) + continue; // Android doesn't implement these. #elif defined(OS_IOS) if (key == base::DIR_USER_DESKTOP) continue; // iOS doesn't implement DIR_USER_DESKTOP; @@ -146,7 +147,7 @@ TEST_F(PathServiceTest, Get) { #endif } -// test that all versions of the Override function of PathService do what they +// Test that all versions of the Override function of PathService do what they // are supposed to do. TEST_F(PathServiceTest, Override) { int my_special_key = 666; @@ -162,12 +163,41 @@ TEST_F(PathServiceTest, Override) { // PathService::OverrideAndCreateIfNeeded should obey the |create| parameter. PathService::OverrideAndCreateIfNeeded(my_special_key, fake_cache_dir2, + false, false); EXPECT_FALSE(base::PathExists(fake_cache_dir2)); EXPECT_TRUE(PathService::OverrideAndCreateIfNeeded(my_special_key, fake_cache_dir2, + false, true)); EXPECT_TRUE(base::PathExists(fake_cache_dir2)); + +#if defined(OS_POSIX) + base::FilePath non_existent( + base::MakeAbsoluteFilePath(temp_dir.path()).AppendASCII("non_existent")); + EXPECT_TRUE(non_existent.IsAbsolute()); + EXPECT_FALSE(base::PathExists(non_existent)); +#if !defined(OS_ANDROID) + // This fails because MakeAbsoluteFilePath fails for non-existent files. + // Earlier versions of Bionic libc don't fail for non-existent files, so + // skip this check on Android. + EXPECT_FALSE(PathService::OverrideAndCreateIfNeeded(my_special_key, + non_existent, + false, + false)); +#endif + // This works because indicating that |non_existent| is absolute skips the + // internal MakeAbsoluteFilePath call. + EXPECT_TRUE(PathService::OverrideAndCreateIfNeeded(my_special_key, + non_existent, + true, + false)); + // Check that the path has been overridden and no directory was created. + EXPECT_FALSE(base::PathExists(non_existent)); + base::FilePath path; + EXPECT_TRUE(PathService::Get(my_special_key, &path)); + EXPECT_EQ(non_existent, path); +#endif } // Check if multiple overrides can co-exist. @@ -178,12 +208,12 @@ TEST_F(PathServiceTest, OverrideMultiple) { base::FilePath fake_cache_dir1(temp_dir.path().AppendASCII("1")); EXPECT_TRUE(PathService::Override(my_special_key, fake_cache_dir1)); EXPECT_TRUE(base::PathExists(fake_cache_dir1)); - ASSERT_EQ(1, file_util::WriteFile(fake_cache_dir1.AppendASCII("t1"), ".", 1)); + ASSERT_EQ(1, base::WriteFile(fake_cache_dir1.AppendASCII("t1"), ".", 1)); base::FilePath fake_cache_dir2(temp_dir.path().AppendASCII("2")); EXPECT_TRUE(PathService::Override(my_special_key + 1, fake_cache_dir2)); EXPECT_TRUE(base::PathExists(fake_cache_dir2)); - ASSERT_EQ(1, file_util::WriteFile(fake_cache_dir2.AppendASCII("t2"), ".", 1)); + ASSERT_EQ(1, base::WriteFile(fake_cache_dir2.AppendASCII("t2"), ".", 1)); base::FilePath result; EXPECT_TRUE(PathService::Get(my_special_key, &result)); diff --git a/chromium/base/pickle.cc b/chromium/base/pickle.cc index 12a32378788..a9e9a0fdfbc 100644 --- a/chromium/base/pickle.cc +++ b/chromium/base/pickle.cc @@ -19,8 +19,9 @@ const int Pickle::kPayloadUnit = 64; static const size_t kCapacityReadOnly = static_cast<size_t>(-1); PickleIterator::PickleIterator(const Pickle& pickle) - : read_ptr_(pickle.payload()), - read_end_ptr_(pickle.end_of_payload()) { + : payload_(pickle.payload()), + read_index_(0), + end_index_(pickle.payload_size()) { } template <typename Type> @@ -35,28 +36,40 @@ inline bool PickleIterator::ReadBuiltinType(Type* result) { return true; } +inline void PickleIterator::Advance(size_t size) { + size_t aligned_size = AlignInt(size, sizeof(uint32_t)); + if (end_index_ - read_index_ < aligned_size) { + read_index_ = end_index_; + } else { + read_index_ += aligned_size; + } +} + template<typename Type> inline const char* PickleIterator::GetReadPointerAndAdvance() { - const char* current_read_ptr = read_ptr_; - if (read_ptr_ + sizeof(Type) > read_end_ptr_) + if (sizeof(Type) > end_index_ - read_index_) { + read_index_ = end_index_; return NULL; - if (sizeof(Type) < sizeof(uint32)) - read_ptr_ += AlignInt(sizeof(Type), sizeof(uint32)); - else - read_ptr_ += sizeof(Type); + } + const char* current_read_ptr = payload_ + read_index_; + Advance(sizeof(Type)); return current_read_ptr; } const char* PickleIterator::GetReadPointerAndAdvance(int num_bytes) { - if (num_bytes < 0 || read_end_ptr_ - read_ptr_ < num_bytes) + if (num_bytes < 0 || + end_index_ - read_index_ < static_cast<size_t>(num_bytes)) { + read_index_ = end_index_; return NULL; - const char* current_read_ptr = read_ptr_; - read_ptr_ += AlignInt(num_bytes, sizeof(uint32)); + } + const char* current_read_ptr = payload_ + read_index_; + Advance(num_bytes); return current_read_ptr; } -inline const char* PickleIterator::GetReadPointerAndAdvance(int num_elements, - size_t size_element) { +inline const char* PickleIterator::GetReadPointerAndAdvance( + int num_elements, + size_t size_element) { // Check for int32 overflow. int64 num_bytes = static_cast<int64>(num_elements) * size_element; int num_bytes32 = static_cast<int>(num_bytes); @@ -332,6 +345,6 @@ inline void Pickle::WriteBytesCommon(const void* data, size_t length) { char* write = mutable_payload() + write_offset_; memcpy(write, data, length); memset(write + length, 0, data_len - length); - header_->payload_size = static_cast<uint32>(write_offset_ + length); + header_->payload_size = static_cast<uint32>(new_size); write_offset_ = new_size; } diff --git a/chromium/base/pickle.h b/chromium/base/pickle.h index dd34f54176c..2e3cd3664f8 100644 --- a/chromium/base/pickle.h +++ b/chromium/base/pickle.h @@ -20,13 +20,14 @@ class Pickle; // while the PickleIterator object is in use. class BASE_EXPORT PickleIterator { public: - PickleIterator() : read_ptr_(NULL), read_end_ptr_(NULL) {} + PickleIterator() : payload_(NULL), read_index_(0), end_index_(0) {} explicit PickleIterator(const Pickle& pickle); // 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. + // result could not be extracted. It is not possible to read from iterator + // after that. bool ReadBool(bool* result) WARN_UNUSED_RESULT; bool ReadInt(int* result) WARN_UNUSED_RESULT; bool ReadLong(long* result) WARN_UNUSED_RESULT; @@ -61,11 +62,15 @@ class BASE_EXPORT PickleIterator { // Read Type from Pickle. template <typename Type> - inline bool ReadBuiltinType(Type* result); + bool ReadBuiltinType(Type* result); + + // Advance read_index_ but do not allow it to exceed end_index_. + // Keeps read_index_ aligned. + void Advance(size_t size); // Get read pointer for Type and advance read pointer. template<typename Type> - inline const char* GetReadPointerAndAdvance(); + const char* GetReadPointerAndAdvance(); // Get read pointer for |num_bytes| and advance read pointer. This method // checks num_bytes for negativity and wrapping. @@ -73,12 +78,12 @@ class BASE_EXPORT PickleIterator { // Get read pointer for (num_elements * size_element) bytes and advance read // pointer. This method checks for int overflow, negativity and wrapping. - inline const char* GetReadPointerAndAdvance(int num_elements, - size_t size_element); + const char* GetReadPointerAndAdvance(int num_elements, + size_t size_element); - // Pointers to the Pickle data. - const char* read_ptr_; - const char* read_end_ptr_; + const char* payload_; // Start of our pickle's payload. + size_t read_index_; // Offset of the next readable byte in payload. + size_t end_index_; // Payload size. FRIEND_TEST_ALL_PREFIXES(PickleTest, GetReadPointerAndAdvance); }; @@ -277,7 +282,9 @@ class BASE_EXPORT Pickle { } // The payload is the pickle data immediately following the header. - size_t payload_size() const { return header_->payload_size; } + size_t payload_size() const { + return header_ ? header_->payload_size : 0; + } const char* payload() const { return reinterpret_cast<const char*>(header_) + header_size_; @@ -330,7 +337,7 @@ class BASE_EXPORT Pickle { size_t write_offset_; // Just like WriteBytes, but with a compile-time size, for performance. - template<size_t length> void WriteBytesStatic(const void* data); + template<size_t length> void BASE_EXPORT WriteBytesStatic(const void* data); // Writes a POD by copying its bytes. template <typename T> bool WritePOD(const T& data) { diff --git a/chromium/base/pickle_unittest.cc b/chromium/base/pickle_unittest.cc index b1c5925f668..bec25d2f5e2 100644 --- a/chromium/base/pickle_unittest.cc +++ b/chromium/base/pickle_unittest.cc @@ -262,7 +262,7 @@ TEST(PickleTest, Resize) { // one more byte should double the capacity pickle.WriteData(data_ptr, 1); - cur_payload += 5; + cur_payload += 8; EXPECT_EQ(unit * 4, pickle.capacity_after_header()); EXPECT_EQ(cur_payload, pickle.payload_size()); } diff --git a/chromium/base/platform_file.cc b/chromium/base/platform_file.cc index bb411b893fe..70700fcf802 100644 --- a/chromium/base/platform_file.cc +++ b/chromium/base/platform_file.cc @@ -14,18 +14,4 @@ PlatformFileInfo::PlatformFileInfo() PlatformFileInfo::~PlatformFileInfo() {} -#if !defined(OS_NACL) -PlatformFile CreatePlatformFile(const FilePath& name, - int flags, - bool* created, - PlatformFileError* error) { - if (name.ReferencesParent()) { - if (error) - *error = PLATFORM_FILE_ERROR_ACCESS_DENIED; - return kInvalidPlatformFileValue; - } - return CreatePlatformFileUnsafe(name, flags, created, error); -} -#endif - } // namespace base diff --git a/chromium/base/platform_file.h b/chromium/base/platform_file.h index d9a82cbd3f4..27686d1044b 100644 --- a/chromium/base/platform_file.h +++ b/chromium/base/platform_file.h @@ -21,7 +21,7 @@ namespace base { // *************************************************************************** // ***** Don't use anything from this file anymore. It is being removed! -// ***** Use base/files/base_file.h instead +// ***** Use base/files/file.h instead // *************************************************************************** // PLATFORM_FILE_(OPEN|CREATE).* are mutually exclusive. You should specify @@ -134,27 +134,6 @@ const PlatformFile kInvalidPlatformFileValue = -1; BASE_EXPORT PlatformFileError ErrnoToPlatformFileError(int saved_errno); #endif -// Creates or opens the given file. If |created| is provided, it will be set to -// true if a new file was created [or an old one truncated to zero length to -// simulate a new file, which can happen with PLATFORM_FILE_CREATE_ALWAYS], and -// false otherwise. |error| can be NULL. -// -// This function fails with 'access denied' if the |name| contains path -// traversal ('..') components. -BASE_EXPORT PlatformFile CreatePlatformFile(const FilePath& name, - int flags, - bool* created, - PlatformFileError* error); - -// Same as CreatePlatformFile but allows paths with traversal (like \..\) -// components. Use only with extreme care. -BASE_EXPORT PlatformFile CreatePlatformFileUnsafe(const FilePath& name, - int flags, - bool* created, - PlatformFileError* error); - -BASE_EXPORT FILE* FdopenPlatformFile(PlatformFile file, const char* mode); - // Closes a file handle. Returns |true| on success and |false| otherwise. BASE_EXPORT bool ClosePlatformFile(PlatformFile file); diff --git a/chromium/base/platform_file_posix.cc b/chromium/base/platform_file_posix.cc index 028a38276d6..cee1f611d11 100644 --- a/chromium/base/platform_file_posix.cc +++ b/chromium/base/platform_file_posix.cc @@ -117,108 +117,6 @@ static PlatformFileError CallFctnlFlock(PlatformFile file, bool do_lock) { } // namespace -// NaCl doesn't implement system calls to open files directly. -#if !defined(OS_NACL) -// TODO(erikkay): does it make sense to support PLATFORM_FILE_EXCLUSIVE_* here? -PlatformFile CreatePlatformFileUnsafe(const FilePath& name, - int flags, - bool* created, - PlatformFileError* error) { - base::ThreadRestrictions::AssertIOAllowed(); - - int open_flags = 0; - if (flags & PLATFORM_FILE_CREATE) - open_flags = O_CREAT | O_EXCL; - - if (created) - *created = false; - - if (flags & PLATFORM_FILE_CREATE_ALWAYS) { - DCHECK(!open_flags); - open_flags = O_CREAT | O_TRUNC; - } - - if (flags & PLATFORM_FILE_OPEN_TRUNCATED) { - DCHECK(!open_flags); - DCHECK(flags & PLATFORM_FILE_WRITE); - open_flags = O_TRUNC; - } - - if (!open_flags && !(flags & PLATFORM_FILE_OPEN) && - !(flags & PLATFORM_FILE_OPEN_ALWAYS)) { - NOTREACHED(); - errno = EOPNOTSUPP; - if (error) - *error = PLATFORM_FILE_ERROR_FAILED; - return kInvalidPlatformFileValue; - } - - if (flags & PLATFORM_FILE_WRITE && flags & PLATFORM_FILE_READ) { - open_flags |= O_RDWR; - } else if (flags & PLATFORM_FILE_WRITE) { - open_flags |= O_WRONLY; - } else if (!(flags & PLATFORM_FILE_READ) && - !(flags & PLATFORM_FILE_WRITE_ATTRIBUTES) && - !(flags & PLATFORM_FILE_APPEND) && - !(flags & PLATFORM_FILE_OPEN_ALWAYS)) { - NOTREACHED(); - } - - if (flags & PLATFORM_FILE_TERMINAL_DEVICE) - open_flags |= O_NOCTTY | O_NDELAY; - - if (flags & PLATFORM_FILE_APPEND && flags & PLATFORM_FILE_READ) - open_flags |= O_APPEND | O_RDWR; - else if (flags & PLATFORM_FILE_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 & PLATFORM_FILE_OPEN_ALWAYS) { - if (descriptor < 0) { - open_flags |= O_CREAT; - if (flags & PLATFORM_FILE_EXCLUSIVE_READ || - flags & PLATFORM_FILE_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 (created && descriptor >= 0) - *created = true; - } - } - - if (created && (descriptor >= 0) && - (flags & (PLATFORM_FILE_CREATE_ALWAYS | PLATFORM_FILE_CREATE))) - *created = true; - - if ((descriptor >= 0) && (flags & PLATFORM_FILE_DELETE_ON_CLOSE)) { - unlink(name.value().c_str()); - } - - if (error) { - if (descriptor >= 0) - *error = PLATFORM_FILE_OK; - else - *error = ErrnoToPlatformFileError(errno); - } - - return descriptor; -} - -FILE* FdopenPlatformFile(PlatformFile file, const char* mode) { - return fdopen(file, mode); -} -#endif // !defined(OS_NACL) - bool ClosePlatformFile(PlatformFile file) { base::ThreadRestrictions::AssertIOAllowed(); return !IGNORE_EINTR(close(file)); @@ -261,7 +159,7 @@ int ReadPlatformFileAtCurrentPos(PlatformFile file, char* data, int size) { int bytes_read = 0; int rv; do { - rv = HANDLE_EINTR(read(file, data, size)); + rv = HANDLE_EINTR(read(file, data + bytes_read, size - bytes_read)); if (rv <= 0) break; @@ -322,7 +220,7 @@ int WritePlatformFileAtCurrentPos(PlatformFile file, int bytes_written = 0; int rv; do { - rv = HANDLE_EINTR(write(file, data, size)); + rv = HANDLE_EINTR(write(file, data + bytes_written, size - bytes_written)); if (rv <= 0) break; diff --git a/chromium/base/platform_file_unittest.cc b/chromium/base/platform_file_unittest.cc index 9cf66a9369a..70058ea4ebf 100644 --- a/chromium/base/platform_file_unittest.cc +++ b/chromium/base/platform_file_unittest.cc @@ -25,6 +25,26 @@ int WriteFully(PlatformFile file, int64 offset, return WritePlatformFile(file, offset, data, size); } +PlatformFile CreatePlatformFile(const FilePath& path, + int flags, + bool* created, + PlatformFileError* error) { + File file(path, flags); + if (!file.IsValid()) { + if (error) + *error = static_cast<PlatformFileError>(file.error_details()); + return kInvalidPlatformFileValue; + } + + if (created) + *created = file.created(); + + if (error) + *error = PLATFORM_FILE_OK; + + return file.TakePlatformFile(); +} + } // namespace TEST(PlatformFile, CreatePlatformFile) { @@ -81,7 +101,7 @@ TEST(PlatformFile, CreatePlatformFile) { error_code = PLATFORM_FILE_OK; file = CreatePlatformFile( file_path, - PLATFORM_FILE_CREATE_ALWAYS | PLATFORM_FILE_READ, + PLATFORM_FILE_CREATE_ALWAYS | PLATFORM_FILE_WRITE, &created, &error_code); EXPECT_NE(kInvalidPlatformFileValue, file); diff --git a/chromium/base/platform_file_win.cc b/chromium/base/platform_file_win.cc index b5e07d7665f..af786ab090a 100644 --- a/chromium/base/platform_file_win.cc +++ b/chromium/base/platform_file_win.cc @@ -12,119 +12,6 @@ #include "base/threading/thread_restrictions.h" namespace base { -PlatformFile CreatePlatformFileUnsafe(const FilePath& name, - int flags, - bool* created, - PlatformFileError* error) { - base::ThreadRestrictions::AssertIOAllowed(); - - DWORD disposition = 0; - if (created) - *created = false; - - if (flags & PLATFORM_FILE_OPEN) - disposition = OPEN_EXISTING; - - if (flags & PLATFORM_FILE_CREATE) { - DCHECK(!disposition); - disposition = CREATE_NEW; - } - - if (flags & PLATFORM_FILE_OPEN_ALWAYS) { - DCHECK(!disposition); - disposition = OPEN_ALWAYS; - } - - if (flags & PLATFORM_FILE_CREATE_ALWAYS) { - DCHECK(!disposition); - disposition = CREATE_ALWAYS; - } - - if (flags & PLATFORM_FILE_OPEN_TRUNCATED) { - DCHECK(!disposition); - DCHECK(flags & PLATFORM_FILE_WRITE); - disposition = TRUNCATE_EXISTING; - } - - if (!disposition) { - NOTREACHED(); - return NULL; - } - - DWORD access = 0; - if (flags & PLATFORM_FILE_WRITE) - access = GENERIC_WRITE; - if (flags & PLATFORM_FILE_APPEND) { - DCHECK(!access); - access = FILE_APPEND_DATA; - } - if (flags & PLATFORM_FILE_READ) - access |= GENERIC_READ; - if (flags & PLATFORM_FILE_WRITE_ATTRIBUTES) - access |= FILE_WRITE_ATTRIBUTES; - if (flags & PLATFORM_FILE_EXECUTE) - access |= GENERIC_EXECUTE; - - DWORD sharing = (flags & PLATFORM_FILE_EXCLUSIVE_READ) ? 0 : FILE_SHARE_READ; - if (!(flags & PLATFORM_FILE_EXCLUSIVE_WRITE)) - sharing |= FILE_SHARE_WRITE; - if (flags & PLATFORM_FILE_SHARE_DELETE) - sharing |= FILE_SHARE_DELETE; - - DWORD create_flags = 0; - if (flags & PLATFORM_FILE_ASYNC) - create_flags |= FILE_FLAG_OVERLAPPED; - if (flags & PLATFORM_FILE_TEMPORARY) - create_flags |= FILE_ATTRIBUTE_TEMPORARY; - if (flags & PLATFORM_FILE_HIDDEN) - create_flags |= FILE_ATTRIBUTE_HIDDEN; - if (flags & PLATFORM_FILE_DELETE_ON_CLOSE) - create_flags |= FILE_FLAG_DELETE_ON_CLOSE; - if (flags & PLATFORM_FILE_BACKUP_SEMANTICS) - create_flags |= FILE_FLAG_BACKUP_SEMANTICS; - - HANDLE file = CreateFile(name.value().c_str(), access, sharing, NULL, - disposition, create_flags, NULL); - - if (INVALID_HANDLE_VALUE != file){ - // Don't allow directories to be opened without the proper flag (block ADS). - if (!(flags & PLATFORM_FILE_BACKUP_SEMANTICS)) { - BY_HANDLE_FILE_INFORMATION info = { 0 }; - BOOL result = GetFileInformationByHandle(file, &info); - DCHECK(result); - if (info.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY | - FILE_ATTRIBUTE_REPARSE_POINT)) { - CloseHandle(file); - file = INVALID_HANDLE_VALUE; - } - } - } - - if (created && (INVALID_HANDLE_VALUE != file)) { - if (flags & (PLATFORM_FILE_OPEN_ALWAYS)) - *created = (ERROR_ALREADY_EXISTS != GetLastError()); - else if (flags & (PLATFORM_FILE_CREATE_ALWAYS | PLATFORM_FILE_CREATE)) - *created = true; - } - - if (error) { - if (file != kInvalidPlatformFileValue) - *error = PLATFORM_FILE_OK; - else - *error = LastErrorToPlatformFileError(GetLastError()); - } - - return file; -} - -FILE* FdopenPlatformFile(PlatformFile file, const char* mode) { - if (file == kInvalidPlatformFileValue) - return NULL; - int fd = _open_osfhandle(reinterpret_cast<intptr_t>(file), 0); - if (fd < 0) - return NULL; - return _fdopen(fd, mode); -} bool ClosePlatformFile(PlatformFile file) { base::ThreadRestrictions::AssertIOAllowed(); diff --git a/chromium/base/port.h b/chromium/base/port.h index af4e450afe4..0a04d55f0f6 100644 --- a/chromium/base/port.h +++ b/chromium/base/port.h @@ -8,6 +8,10 @@ #include <stdarg.h> #include "build/build_config.h" +// DEPRECATED: Use ...LL and ...ULL suffixes. +// TODO(viettrungluu): Delete these. These are only here until |GG_(U)INT64_C| +// are deleted (some other header files (re)define |GG_(U)INT64_C|, so our +// definitions of them must exactly match theirs). #ifdef COMPILER_MSVC #define GG_LONGLONG(x) x##I64 #define GG_ULONGLONG(x) x##UI64 @@ -16,20 +20,10 @@ #define GG_ULONGLONG(x) x##ULL #endif -// Per C99 7.8.14, define __STDC_CONSTANT_MACROS before including <stdint.h> -// to get the INTn_C and UINTn_C macros for integer constants. It's difficult -// to guarantee any specific ordering of header includes, so it's difficult to -// guarantee that the INTn_C macros can be defined by including <stdint.h> at -// any specific point. Provide GG_INTn_C macros instead. - -#define GG_INT8_C(x) (x) -#define GG_INT16_C(x) (x) -#define GG_INT32_C(x) (x) +// DEPRECATED: In Chromium, we force-define __STDC_CONSTANT_MACROS, so you can +// just use the regular (U)INTn_C macros from <stdint.h>. +// TODO(viettrungluu): Remove the remaining GG_(U)INTn_C macros. #define GG_INT64_C(x) GG_LONGLONG(x) - -#define GG_UINT8_C(x) (x ## U) -#define GG_UINT16_C(x) (x ## U) -#define GG_UINT32_C(x) (x ## U) #define GG_UINT64_C(x) GG_ULONGLONG(x) // It's possible for functions that use a va_list, such as StringPrintf, to diff --git a/chromium/base/posix/file_descriptor_shuffle.cc b/chromium/base/posix/file_descriptor_shuffle.cc index 7bc9e26eb58..d2fd39a95aa 100644 --- a/chromium/base/posix/file_descriptor_shuffle.cc +++ b/chromium/base/posix/file_descriptor_shuffle.cc @@ -19,20 +19,24 @@ bool PerformInjectiveMultimapDestructive( int extra_fds[kMaxExtraFDs]; unsigned next_extra_fd = 0; - // DANGER: this function may not allocate. + // DANGER: this function must not allocate or lock. + // Cannot use STL iterators here, since debug iterators use locks. - for (InjectiveMultimap::iterator i = m->begin(); i != m->end(); ++i) { + for (size_t i_index = 0; i_index < m->size(); ++i_index) { + InjectiveMultimap::value_type* i = &(*m)[i_index]; int temp_fd = -1; // We DCHECK the injectiveness of the mapping. - for (InjectiveMultimap::iterator j = i + 1; j != m->end(); ++j) { + for (size_t j_index = i_index + 1; j_index < m->size(); ++j_index) { + InjectiveMultimap::value_type* j = &(*m)[j_index]; DCHECK(i->dest != j->dest) << "Both fd " << i->source << " and " << j->source << " map to " << i->dest; } const bool is_identity = i->source == i->dest; - for (InjectiveMultimap::iterator j = i + 1; j != m->end(); ++j) { + for (size_t j_index = i_index + 1; j_index < m->size(); ++j_index) { + InjectiveMultimap::value_type* j = &(*m)[j_index]; if (!is_identity && i->dest == j->source) { if (temp_fd == -1) { if (!delegate->Duplicate(&temp_fd, i->dest)) diff --git a/chromium/base/posix/file_descriptor_shuffle.h b/chromium/base/posix/file_descriptor_shuffle.h index 9cd918f2e15..5fa590bf47b 100644 --- a/chromium/base/posix/file_descriptor_shuffle.h +++ b/chromium/base/posix/file_descriptor_shuffle.h @@ -5,10 +5,10 @@ #ifndef BASE_POSIX_FILE_DESCRIPTOR_SHUFFLE_H_ #define BASE_POSIX_FILE_DESCRIPTOR_SHUFFLE_H_ -// This code exists to perform the shuffling of file descriptors which is -// commonly needed when forking subprocesses. The naive approve is very simple, -// just call dup2 to setup the desired descriptors, but wrong. It's tough to -// handle the edge cases (like mapping 0 -> 1, 1 -> 0) correctly. +// This code exists to shuffle file descriptors, which is commonly needed when +// forking subprocesses. The naive approach (just call dup2 to set up the +// desired descriptors) is very simple, but wrong: it won't handle edge cases +// (like mapping 0 -> 1, 1 -> 0) correctly. // // In order to unittest this code, it's broken into the abstract action (an // injective multimap) and the concrete code for dealing with file descriptors. diff --git a/chromium/base/posix/global_descriptors.h b/chromium/base/posix/global_descriptors.h index c7b9f87fa4b..3d7369c3174 100644 --- a/chromium/base/posix/global_descriptors.h +++ b/chromium/base/posix/global_descriptors.h @@ -41,7 +41,11 @@ class BASE_EXPORT GlobalDescriptors { // Often we want a canonical descriptor for a given Key. In this case, we add // the following constant to the key value: +#if !defined(OS_ANDROID) static const int kBaseDescriptor = 3; // 0, 1, 2 are already taken. +#else + static const int kBaseDescriptor = 4; // 3 used by __android_log_write(). +#endif // Return the singleton instance of GlobalDescriptors. static GlobalDescriptors* GetInstance(); diff --git a/chromium/base/posix/unix_domain_socket_linux.cc b/chromium/base/posix/unix_domain_socket_linux.cc index 757db983cd0..51b936ba45a 100644 --- a/chromium/base/posix/unix_domain_socket_linux.cc +++ b/chromium/base/posix/unix_domain_socket_linux.cc @@ -9,13 +9,35 @@ #include <sys/uio.h> #include <unistd.h> +#include <vector> + +#include "base/files/scoped_file.h" #include "base/logging.h" +#include "base/memory/scoped_vector.h" #include "base/pickle.h" #include "base/posix/eintr_wrapper.h" #include "base/stl_util.h" const size_t UnixDomainSocket::kMaxFileDescriptors = 16; +// Creates a connected pair of UNIX-domain SOCK_SEQPACKET sockets, and passes +// ownership of the newly allocated file descriptors to |one| and |two|. +// Returns true on success. +static bool CreateSocketPair(base::ScopedFD* one, base::ScopedFD* two) { + int raw_socks[2]; + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, raw_socks) == -1) + return false; + one->reset(raw_socks[0]); + two->reset(raw_socks[1]); + return true; +} + +// static +bool UnixDomainSocket::EnableReceiveProcessId(int fd) { + const int enable = 1; + return setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &enable, sizeof(enable)) == 0; +} + // static bool UnixDomainSocket::SendMsg(int fd, const void* buf, @@ -57,8 +79,17 @@ bool UnixDomainSocket::SendMsg(int fd, ssize_t UnixDomainSocket::RecvMsg(int fd, void* buf, size_t length, - std::vector<int>* fds) { - return UnixDomainSocket::RecvMsgWithFlags(fd, buf, length, 0, fds); + ScopedVector<base::ScopedFD>* fds) { + return UnixDomainSocket::RecvMsgWithPid(fd, buf, length, fds, NULL); +} + +// static +ssize_t UnixDomainSocket::RecvMsgWithPid(int fd, + void* buf, + size_t length, + ScopedVector<base::ScopedFD>* fds, + base::ProcessId* pid) { + return UnixDomainSocket::RecvMsgWithFlags(fd, buf, length, 0, fds, pid); } // static @@ -66,7 +97,8 @@ ssize_t UnixDomainSocket::RecvMsgWithFlags(int fd, void* buf, size_t length, int flags, - std::vector<int>* fds) { + ScopedVector<base::ScopedFD>* fds, + base::ProcessId* out_pid) { fds->clear(); struct msghdr msg = {}; @@ -74,7 +106,8 @@ ssize_t UnixDomainSocket::RecvMsgWithFlags(int fd, msg.msg_iov = &iov; msg.msg_iovlen = 1; - char control_buffer[CMSG_SPACE(sizeof(int) * kMaxFileDescriptors)]; + char control_buffer[CMSG_SPACE(sizeof(int) * kMaxFileDescriptors) + + CMSG_SPACE(sizeof(struct ucred))]; msg.msg_control = control_buffer; msg.msg_controllen = sizeof(control_buffer); @@ -84,17 +117,24 @@ ssize_t UnixDomainSocket::RecvMsgWithFlags(int fd, int* wire_fds = NULL; unsigned wire_fds_len = 0; + base::ProcessId pid = -1; if (msg.msg_controllen > 0) { struct cmsghdr* cmsg; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + const unsigned payload_len = cmsg->cmsg_len - CMSG_LEN(0); if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { - const unsigned payload_len = cmsg->cmsg_len - CMSG_LEN(0); DCHECK(payload_len % sizeof(int) == 0); + DCHECK(wire_fds == NULL); wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg)); wire_fds_len = payload_len / sizeof(int); - break; + } + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_CREDENTIALS) { + DCHECK(payload_len == sizeof(struct ucred)); + DCHECK(pid == -1); + pid = reinterpret_cast<struct ucred*>(CMSG_DATA(cmsg))->pid; } } } @@ -106,8 +146,21 @@ ssize_t UnixDomainSocket::RecvMsgWithFlags(int fd, return -1; } - fds->resize(wire_fds_len); - memcpy(vector_as_array(fds), wire_fds, sizeof(int) * wire_fds_len); + if (wire_fds) { + for (unsigned i = 0; i < wire_fds_len; ++i) + fds->push_back(new base::ScopedFD(wire_fds[i])); + } + + if (out_pid) { + // |pid| will legitimately be -1 if we read EOF, so only DCHECK if we + // actually received a message. Unfortunately, Linux allows sending zero + // length messages, which are indistinguishable from EOF, so this check + // has false negatives. + if (r > 0 || msg.msg_controllen > 0) + DCHECK_GE(pid, 0); + + *out_pid = pid; + } return r; } @@ -130,44 +183,42 @@ ssize_t UnixDomainSocket::SendRecvMsgWithFlags(int fd, int recvmsg_flags, int* result_fd, const Pickle& request) { - int fds[2]; - // This socketpair is only used for the IPC and is cleaned up before // returning. - if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds) == -1) + base::ScopedFD recv_sock, send_sock; + if (!CreateSocketPair(&recv_sock, &send_sock)) return -1; - std::vector<int> fd_vector; - fd_vector.push_back(fds[1]); - if (!SendMsg(fd, request.data(), request.size(), fd_vector)) { - close(fds[0]); - close(fds[1]); - return -1; + { + std::vector<int> send_fds; + send_fds.push_back(send_sock.get()); + if (!SendMsg(fd, request.data(), request.size(), send_fds)) + return -1; } - close(fds[1]); - fd_vector.clear(); + // Close the sending end of the socket right away so that if our peer closes + // it before sending a response (e.g., from exiting), RecvMsgWithFlags() will + // return EOF instead of hanging. + send_sock.reset(); + + ScopedVector<base::ScopedFD> recv_fds; // When porting to OSX keep in mind it doesn't support MSG_NOSIGNAL, so the // sender might get a SIGPIPE. - const ssize_t reply_len = RecvMsgWithFlags(fds[0], reply, max_reply_len, - recvmsg_flags, &fd_vector); - close(fds[0]); + const ssize_t reply_len = RecvMsgWithFlags( + recv_sock.get(), reply, max_reply_len, recvmsg_flags, &recv_fds, NULL); + recv_sock.reset(); if (reply_len == -1) return -1; - if ((!fd_vector.empty() && result_fd == NULL) || fd_vector.size() > 1) { - for (std::vector<int>::const_iterator - i = fd_vector.begin(); i != fd_vector.end(); ++i) { - close(*i); - } - + // If we received more file descriptors than caller expected, then we treat + // that as an error. + if (recv_fds.size() > (result_fd != NULL ? 1 : 0)) { NOTREACHED(); - return -1; } if (result_fd) - *result_fd = fd_vector.empty() ? -1 : fd_vector[0]; + *result_fd = recv_fds.empty() ? -1 : recv_fds[0]->release(); return reply_len; } diff --git a/chromium/base/posix/unix_domain_socket_linux.h b/chromium/base/posix/unix_domain_socket_linux.h index 66fb8bb1bf6..59bb8840b9d 100644 --- a/chromium/base/posix/unix_domain_socket_linux.h +++ b/chromium/base/posix/unix_domain_socket_linux.h @@ -10,6 +10,9 @@ #include <vector> #include "base/base_export.h" +#include "base/files/scoped_file.h" +#include "base/memory/scoped_vector.h" +#include "base/process/process_handle.h" class Pickle; @@ -18,6 +21,11 @@ class BASE_EXPORT UnixDomainSocket { // Maximum number of file descriptors that can be read by RecvMsg(). static const size_t kMaxFileDescriptors; + // Use to enable receiving process IDs in RecvMsgWithPid. Should be called on + // the receiving socket (i.e., the socket passed to RecvMsgWithPid). Returns + // true if successful. + static bool EnableReceiveProcessId(int fd); + // Use sendmsg to write the given msg and include a vector of file // descriptors. Returns true if successful. static bool SendMsg(int fd, @@ -30,7 +38,17 @@ class BASE_EXPORT UnixDomainSocket { static ssize_t RecvMsg(int fd, void* msg, size_t length, - std::vector<int>* fds); + ScopedVector<base::ScopedFD>* fds); + + // Same as RecvMsg above, but also returns the sender's process ID (as seen + // from the caller's namespace). However, before using this function to + // receive process IDs, EnableReceiveProcessId() should be called on the + // receiving socket. + static ssize_t RecvMsgWithPid(int fd, + void* msg, + size_t length, + ScopedVector<base::ScopedFD>* fds, + base::ProcessId* pid); // Perform a sendmsg/recvmsg pair. // 1. This process creates a UNIX SEQPACKET socketpair. Using @@ -70,7 +88,8 @@ class BASE_EXPORT UnixDomainSocket { void* msg, size_t length, int flags, - std::vector<int>* fds); + ScopedVector<base::ScopedFD>* fds, + base::ProcessId* pid); }; #endif // BASE_POSIX_UNIX_DOMAIN_SOCKET_LINUX_H_ diff --git a/chromium/base/posix/unix_domain_socket_linux_unittest.cc b/chromium/base/posix/unix_domain_socket_linux_unittest.cc index 22bb172ad5b..7b2e2af2332 100644 --- a/chromium/base/posix/unix_domain_socket_linux_unittest.cc +++ b/chromium/base/posix/unix_domain_socket_linux_unittest.cc @@ -9,6 +9,8 @@ #include "base/bind.h" #include "base/bind_helpers.h" #include "base/file_util.h" +#include "base/files/scoped_file.h" +#include "base/memory/scoped_vector.h" #include "base/pickle.h" #include "base/posix/unix_domain_socket_linux.h" #include "base/synchronization/waitable_event.h" @@ -25,8 +27,8 @@ TEST(UnixDomainSocketTest, SendRecvMsgAbortOnReplyFDClose) { int fds[2]; ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds)); - file_util::ScopedFD scoped_fd0(&fds[0]); - file_util::ScopedFD scoped_fd1(&fds[1]); + ScopedFD scoped_fd0(fds[0]); + ScopedFD scoped_fd1(fds[1]); // Have the thread send a synchronous message via the socket. Pickle request; @@ -37,7 +39,7 @@ TEST(UnixDomainSocketTest, SendRecvMsgAbortOnReplyFDClose) { request)); // Receive the message. - std::vector<int> message_fds; + ScopedVector<base::ScopedFD> message_fds; uint8_t buffer[16]; ASSERT_EQ(static_cast<int>(request.size()), UnixDomainSocket::RecvMsg(fds[0], buffer, sizeof(buffer), @@ -45,7 +47,7 @@ TEST(UnixDomainSocketTest, SendRecvMsgAbortOnReplyFDClose) { ASSERT_EQ(1U, message_fds.size()); // Close the reply FD. - ASSERT_EQ(0, IGNORE_EINTR(close(message_fds.front()))); + message_fds.clear(); // Check that the thread didn't get blocked. WaitableEvent event(false, false); @@ -62,7 +64,7 @@ TEST(UnixDomainSocketTest, SendRecvMsgAvoidsSIGPIPE) { ASSERT_EQ(0, sigaction(SIGPIPE, &act, &oldact)); int fds[2]; ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds)); - file_util::ScopedFD scoped_fd1(&fds[1]); + ScopedFD scoped_fd1(fds[1]); ASSERT_EQ(0, IGNORE_EINTR(close(fds[0]))); // Have the thread send a synchronous message via the socket. Unless the @@ -76,6 +78,84 @@ TEST(UnixDomainSocketTest, SendRecvMsgAvoidsSIGPIPE) { ASSERT_EQ(0, sigaction(SIGPIPE, &oldact, NULL)); } +// Simple sanity check within a single process that receiving PIDs works. +TEST(UnixDomainSocketTest, RecvPid) { + int fds[2]; + ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds)); + base::ScopedFD recv_sock(fds[0]); + base::ScopedFD send_sock(fds[1]); + + ASSERT_TRUE(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get())); + + static const char kHello[] = "hello"; + ASSERT_TRUE(UnixDomainSocket::SendMsg( + send_sock.get(), kHello, sizeof(kHello), std::vector<int>())); + + // Extra receiving buffer space to make sure we really received only + // sizeof(kHello) bytes and it wasn't just truncated to fit the buffer. + char buf[sizeof(kHello) + 1]; + base::ProcessId sender_pid; + ScopedVector<base::ScopedFD> fd_vec; + const ssize_t nread = UnixDomainSocket::RecvMsgWithPid( + recv_sock.get(), buf, sizeof(buf), &fd_vec, &sender_pid); + ASSERT_EQ(sizeof(kHello), static_cast<size_t>(nread)); + ASSERT_EQ(0, memcmp(buf, kHello, sizeof(kHello))); + ASSERT_EQ(0U, fd_vec.size()); + + ASSERT_EQ(getpid(), sender_pid); +} + +// Same as above, but send the max number of file descriptors too. +TEST(UnixDomainSocketTest, RecvPidWithMaxDescriptors) { + int fds[2]; + ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds)); + base::ScopedFD recv_sock(fds[0]); + base::ScopedFD send_sock(fds[1]); + + ASSERT_TRUE(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get())); + + static const char kHello[] = "hello"; + std::vector<int> send_fds(UnixDomainSocket::kMaxFileDescriptors, + send_sock.get()); + ASSERT_TRUE(UnixDomainSocket::SendMsg( + send_sock.get(), kHello, sizeof(kHello), send_fds)); + + // Extra receiving buffer space to make sure we really received only + // sizeof(kHello) bytes and it wasn't just truncated to fit the buffer. + char buf[sizeof(kHello) + 1]; + base::ProcessId sender_pid; + ScopedVector<base::ScopedFD> recv_fds; + const ssize_t nread = UnixDomainSocket::RecvMsgWithPid( + recv_sock.get(), buf, sizeof(buf), &recv_fds, &sender_pid); + ASSERT_EQ(sizeof(kHello), static_cast<size_t>(nread)); + ASSERT_EQ(0, memcmp(buf, kHello, sizeof(kHello))); + ASSERT_EQ(UnixDomainSocket::kMaxFileDescriptors, recv_fds.size()); + + ASSERT_EQ(getpid(), sender_pid); +} + +// Check that RecvMsgWithPid doesn't DCHECK fail when reading EOF from a +// disconnected socket. +TEST(UnixDomianSocketTest, RecvPidDisconnectedSocket) { + int fds[2]; + ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds)); + base::ScopedFD recv_sock(fds[0]); + base::ScopedFD send_sock(fds[1]); + + ASSERT_TRUE(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get())); + + send_sock.reset(); + + char ch; + base::ProcessId sender_pid; + ScopedVector<base::ScopedFD> recv_fds; + const ssize_t nread = UnixDomainSocket::RecvMsgWithPid( + recv_sock.get(), &ch, sizeof(ch), &recv_fds, &sender_pid); + ASSERT_EQ(0, nread); + ASSERT_EQ(-1, sender_pid); + ASSERT_EQ(0U, recv_fds.size()); +} + } // namespace } // 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 6609bd0e9e0..b8b16e1d344 100644 --- a/chromium/base/power_monitor/power_monitor_device_source_win.cc +++ b/chromium/base/power_monitor/power_monitor_device_source_win.cc @@ -55,7 +55,7 @@ void ProcessWmPowerBroadcastMessage(WPARAM event_id) { bool PowerMonitorDeviceSource::IsOnBatteryPowerImpl() { SYSTEM_POWER_STATUS status; if (!GetSystemPowerStatus(&status)) { - DLOG_GETLASTERROR(ERROR) << "GetSystemPowerStatus failed"; + DPLOG(ERROR) << "GetSystemPowerStatus failed"; return false; } return (status.ACLineStatus == 0); @@ -63,7 +63,7 @@ bool PowerMonitorDeviceSource::IsOnBatteryPowerImpl() { PowerMonitorDeviceSource::PowerMessageWindow::PowerMessageWindow() : instance_(NULL), message_hwnd_(NULL) { - if (MessageLoop::current()->type() != MessageLoop::TYPE_UI) { + if (!MessageLoopForUI::IsCurrent()) { // Creating this window in (e.g.) a renderer inhibits shutdown on Windows. // See http://crbug.com/230122. TODO(vandebo): http://crbug.com/236031 DLOG(ERROR) diff --git a/chromium/base/prefs/OWNERS b/chromium/base/prefs/OWNERS index 024da1cb54c..97ab6952245 100644 --- a/chromium/base/prefs/OWNERS +++ b/chromium/base/prefs/OWNERS @@ -1,4 +1,5 @@ battre@chromium.org bauerb@chromium.org +gab@chromium.org mnissler@chromium.org pam@chromium.org diff --git a/chromium/base/prefs/json_pref_store.cc b/chromium/base/prefs/json_pref_store.cc index ad97b8459cb..9180984d0e8 100644 --- a/chromium/base/prefs/json_pref_store.cc +++ b/chromium/base/prefs/json_pref_store.cc @@ -13,6 +13,7 @@ #include "base/json/json_string_value_serializer.h" #include "base/memory/ref_counted.h" #include "base/message_loop/message_loop_proxy.h" +#include "base/prefs/pref_filter.h" #include "base/sequenced_task_runner.h" #include "base/threading/sequenced_worker_pool.h" #include "base/values.h" @@ -36,19 +37,23 @@ class FileThreadDeserializer origin_loop_proxy_(base::MessageLoopProxy::current()) { } - void Start(const base::FilePath& path) { + void Start(const base::FilePath& path, + const base::FilePath& alternate_path) { DCHECK(origin_loop_proxy_->BelongsToCurrentThread()); + // TODO(gab): This should use PostTaskAndReplyWithResult instead of using + // the |error_| member to pass data across tasks. sequenced_task_runner_->PostTask( FROM_HERE, base::Bind(&FileThreadDeserializer::ReadFileAndReport, - this, path)); + this, path, alternate_path)); } // Deserializes JSON on the sequenced task runner. - void ReadFileAndReport(const base::FilePath& path) { + void ReadFileAndReport(const base::FilePath& path, + const base::FilePath& alternate_path) { DCHECK(sequenced_task_runner_->RunsTasksOnCurrentThread()); - value_.reset(DoReading(path, &error_, &no_dir_)); + value_.reset(DoReading(path, alternate_path, &error_, &no_dir_)); origin_loop_proxy_->PostTask( FROM_HERE, @@ -58,12 +63,18 @@ class FileThreadDeserializer // Reports deserialization result on the origin thread. void ReportOnOriginThread() { DCHECK(origin_loop_proxy_->BelongsToCurrentThread()); - delegate_->OnFileRead(value_.release(), error_, no_dir_); + delegate_->OnFileRead(value_.Pass(), error_, no_dir_); } static base::Value* DoReading(const base::FilePath& path, + const base::FilePath& alternate_path, PersistentPrefStore::PrefReadError* error, bool* no_dir) { + if (!base::PathExists(path) && !alternate_path.empty() && + base::PathExists(alternate_path)) { + base::Move(alternate_path, path); + } + int error_code; std::string error_msg; JSONFileValueSerializer serializer(path); @@ -151,14 +162,34 @@ scoped_refptr<base::SequencedTaskRunner> JsonPrefStore::GetTaskRunnerForFile( } JsonPrefStore::JsonPrefStore(const base::FilePath& filename, - base::SequencedTaskRunner* sequenced_task_runner) + 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), - read_error_(PREF_READ_ERROR_OTHER) {} + filtering_in_progress_(false), + read_error_(PREF_READ_ERROR_NONE) { +} + +JsonPrefStore::JsonPrefStore(const base::FilePath& filename, + const base::FilePath& alternate_filename, + base::SequencedTaskRunner* sequenced_task_runner, + scoped_ptr<PrefFilter> pref_filter) + : path_(filename), + alternate_path_(alternate_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) { +} bool JsonPrefStore::GetValue(const std::string& key, const base::Value** result) const { @@ -221,6 +252,12 @@ void JsonPrefStore::RemoveValue(const std::string& key) { ReportValueChanged(key); } +void JsonPrefStore::RemoveValueSilently(const std::string& key) { + prefs_->RemovePath(key, NULL); + if (!read_only_) + writer_.ScheduleWrite(this); +} + bool JsonPrefStore::ReadOnly() const { return read_only_; } @@ -231,23 +268,27 @@ PersistentPrefStore::PrefReadError JsonPrefStore::GetReadError() const { PersistentPrefStore::PrefReadError JsonPrefStore::ReadPrefs() { if (path_.empty()) { - OnFileRead(NULL, PREF_READ_ERROR_FILE_NOT_SPECIFIED, false); + OnFileRead( + scoped_ptr<base::Value>(), PREF_READ_ERROR_FILE_NOT_SPECIFIED, false); return PREF_READ_ERROR_FILE_NOT_SPECIFIED; } PrefReadError error; bool no_dir; - base::Value* value = - FileThreadDeserializer::DoReading(path_, &error, &no_dir); - OnFileRead(value, error, no_dir); - return error; + scoped_ptr<base::Value> value( + FileThreadDeserializer::DoReading(path_, alternate_path_, &error, + &no_dir)); + OnFileRead(value.Pass(), error, no_dir); + return filtering_in_progress_ ? PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE : + error; } -void JsonPrefStore::ReadPrefsAsync(ReadErrorDelegate *error_delegate) { +void JsonPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) { initialized_ = false; error_delegate_.reset(error_delegate); if (path_.empty()) { - OnFileRead(NULL, PREF_READ_ERROR_FILE_NOT_SPECIFIED, false); + OnFileRead( + scoped_ptr<base::Value>(), PREF_READ_ERROR_FILE_NOT_SPECIFIED, false); return; } @@ -255,7 +296,7 @@ void JsonPrefStore::ReadPrefsAsync(ReadErrorDelegate *error_delegate) { // in the end. scoped_refptr<FileThreadDeserializer> deserializer( new FileThreadDeserializer(this, sequenced_task_runner_.get())); - deserializer->Start(path_); + deserializer->Start(path_, alternate_path_); } void JsonPrefStore::CommitPendingWrite() { @@ -264,63 +305,117 @@ void JsonPrefStore::CommitPendingWrite() { } void JsonPrefStore::ReportValueChanged(const std::string& key) { + if (pref_filter_) + pref_filter_->FilterUpdate(key); + FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key)); + if (!read_only_) writer_.ScheduleWrite(this); } -void JsonPrefStore::OnFileRead(base::Value* value_owned, +void JsonPrefStore::RegisterOnNextSuccessfulWriteCallback( + const base::Closure& on_next_successful_write) { + writer_.RegisterOnNextSuccessfulWriteCallback(on_next_successful_write); +} + +void JsonPrefStore::OnFileRead(scoped_ptr<base::Value> value, PersistentPrefStore::PrefReadError error, bool no_dir) { - scoped_ptr<base::Value> value(value_owned); + scoped_ptr<base::DictionaryValue> unfiltered_prefs(new base::DictionaryValue); + read_error_ = error; - if (no_dir) { + bool initialization_successful = !no_dir; + + if (initialization_successful) { + switch (read_error_) { + case PREF_READ_ERROR_ACCESS_DENIED: + case PREF_READ_ERROR_FILE_OTHER: + case PREF_READ_ERROR_FILE_LOCKED: + case PREF_READ_ERROR_JSON_TYPE: + case PREF_READ_ERROR_FILE_NOT_SPECIFIED: + read_only_ = true; + break; + case PREF_READ_ERROR_NONE: + DCHECK(value.get()); + unfiltered_prefs.reset( + static_cast<base::DictionaryValue*>(value.release())); + break; + case PREF_READ_ERROR_NO_FILE: + // If the file just doesn't exist, maybe this is first run. In any case + // there's no harm in writing out default prefs in this case. + break; + case PREF_READ_ERROR_JSON_PARSE: + case PREF_READ_ERROR_JSON_REPEAT: + break; + case PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE: + // This is a special error code to be returned by ReadPrefs when it + // can't complete synchronously, it should never be returned by the read + // operation itself. + NOTREACHED(); + break; + case PREF_READ_ERROR_LEVELDB_IO: + case PREF_READ_ERROR_LEVELDB_CORRUPTION_READ_ONLY: + case PREF_READ_ERROR_LEVELDB_CORRUPTION: + // These are specific to LevelDBPrefStore. + NOTREACHED(); + case PREF_READ_ERROR_MAX_ENUM: + NOTREACHED(); + break; + } + } + + if (pref_filter_) { + filtering_in_progress_ = true; + const PrefFilter::PostFilterOnLoadCallback post_filter_on_load_callback( + base::Bind( + &JsonPrefStore::FinalizeFileRead, this, initialization_successful)); + pref_filter_->FilterOnLoad(post_filter_on_load_callback, + unfiltered_prefs.Pass()); + } else { + FinalizeFileRead(initialization_successful, unfiltered_prefs.Pass(), false); + } +} + +JsonPrefStore::~JsonPrefStore() { + CommitPendingWrite(); +} + +bool JsonPrefStore::SerializeData(std::string* output) { + if (pref_filter_) + pref_filter_->FilterSerializeData(prefs_.get()); + + JSONStringValueSerializer serializer(output); + serializer.set_pretty_print(true); + return serializer.Serialize(*prefs_); +} + +void JsonPrefStore::FinalizeFileRead(bool initialization_successful, + scoped_ptr<base::DictionaryValue> prefs, + bool schedule_write) { + filtering_in_progress_ = false; + + if (!initialization_successful) { FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnInitializationCompleted(false)); return; } + prefs_ = prefs.Pass(); + initialized_ = true; - switch (error) { - case PREF_READ_ERROR_ACCESS_DENIED: - case PREF_READ_ERROR_FILE_OTHER: - case PREF_READ_ERROR_FILE_LOCKED: - case PREF_READ_ERROR_JSON_TYPE: - case PREF_READ_ERROR_FILE_NOT_SPECIFIED: - read_only_ = true; - break; - case PREF_READ_ERROR_NONE: - DCHECK(value.get()); - prefs_.reset(static_cast<base::DictionaryValue*>(value.release())); - break; - case PREF_READ_ERROR_NO_FILE: - // If the file just doesn't exist, maybe this is first run. In any case - // there's no harm in writing out default prefs in this case. - break; - case PREF_READ_ERROR_JSON_PARSE: - case PREF_READ_ERROR_JSON_REPEAT: - break; - default: - NOTREACHED() << "Unknown error: " << error; - } + if (schedule_write && !read_only_) + writer_.ScheduleWrite(this); - if (error_delegate_.get() && error != PREF_READ_ERROR_NONE) - error_delegate_->OnError(error); + if (error_delegate_ && read_error_ != PREF_READ_ERROR_NONE) + error_delegate_->OnError(read_error_); FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnInitializationCompleted(true)); -} - -JsonPrefStore::~JsonPrefStore() { - CommitPendingWrite(); -} -bool JsonPrefStore::SerializeData(std::string* output) { - JSONStringValueSerializer serializer(output); - serializer.set_pretty_print(true); - return serializer.Serialize(*prefs_); + return; } diff --git a/chromium/base/prefs/json_pref_store.h b/chromium/base/prefs/json_pref_store.h index 21fc8f95ac9..6ceea688548 100644 --- a/chromium/base/prefs/json_pref_store.h +++ b/chromium/base/prefs/json_pref_store.h @@ -9,15 +9,19 @@ #include <string> #include "base/basictypes.h" +#include "base/callback_forward.h" #include "base/compiler_specific.h" #include "base/files/file_path.h" #include "base/files/important_file_writer.h" #include "base/memory/scoped_ptr.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" +class PrefFilter; + namespace base { class DictionaryValue; class FilePath; @@ -30,7 +34,8 @@ class Value; // A writable PrefStore implementation that is used for user preferences. class BASE_PREFS_EXPORT JsonPrefStore : public PersistentPrefStore, - public base::ImportantFileWriter::DataSerializer { + public base::ImportantFileWriter::DataSerializer, + public base::SupportsWeakPtr<JsonPrefStore> { public: // Returns instance of SequencedTaskRunner which guarantees that file // operations on the same file will be executed in sequenced order. @@ -38,10 +43,22 @@ class BASE_PREFS_EXPORT JsonPrefStore const base::FilePath& pref_filename, base::SequencedWorkerPool* worker_pool); - // |sequenced_task_runner| is must be a shutdown-blocking task runner, ideally - // created by GetTaskRunnerForFile() method above. + // Same as the constructor below with no alternate filename. + JsonPrefStore(const base::FilePath& pref_filename, + base::SequencedTaskRunner* sequenced_task_runner, + scoped_ptr<PrefFilter> pref_filter); + + // |sequenced_task_runner| must be a shutdown-blocking task runner, ideally + // created by the GetTaskRunnerForFile() method above. + // |pref_filename| is the path to the file to read prefs from. + // |pref_alternate_filename| is the path to an alternate file which the + // desired prefs may have previously been written to. If |pref_filename| + // doesn't exist and |pref_alternate_filename| does, |pref_alternate_filename| + // will be moved to |pref_filename| before the read occurs. JsonPrefStore(const base::FilePath& pref_filename, - base::SequencedTaskRunner* sequenced_task_runner); + const base::FilePath& pref_alternate_filename, + base::SequencedTaskRunner* sequenced_task_runner, + scoped_ptr<PrefFilter> pref_filter); // PrefStore overrides: virtual bool GetValue(const std::string& key, @@ -60,16 +77,37 @@ class BASE_PREFS_EXPORT JsonPrefStore virtual void RemoveValue(const std::string& key) OVERRIDE; virtual bool ReadOnly() const OVERRIDE; virtual PrefReadError GetReadError() const OVERRIDE; + // Note this method may be asynchronous if this instance has a |pref_filter_| + // in which case it will return PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE. + // See details in pref_filter.h. virtual PrefReadError ReadPrefs() OVERRIDE; virtual void ReadPrefsAsync(ReadErrorDelegate* error_delegate) OVERRIDE; virtual void CommitPendingWrite() OVERRIDE; virtual void ReportValueChanged(const std::string& key) OVERRIDE; - // This method is called after JSON file has been read. Method takes - // ownership of the |value| pointer. Note, this method is used with - // asynchronous file reading, so class exposes it only for the internal needs. - // (read: do not call it manually). - void OnFileRead(base::Value* value_owned, PrefReadError error, bool no_dir); + // 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); + + // Registers |on_next_successful_write| to be called once, on the next + // successful write event of |writer_|. + void RegisterOnNextSuccessfulWriteCallback( + const base::Closure& on_next_successful_write); + + // This method is called after the JSON file has been read. It then hands + // |value| (or an empty dictionary in some read error cases) to the + // |pref_filter| if one is set. It also gives a callback pointing at + // FinalizeFileRead() to that |pref_filter_| which is then responsible for + // invoking it when done. If there is no |pref_filter_|, FinalizeFileRead() + // is invoked directly. + // Note, this method is used with asynchronous file reading, so this class + // exposes it only for the internal needs (read: do not call it manually). + // TODO(gab): Move this method to the private section and hand a callback to + // it to FileThreadDeserializer rather than exposing this public method and + // giving a JsonPrefStore* to FileThreadDeserializer. + void OnFileRead(scoped_ptr<base::Value> value, + PrefReadError error, + bool no_dir); private: virtual ~JsonPrefStore(); @@ -77,7 +115,19 @@ class BASE_PREFS_EXPORT JsonPrefStore // ImportantFileWriter::DataSerializer overrides: virtual bool SerializeData(std::string* output) OVERRIDE; - base::FilePath path_; + // This method is called after the JSON file has been read and the result has + // potentially been intercepted and modified by |pref_filter_|. + // |initialization_successful| is pre-determined by OnFileRead() and should + // be used when reporting OnInitializationCompleted(). + // |schedule_write| indicates whether a write should be immediately scheduled + // (typically because the |pref_filter_| has already altered the |prefs|) -- + // this will be ignored if this store is read-only. + void FinalizeFileRead(bool initialization_successful, + scoped_ptr<base::DictionaryValue> prefs, + bool schedule_write); + + const base::FilePath path_; + const base::FilePath alternate_path_; const scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_; scoped_ptr<base::DictionaryValue> prefs_; @@ -87,11 +137,13 @@ class BASE_PREFS_EXPORT JsonPrefStore // Helper for safely writing pref data. base::ImportantFileWriter writer_; + scoped_ptr<PrefFilter> pref_filter_; ObserverList<PrefStore::Observer, true> observers_; scoped_ptr<ReadErrorDelegate> error_delegate_; bool initialized_; + bool filtering_in_progress_; PrefReadError read_error_; std::set<std::string> keys_need_empty_value_; diff --git a/chromium/base/prefs/json_pref_store_unittest.cc b/chromium/base/prefs/json_pref_store_unittest.cc index a26afd71375..441c2293298 100644 --- a/chromium/base/prefs/json_pref_store_unittest.cc +++ b/chromium/base/prefs/json_pref_store_unittest.cc @@ -4,11 +4,14 @@ #include "base/prefs/json_pref_store.h" +#include "base/bind.h" #include "base/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop.h" #include "base/path_service.h" +#include "base/prefs/pref_filter.h" #include "base/run_loop.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" @@ -24,6 +27,50 @@ namespace { const char kHomePage[] = "homepage"; +// 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 { + public: + InterceptingPrefFilter(); + virtual ~InterceptingPrefFilter(); + + // PrefFilter implementation: + virtual void FilterOnLoad( + const PostFilterOnLoadCallback& post_filter_on_load_callback, + scoped_ptr<base::DictionaryValue> pref_store_contents) OVERRIDE; + virtual void FilterUpdate(const std::string& path) OVERRIDE {} + virtual void FilterSerializeData( + base::DictionaryValue* pref_store_contents) OVERRIDE {} + + bool has_intercepted_prefs() const { return intercepted_prefs_ != NULL; } + + // Finalize an intercepted read, handing |intercepted_prefs_| back to its + // JsonPrefStore. + void ReleasePrefs(); + + private: + PostFilterOnLoadCallback post_filter_on_load_callback_; + scoped_ptr<base::DictionaryValue> intercepted_prefs_; + + DISALLOW_COPY_AND_ASSIGN(InterceptingPrefFilter); +}; + +InterceptingPrefFilter::InterceptingPrefFilter() {} +InterceptingPrefFilter::~InterceptingPrefFilter() {} + +void InterceptingPrefFilter::FilterOnLoad( + const PostFilterOnLoadCallback& post_filter_on_load_callback, + scoped_ptr<base::DictionaryValue> pref_store_contents) { + post_filter_on_load_callback_ = post_filter_on_load_callback; + intercepted_prefs_ = pref_store_contents.Pass(); +} + +void InterceptingPrefFilter::ReleasePrefs() { + EXPECT_FALSE(post_filter_on_load_callback_.is_null()); + post_filter_on_load_callback_.Run(intercepted_prefs_.Pass(), false); + post_filter_on_load_callback_.Reset(); +} + class MockPrefStoreObserver : public PrefStore::Observer { public: MOCK_METHOD1(OnPrefValueChanged, void (const std::string&)); @@ -47,6 +94,13 @@ class JsonPrefStoreTest : public testing::Test { ASSERT_TRUE(PathExists(data_dir_)); } + virtual 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(); + } + // The path to temporary directory used to contain the test operations. base::ScopedTempDir temp_dir_; // The path to the directory where the test data is stored. @@ -60,7 +114,26 @@ TEST_F(JsonPrefStoreTest, NonExistentFile) { base::FilePath bogus_input_file = data_dir_.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()); + bogus_input_file, + message_loop_.message_loop_proxy().get(), + scoped_ptr<PrefFilter>()); + EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE, + pref_store->ReadPrefs()); + EXPECT_FALSE(pref_store->ReadOnly()); +} + +// 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_alternate_input_file = + data_dir_.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>()); EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE, pref_store->ReadPrefs()); EXPECT_FALSE(pref_store->ReadOnly()); @@ -72,7 +145,9 @@ TEST_F(JsonPrefStoreTest, InvalidFile) { 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()); + new JsonPrefStore(invalid_file, + message_loop_.message_loop_proxy().get(), + scoped_ptr<PrefFilter>()); EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE, pref_store->ReadPrefs()); EXPECT_FALSE(pref_store->ReadOnly()); @@ -152,15 +227,18 @@ void RunBasicJsonPrefStoreTest(JsonPrefStore* pref_store, TEST_F(JsonPrefStoreTest, Basic) { ASSERT_TRUE(base::CopyFile(data_dir_.AppendASCII("read.json"), - temp_dir_.path().AppendASCII("write.json"))); + temp_dir_.path().AppendASCII("write.json"))); // Test that the persistent value can be loaded. 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_refptr<JsonPrefStore> pref_store = new JsonPrefStore( + input_file, + message_loop_.message_loop_proxy().get(), + scoped_ptr<PrefFilter>()); ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs()); - ASSERT_FALSE(pref_store->ReadOnly()); + EXPECT_FALSE(pref_store->ReadOnly()); + EXPECT_TRUE(pref_store->IsInitializationComplete()); // The JSON file looks like this: // { @@ -178,13 +256,15 @@ TEST_F(JsonPrefStoreTest, Basic) { TEST_F(JsonPrefStoreTest, BasicAsync) { ASSERT_TRUE(base::CopyFile(data_dir_.AppendASCII("read.json"), - temp_dir_.path().AppendASCII("write.json"))); + temp_dir_.path().AppendASCII("write.json"))); // Test that the persistent value can be loaded. 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_refptr<JsonPrefStore> pref_store = new JsonPrefStore( + input_file, + message_loop_.message_loop_proxy().get(), + scoped_ptr<PrefFilter>()); { MockPrefStoreObserver mock_observer; @@ -199,7 +279,8 @@ TEST_F(JsonPrefStoreTest, BasicAsync) { RunLoop().RunUntilIdle(); pref_store->RemoveObserver(&mock_observer); - ASSERT_FALSE(pref_store->ReadOnly()); + EXPECT_FALSE(pref_store->ReadOnly()); + EXPECT_TRUE(pref_store->IsInitializationComplete()); } // The JSON file looks like this: @@ -219,8 +300,10 @@ TEST_F(JsonPrefStoreTest, BasicAsync) { 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_refptr<JsonPrefStore> pref_store = new JsonPrefStore( + pref_file, + message_loop_.message_loop_proxy(), + scoped_ptr<PrefFilter>()); // Set some keys with empty values. pref_store->SetValue("list", new base::ListValue); @@ -231,7 +314,10 @@ TEST_F(JsonPrefStoreTest, PreserveEmptyValues) { MessageLoop::current()->RunUntilIdle(); // Reload. - pref_store = new JsonPrefStore(pref_file, message_loop_.message_loop_proxy()); + pref_store = new JsonPrefStore( + pref_file, + message_loop_.message_loop_proxy(), + scoped_ptr<PrefFilter>()); ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs()); ASSERT_FALSE(pref_store->ReadOnly()); @@ -243,12 +329,35 @@ TEST_F(JsonPrefStoreTest, PreserveEmptyValues) { EXPECT_TRUE(DictionaryValue().Equals(result)); } +// This test is just documenting some potentially non-obvious behavior. It +// shouldn't be taken as normative. +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>()); + + base::DictionaryValue* dict = new base::DictionaryValue; + dict->SetString("key", "value"); + pref_store->SetValue("dict", dict); + + pref_store->RemoveValue("dict.key"); + + const base::Value* retrieved_dict = NULL; + bool has_dict = pref_store->GetValue("dict", &retrieved_dict); + EXPECT_FALSE(has_dict); +} + // 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"); ASSERT_FALSE(PathExists(bogus_input_file)); scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore( - bogus_input_file, message_loop_.message_loop_proxy().get()); + bogus_input_file, + message_loop_.message_loop_proxy().get(), + scoped_ptr<PrefFilter>()); MockPrefStoreObserver mock_observer; pref_store->AddObserver(&mock_observer); @@ -264,4 +373,302 @@ TEST_F(JsonPrefStoreTest, AsyncNonExistingFile) { EXPECT_FALSE(pref_store->ReadOnly()); } +TEST_F(JsonPrefStoreTest, ReadWithInterceptor) { + ASSERT_TRUE(base::CopyFile(data_dir_.AppendASCII("read.json"), + temp_dir_.path().AppendASCII("write.json"))); + + // Test that the persistent value can be loaded. + base::FilePath input_file = temp_dir_.path().AppendASCII("write.json"); + ASSERT_TRUE(PathExists(input_file)); + + scoped_ptr<InterceptingPrefFilter> intercepting_pref_filter( + 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.PassAs<PrefFilter>()); + + ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE, + pref_store->ReadPrefs()); + EXPECT_FALSE(pref_store->ReadOnly()); + + // The store shouldn't be considered initialized until the interceptor + // returns. + EXPECT_TRUE(raw_intercepting_pref_filter_->has_intercepted_prefs()); + EXPECT_FALSE(pref_store->IsInitializationComplete()); + EXPECT_FALSE(pref_store->GetValue(kHomePage, NULL)); + + raw_intercepting_pref_filter_->ReleasePrefs(); + + EXPECT_FALSE(raw_intercepting_pref_filter_->has_intercepted_prefs()); + EXPECT_TRUE(pref_store->IsInitializationComplete()); + EXPECT_TRUE(pref_store->GetValue(kHomePage, NULL)); + + // The JSON file looks like this: + // { + // "homepage": "http://www.cnn.com", + // "some_directory": "/usr/local/", + // "tabs": { + // "new_windows_in_tabs": true, + // "max_tabs": 20 + // } + // } + + RunBasicJsonPrefStoreTest( + pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json")); +} + +TEST_F(JsonPrefStoreTest, ReadAsyncWithInterceptor) { + ASSERT_TRUE(base::CopyFile(data_dir_.AppendASCII("read.json"), + temp_dir_.path().AppendASCII("write.json"))); + + // Test that the persistent value can be loaded. + base::FilePath input_file = temp_dir_.path().AppendASCII("write.json"); + ASSERT_TRUE(PathExists(input_file)); + + scoped_ptr<InterceptingPrefFilter> intercepting_pref_filter( + 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.PassAs<PrefFilter>()); + + MockPrefStoreObserver mock_observer; + pref_store->AddObserver(&mock_observer); + + // Ownership of the |mock_error_delegate| is handed to the |pref_store| below. + MockReadErrorDelegate* mock_error_delegate = new MockReadErrorDelegate; + + { + pref_store->ReadPrefsAsync(mock_error_delegate); + + EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(0); + // EXPECT_CALL(*mock_error_delegate, + // OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0); + RunLoop().RunUntilIdle(); + + EXPECT_FALSE(pref_store->ReadOnly()); + EXPECT_TRUE(raw_intercepting_pref_filter_->has_intercepted_prefs()); + EXPECT_FALSE(pref_store->IsInitializationComplete()); + EXPECT_FALSE(pref_store->GetValue(kHomePage, NULL)); + } + + { + EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1); + // EXPECT_CALL(*mock_error_delegate, + // OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0); + + raw_intercepting_pref_filter_->ReleasePrefs(); + + EXPECT_FALSE(pref_store->ReadOnly()); + EXPECT_FALSE(raw_intercepting_pref_filter_->has_intercepted_prefs()); + EXPECT_TRUE(pref_store->IsInitializationComplete()); + EXPECT_TRUE(pref_store->GetValue(kHomePage, NULL)); + } + + pref_store->RemoveObserver(&mock_observer); + + // The JSON file looks like this: + // { + // "homepage": "http://www.cnn.com", + // "some_directory": "/usr/local/", + // "tabs": { + // "new_windows_in_tabs": true, + // "max_tabs": 20 + // } + // } + + RunBasicJsonPrefStoreTest( + pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json")); +} + +TEST_F(JsonPrefStoreTest, AlternateFile) { + ASSERT_TRUE( + base::CopyFile(data_dir_.AppendASCII("read.json"), + temp_dir_.path().AppendASCII("alternate.json"))); + + // Test that the alternate file is moved to the main file and read as-is from + // there. + base::FilePath input_file = temp_dir_.path().AppendASCII("write.json"); + base::FilePath alternate_input_file = + 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>()); + + ASSERT_FALSE(PathExists(input_file)); + ASSERT_TRUE(PathExists(alternate_input_file)); + ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs()); + + ASSERT_TRUE(PathExists(input_file)); + ASSERT_FALSE(PathExists(alternate_input_file)); + + EXPECT_FALSE(pref_store->ReadOnly()); + EXPECT_TRUE(pref_store->IsInitializationComplete()); + + // The JSON file looks like this: + // { + // "homepage": "http://www.cnn.com", + // "some_directory": "/usr/local/", + // "tabs": { + // "new_windows_in_tabs": true, + // "max_tabs": 20 + // } + // } + + RunBasicJsonPrefStoreTest( + pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json")); +} + +TEST_F(JsonPrefStoreTest, AlternateFileIgnoredWhenMainFileExists) { + ASSERT_TRUE( + base::CopyFile(data_dir_.AppendASCII("read.json"), + temp_dir_.path().AppendASCII("write.json"))); + ASSERT_TRUE( + base::CopyFile(data_dir_.AppendASCII("invalid.json"), + temp_dir_.path().AppendASCII("alternate.json"))); + + // Test that the alternate file is ignored and that the read occurs from the + // existing main file. There is no attempt at even deleting the alternate + // file as this scenario should never happen in normal user-data-dirs. + base::FilePath input_file = temp_dir_.path().AppendASCII("write.json"); + base::FilePath alternate_input_file = + 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>()); + + ASSERT_TRUE(PathExists(input_file)); + ASSERT_TRUE(PathExists(alternate_input_file)); + ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs()); + + ASSERT_TRUE(PathExists(input_file)); + ASSERT_TRUE(PathExists(alternate_input_file)); + + EXPECT_FALSE(pref_store->ReadOnly()); + EXPECT_TRUE(pref_store->IsInitializationComplete()); + + // The JSON file looks like this: + // { + // "homepage": "http://www.cnn.com", + // "some_directory": "/usr/local/", + // "tabs": { + // "new_windows_in_tabs": true, + // "max_tabs": 20 + // } + // } + + RunBasicJsonPrefStoreTest( + pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json")); +} + +TEST_F(JsonPrefStoreTest, AlternateFileDNE) { + ASSERT_TRUE( + base::CopyFile(data_dir_.AppendASCII("read.json"), + temp_dir_.path().AppendASCII("write.json"))); + + // Test that the basic read works fine when an alternate file is specified but + // does not exist. + base::FilePath input_file = temp_dir_.path().AppendASCII("write.json"); + base::FilePath alternate_input_file = + 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>()); + + ASSERT_TRUE(PathExists(input_file)); + ASSERT_FALSE(PathExists(alternate_input_file)); + ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs()); + + ASSERT_TRUE(PathExists(input_file)); + ASSERT_FALSE(PathExists(alternate_input_file)); + + EXPECT_FALSE(pref_store->ReadOnly()); + EXPECT_TRUE(pref_store->IsInitializationComplete()); + + // The JSON file looks like this: + // { + // "homepage": "http://www.cnn.com", + // "some_directory": "/usr/local/", + // "tabs": { + // "new_windows_in_tabs": true, + // "max_tabs": 20 + // } + // } + + RunBasicJsonPrefStoreTest( + pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json")); +} + +TEST_F(JsonPrefStoreTest, BasicAsyncWithAlternateFile) { + ASSERT_TRUE( + base::CopyFile(data_dir_.AppendASCII("read.json"), + temp_dir_.path().AppendASCII("alternate.json"))); + + // Test that the alternate file is moved to the main file and read as-is from + // there even when the read is made asynchronously. + base::FilePath input_file = temp_dir_.path().AppendASCII("write.json"); + base::FilePath alternate_input_file = + 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>()); + + ASSERT_FALSE(PathExists(input_file)); + ASSERT_TRUE(PathExists(alternate_input_file)); + + { + MockPrefStoreObserver mock_observer; + pref_store->AddObserver(&mock_observer); + + MockReadErrorDelegate* mock_error_delegate = new MockReadErrorDelegate; + pref_store->ReadPrefsAsync(mock_error_delegate); + + EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1); + EXPECT_CALL(*mock_error_delegate, + OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0); + RunLoop().RunUntilIdle(); + pref_store->RemoveObserver(&mock_observer); + + EXPECT_FALSE(pref_store->ReadOnly()); + EXPECT_TRUE(pref_store->IsInitializationComplete()); + } + + ASSERT_TRUE(PathExists(input_file)); + ASSERT_FALSE(PathExists(alternate_input_file)); + + // The JSON file looks like this: + // { + // "homepage": "http://www.cnn.com", + // "some_directory": "/usr/local/", + // "tabs": { + // "new_windows_in_tabs": true, + // "max_tabs": 20 + // } + // } + + RunBasicJsonPrefStoreTest( + pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json")); +} + } // namespace base diff --git a/chromium/base/prefs/overlay_user_pref_store_unittest.cc b/chromium/base/prefs/overlay_user_pref_store_unittest.cc index c4e980bbae3..18e9a5c9910 100644 --- a/chromium/base/prefs/overlay_user_pref_store_unittest.cc +++ b/chromium/base/prefs/overlay_user_pref_store_unittest.cc @@ -48,47 +48,39 @@ TEST_F(OverlayUserPrefStoreTest, Observer) { overlay_->AddObserver(&obs); // Check that underlay first value is reported. - EXPECT_CALL(obs, OnPrefValueChanged(StrEq(overlay_key))).Times(1); underlay_->SetValue(overlay_key, new FundamentalValue(42)); - Mock::VerifyAndClearExpectations(&obs); + obs.VerifyAndResetChangedKey(overlay_key); // Check that underlay overwriting is reported. - EXPECT_CALL(obs, OnPrefValueChanged(StrEq(overlay_key))).Times(1); underlay_->SetValue(overlay_key, new FundamentalValue(43)); - Mock::VerifyAndClearExpectations(&obs); + obs.VerifyAndResetChangedKey(overlay_key); // Check that overwriting change in overlay is reported. - EXPECT_CALL(obs, OnPrefValueChanged(StrEq(overlay_key))).Times(1); overlay_->SetValue(overlay_key, new FundamentalValue(44)); - Mock::VerifyAndClearExpectations(&obs); + obs.VerifyAndResetChangedKey(overlay_key); // Check that hidden underlay change is not reported. - EXPECT_CALL(obs, OnPrefValueChanged(StrEq(overlay_key))).Times(0); underlay_->SetValue(overlay_key, new FundamentalValue(45)); - Mock::VerifyAndClearExpectations(&obs); + EXPECT_TRUE(obs.changed_keys.empty()); // Check that overlay remove is reported. - EXPECT_CALL(obs, OnPrefValueChanged(StrEq(overlay_key))).Times(1); overlay_->RemoveValue(overlay_key); - Mock::VerifyAndClearExpectations(&obs); + obs.VerifyAndResetChangedKey(overlay_key); // Check that underlay remove is reported. - EXPECT_CALL(obs, OnPrefValueChanged(StrEq(overlay_key))).Times(1); underlay_->RemoveValue(overlay_key); - Mock::VerifyAndClearExpectations(&obs); + obs.VerifyAndResetChangedKey(overlay_key); // Check respecting of silence. - EXPECT_CALL(obs, OnPrefValueChanged(StrEq(overlay_key))).Times(0); overlay_->SetValueSilently(overlay_key, new FundamentalValue(46)); - Mock::VerifyAndClearExpectations(&obs); + EXPECT_TRUE(obs.changed_keys.empty()); overlay_->RemoveObserver(&obs); // Check successful unsubscription. - EXPECT_CALL(obs, OnPrefValueChanged(StrEq(overlay_key))).Times(0); underlay_->SetValue(overlay_key, new FundamentalValue(47)); overlay_->SetValue(overlay_key, new FundamentalValue(48)); - Mock::VerifyAndClearExpectations(&obs); + EXPECT_TRUE(obs.changed_keys.empty()); } TEST_F(OverlayUserPrefStoreTest, GetAndSet) { @@ -154,23 +146,20 @@ TEST_F(OverlayUserPrefStoreTest, GlobalPref) { const Value* value = NULL; // Check that underlay first value is reported. - EXPECT_CALL(obs, OnPrefValueChanged(StrEq(regular_key))).Times(1); underlay_->SetValue(regular_key, new FundamentalValue(42)); - Mock::VerifyAndClearExpectations(&obs); + obs.VerifyAndResetChangedKey(regular_key); // Check that underlay overwriting is reported. - EXPECT_CALL(obs, OnPrefValueChanged(StrEq(regular_key))).Times(1); underlay_->SetValue(regular_key, new FundamentalValue(43)); - Mock::VerifyAndClearExpectations(&obs); + obs.VerifyAndResetChangedKey(regular_key); // Check that we get this value from the overlay EXPECT_TRUE(overlay_->GetValue(regular_key, &value)); EXPECT_TRUE(base::FundamentalValue(43).Equals(value)); // Check that overwriting change in overlay is reported. - EXPECT_CALL(obs, OnPrefValueChanged(StrEq(regular_key))).Times(1); overlay_->SetValue(regular_key, new FundamentalValue(44)); - Mock::VerifyAndClearExpectations(&obs); + obs.VerifyAndResetChangedKey(regular_key); // Check that we get this value from the overlay and the underlay. EXPECT_TRUE(overlay_->GetValue(regular_key, &value)); @@ -179,26 +168,23 @@ TEST_F(OverlayUserPrefStoreTest, GlobalPref) { EXPECT_TRUE(base::FundamentalValue(44).Equals(value)); // Check that overlay remove is reported. - EXPECT_CALL(obs, OnPrefValueChanged(StrEq(regular_key))).Times(1); overlay_->RemoveValue(regular_key); - Mock::VerifyAndClearExpectations(&obs); + obs.VerifyAndResetChangedKey(regular_key); // Check that value was removed from overlay and underlay EXPECT_FALSE(overlay_->GetValue(regular_key, &value)); EXPECT_FALSE(underlay_->GetValue(regular_key, &value)); // Check respecting of silence. - EXPECT_CALL(obs, OnPrefValueChanged(StrEq(regular_key))).Times(0); overlay_->SetValueSilently(regular_key, new FundamentalValue(46)); - Mock::VerifyAndClearExpectations(&obs); + EXPECT_TRUE(obs.changed_keys.empty()); overlay_->RemoveObserver(&obs); // Check successful unsubscription. - EXPECT_CALL(obs, OnPrefValueChanged(StrEq(regular_key))).Times(0); underlay_->SetValue(regular_key, new FundamentalValue(47)); overlay_->SetValue(regular_key, new FundamentalValue(48)); - Mock::VerifyAndClearExpectations(&obs); + EXPECT_TRUE(obs.changed_keys.empty()); } // Check that names mapping works correctly. @@ -210,14 +196,12 @@ TEST_F(OverlayUserPrefStoreTest, NamesMapping) { // Check that if there is no override in the overlay, changing underlay value // is reported as changing an overlay value. - EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_overlay_key))).Times(1); underlay_->SetValue(mapped_underlay_key, new FundamentalValue(42)); - Mock::VerifyAndClearExpectations(&obs); + obs.VerifyAndResetChangedKey(mapped_overlay_key); // Check that underlay overwriting is reported. - EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_overlay_key))).Times(1); underlay_->SetValue(mapped_underlay_key, new FundamentalValue(43)); - Mock::VerifyAndClearExpectations(&obs); + obs.VerifyAndResetChangedKey(mapped_overlay_key); // Check that we get this value from the overlay with both keys EXPECT_TRUE(overlay_->GetValue(mapped_overlay_key, &value)); @@ -227,9 +211,8 @@ TEST_F(OverlayUserPrefStoreTest, NamesMapping) { EXPECT_TRUE(base::FundamentalValue(43).Equals(value)); // Check that overwriting change in overlay is reported. - EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_overlay_key))).Times(1); overlay_->SetValue(mapped_overlay_key, new FundamentalValue(44)); - Mock::VerifyAndClearExpectations(&obs); + obs.VerifyAndResetChangedKey(mapped_overlay_key); // Check that we get an overriden value from overlay, while reading the // value from underlay still holds an old value. @@ -241,38 +224,31 @@ TEST_F(OverlayUserPrefStoreTest, NamesMapping) { EXPECT_TRUE(base::FundamentalValue(43).Equals(value)); // Check that hidden underlay change is not reported. - EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_overlay_key))).Times(0); underlay_->SetValue(mapped_underlay_key, new FundamentalValue(45)); - Mock::VerifyAndClearExpectations(&obs); + EXPECT_TRUE(obs.changed_keys.empty()); // Check that overlay remove is reported. - EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_overlay_key))).Times(1); overlay_->RemoveValue(mapped_overlay_key); - Mock::VerifyAndClearExpectations(&obs); + obs.VerifyAndResetChangedKey(mapped_overlay_key); // Check that underlay remove is reported. - EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_overlay_key))).Times(1); underlay_->RemoveValue(mapped_underlay_key); - Mock::VerifyAndClearExpectations(&obs); + obs.VerifyAndResetChangedKey(mapped_overlay_key); // Check that value was removed. EXPECT_FALSE(overlay_->GetValue(mapped_overlay_key, &value)); EXPECT_FALSE(overlay_->GetValue(mapped_underlay_key, &value)); // Check respecting of silence. - EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_overlay_key))).Times(0); - EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_underlay_key))).Times(0); overlay_->SetValueSilently(mapped_overlay_key, new FundamentalValue(46)); - Mock::VerifyAndClearExpectations(&obs); + EXPECT_TRUE(obs.changed_keys.empty()); overlay_->RemoveObserver(&obs); // Check successful unsubscription. - EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_overlay_key))).Times(0); - EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_underlay_key))).Times(0); underlay_->SetValue(mapped_underlay_key, new FundamentalValue(47)); overlay_->SetValue(mapped_overlay_key, new FundamentalValue(48)); - Mock::VerifyAndClearExpectations(&obs); + EXPECT_TRUE(obs.changed_keys.empty()); } } // namespace base diff --git a/chromium/base/prefs/persistent_pref_store.h b/chromium/base/prefs/persistent_pref_store.h index 811ebff7014..fb9d6dcdab6 100644 --- a/chromium/base/prefs/persistent_pref_store.h +++ b/chromium/base/prefs/persistent_pref_store.h @@ -8,28 +8,34 @@ #include <string> #include "base/prefs/base_prefs_export.h" -#include "base/prefs/pref_store.h" +#include "base/prefs/writeable_pref_store.h" // This interface is complementary to the PrefStore interface, declaring // additional functionality that adds support for setting values and persisting // the data to some backing store. -class BASE_PREFS_EXPORT PersistentPrefStore : public PrefStore { +class BASE_PREFS_EXPORT PersistentPrefStore : public WriteablePrefStore { public: // Unique integer code for each type of error so we can report them // distinctly in a histogram. - // NOTE: Don't change the order here as it will change the server's meaning - // of the histogram. + // NOTE: Don't change the explicit values of the enums as it will change the + // server's meaning of the histogram. enum PrefReadError { PREF_READ_ERROR_NONE = 0, - PREF_READ_ERROR_JSON_PARSE, - PREF_READ_ERROR_JSON_TYPE, - PREF_READ_ERROR_ACCESS_DENIED, - PREF_READ_ERROR_FILE_OTHER, - PREF_READ_ERROR_FILE_LOCKED, - PREF_READ_ERROR_NO_FILE, - PREF_READ_ERROR_JSON_REPEAT, - PREF_READ_ERROR_OTHER, - PREF_READ_ERROR_FILE_NOT_SPECIFIED, + PREF_READ_ERROR_JSON_PARSE = 1, + PREF_READ_ERROR_JSON_TYPE = 2, + PREF_READ_ERROR_ACCESS_DENIED = 3, + PREF_READ_ERROR_FILE_OTHER = 4, + PREF_READ_ERROR_FILE_LOCKED = 5, + PREF_READ_ERROR_NO_FILE = 6, + PREF_READ_ERROR_JSON_REPEAT = 7, + // PREF_READ_ERROR_OTHER = 8, // Deprecated. + PREF_READ_ERROR_FILE_NOT_SPECIFIED = 9, + // Indicates that ReadPrefs() couldn't complete synchronously and is waiting + // for an asynchronous task to complete first. + PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE = 10, + PREF_READ_ERROR_LEVELDB_IO = 11, + PREF_READ_ERROR_LEVELDB_CORRUPTION_READ_ONLY = 12, + PREF_READ_ERROR_LEVELDB_CORRUPTION = 13, PREF_READ_ERROR_MAX_ENUM }; @@ -40,29 +46,12 @@ class BASE_PREFS_EXPORT PersistentPrefStore : public PrefStore { virtual void OnError(PrefReadError error) = 0; }; - // Equivalent to PrefStore::GetValue but returns a mutable value. - virtual bool GetMutableValue(const std::string& key, - base::Value** result) = 0; - - // Triggers a value changed notification. This function needs to be called - // 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; - - // 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; - // 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; - // Removes the value for |key|. - virtual void RemoveValue(const std::string& key) = 0; - // Whether the store is in a pseudo-read-only mode where changes are not // actually persisted to disk. This happens in some cases when there are // read errors during startup. diff --git a/chromium/base/prefs/pref_filter.h b/chromium/base/prefs/pref_filter.h new file mode 100644 index 00000000000..82a44c6ef9b --- /dev/null +++ b/chromium/base/prefs/pref_filter.h @@ -0,0 +1,55 @@ +// 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_PREFS_PREF_FILTER_H_ +#define BASE_PREFS_PREF_FILTER_H_ + +#include <string> + +#include "base/callback_forward.h" +#include "base/memory/scoped_ptr.h" +#include "base/prefs/base_prefs_export.h" + +namespace base { +class DictionaryValue; +class Value; +} // namespace base + +// Filters preferences as they are loaded from disk or updated at runtime. +// Currently supported only by JsonPrefStore. +class BASE_PREFS_EXPORT PrefFilter { + public: + // A callback to be invoked when |prefs| have been read (and possibly + // pre-modified) and are now ready to be handed back to this callback's + // builder. |schedule_write| indicates whether a write should be immediately + // scheduled (typically because the |prefs| were pre-modified). + typedef base::Callback<void(scoped_ptr<base::DictionaryValue> prefs, + bool schedule_write)> PostFilterOnLoadCallback; + + virtual ~PrefFilter() {} + + // This method is given ownership of the |pref_store_contents| read from disk + // before the underlying PersistentPrefStore gets to use them. It must hand + // them back via |post_filter_on_load_callback|, but may modify them first. + // Note: This method is asynchronous, which may make calls like + // PersistentPrefStore::ReadPrefs() asynchronous. The owner of filtered + // PersistentPrefStores should handle this to make the reads look synchronous + // to external users (see SegregatedPrefStore::ReadPrefs() for an example). + virtual void FilterOnLoad( + const PostFilterOnLoadCallback& post_filter_on_load_callback, + scoped_ptr<base::DictionaryValue> pref_store_contents) = 0; + + // Receives notification when a pref store value is changed, before Observers + // are notified. + virtual void FilterUpdate(const std::string& path) = 0; + + // Receives notification when the pref store is about to serialize data + // contained in |pref_store_contents| to a string. Modifications to + // |pref_store_contents| will be persisted to disk and also affect the + // in-memory state. + virtual void FilterSerializeData( + base::DictionaryValue* pref_store_contents) = 0; +}; + +#endif // BASE_PREFS_PREF_FILTER_H_ diff --git a/chromium/base/prefs/pref_notifier_impl_unittest.cc b/chromium/base/prefs/pref_notifier_impl_unittest.cc index 29ea322e7ed..8482c0289c7 100644 --- a/chromium/base/prefs/pref_notifier_impl_unittest.cc +++ b/chromium/base/prefs/pref_notifier_impl_unittest.cc @@ -80,13 +80,6 @@ class PrefObserverMock : public PrefObserver { virtual ~PrefObserverMock() {} MOCK_METHOD2(OnPreferenceChanged, void(PrefService*, const std::string&)); - - void Expect(PrefService* prefs, - const std::string& pref_name, - const base::Value* value) { - EXPECT_CALL(*this, OnPreferenceChanged(prefs, pref_name)) - .With(PrefValueMatches(prefs, pref_name, value)); - } }; // Test fixture class. diff --git a/chromium/base/prefs/pref_registry.cc b/chromium/base/prefs/pref_registry.cc index 9d6b05c8362..134996185c4 100644 --- a/chromium/base/prefs/pref_registry.cc +++ b/chromium/base/prefs/pref_registry.cc @@ -31,13 +31,11 @@ PrefRegistry::const_iterator PrefRegistry::end() const { void PrefRegistry::SetDefaultPrefValue(const char* pref_name, base::Value* value) { DCHECK(value); - if (DCHECK_IS_ON()) { - const base::Value* current_value = NULL; - DCHECK(defaults_->GetValue(pref_name, ¤t_value)) - << "Setting default for unregistered pref: " << pref_name; - DCHECK(value->IsType(current_value->GetType())) - << "Wrong type for new default: " << pref_name; - } + const base::Value* current_value = NULL; + DCHECK(defaults_->GetValue(pref_name, ¤t_value)) + << "Setting default for unregistered pref: " << pref_name; + DCHECK(value->IsType(current_value->GetType())) + << "Wrong type for new default: " << pref_name; defaults_->ReplaceDefaultValue(pref_name, make_scoped_ptr(value)); } diff --git a/chromium/base/prefs/pref_registry_simple.cc b/chromium/base/prefs/pref_registry_simple.cc index c068b4ba43a..7453016a272 100644 --- a/chromium/base/prefs/pref_registry_simple.cc +++ b/chromium/base/prefs/pref_registry_simple.cc @@ -16,29 +16,28 @@ PrefRegistrySimple::~PrefRegistrySimple() { void PrefRegistrySimple::RegisterBooleanPref(const char* path, bool default_value) { - RegisterPreference(path, base::Value::CreateBooleanValue(default_value)); + RegisterPreference(path, new base::FundamentalValue(default_value)); } void PrefRegistrySimple::RegisterIntegerPref(const char* path, int default_value) { - RegisterPreference(path, base::Value::CreateIntegerValue(default_value)); + RegisterPreference(path, new base::FundamentalValue(default_value)); } void PrefRegistrySimple::RegisterDoublePref(const char* path, double default_value) { - RegisterPreference(path, base::Value::CreateDoubleValue(default_value)); + RegisterPreference(path, new base::FundamentalValue(default_value)); } void PrefRegistrySimple::RegisterStringPref(const char* path, const std::string& default_value) { - RegisterPreference(path, base::Value::CreateStringValue(default_value)); + RegisterPreference(path, new base::StringValue(default_value)); } void PrefRegistrySimple::RegisterFilePathPref( const char* path, const base::FilePath& default_value) { - RegisterPreference(path, - base::Value::CreateStringValue(default_value.value())); + RegisterPreference(path, new base::StringValue(default_value.value())); } void PrefRegistrySimple::RegisterListPref(const char* path) { @@ -63,5 +62,5 @@ void PrefRegistrySimple::RegisterDictionaryPref( void PrefRegistrySimple::RegisterInt64Pref(const char* path, int64 default_value) { RegisterPreference( - path, base::Value::CreateStringValue(base::Int64ToString(default_value))); + path, new base::StringValue(base::Int64ToString(default_value))); } diff --git a/chromium/base/prefs/pref_service.cc b/chromium/base/prefs/pref_service.cc index 576043b5489..a8e56d0b370 100644 --- a/chromium/base/prefs/pref_service.cc +++ b/chromium/base/prefs/pref_service.cc @@ -67,7 +67,9 @@ PrefService::~PrefService() { } void PrefService::InitFromStorage(bool async) { - if (!async) { + if (user_pref_store_->IsInitializationComplete()) { + read_error_callback_.Run(user_pref_store_->GetReadError()); + } else if (!async) { read_error_callback_.Run(user_pref_store_->ReadPrefs()); } else { // Guarantee that initialization happens after this function returned. @@ -336,19 +338,19 @@ void PrefService::Set(const char* path, const base::Value& value) { } void PrefService::SetBoolean(const char* path, bool value) { - SetUserPrefValue(path, base::Value::CreateBooleanValue(value)); + SetUserPrefValue(path, new base::FundamentalValue(value)); } void PrefService::SetInteger(const char* path, int value) { - SetUserPrefValue(path, base::Value::CreateIntegerValue(value)); + SetUserPrefValue(path, new base::FundamentalValue(value)); } void PrefService::SetDouble(const char* path, double value) { - SetUserPrefValue(path, base::Value::CreateDoubleValue(value)); + SetUserPrefValue(path, new base::FundamentalValue(value)); } void PrefService::SetString(const char* path, const std::string& value) { - SetUserPrefValue(path, base::Value::CreateStringValue(value)); + SetUserPrefValue(path, new base::StringValue(value)); } void PrefService::SetFilePath(const char* path, const base::FilePath& value) { @@ -356,8 +358,7 @@ void PrefService::SetFilePath(const char* path, const base::FilePath& value) { } void PrefService::SetInt64(const char* path, int64 value) { - SetUserPrefValue(path, - base::Value::CreateStringValue(base::Int64ToString(value))); + SetUserPrefValue(path, new base::StringValue(base::Int64ToString(value))); } int64 PrefService::GetInt64(const char* path) const { @@ -378,8 +379,7 @@ int64 PrefService::GetInt64(const char* path) const { } void PrefService::SetUint64(const char* path, uint64 value) { - SetUserPrefValue(path, - base::Value::CreateStringValue(base::Uint64ToString(value))); + SetUserPrefValue(path, new base::StringValue(base::Uint64ToString(value))); } uint64 PrefService::GetUint64(const char* path) const { diff --git a/chromium/base/prefs/pref_service_factory.cc b/chromium/base/prefs/pref_service_factory.cc index 9c598530c1b..d644cb1c2b0 100644 --- a/chromium/base/prefs/pref_service_factory.cc +++ b/chromium/base/prefs/pref_service_factory.cc @@ -7,6 +7,7 @@ #include "base/bind.h" #include "base/prefs/default_pref_store.h" #include "base/prefs/json_pref_store.h" +#include "base/prefs/pref_filter.h" #include "base/prefs/pref_notifier_impl.h" #include "base/prefs/pref_service.h" @@ -37,7 +38,8 @@ PrefServiceFactory::~PrefServiceFactory() {} void PrefServiceFactory::SetUserPrefsFile( const base::FilePath& prefs_file, base::SequencedTaskRunner* task_runner) { - user_prefs_ = new JsonPrefStore(prefs_file, task_runner); + user_prefs_ = new JsonPrefStore( + prefs_file, task_runner, scoped_ptr<PrefFilter>()); } scoped_ptr<PrefService> PrefServiceFactory::Create( diff --git a/chromium/base/prefs/pref_service_unittest.cc b/chromium/base/prefs/pref_service_unittest.cc index ff6bb2f2de3..36ad887df2c 100644 --- a/chromium/base/prefs/pref_service_unittest.cc +++ b/chromium/base/prefs/pref_service_unittest.cc @@ -79,7 +79,7 @@ TEST(PrefServiceTest, Observers) { TestingPrefServiceSimple prefs; prefs.SetUserPref(pref_name, - base::Value::CreateStringValue("http://www.cnn.com")); + new base::StringValue("http://www.cnn.com")); prefs.registry()->RegisterStringPref(pref_name, std::string()); const char new_pref_value[] = "http://www.google.com/"; @@ -138,7 +138,7 @@ TEST(PrefServiceTest, GetValueChangedType) { // Check falling back to a recommended value. prefs.SetUserPref(kPrefName, - base::Value::CreateStringValue("not an integer")); + new base::StringValue("not an integer")); const PrefService::Preference* pref = prefs.FindPreference(kPrefName); ASSERT_TRUE(pref); const base::Value* value = pref->GetValue(); @@ -173,7 +173,7 @@ TEST(PrefServiceTest, GetValueAndGetRecommendedValue) { ASSERT_FALSE(value); // Set a user-set value. - prefs.SetUserPref(kPrefName, base::Value::CreateIntegerValue(kUserValue)); + prefs.SetUserPref(kPrefName, new base::FundamentalValue(kUserValue)); // Check that GetValue() returns the user-set value. value = pref->GetValue(); @@ -189,7 +189,7 @@ TEST(PrefServiceTest, GetValueAndGetRecommendedValue) { // Set a recommended value. prefs.SetRecommendedPref(kPrefName, - base::Value::CreateIntegerValue(kRecommendedValue)); + new base::FundamentalValue(kRecommendedValue)); // Check that GetValue() returns the user-set value. value = pref->GetValue(); @@ -302,7 +302,7 @@ TEST_F(PrefServiceSetValueTest, SetListValue) { Mock::VerifyAndClearExpectations(&observer_); base::ListValue new_value; - new_value.Append(base::Value::CreateStringValue(kValue)); + new_value.Append(new base::StringValue(kValue)); observer_.Expect(kName, &new_value); prefs_.Set(kName, new_value); Mock::VerifyAndClearExpectations(&observer_); diff --git a/chromium/base/prefs/pref_store_observer_mock.cc b/chromium/base/prefs/pref_store_observer_mock.cc index 0970e6310d7..f1a31bb985d 100644 --- a/chromium/base/prefs/pref_store_observer_mock.cc +++ b/chromium/base/prefs/pref_store_observer_mock.cc @@ -4,6 +4,26 @@ #include "base/prefs/pref_store_observer_mock.h" -PrefStoreObserverMock::PrefStoreObserverMock() {} +#include "testing/gtest/include/gtest/gtest.h" + +PrefStoreObserverMock::PrefStoreObserverMock() + : initialized(false), initialization_success(false) {} PrefStoreObserverMock::~PrefStoreObserverMock() {} + +void PrefStoreObserverMock::VerifyAndResetChangedKey( + const std::string& expected) { + EXPECT_EQ(1u, changed_keys.size()); + if (changed_keys.size() >= 1) + EXPECT_EQ(expected, changed_keys.front()); + changed_keys.clear(); +} + +void PrefStoreObserverMock::OnPrefValueChanged(const std::string& key) { + changed_keys.push_back(key); +} + +void PrefStoreObserverMock::OnInitializationCompleted(bool success) { + initialized = true; + initialization_success = success; +} diff --git a/chromium/base/prefs/pref_store_observer_mock.h b/chromium/base/prefs/pref_store_observer_mock.h index 8252c3b3599..594807f31e0 100644 --- a/chromium/base/prefs/pref_store_observer_mock.h +++ b/chromium/base/prefs/pref_store_observer_mock.h @@ -5,18 +5,28 @@ #ifndef BASE_PREFS_PREF_STORE_OBSERVER_MOCK_H_ #define BASE_PREFS_PREF_STORE_OBSERVER_MOCK_H_ -#include "base/basictypes.h" +#include <string> +#include <vector> + +#include "base/compiler_specific.h" +#include "base/macros.h" #include "base/prefs/pref_store.h" -#include "testing/gmock/include/gmock/gmock.h" -// A gmock-ified implementation of PrefStore::Observer. +// A mock implementation of PrefStore::Observer. class PrefStoreObserverMock : public PrefStore::Observer { public: PrefStoreObserverMock(); virtual ~PrefStoreObserverMock(); - MOCK_METHOD1(OnPrefValueChanged, void(const std::string&)); - MOCK_METHOD1(OnInitializationCompleted, void(bool)); + void VerifyAndResetChangedKey(const std::string& expected); + + // PrefStore::Observer implementation + virtual void OnPrefValueChanged(const std::string& key) OVERRIDE; + virtual void OnInitializationCompleted(bool success) OVERRIDE; + + std::vector<std::string> changed_keys; + bool initialized; + bool initialization_success; // Only valid if |initialized|. private: DISALLOW_COPY_AND_ASSIGN(PrefStoreObserverMock); diff --git a/chromium/base/prefs/scoped_user_pref_update.cc b/chromium/base/prefs/scoped_user_pref_update.cc index c86b1634b52..41b654cd58c 100644 --- a/chromium/base/prefs/scoped_user_pref_update.cc +++ b/chromium/base/prefs/scoped_user_pref_update.cc @@ -20,7 +20,7 @@ ScopedUserPrefUpdateBase::~ScopedUserPrefUpdateBase() { Notify(); } -Value* ScopedUserPrefUpdateBase::GetValueOfType(base::Value::Type type) { +base::Value* ScopedUserPrefUpdateBase::GetValueOfType(base::Value::Type type) { if (!value_) value_ = service_->GetMutableUserPref(path_.c_str(), type); return value_; diff --git a/chromium/base/prefs/scoped_user_pref_update_unittest.cc b/chromium/base/prefs/scoped_user_pref_update_unittest.cc index 505526c07cf..dfe2074720d 100644 --- a/chromium/base/prefs/scoped_user_pref_update_unittest.cc +++ b/chromium/base/prefs/scoped_user_pref_update_unittest.cc @@ -40,13 +40,13 @@ const char ScopedUserPrefUpdateTest::kValue[] = "value"; TEST_F(ScopedUserPrefUpdateTest, RegularUse) { // Dictionary that will be expected to be set at the end. - DictionaryValue expected_dictionary; + base::DictionaryValue expected_dictionary; expected_dictionary.SetString(kKey, kValue); { EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0); DictionaryPrefUpdate update(&prefs_, kPref); - DictionaryValue* value = update.Get(); + base::DictionaryValue* value = update.Get(); ASSERT_TRUE(value); value->SetString(kKey, kValue); @@ -55,7 +55,7 @@ TEST_F(ScopedUserPrefUpdateTest, RegularUse) { Mock::VerifyAndClearExpectations(&observer_); // Modifications happen online and are instantly visible, though. - const DictionaryValue* current_value = prefs_.GetDictionary(kPref); + const base::DictionaryValue* current_value = prefs_.GetDictionary(kPref); ASSERT_TRUE(current_value); EXPECT_TRUE(expected_dictionary.Equals(current_value)); @@ -64,18 +64,18 @@ TEST_F(ScopedUserPrefUpdateTest, RegularUse) { } Mock::VerifyAndClearExpectations(&observer_); - const DictionaryValue* current_value = prefs_.GetDictionary(kPref); + const base::DictionaryValue* current_value = prefs_.GetDictionary(kPref); ASSERT_TRUE(current_value); EXPECT_TRUE(expected_dictionary.Equals(current_value)); } TEST_F(ScopedUserPrefUpdateTest, NeverTouchAnything) { - const DictionaryValue* old_value = prefs_.GetDictionary(kPref); + const base::DictionaryValue* old_value = prefs_.GetDictionary(kPref); EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0); { DictionaryPrefUpdate update(&prefs_, kPref); } - const DictionaryValue* new_value = prefs_.GetDictionary(kPref); + const base::DictionaryValue* new_value = prefs_.GetDictionary(kPref); EXPECT_EQ(old_value, new_value); Mock::VerifyAndClearExpectations(&observer_); } diff --git a/chromium/base/prefs/testing_pref_store.cc b/chromium/base/prefs/testing_pref_store.cc index 2f429c9a1a9..f20824e1733 100644 --- a/chromium/base/prefs/testing_pref_store.cc +++ b/chromium/base/prefs/testing_pref_store.cc @@ -9,8 +9,12 @@ TestingPrefStore::TestingPrefStore() : read_only_(true), - init_complete_(false) { -} + read_success_(true), + read_error_(PersistentPrefStore::PREF_READ_ERROR_NONE), + block_async_read_(false), + pending_async_read_(false), + init_complete_(false), + committed_(true) {} bool TestingPrefStore::GetValue(const std::string& key, const base::Value** value) const { @@ -39,18 +43,23 @@ bool TestingPrefStore::IsInitializationComplete() const { } void TestingPrefStore::SetValue(const std::string& key, base::Value* value) { - if (prefs_.SetValue(key, value)) + if (prefs_.SetValue(key, value)) { + committed_ = false; NotifyPrefValueChanged(key); + } } void TestingPrefStore::SetValueSilently(const std::string& key, base::Value* value) { - prefs_.SetValue(key, value); + if (prefs_.SetValue(key, value)) + committed_ = false; } void TestingPrefStore::RemoveValue(const std::string& key) { - if (prefs_.RemoveValue(key)) + if (prefs_.RemoveValue(key)) { + committed_ = false; NotifyPrefValueChanged(key); + } } bool TestingPrefStore::ReadOnly() const { @@ -58,21 +67,26 @@ bool TestingPrefStore::ReadOnly() const { } PersistentPrefStore::PrefReadError TestingPrefStore::GetReadError() const { - return PersistentPrefStore::PREF_READ_ERROR_NONE; + return read_error_; } PersistentPrefStore::PrefReadError TestingPrefStore::ReadPrefs() { NotifyInitializationCompleted(); - return PersistentPrefStore::PREF_READ_ERROR_NONE; + return read_error_; } -void TestingPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate_raw) { - scoped_ptr<ReadErrorDelegate> error_delegate(error_delegate_raw); - NotifyInitializationCompleted(); +void TestingPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) { + DCHECK(!pending_async_read_); + error_delegate_.reset(error_delegate); + if (block_async_read_) + pending_async_read_ = true; + else + NotifyInitializationCompleted(); } +void TestingPrefStore::CommitPendingWrite() { committed_ = true; } + void TestingPrefStore::SetInitializationCompleted() { - init_complete_ = true; NotifyInitializationCompleted(); } @@ -81,7 +95,12 @@ void TestingPrefStore::NotifyPrefValueChanged(const std::string& key) { } void TestingPrefStore::NotifyInitializationCompleted() { - FOR_EACH_OBSERVER(Observer, observers_, OnInitializationCompleted(true)); + DCHECK(!init_complete_); + init_complete_ = true; + if (read_success_ && read_error_ != PREF_READ_ERROR_NONE && error_delegate_) + error_delegate_->OnError(read_error_); + FOR_EACH_OBSERVER( + Observer, observers_, OnInitializationCompleted(read_success_)); } void TestingPrefStore::ReportValueChanged(const std::string& key) { @@ -126,8 +145,26 @@ bool TestingPrefStore::GetBoolean(const std::string& key, bool* value) const { return stored_value->GetAsBoolean(value); } +void TestingPrefStore::SetBlockAsyncRead(bool block_async_read) { + DCHECK(!init_complete_); + block_async_read_ = block_async_read; + if (pending_async_read_ && !block_async_read_) + NotifyInitializationCompleted(); +} + void TestingPrefStore::set_read_only(bool read_only) { read_only_ = read_only; } +void TestingPrefStore::set_read_success(bool read_success) { + DCHECK(!init_complete_); + read_success_ = read_success; +} + +void TestingPrefStore::set_read_error( + PersistentPrefStore::PrefReadError read_error) { + DCHECK(!init_complete_); + read_error_ = read_error; +} + TestingPrefStore::~TestingPrefStore() {} diff --git a/chromium/base/prefs/testing_pref_store.h b/chromium/base/prefs/testing_pref_store.h index c6a2b8336f0..785f935539e 100644 --- a/chromium/base/prefs/testing_pref_store.h +++ b/chromium/base/prefs/testing_pref_store.h @@ -40,7 +40,7 @@ class TestingPrefStore : public PersistentPrefStore { virtual PrefReadError GetReadError() const OVERRIDE; virtual PersistentPrefStore::PrefReadError ReadPrefs() OVERRIDE; virtual void ReadPrefsAsync(ReadErrorDelegate* error_delegate) OVERRIDE; - virtual void CommitPendingWrite() OVERRIDE {} + virtual void CommitPendingWrite() OVERRIDE; // Marks the store as having completed initialization. void SetInitializationCompleted(); @@ -58,9 +58,18 @@ class TestingPrefStore : public PersistentPrefStore { bool GetInteger(const std::string& key, int* value) const; bool GetBoolean(const std::string& key, bool* value) const; + // Determines whether ReadPrefsAsync completes immediately. Defaults to false + // (non-blocking). To block, invoke this with true (blocking) before the call + // to ReadPrefsAsync. To unblock, invoke again with false (non-blocking) after + // the call to ReadPrefsAsync. + void SetBlockAsyncRead(bool block_async_read); + // Getter and Setter methods for setting and getting the state of the // |TestingPrefStore|. virtual void set_read_only(bool read_only); + void set_read_success(bool read_success); + void set_read_error(PersistentPrefStore::PrefReadError read_error); + bool committed() { return committed_; } protected: virtual ~TestingPrefStore(); @@ -72,9 +81,26 @@ class TestingPrefStore : public PersistentPrefStore { // Flag that indicates if the PrefStore is read-only bool read_only_; + // The result to pass to PrefStore::Observer::OnInitializationCompleted + bool read_success_; + + // The result to return from ReadPrefs or ReadPrefsAsync. + PersistentPrefStore::PrefReadError read_error_; + + // Whether a call to ReadPrefsAsync should block. + bool block_async_read_; + + // Whether there is a pending call to ReadPrefsAsync. + bool pending_async_read_; + // Whether initialization has been completed. bool init_complete_; + // Whether the store contents have been committed to disk since the last + // mutation. + bool committed_; + + scoped_ptr<ReadErrorDelegate> error_delegate_; ObserverList<PrefStore::Observer, true> observers_; DISALLOW_COPY_AND_ASSIGN(TestingPrefStore); diff --git a/chromium/base/prefs/value_map_pref_store.cc b/chromium/base/prefs/value_map_pref_store.cc index 750688c373f..5e890e2589f 100644 --- a/chromium/base/prefs/value_map_pref_store.cc +++ b/chromium/base/prefs/value_map_pref_store.cc @@ -28,8 +28,6 @@ bool ValueMapPrefStore::HasObservers() const { return observers_.might_have_observers(); } -ValueMapPrefStore::~ValueMapPrefStore() {} - void ValueMapPrefStore::SetValue(const std::string& key, base::Value* value) { if (prefs_.SetValue(key, value)) FOR_EACH_OBSERVER(Observer, observers_, OnPrefValueChanged(key)); @@ -40,6 +38,17 @@ void ValueMapPrefStore::RemoveValue(const std::string& key) { FOR_EACH_OBSERVER(Observer, observers_, OnPrefValueChanged(key)); } +bool ValueMapPrefStore::GetMutableValue(const std::string& key, + base::Value** value) { + return prefs_.GetValue(key, value); +} + +void ValueMapPrefStore::ReportValueChanged(const std::string& key) { + FOR_EACH_OBSERVER(Observer, observers_, OnPrefValueChanged(key)); +} + +ValueMapPrefStore::~ValueMapPrefStore() {} + void ValueMapPrefStore::NotifyInitializationCompleted() { FOR_EACH_OBSERVER(Observer, observers_, OnInitializationCompleted(true)); } diff --git a/chromium/base/prefs/value_map_pref_store.h b/chromium/base/prefs/value_map_pref_store.h index 21e4b888175..c202725d947 100644 --- a/chromium/base/prefs/value_map_pref_store.h +++ b/chromium/base/prefs/value_map_pref_store.h @@ -11,12 +11,12 @@ #include "base/basictypes.h" #include "base/observer_list.h" #include "base/prefs/base_prefs_export.h" -#include "base/prefs/pref_store.h" #include "base/prefs/pref_value_map.h" +#include "base/prefs/writeable_pref_store.h" // A basic PrefStore implementation that uses a simple name-value map for // storing the preference values. -class BASE_PREFS_EXPORT ValueMapPrefStore : public PrefStore { +class BASE_PREFS_EXPORT ValueMapPrefStore : public WriteablePrefStore { public: ValueMapPrefStore(); @@ -27,17 +27,16 @@ class BASE_PREFS_EXPORT ValueMapPrefStore : public PrefStore { virtual void RemoveObserver(PrefStore::Observer* observer) OVERRIDE; virtual bool HasObservers() const OVERRIDE; + // WriteablePrefStore overrides: + virtual void SetValue(const std::string& key, base::Value* value) OVERRIDE; + virtual void RemoveValue(const std::string& key) OVERRIDE; + virtual bool GetMutableValue(const std::string& key, + base::Value** value) OVERRIDE; + virtual void ReportValueChanged(const std::string& key) OVERRIDE; + protected: virtual ~ValueMapPrefStore(); - // Store a |value| for |key| in the store. Also generates an notification if - // the value changed. Assumes ownership of |value|, which must be non-NULL. - void SetValue(const std::string& key, base::Value* value); - - // Remove the value for |key| from the store. Sends a notification if there - // was a value to be removed. - void RemoveValue(const std::string& key); - // Notify observers about the initialization completed event. void NotifyInitializationCompleted(); diff --git a/chromium/base/prefs/writeable_pref_store.h b/chromium/base/prefs/writeable_pref_store.h new file mode 100644 index 00000000000..908d867dbf5 --- /dev/null +++ b/chromium/base/prefs/writeable_pref_store.h @@ -0,0 +1,47 @@ +// 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_PREFS_WRITEABLE_PREF_STORE_H_ +#define BASE_PREFS_WRITEABLE_PREF_STORE_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/prefs/pref_store.h" + +namespace base { +class Value; +} + +// A pref store that can be written to as well as read from. +class BASE_PREFS_EXPORT WriteablePrefStore : public PrefStore { + public: + 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; + + // Removes the value for |key|. + virtual void RemoveValue(const std::string& key) = 0; + + // Equivalent to PrefStore::GetValue but returns a mutable value. + virtual bool GetMutableValue(const std::string& key, + base::Value** result) = 0; + + // Triggers a value changed notification. This function needs to be called + // 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; + + + protected: + virtual ~WriteablePrefStore() {} + + private: + DISALLOW_COPY_AND_ASSIGN(WriteablePrefStore); +}; + +#endif // BASE_PREFS_WRITEABLE_PREF_STORE_H_ diff --git a/chromium/base/process/internal_linux.cc b/chromium/base/process/internal_linux.cc index b1d1b6f174e..57a3ab4271a 100644 --- a/chromium/base/process/internal_linux.cc +++ b/chromium/base/process/internal_linux.cc @@ -115,13 +115,13 @@ void ParseProcStat(const std::string& contents, ProcStatMap* output) { } } -int GetProcStatsFieldAsInt(const std::vector<std::string>& proc_stats, - ProcStatsFields field_num) { +int64 GetProcStatsFieldAsInt64(const std::vector<std::string>& proc_stats, + ProcStatsFields field_num) { DCHECK_GE(field_num, VM_PPID); CHECK_LT(static_cast<size_t>(field_num), proc_stats.size()); - int value; - return StringToInt(proc_stats[field_num], &value) ? value : 0; + int64 value; + return StringToInt64(proc_stats[field_num], &value) ? value : 0; } size_t GetProcStatsFieldAsSizeT(const std::vector<std::string>& proc_stats, @@ -133,15 +133,14 @@ size_t GetProcStatsFieldAsSizeT(const std::vector<std::string>& proc_stats, return StringToSizeT(proc_stats[field_num], &value) ? value : 0; } -int ReadProcStatsAndGetFieldAsInt(pid_t pid, - ProcStatsFields field_num) { +int64 ReadProcStatsAndGetFieldAsInt64(pid_t pid, ProcStatsFields field_num) { std::string stats_data; if (!ReadProcStats(pid, &stats_data)) return 0; std::vector<std::string> proc_stats; if (!ParseProcStats(stats_data, &proc_stats)) return 0; - return GetProcStatsFieldAsInt(proc_stats, field_num); + return GetProcStatsFieldAsInt64(proc_stats, field_num); } size_t ReadProcStatsAndGetFieldAsSizeT(pid_t pid, diff --git a/chromium/base/process/internal_linux.h b/chromium/base/process/internal_linux.h index 0cacd3f3a84..5fc33566607 100644 --- a/chromium/base/process/internal_linux.h +++ b/chromium/base/process/internal_linux.h @@ -63,19 +63,18 @@ enum ProcStatsFields { // Reads the |field_num|th field from |proc_stats|. Returns 0 on failure. // This version does not handle the first 3 values, since the first value is // simply |pid|, and the next two values are strings. -int GetProcStatsFieldAsInt(const std::vector<std::string>& proc_stats, - ProcStatsFields field_num); +int64 GetProcStatsFieldAsInt64(const std::vector<std::string>& proc_stats, + ProcStatsFields field_num); -// Same as GetProcStatsFieldAsInt(), but for size_t values. +// Same as GetProcStatsFieldAsInt64(), but for size_t values. size_t GetProcStatsFieldAsSizeT(const std::vector<std::string>& proc_stats, ProcStatsFields field_num); -// Convenience wrapper around GetProcStatsFieldAsInt(), ParseProcStats() and -// ReadProcStats(). See GetProcStatsFieldAsInt() for details. -int ReadProcStatsAndGetFieldAsInt(pid_t pid, - ProcStatsFields field_num); +// Convenience wrapper around GetProcStatsFieldAsInt64(), ParseProcStats() and +// ReadProcStats(). See GetProcStatsFieldAsInt64() for details. +int64 ReadProcStatsAndGetFieldAsInt64(pid_t pid, ProcStatsFields field_num); -// Same as ReadProcStatsAndGetFieldAsInt() but for size_t values. +// Same as ReadProcStatsAndGetFieldAsInt64() but for size_t values. size_t ReadProcStatsAndGetFieldAsSizeT(pid_t pid, ProcStatsFields field_num); diff --git a/chromium/base/process/kill_mac.cc b/chromium/base/process/kill_mac.cc index 5ebca5d0076..1589a641a04 100644 --- a/chromium/base/process/kill_mac.cc +++ b/chromium/base/process/kill_mac.cc @@ -10,6 +10,7 @@ #include <sys/wait.h> #include "base/file_util.h" +#include "base/files/scoped_file.h" #include "base/logging.h" #include "base/posix/eintr_wrapper.h" @@ -76,15 +77,13 @@ void WaitForChildToDie(pid_t child, int timeout) { int result; - int kq = HANDLE_EINTR(kqueue()); - if (kq == -1) { + ScopedFD kq(HANDLE_EINTR(kqueue())); + if (!kq.is_valid()) { DPLOG(ERROR) << "kqueue()"; } else { - file_util::ScopedFD auto_close_kq(&kq); - struct kevent change = {0}; EV_SET(&change, child, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL); - result = HANDLE_EINTR(kevent(kq, &change, 1, NULL, 0, NULL)); + result = HANDLE_EINTR(kevent(kq.get(), &change, 1, NULL, 0, NULL)); if (result == -1) { if (errno != ESRCH) { @@ -120,7 +119,7 @@ void WaitForChildToDie(pid_t child, int timeout) { struct kevent event = {0}; while (remaining_delta.InMilliseconds() > 0) { const struct timespec remaining_timespec = remaining_delta.ToTimeSpec(); - result = kevent(kq, NULL, 0, &event, 1, &remaining_timespec); + result = kevent(kq.get(), NULL, 0, &event, 1, &remaining_timespec); if (result == -1 && errno == EINTR) { remaining_delta = deadline - TimeTicks::Now(); result = 0; diff --git a/chromium/base/process/kill_posix.cc b/chromium/base/process/kill_posix.cc index 99d70d9ab55..18c14fd3a77 100644 --- a/chromium/base/process/kill_posix.cc +++ b/chromium/base/process/kill_posix.cc @@ -10,6 +10,7 @@ #include <unistd.h> #include "base/file_util.h" +#include "base/files/scoped_file.h" #include "base/logging.h" #include "base/posix/eintr_wrapper.h" #include "base/process/process_iterator.h" @@ -21,11 +22,11 @@ namespace base { namespace { -int WaitpidWithTimeout(ProcessHandle handle, - int64 wait_milliseconds, - bool* success) { +bool WaitpidWithTimeout(ProcessHandle handle, + int* status, + base::TimeDelta wait) { // This POSIX version of this function only guarantees that we wait no less - // than |wait_milliseconds| for the process to exit. The child process may + // 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. // @@ -36,7 +37,7 @@ int WaitpidWithTimeout(ProcessHandle handle, // 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_milliseconds, sleeping for + // 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. @@ -47,15 +48,18 @@ int WaitpidWithTimeout(ProcessHandle handle, // // 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. - int status = -1; - pid_t ret_pid = HANDLE_EINTR(waitpid(handle, &status, WNOHANG)); + + 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() + - TimeDelta::FromMilliseconds(wait_milliseconds); + TimeTicks wakeup_time = TimeTicks::Now() + wait; while (ret_pid == 0) { TimeTicks now = TimeTicks::Now(); if (now > wakeup_time) @@ -69,7 +73,7 @@ int WaitpidWithTimeout(ProcessHandle handle, // 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)); + ret_pid = HANDLE_EINTR(waitpid(handle, status, WNOHANG)); if ((max_sleep_time_usecs < kMaxSleepInMicroseconds) && (double_sleep_time++ % 4 == 0)) { @@ -77,10 +81,7 @@ int WaitpidWithTimeout(ProcessHandle handle, } } - if (success) - *success = (ret_pid != -1); - - return status; + return ret_pid > 0; } TerminationStatus GetTerminationStatusImpl(ProcessHandle handle, @@ -225,12 +226,8 @@ bool WaitForExitCode(ProcessHandle handle, int* exit_code) { bool WaitForExitCodeWithTimeout(ProcessHandle handle, int* exit_code, base::TimeDelta timeout) { - bool waitpid_success = false; - int status = WaitpidWithTimeout(handle, timeout.InMilliseconds(), - &waitpid_success); - if (status == -1) - return false; - if (!waitpid_success) + int status; + if (!WaitpidWithTimeout(handle, &status, timeout)) return false; if (WIFSIGNALED(status)) { *exit_code = -1; @@ -273,16 +270,15 @@ static bool WaitForSingleNonChildProcess(ProcessHandle handle, DCHECK_GT(handle, 0); DCHECK(wait.InMilliseconds() == base::kNoTimeout || wait > base::TimeDelta()); - int kq = kqueue(); - if (kq == -1) { + ScopedFD kq(kqueue()); + if (!kq.is_valid()) { DPLOG(ERROR) << "kqueue"; return false; } - file_util::ScopedFD kq_closer(&kq); struct kevent change = {0}; EV_SET(&change, handle, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL); - int result = HANDLE_EINTR(kevent(kq, &change, 1, NULL, 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. @@ -316,7 +312,7 @@ static bool WaitForSingleNonChildProcess(ProcessHandle handle, remaining_timespec_ptr = &remaining_timespec; } - result = kevent(kq, NULL, 0, &event, 1, remaining_timespec_ptr); + result = kevent(kq.get(), NULL, 0, &event, 1, remaining_timespec_ptr); if (result == -1 && errno == EINTR) { if (!wait_forever) { @@ -369,21 +365,10 @@ bool WaitForSingleProcess(ProcessHandle handle, base::TimeDelta wait) { #endif // OS_MACOSX } - bool waitpid_success; - int status = -1; - if (wait.InMilliseconds() == base::kNoTimeout) { - waitpid_success = (HANDLE_EINTR(waitpid(handle, &status, 0)) != -1); - } else { - status = WaitpidWithTimeout( - handle, wait.InMilliseconds(), &waitpid_success); - } - - if (status != -1) { - DCHECK(waitpid_success); - return WIFEXITED(status); - } else { + int status; + if (!WaitpidWithTimeout(handle, &status, wait)) return false; - } + return WIFEXITED(status); } bool CleanupProcesses(const FilePath::StringType& executable_name, diff --git a/chromium/base/process/kill_win.cc b/chromium/base/process/kill_win.cc index 99a7c661851..aaf3f30a936 100644 --- a/chromium/base/process/kill_win.cc +++ b/chromium/base/process/kill_win.cc @@ -96,9 +96,9 @@ bool KillProcess(ProcessHandle process, int exit_code, bool wait) { if (result && wait) { // The process may not end immediately due to pending I/O if (WAIT_OBJECT_0 != WaitForSingleObject(process, 60 * 1000)) - DLOG_GETLASTERROR(ERROR) << "Error waiting for process exit"; + DPLOG(ERROR) << "Error waiting for process exit"; } else if (!result) { - DLOG_GETLASTERROR(ERROR) << "Unable to terminate process"; + DPLOG(ERROR) << "Unable to terminate process"; } return result; } @@ -111,7 +111,7 @@ bool KillProcessById(ProcessId process_id, int exit_code, bool wait) { FALSE, // Don't inherit handle process_id); if (!process) { - DLOG_GETLASTERROR(ERROR) << "Unable to open process " << process_id; + DPLOG(ERROR) << "Unable to open process " << process_id; return false; } bool ret = KillProcess(process, exit_code, wait); @@ -123,7 +123,7 @@ TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) { DWORD tmp_exit_code = 0; if (!::GetExitCodeProcess(handle, &tmp_exit_code)) { - DLOG_GETLASTERROR(FATAL) << "GetExitCodeProcess() failed"; + DPLOG(FATAL) << "GetExitCodeProcess() failed"; if (exit_code) { // This really is a random number. We haven't received any // information about the exit code, presumably because this @@ -149,7 +149,7 @@ TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) { } if (wait_result == WAIT_FAILED) { - DLOG_GETLASTERROR(ERROR) << "WaitForSingleObject() failed"; + DPLOG(ERROR) << "WaitForSingleObject() failed"; } else { DCHECK_EQ(WAIT_OBJECT_0, wait_result); diff --git a/chromium/base/process/launch.cc b/chromium/base/process/launch.cc index 0c9f3a88fef..a1c4d2159cb 100644 --- a/chromium/base/process/launch.cc +++ b/chromium/base/process/launch.cc @@ -20,11 +20,13 @@ LaunchOptions::LaunchOptions() stderr_handle(NULL), force_breakaway_from_job_(false) #else + clear_environ(false), fds_to_remap(NULL), maximize_rlimits(NULL), new_process_group(false) #if defined(OS_LINUX) , clone_flags(0) + , allow_new_privs(false) #endif // OS_LINUX #if defined(OS_CHROMEOS) , ctrl_terminal_fd(-1) @@ -36,4 +38,15 @@ LaunchOptions::LaunchOptions() LaunchOptions::~LaunchOptions() { } +LaunchOptions LaunchOptionsForTest() { + LaunchOptions options; +#if defined(OS_LINUX) + // To prevent accidental privilege sharing to an untrusted child, processes + // are started with PR_SET_NO_NEW_PRIVS. Do not set that here, since this + // new child will be used for testing only. + options.allow_new_privs = true; +#endif + return options; +} + } // namespace base diff --git a/chromium/base/process/launch.h b/chromium/base/process/launch.h index 336bfba16e9..261019b138f 100644 --- a/chromium/base/process/launch.h +++ b/chromium/base/process/launch.h @@ -7,7 +7,6 @@ #ifndef BASE_PROCESS_LAUNCH_H_ #define BASE_PROCESS_LAUNCH_H_ -#include <set> #include <string> #include <utility> #include <vector> @@ -25,10 +24,10 @@ #include "base/win/scoped_handle.h" #endif -class CommandLine; - namespace base { +class CommandLine; + #if defined(OS_WIN) typedef std::vector<HANDLE> HandlesToInheritVector; #endif @@ -89,10 +88,15 @@ struct BASE_EXPORT LaunchOptions { // job if any. bool force_breakaway_from_job_; #else - // Set/unset environment variables. Empty (the default) means to inherit - // the same environment. See AlterEnvironment(). + // Set/unset environment variables. These are applied on top of the parent + // process environment. Empty (the default) means to inherit the same + // environment. See AlterEnvironment(). EnvironmentMap environ; + // Clear the environment for the new process before processing changes from + // |environ|. + bool clear_environ; + // If non-null, remap file descriptors according to the mapping of // src fd->dest fd to propagate FDs into the child process. // This pointer is owned by the caller and must live through the @@ -102,7 +106,7 @@ struct BASE_EXPORT LaunchOptions { // Each element is an RLIMIT_* constant that should be raised to its // rlim_max. This pointer is owned by the caller and must live through // the call to LaunchProcess(). - const std::set<int>* maximize_rlimits; + const std::vector<int>* maximize_rlimits; // If true, start the process in a new process group, instead of // inheriting the parent's process group. The pgid of the child process @@ -112,6 +116,10 @@ struct BASE_EXPORT LaunchOptions { #if defined(OS_LINUX) // If non-zero, start the process using clone(), using flags as provided. 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; #endif // defined(OS_LINUX) #if defined(OS_CHROMEOS) @@ -120,6 +128,15 @@ struct BASE_EXPORT LaunchOptions { int ctrl_terminal_fd; #endif // defined(OS_CHROMEOS) +#if defined(OS_MACOSX) + // If this name is non-empty, the new child, after fork() but before exec(), + // will look up this server name in the bootstrap namespace. The resulting + // service port will be replaced as the bootstrap port in the child. Because + // the process's IPC space is cleared on exec(), any rights to the old + // bootstrap port will not be transferred to the new process. + std::string replacement_bootstrap_name; +#endif + #endif // !defined(OS_WIN) }; @@ -160,6 +177,16 @@ BASE_EXPORT bool LaunchProcess(const string16& cmdline, const LaunchOptions& options, win::ScopedHandle* process_handle); +// 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); + #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 @@ -232,8 +259,17 @@ BASE_EXPORT void RaiseProcessToHighPriority(); // in the child after forking will restore the standard exception handler. // See http://crbug.com/20371/ for more details. void RestoreDefaultExceptionHandler(); + +// Look up the bootstrap server named |replacement_bootstrap_name| via the +// current |bootstrap_port|. Then replace the task's bootstrap port with the +// received right. +void ReplaceBootstrapPort(const std::string& replacement_bootstrap_name); #endif // defined(OS_MACOSX) +// Creates a LaunchOptions object suitable for launching processes in a test +// binary. This should not be called in production/released code. +BASE_EXPORT LaunchOptions LaunchOptionsForTest(); + } // namespace base #endif // BASE_PROCESS_LAUNCH_H_ diff --git a/chromium/base/process/launch_mac.cc b/chromium/base/process/launch_mac.cc index 176edca72ea..ce02475541e 100644 --- a/chromium/base/process/launch_mac.cc +++ b/chromium/base/process/launch_mac.cc @@ -5,6 +5,9 @@ #include "base/process/launch.h" #include <mach/mach.h> +#include <servers/bootstrap.h> + +#include "base/logging.h" namespace base { @@ -25,4 +28,21 @@ void RestoreDefaultExceptionHandler() { EXCEPTION_DEFAULT, THREAD_STATE_NONE); } +void ReplaceBootstrapPort(const std::string& new_bootstrap_name) { + // This function is called between fork() and exec(), so it should take care + // to run properly in that situation. + + mach_port_t port = MACH_PORT_NULL; + kern_return_t kr = bootstrap_look_up(bootstrap_port, + new_bootstrap_name.c_str(), &port); + if (kr != KERN_SUCCESS) { + RAW_LOG(FATAL, "Failed to look up replacement bootstrap port."); + } + + kr = task_set_bootstrap_port(mach_task_self(), port); + if (kr != KERN_SUCCESS) { + RAW_LOG(FATAL, "Failed to replace bootstrap port."); + } +} + } // namespace base diff --git a/chromium/base/process/launch_posix.cc b/chromium/base/process/launch_posix.cc index 8dc8f9e215d..78a3be47eb0 100644 --- a/chromium/base/process/launch_posix.cc +++ b/chromium/base/process/launch_posix.cc @@ -26,6 +26,7 @@ #include "base/debug/stack_trace.h" #include "base/file_util.h" #include "base/files/dir_reader_posix.h" +#include "base/files/scoped_file.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/posix/eintr_wrapper.h" @@ -37,6 +38,10 @@ #include "base/threading/platform_thread.h" #include "base/threading/thread_restrictions.h" +#if defined(OS_LINUX) +#include <sys/prctl.h> +#endif + #if defined(OS_CHROMEOS) #include <sys/ioctl.h> #endif @@ -181,16 +186,16 @@ void ResetChildSignalHandlersToDefaults(void) { } // anonymous namespace -// A class to handle auto-closing of DIR*'s. -class ScopedDIRClose { - public: +// Functor for |ScopedDIR| (below). +struct ScopedDIRClose { inline void operator()(DIR* x) const { - if (x) { + if (x) closedir(x); - } } }; -typedef scoped_ptr_malloc<DIR, ScopedDIRClose> ScopedDIR; + +// Automatically closes |DIR*|s. +typedef scoped_ptr<DIR, ScopedDIRClose> ScopedDIR; #if defined(OS_LINUX) static const char kFDDir[] = "/proc/self/fd"; @@ -207,7 +212,7 @@ static const char kFDDir[] = "/proc/self/fd"; #endif void CloseSuperfluousFds(const base::InjectiveMultimap& saved_mapping) { - // DANGER: no calls to malloc are allowed from now on: + // DANGER: no calls to malloc or locks are allowed from now on: // http://crbug.com/36678 // Get the maximum number of FDs possible. @@ -220,12 +225,13 @@ void CloseSuperfluousFds(const base::InjectiveMultimap& saved_mapping) { const int fd = static_cast<int>(i); if (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO) continue; - InjectiveMultimap::const_iterator j; - for (j = saved_mapping.begin(); j != saved_mapping.end(); j++) { - if (fd == j->dest) + // Cannot use STL iterators here, since debug iterators use locks. + size_t j; + for (j = 0; j < saved_mapping.size(); j++) { + if (fd == saved_mapping[j].dest) break; } - if (j != saved_mapping.end()) + if (j < saved_mapping.size()) continue; // Since we're just trying to close anything we can find, @@ -249,12 +255,13 @@ void CloseSuperfluousFds(const base::InjectiveMultimap& saved_mapping) { continue; if (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO) continue; - InjectiveMultimap::const_iterator i; - for (i = saved_mapping.begin(); i != saved_mapping.end(); i++) { - if (fd == i->dest) + // Cannot use STL iterators here, since debug iterators use locks. + size_t i; + for (i = 0; i < saved_mapping.size(); i++) { + if (fd == saved_mapping[i].dest) break; } - if (i != saved_mapping.end()) + if (i < saved_mapping.size()) continue; if (fd == dir_fd) continue; @@ -285,8 +292,12 @@ bool LaunchProcess(const std::vector<std::string>& argv, scoped_ptr<char*[]> argv_cstr(new char*[argv.size() + 1]); scoped_ptr<char*[]> new_environ; + char* const empty_environ = NULL; + char* const* old_environ = GetEnvironment(); + if (options.clear_environ) + old_environ = &empty_environ; if (!options.environ.empty()) - new_environ = AlterEnvironment(GetEnvironment(), options.environ); + new_environ = AlterEnvironment(old_environ, options.environ); sigset_t full_sigset; sigfillset(&full_sigset); @@ -318,6 +329,9 @@ bool LaunchProcess(const std::vector<std::string>& argv, } else if (pid == 0) { // Child process + // DANGER: no calls to malloc or locks are allowed from now on: + // http://crbug.com/36678 + // DANGER: fork() rule: in the child, if you don't end up doing exec*(), // you call _exit() instead of exit(). This is because _exit() does not // call any previously-registered (in the parent) exit handlers, which @@ -327,14 +341,13 @@ bool LaunchProcess(const std::vector<std::string>& argv, // If a child process uses the readline library, the process block forever. // In BSD like OSes including OS X it is safe to assign /dev/null as stdin. // See http://crbug.com/56596. - int null_fd = HANDLE_EINTR(open("/dev/null", O_RDONLY)); - if (null_fd < 0) { + base::ScopedFD null_fd(HANDLE_EINTR(open("/dev/null", O_RDONLY))); + if (!null_fd.is_valid()) { RAW_LOG(ERROR, "Failed to open /dev/null"); _exit(127); } - file_util::ScopedFD null_fd_closer(&null_fd); - int new_fd = HANDLE_EINTR(dup2(null_fd, STDIN_FILENO)); + int new_fd = HANDLE_EINTR(dup2(null_fd.get(), STDIN_FILENO)); if (new_fd != STDIN_FILENO) { RAW_LOG(ERROR, "Failed to dup /dev/null for stdin"); _exit(127); @@ -356,16 +369,14 @@ bool LaunchProcess(const std::vector<std::string>& argv, if (options.maximize_rlimits) { // Some resource limits need to be maximal in this child. - std::set<int>::const_iterator resource; - for (resource = options.maximize_rlimits->begin(); - resource != options.maximize_rlimits->end(); - ++resource) { + for (size_t i = 0; i < options.maximize_rlimits->size(); ++i) { + const int resource = (*options.maximize_rlimits)[i]; struct rlimit limit; - if (getrlimit(*resource, &limit) < 0) { + if (getrlimit(resource, &limit) < 0) { RAW_LOG(WARNING, "getrlimit failed"); } else if (limit.rlim_cur < limit.rlim_max) { limit.rlim_cur = limit.rlim_max; - if (setrlimit(*resource, &limit) < 0) { + if (setrlimit(resource, &limit) < 0) { RAW_LOG(WARNING, "setrlimit failed"); } } @@ -374,6 +385,8 @@ bool LaunchProcess(const std::vector<std::string>& argv, #if defined(OS_MACOSX) RestoreDefaultExceptionHandler(); + if (!options.replacement_bootstrap_name.empty()) + ReplaceBootstrapPort(options.replacement_bootstrap_name); #endif // defined(OS_MACOSX) ResetChildSignalHandlersToDefaults(); @@ -388,9 +401,6 @@ bool LaunchProcess(const std::vector<std::string>& argv, memset(reinterpret_cast<void*>(malloc), 0xff, 8); #endif // 0 - // DANGER: no calls to malloc are allowed from now on: - // http://crbug.com/36678 - #if defined(OS_CHROMEOS) if (options.ctrl_terminal_fd >= 0) { // Set process' controlling terminal. @@ -406,15 +416,16 @@ bool LaunchProcess(const std::vector<std::string>& argv, #endif // defined(OS_CHROMEOS) if (options.fds_to_remap) { - for (FileHandleMappingVector::const_iterator - it = options.fds_to_remap->begin(); - it != options.fds_to_remap->end(); ++it) { - fd_shuffle1.push_back(InjectionArc(it->first, it->second, false)); - fd_shuffle2.push_back(InjectionArc(it->first, it->second, false)); + // Cannot use STL iterators here, since debug iterators use locks. + for (size_t i = 0; i < options.fds_to_remap->size(); ++i) { + const FileHandleMappingVector::value_type& value = + (*options.fds_to_remap)[i]; + fd_shuffle1.push_back(InjectionArc(value.first, value.second, false)); + fd_shuffle2.push_back(InjectionArc(value.first, value.second, false)); } } - if (!options.environ.empty()) + if (!options.environ.empty() || options.clear_environ) SetEnvironment(new_environ.get()); // fd_shuffle1 is mutated by this call because it cannot malloc. @@ -423,6 +434,20 @@ bool LaunchProcess(const std::vector<std::string>& argv, CloseSuperfluousFds(fd_shuffle2); + // Set NO_NEW_PRIVS by default. Since NO_NEW_PRIVS only exists in kernel + // 3.5+, do not check the return value of prctl here. +#if defined(OS_LINUX) +#ifndef PR_SET_NO_NEW_PRIVS +#define PR_SET_NO_NEW_PRIVS 38 +#endif + if (!options.allow_new_privs) { + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) && errno != EINVAL) { + // Only log if the error is not EINVAL (i.e. not supported). + RAW_LOG(FATAL, "prctl(PR_SET_NO_NEW_PRIVS) failed"); + } + } +#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; @@ -518,11 +543,12 @@ static GetAppOutputInternalResult GetAppOutputInternal( return EXECUTE_FAILURE; case 0: // child { + // DANGER: no calls to malloc or locks are allowed from now on: + // http://crbug.com/36678 + #if defined(OS_MACOSX) RestoreDefaultExceptionHandler(); #endif - // DANGER: no calls to malloc are allowed from now on: - // http://crbug.com/36678 // Obscure fork() rule: in the child, if you don't end up doing exec*(), // you call _exit() instead of exit(). This is because _exit() does not @@ -544,8 +570,8 @@ static GetAppOutputInternalResult GetAppOutputInternal( // Adding another element here? Remeber to increase the argument to // reserve(), above. - std::copy(fd_shuffle1.begin(), fd_shuffle1.end(), - std::back_inserter(fd_shuffle2)); + for (size_t i = 0; i < fd_shuffle1.size(); ++i) + fd_shuffle2.push_back(fd_shuffle1[i]); if (!ShuffleFileDescriptors(&fd_shuffle1)) _exit(127); diff --git a/chromium/base/process/launch_win.cc b/chromium/base/process/launch_win.cc index 0c831cf7b4a..9e4db384ebb 100644 --- a/chromium/base/process/launch_win.cc +++ b/chromium/base/process/launch_win.cc @@ -6,6 +6,7 @@ #include <fcntl.h> #include <io.h> +#include <shellapi.h> #include <windows.h> #include <userenv.h> #include <psapi.h> @@ -22,6 +23,7 @@ #include "base/message_loop/message_loop.h" #include "base/metrics/histogram.h" #include "base/process/kill.h" +#include "base/strings/utf_string_conversions.h" #include "base/sys_info.h" #include "base/win/object_watcher.h" #include "base/win/scoped_handle.h" @@ -192,7 +194,8 @@ bool LaunchProcess(const string16& cmdline, &temp_process_info); DestroyEnvironmentBlock(enviroment_block); if (!launched) { - DPLOG(ERROR); + DPLOG(ERROR) << "Command line:" << std::endl << UTF16ToUTF8(cmdline) + << std::endl;; return false; } } else { @@ -200,7 +203,8 @@ bool LaunchProcess(const string16& cmdline, const_cast<wchar_t*>(cmdline.c_str()), NULL, NULL, inherit_handles, flags, NULL, NULL, startup_info, &temp_process_info)) { - DPLOG(ERROR); + DPLOG(ERROR) << "Command line:" << std::endl << UTF16ToUTF8(cmdline) + << std::endl;; return false; } } @@ -239,6 +243,41 @@ bool LaunchProcess(const CommandLine& cmdline, return rv; } +bool LaunchElevatedProcess(const CommandLine& cmdline, + const LaunchOptions& options, + ProcessHandle* process_handle) { + const string16 file = cmdline.GetProgram().value(); + const string16 arguments = cmdline.GetArgumentsString(); + + SHELLEXECUTEINFO shex_info = {0}; + shex_info.cbSize = sizeof(shex_info); + shex_info.fMask = SEE_MASK_NOCLOSEPROCESS; + shex_info.hwnd = GetActiveWindow(); + shex_info.lpVerb = L"runas"; + shex_info.lpFile = file.c_str(); + shex_info.lpParameters = arguments.c_str(); + shex_info.lpDirectory = NULL; + shex_info.nShow = options.start_hidden ? SW_HIDE : SW_SHOW; + shex_info.hInstApp = NULL; + + if (!ShellExecuteEx(&shex_info)) { + DPLOG(ERROR); + return false; + } + + 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; +} + bool SetJobObjectLimitFlags(HANDLE job_object, DWORD limit_flags) { JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info = {0}; limit_info.BasicLimitInformation.LimitFlags = limit_flags; diff --git a/chromium/base/process/memory.cc b/chromium/base/process/memory.cc new file mode 100644 index 00000000000..1dbc3630703 --- /dev/null +++ b/chromium/base/process/memory.cc @@ -0,0 +1,30 @@ +// 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/process/memory.h" + +namespace base { + +// Defined in memory_mac.mm for Mac. +#if !defined(OS_MACOSX) + +bool UncheckedCalloc(size_t num_items, size_t size, void** result) { + const size_t alloc_size = num_items * size; + + // Overflow check + if (size && ((alloc_size / size) != num_items)) { + *result = NULL; + return false; + } + + if (!UncheckedMalloc(alloc_size, result)) + return false; + + memset(*result, 0, alloc_size); + return true; +} + +#endif + +} diff --git a/chromium/base/process/memory.h b/chromium/base/process/memory.h index e6696cb8a70..100d9c72f43 100644 --- a/chromium/base/process/memory.h +++ b/chromium/base/process/memory.h @@ -14,6 +14,14 @@ #include <windows.h> #endif +#ifdef PVALLOC_AVAILABLE +// Build config explicitly tells us whether or not pvalloc is available. +#elif defined(LIBC_GLIBC) && !defined(USE_TCMALLOC) +#define PVALLOC_AVAILABLE 1 +#else +#define PVALLOC_AVAILABLE 0 +#endif + namespace base { // Enables low fragmentation heap (LFH) for every heaps of this process. This @@ -53,17 +61,21 @@ const int kMaxOomScore = 1000; BASE_EXPORT bool AdjustOOMScore(ProcessId process, int score); #endif -#if defined(OS_MACOSX) -// Very large images or svg canvases can cause huge mallocs. Skia -// does tricks on tcmalloc-based systems to allow malloc to fail with -// a NULL rather than hit the oom crasher. This replicates that for -// OSX. -// -// IF YOU USE THIS WITHOUT CONSULTING YOUR FRIENDLY OSX DEVELOPER, -// YOUR CODE IS LIKELY TO BE REVERTED. THANK YOU. -BASE_EXPORT void* UncheckedMalloc(size_t size); -BASE_EXPORT void* UncheckedCalloc(size_t num_items, size_t size); -#endif // defined(OS_MACOSX) +// Special allocator functions for callers that want to check for OOM. +// These will not abort if the allocation fails even if +// EnableTerminationOnOutOfMemory has been called. +// This can be useful for huge and/or unpredictable size memory allocations. +// Please only use this if you really handle the case when the allocation +// fails. Doing otherwise would risk security. +// These functions may still crash on OOM when running under memory tools, +// specifically ASan and other sanitizers. +// Return value tells whether the allocation succeeded. If it fails |result| is +// set to NULL, otherwise it holds the memory address. +BASE_EXPORT WARN_UNUSED_RESULT bool UncheckedMalloc(size_t size, + void** result); +BASE_EXPORT WARN_UNUSED_RESULT bool UncheckedCalloc(size_t num_items, + size_t size, + void** result); } // namespace base diff --git a/chromium/base/process/memory_linux.cc b/chromium/base/process/memory_linux.cc index 6bed68bd832..befd832d667 100644 --- a/chromium/base/process/memory_linux.cc +++ b/chromium/base/process/memory_linux.cc @@ -12,6 +12,22 @@ #include "base/process/internal_linux.h" #include "base/strings/string_number_conversions.h" +#if defined(USE_TCMALLOC) +// Used by UncheckedMalloc. If tcmalloc is linked to the executable +// this will be replaced by a strong symbol that actually implement +// the semantics and don't call new handler in case the allocation fails. +extern "C" { + +__attribute__((weak, visibility("default"))) +void* tc_malloc_skip_new_handler_weak(size_t size); + +void* tc_malloc_skip_new_handler_weak(size_t size) { + return malloc(size); +} + +} +#endif + namespace base { size_t g_oom_size = 0U; @@ -44,7 +60,9 @@ void* __libc_malloc(size_t size); void* __libc_realloc(void* ptr, size_t size); void* __libc_calloc(size_t nmemb, size_t size); void* __libc_valloc(size_t size); +#if PVALLOC_AVAILABLE == 1 void* __libc_pvalloc(size_t size); +#endif void* __libc_memalign(size_t alignment, size_t size); // Overriding the system memory allocation functions: @@ -99,7 +117,9 @@ void* __libc_memalign(size_t alignment, size_t size); DIE_ON_OOM_1(malloc) DIE_ON_OOM_1(valloc) +#if PVALLOC_AVAILABLE == 1 DIE_ON_OOM_1(pvalloc) +#endif DIE_ON_OOM_2(calloc, size_t) DIE_ON_OOM_2(realloc, void*) @@ -157,9 +177,7 @@ bool AdjustOOMScore(ProcessId process, int score) { DVLOG(1) << "Adjusting oom_score_adj of " << process << " to " << score_str; int score_len = static_cast<int>(score_str.length()); - return (score_len == file_util::WriteFile(oom_file, - score_str.c_str(), - score_len)); + return (score_len == WriteFile(oom_file, score_str.c_str(), score_len)); } // If the oom_score_adj file doesn't exist, then we write the old @@ -174,12 +192,22 @@ bool AdjustOOMScore(ProcessId process, int score) { std::string score_str = IntToString(converted_score); DVLOG(1) << "Adjusting oom_adj of " << process << " to " << score_str; int score_len = static_cast<int>(score_str.length()); - return (score_len == file_util::WriteFile(oom_file, - score_str.c_str(), - score_len)); + return (score_len == WriteFile(oom_file, score_str.c_str(), score_len)); } return false; } +bool UncheckedMalloc(size_t size, void** result) { +#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR) || \ + (!defined(LIBC_GLIBC) && !defined(USE_TCMALLOC)) + *result = malloc(size); +#elif defined(LIBC_GLIBC) && !defined(USE_TCMALLOC) + *result = __libc_malloc(size); +#elif defined(USE_TCMALLOC) + *result = tc_malloc_skip_new_handler_weak(size); +#endif + return *result != NULL; +} + } // namespace base diff --git a/chromium/base/process/memory_mac.mm b/chromium/base/process/memory_mac.mm index 3e281cd8e3c..575e886a175 100644 --- a/chromium/base/process/memory_mac.mm +++ b/chromium/base/process/memory_mac.mm @@ -16,6 +16,7 @@ #include "base/lazy_instance.h" #include "base/logging.h" #include "base/mac/mac_util.h" +#include "base/mac/mach_logging.h" #include "base/scoped_clear_errno.h" #include "third_party/apple_apsl/CFBase.h" #include "third_party/apple_apsl/malloc.h" @@ -222,10 +223,12 @@ void DeprotectMallocZone(ChromeMallocZone* default_zone, reinterpret_cast<vm_region_info_t>(&info), &count, &unused); - CHECK(result == KERN_SUCCESS); + MACH_CHECK(result == KERN_SUCCESS, result) << "mach_vm_region"; - result = mach_port_deallocate(mach_task_self(), unused); - CHECK(result == KERN_SUCCESS); + // The kernel always returns a null object for VM_REGION_BASIC_INFO_64, but + // balance it with a deallocate in case this ever changes. See 10.9.2 + // xnu-2422.90.20/osfmk/vm/vm_map.c vm_map_region. + mach_port_deallocate(mach_task_self(), unused); // Does the region fully enclose the zone pointers? Possibly unwarranted // simplification used: using the size of a full version 8 malloc zone rather @@ -248,7 +251,7 @@ void DeprotectMallocZone(ChromeMallocZone* default_zone, *reprotection_length, false, info.protection | VM_PROT_WRITE); - CHECK(result == KERN_SUCCESS); + MACH_CHECK(result == KERN_SUCCESS, result) << "mach_vm_protect"; } } @@ -435,7 +438,7 @@ void oom_killer_new() { // === Core Foundation CFAllocators === bool CanGetContextForCFAllocator() { - return !base::mac::IsOSLaterThanMavericks_DontCallThis(); + return !base::mac::IsOSLaterThanYosemite_DontCallThis(); } CFAllocatorContext* ContextForCFAllocator(CFAllocatorRef allocator) { @@ -446,7 +449,8 @@ CFAllocatorContext* ContextForCFAllocator(CFAllocatorRef allocator) { return &our_allocator->_context; } else if (base::mac::IsOSLion() || base::mac::IsOSMountainLion() || - base::mac::IsOSMavericks()) { + base::mac::IsOSMavericks() || + base::mac::IsOSYosemite()) { ChromeCFAllocatorLions* our_allocator = const_cast<ChromeCFAllocatorLions*>( reinterpret_cast<const ChromeCFAllocatorLions*>(allocator)); @@ -502,26 +506,42 @@ id oom_killer_allocWithZone(id self, SEL _cmd, NSZone* zone) } // namespace -void* UncheckedMalloc(size_t size) { +bool UncheckedMalloc(size_t size, void** result) { if (g_old_malloc) { #if ARCH_CPU_32_BITS ScopedClearErrno clear_errno; ThreadLocalBooleanAutoReset flag(g_unchecked_alloc.Pointer(), true); #endif // ARCH_CPU_32_BITS - return g_old_malloc(malloc_default_zone(), size); + *result = g_old_malloc(malloc_default_zone(), size); + } else { + *result = malloc(size); } - return malloc(size); + + return *result != NULL; } -void* UncheckedCalloc(size_t num_items, size_t size) { +bool UncheckedCalloc(size_t num_items, size_t size, void** result) { if (g_old_calloc) { #if ARCH_CPU_32_BITS ScopedClearErrno clear_errno; ThreadLocalBooleanAutoReset flag(g_unchecked_alloc.Pointer(), true); #endif // ARCH_CPU_32_BITS - return g_old_calloc(malloc_default_zone(), num_items, size); + *result = g_old_calloc(malloc_default_zone(), num_items, size); + } else { + *result = calloc(num_items, size); } - return calloc(num_items, size); + + return *result != NULL; +} + +void* UncheckedMalloc(size_t size) { + void* address; + return UncheckedMalloc(size, &address) ? address : NULL; +} + +void* UncheckedCalloc(size_t num_items, size_t size) { + void* address; + return UncheckedCalloc(num_items, size, &address) ? address : NULL; } void EnableTerminationOnOutOfMemory() { @@ -630,7 +650,7 @@ void EnableTerminationOnOutOfMemory() { default_reprotection_length, false, default_reprotection_value); - CHECK(result == KERN_SUCCESS); + MACH_CHECK(result == KERN_SUCCESS, result) << "mach_vm_protect"; } if (purgeable_reprotection_start) { @@ -639,7 +659,7 @@ void EnableTerminationOnOutOfMemory() { purgeable_reprotection_length, false, purgeable_reprotection_value); - CHECK(result == KERN_SUCCESS); + MACH_CHECK(result == KERN_SUCCESS, result) << "mach_vm_protect"; } #endif @@ -703,8 +723,9 @@ void EnableTerminationOnOutOfMemory() { << "Failed to get kCFAllocatorMallocZone allocation function."; context->allocate = oom_killer_cfallocator_malloc_zone; } else { - NSLog(@"Internals of CFAllocator not known; out-of-memory failures via " - "CFAllocator will not result in termination. http://crbug.com/45650"); + DLOG(WARNING) << "Internals of CFAllocator not known; out-of-memory " + "failures via CFAllocator will not result in termination. " + "http://crbug.com/45650"; } #endif diff --git a/chromium/base/process/memory_unittest.cc b/chromium/base/process/memory_unittest.cc index e5c759d5711..048c09d38c1 100644 --- a/chromium/base/process/memory_unittest.cc +++ b/chromium/base/process/memory_unittest.cc @@ -121,9 +121,7 @@ TEST(ProcessMemoryTest, MacMallocFailureDoesNotTerminate) { buf = malloc(std::numeric_limits<size_t>::max() - (2 * PAGE_SIZE) - 1); }, testing::KilledBySignal(SIGTRAP), - "\\*\\*\\* error: can't allocate region.*" - "(Terminating process due to a potential for future heap " - "corruption){0}"); + "\\*\\*\\* error: can't allocate region.*\\n?.*"); base::debug::Alias(buf); } @@ -143,8 +141,8 @@ TEST(ProcessMemoryTest, MacTerminateOnHeapCorruption) { ASSERT_DEATH(free(buf), "attempting free on address which " "was not malloc\\(\\)-ed"); #else - ASSERT_DEATH(free(buf), "being freed.*" - "\\*\\*\\* set a breakpoint in malloc_error_break to debug.*" + 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) } @@ -154,13 +152,9 @@ TEST(ProcessMemoryTest, MacTerminateOnHeapCorruption) { // Android doesn't implement set_new_handler, so we can't use the // OutOfMemoryTest cases. // OpenBSD does not support these tests either. -// AddressSanitizer and ThreadSanitizer define the malloc()/free()/etc. -// functions so that they don't crash if the program is out of memory, so the -// OOM tests aren't supposed to work. // TODO(vandebo) make this work on Windows too. #if !defined(OS_ANDROID) && !defined(OS_OPENBSD) && \ - !defined(OS_WIN) && \ - !defined(ADDRESS_SANITIZER) && !defined(THREAD_SANITIZER) + !defined(OS_WIN) #if defined(USE_TCMALLOC) extern "C" { @@ -168,14 +162,14 @@ int tc_set_new_mode(int mode); } #endif // defined(USE_TCMALLOC) -class OutOfMemoryDeathTest : public testing::Test { +class OutOfMemoryTest : public testing::Test { public: - OutOfMemoryDeathTest() - : value_(NULL), - // Make test size as large as possible minus a few pages so - // that alignment or other rounding doesn't make it wrap. - test_size_(std::numeric_limits<std::size_t>::max() - 12 * 1024), - signed_test_size_(std::numeric_limits<ssize_t>::max()) { + OutOfMemoryTest() + : value_(NULL), + // Make test size as large as possible minus a few pages so + // that alignment or other rounding doesn't make it wrap. + test_size_(std::numeric_limits<std::size_t>::max() - 12 * 1024), + signed_test_size_(std::numeric_limits<ssize_t>::max()) { } #if defined(USE_TCMALLOC) @@ -188,6 +182,14 @@ class OutOfMemoryDeathTest : public testing::Test { } #endif // defined(USE_TCMALLOC) + protected: + void* value_; + size_t test_size_; + ssize_t signed_test_size_; +}; + +class OutOfMemoryDeathTest : public OutOfMemoryTest { + public: void SetUpInDeathAssert() { // Must call EnableTerminationOnOutOfMemory() because that is called from // chrome's main function and therefore hasn't been called yet. @@ -196,10 +198,6 @@ class OutOfMemoryDeathTest : public testing::Test { // should be done inside of the ASSERT_DEATH. base::EnableTerminationOnOutOfMemory(); } - - void* value_; - size_t test_size_; - ssize_t signed_test_size_; }; TEST_F(OutOfMemoryDeathTest, New) { @@ -245,12 +243,15 @@ TEST_F(OutOfMemoryDeathTest, Valloc) { } #if defined(OS_LINUX) + +#if PVALLOC_AVAILABLE == 1 TEST_F(OutOfMemoryDeathTest, Pvalloc) { ASSERT_DEATH({ SetUpInDeathAssert(); value_ = pvalloc(test_size_); }, ""); } +#endif // PVALLOC_AVAILABLE == 1 TEST_F(OutOfMemoryDeathTest, Memalign) { ASSERT_DEATH({ @@ -374,5 +375,54 @@ TEST_F(OutOfMemoryDeathTest, PsychoticallyBigObjCObject) { #endif // !ARCH_CPU_64_BITS #endif // OS_MACOSX -#endif // !defined(OS_ANDROID) && !defined(OS_OPENBSD) && - // !defined(OS_WIN) && !defined(ADDRESS_SANITIZER) +class OutOfMemoryHandledTest : public OutOfMemoryTest { + public: + static const size_t kSafeMallocSize = 512; + static const size_t kSafeCallocSize = 128; + static const size_t kSafeCallocItems = 4; + + virtual void SetUp() { + OutOfMemoryTest::SetUp(); + + // We enable termination on OOM - just as Chrome does at early + // initialization - and test that UncheckedMalloc and UncheckedCalloc + // properly by-pass this in order to allow the caller to handle OOM. + base::EnableTerminationOnOutOfMemory(); + } +}; + +// TODO(b.kelemen): make UncheckedMalloc and UncheckedCalloc work +// on Windows as well. +// UncheckedMalloc() and UncheckedCalloc() work as regular malloc()/calloc() +// under sanitizer tools. +#if !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) +TEST_F(OutOfMemoryHandledTest, UncheckedMalloc) { + EXPECT_TRUE(base::UncheckedMalloc(kSafeMallocSize, &value_)); + EXPECT_TRUE(value_ != NULL); + free(value_); + + EXPECT_FALSE(base::UncheckedMalloc(test_size_, &value_)); + EXPECT_TRUE(value_ == NULL); +} + +TEST_F(OutOfMemoryHandledTest, UncheckedCalloc) { + EXPECT_TRUE(base::UncheckedCalloc(1, kSafeMallocSize, &value_)); + EXPECT_TRUE(value_ != NULL); + const char* bytes = static_cast<const char*>(value_); + for (size_t i = 0; i < kSafeMallocSize; ++i) + EXPECT_EQ(0, bytes[i]); + free(value_); + + EXPECT_TRUE( + base::UncheckedCalloc(kSafeCallocItems, kSafeCallocSize, &value_)); + EXPECT_TRUE(value_ != NULL); + bytes = static_cast<const char*>(value_); + for (size_t i = 0; i < (kSafeCallocItems * kSafeCallocSize); ++i) + EXPECT_EQ(0, bytes[i]); + free(value_); + + EXPECT_FALSE(base::UncheckedCalloc(1, test_size_, &value_)); + EXPECT_TRUE(value_ == NULL); +} +#endif // !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) +#endif // !defined(OS_ANDROID) && !defined(OS_OPENBSD) && !defined(OS_WIN) diff --git a/chromium/base/process/memory_win.cc b/chromium/base/process/memory_win.cc index c53a1be5395..668214ceaf0 100644 --- a/chromium/base/process/memory_win.cc +++ b/chromium/base/process/memory_win.cc @@ -82,4 +82,15 @@ HMODULE GetModuleFromAddress(void* address) { return instance; } +// TODO(b.kelemen): implement it with the required semantics. On Linux this is +// implemented with a weak symbol that is overridden by tcmalloc. This is +// neccessary because base cannot have a direct dependency on tcmalloc. Since +// weak symbols are not supported on Windows this will involve some build time +// magic, much like what is done for libcrt in order to override the allocation +// functions. +bool UncheckedMalloc(size_t size, void** result) { + *result = malloc(size); + return *result != NULL; +} + } // namespace base diff --git a/chromium/base/process/process_handle.h b/chromium/base/process/process_handle.h index a37784275ea..6f8094ee80b 100644 --- a/chromium/base/process/process_handle.h +++ b/chromium/base/process/process_handle.h @@ -81,12 +81,10 @@ BASE_EXPORT bool GetProcessIntegrityLevel(ProcessHandle process, IntegrityLevel* level); #endif -#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_BSD) +#if defined(OS_POSIX) // Returns the path to the executable of the given process. BASE_EXPORT FilePath GetProcessExecutablePath(ProcessHandle process); -#endif -#if defined(OS_POSIX) // Returns the ID for the parent of the given process. BASE_EXPORT ProcessId GetParentProcessId(ProcessHandle process); #endif diff --git a/chromium/base/process/process_handle_freebsd.cc b/chromium/base/process/process_handle_freebsd.cc index 8a0e9de8f2d..e465a85b7e0 100644 --- a/chromium/base/process/process_handle_freebsd.cc +++ b/chromium/base/process/process_handle_freebsd.cc @@ -6,6 +6,7 @@ #include <sys/sysctl.h> #include <sys/types.h> +#include <sys/user.h> #include <unistd.h> namespace base { diff --git a/chromium/base/process/process_handle_linux.cc b/chromium/base/process/process_handle_linux.cc index 0f7ccd348a8..6c5cd2fa182 100644 --- a/chromium/base/process/process_handle_linux.cc +++ b/chromium/base/process/process_handle_linux.cc @@ -11,7 +11,7 @@ namespace base { ProcessId GetParentProcessId(ProcessHandle process) { ProcessId pid = - internal::ReadProcStatsAndGetFieldAsInt(process, internal::VM_PPID); + internal::ReadProcStatsAndGetFieldAsInt64(process, internal::VM_PPID); if (pid) return pid; return -1; diff --git a/chromium/base/process/process_handle_mac.cc b/chromium/base/process/process_handle_mac.cc index 6cb8d686e4d..cbf0bc5c439 100644 --- a/chromium/base/process/process_handle_mac.cc +++ b/chromium/base/process/process_handle_mac.cc @@ -4,6 +4,7 @@ #include "base/process/process_handle.h" +#include <libproc.h> #include <sys/sysctl.h> #include <sys/types.h> @@ -24,4 +25,12 @@ ProcessId GetParentProcessId(ProcessHandle process) { return info.kp_eproc.e_ppid; } +FilePath GetProcessExecutablePath(ProcessHandle process) { + char pathbuf[PROC_PIDPATHINFO_MAXSIZE]; + if (!proc_pidpath(process, pathbuf, sizeof(pathbuf))) + return FilePath(); + + return FilePath(pathbuf); +} + } // namespace base diff --git a/chromium/base/process/process_info_linux.cc b/chromium/base/process/process_info_linux.cc index da34c180e8a..9ec23135bfc 100644 --- a/chromium/base/process/process_info_linux.cc +++ b/chromium/base/process/process_info_linux.cc @@ -15,8 +15,8 @@ namespace base { //static const Time CurrentProcessInfo::CreationTime() { ProcessHandle pid = GetCurrentProcessHandle(); - int start_ticks = internal::ReadProcStatsAndGetFieldAsInt( - pid, internal::VM_STARTTIME); + int64 start_ticks = + internal::ReadProcStatsAndGetFieldAsInt64(pid, internal::VM_STARTTIME); DCHECK(start_ticks); TimeDelta start_offset = internal::ClockTicksToTimeDelta(start_ticks); Time boot_time = internal::GetBootTime(); diff --git a/chromium/base/process/process_info_mac.cc b/chromium/base/process/process_info_mac.cc index ab8394b891d..b7cfdceda05 100644 --- a/chromium/base/process/process_info_mac.cc +++ b/chromium/base/process/process_info_mac.cc @@ -21,7 +21,7 @@ const Time CurrentProcessInfo::CreationTime() { if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) < 0) return Time(); - scoped_ptr_malloc<struct kinfo_proc> + scoped_ptr<struct kinfo_proc, base::FreeDeleter> proc(static_cast<struct kinfo_proc*>(malloc(len))); if (sysctl(mib, arraysize(mib), proc.get(), &len, NULL, 0) < 0) return Time(); diff --git a/chromium/base/process/process_iterator.h b/chromium/base/process/process_iterator.h index aa8ba74aba0..aa2fc4148ad 100644 --- a/chromium/base/process/process_iterator.h +++ b/chromium/base/process/process_iterator.h @@ -20,8 +20,10 @@ #if defined(OS_WIN) #include <windows.h> #include <tlhelp32.h> -#elif defined(OS_MACOSX) || defined(OS_BSD) +#elif defined(OS_MACOSX) || defined(OS_OPENBSD) #include <sys/sysctl.h> +#elif defined(OS_FREEBSD) +#include <sys/user.h> #elif defined(OS_POSIX) #include <dirent.h> #endif diff --git a/chromium/base/process/process_iterator_freebsd.cc b/chromium/base/process/process_iterator_freebsd.cc index e8225b0054e..5b1e2ab09db 100644 --- a/chromium/base/process/process_iterator_freebsd.cc +++ b/chromium/base/process/process_iterator_freebsd.cc @@ -4,7 +4,9 @@ #include "base/process/process_iterator.h" +#include <sys/types.h> #include <sys/sysctl.h> +#include <unistd.h> #include "base/logging.h" #include "base/strings/string_util.h" diff --git a/chromium/base/process/process_iterator_linux.cc b/chromium/base/process/process_iterator_linux.cc index 0587d7b88c5..8f746aa3391 100644 --- a/chromium/base/process/process_iterator_linux.cc +++ b/chromium/base/process/process_iterator_linux.cc @@ -121,8 +121,8 @@ bool ProcessIterator::CheckForNextProcess() { } entry_.pid_ = pid; - entry_.ppid_ = GetProcStatsFieldAsInt(proc_stats, internal::VM_PPID); - entry_.gid_ = GetProcStatsFieldAsInt(proc_stats, internal::VM_PGRP); + entry_.ppid_ = GetProcStatsFieldAsInt64(proc_stats, internal::VM_PPID); + entry_.gid_ = GetProcStatsFieldAsInt64(proc_stats, internal::VM_PGRP); entry_.cmd_line_args_.assign(cmd_line_args.begin(), cmd_line_args.end()); entry_.exe_file_ = GetProcessExecutablePath(pid).BaseName().value(); return true; diff --git a/chromium/base/process/process_iterator_mac.cc b/chromium/base/process/process_iterator_mac.cc index 29daa2d489f..e35c2ae19ba 100644 --- a/chromium/base/process/process_iterator_mac.cc +++ b/chromium/base/process/process_iterator_mac.cc @@ -7,6 +7,7 @@ #include <errno.h> #include <sys/sysctl.h> #include <sys/types.h> +#include <unistd.h> #include "base/logging.h" #include "base/strings/string_util.h" diff --git a/chromium/base/process/process_linux.cc b/chromium/base/process/process_linux.cc index b12e994848d..d92d7c30eee 100644 --- a/chromium/base/process/process_linux.cc +++ b/chromium/base/process/process_linux.cc @@ -14,6 +14,8 @@ #include "base/strings/stringprintf.h" #include "base/synchronization/lock.h" +namespace base { + namespace { const int kForegroundPriority = 0; @@ -46,13 +48,13 @@ struct CGroups { base::FilePath(base::StringPrintf(kControlPath, kForeground)); background_file = base::FilePath(base::StringPrintf(kControlPath, kBackground)); - file_util::FileSystemType foreground_type; - file_util::FileSystemType background_type; + base::FileSystemType foreground_type; + base::FileSystemType background_type; enabled = - file_util::GetFileSystemType(foreground_file, &foreground_type) && - file_util::GetFileSystemType(background_file, &background_type) && - foreground_type == file_util::FILE_SYSTEM_CGROUP && - background_type == file_util::FILE_SYSTEM_CGROUP; + base::GetFileSystemType(foreground_file, &foreground_type) && + base::GetFileSystemType(background_file, &background_type) && + foreground_type == FILE_SYSTEM_CGROUP && + background_type == FILE_SYSTEM_CGROUP; } }; @@ -62,8 +64,6 @@ const int kBackgroundPriority = 5; #endif } -namespace base { - bool Process::IsProcessBackgrounded() const { DCHECK(process_); @@ -95,7 +95,7 @@ bool Process::SetProcessBackgrounded(bool background) { const base::FilePath file = background ? cgroups.Get().background_file : cgroups.Get().foreground_file; - return file_util::WriteFile(file, pid.c_str(), pid.size()) > 0; + return base::WriteFile(file, pid.c_str(), pid.size()) > 0; } #endif // OS_CHROMEOS diff --git a/chromium/base/process/process_metrics.cc b/chromium/base/process/process_metrics.cc index 83289b8c78b..95fb87ddf62 100644 --- a/chromium/base/process/process_metrics.cc +++ b/chromium/base/process/process_metrics.cc @@ -4,6 +4,7 @@ #include "base/process/process_metrics.h" +#include "base/logging.h" #include "base/values.h" namespace base { @@ -50,4 +51,11 @@ double ProcessMetrics::GetPlatformIndependentCPUUsage() { #endif } +#if !defined(OS_MACOSX) +int ProcessMetrics::GetIdleWakeupsPerSecond() { + NOTIMPLEMENTED(); // http://crbug.com/20488 + return 0; +} +#endif // !defined(OS_MACOSX) + } // namespace base diff --git a/chromium/base/process/process_metrics.h b/chromium/base/process/process_metrics.h index 560490a9b8c..d7a49ab5948 100644 --- a/chromium/base/process/process_metrics.h +++ b/chromium/base/process/process_metrics.h @@ -168,6 +168,10 @@ class BASE_EXPORT ProcessMetrics { // CPU, this method returns 50. double GetCPUUsage(); + // Returns the number of average idle cpu wakeups per second since the last + // call. + int GetIdleWakeupsPerSecond(); + // Same as GetCPUUsage(), but will return consistent values on all platforms // (cancelling the Windows exception mentioned above) by returning a value in // the range of 0 to (100 * numCPUCores) everywhere. @@ -201,9 +205,13 @@ class BASE_EXPORT ProcessMetrics { // Used to store the previous times and CPU usage counts so we can // compute the CPU usage between calls. - int64 last_time_; + TimeTicks last_cpu_time_; int64 last_system_time_; + // Same thing for idle wakeups. + TimeTicks last_idle_wakeups_time_; + int64 last_absolute_idle_wakeups_; + #if !defined(OS_IOS) #if defined(OS_MACOSX) // Queries the port provider if it's set. @@ -211,7 +219,7 @@ class BASE_EXPORT ProcessMetrics { PortProvider* port_provider_; #elif defined(OS_POSIX) - // Jiffie count at the last_time_ we updated. + // Jiffie count at the last_cpu_time_ we updated. int last_cpu_; #endif // defined(OS_POSIX) #endif // !defined(OS_IOS) @@ -227,6 +235,10 @@ BASE_EXPORT size_t GetSystemCommitCharge(); // 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(); + +// Sets the file descriptor soft limit to |max_descriptors| or the OS hard +// limit, whichever is lower. +BASE_EXPORT void SetFdLimit(unsigned int max_descriptors); #endif // defined(OS_POSIX) #if defined(OS_LINUX) || defined(OS_ANDROID) diff --git a/chromium/base/process/process_metrics_freebsd.cc b/chromium/base/process/process_metrics_freebsd.cc index 019454cd81a..9d4149de2ad 100644 --- a/chromium/base/process/process_metrics_freebsd.cc +++ b/chromium/base/process/process_metrics_freebsd.cc @@ -4,11 +4,16 @@ #include "base/process/process_metrics.h" +#include <sys/sysctl.h> +#include <sys/user.h> +#include <unistd.h> + +#include "base/sys_info.h" + namespace base { ProcessMetrics::ProcessMetrics(ProcessHandle process) : process_(process), - last_time_(0), last_system_time_(0), last_cpu_(0) { processor_count_ = base::SysInfo::NumberOfProcessors(); @@ -81,11 +86,6 @@ double ProcessMetrics::GetCPUUsage() { int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process_ }; size_t length = sizeof(info); - struct timeval now; - int retval = gettimeofday(&now, NULL); - if (retval) - return 0; - if (sysctl(mib, arraysize(mib), &info, &length, NULL, 0) < 0) return 0; diff --git a/chromium/base/process/process_metrics_ios.cc b/chromium/base/process/process_metrics_ios.cc index 94c671901b6..405c373c9fd 100644 --- a/chromium/base/process/process_metrics_ios.cc +++ b/chromium/base/process/process_metrics_ios.cc @@ -61,4 +61,8 @@ size_t GetMaxFds() { return static_cast<size_t>(max_fds); } +void SetFdLimit(unsigned int max_descriptors) { + // Unimplemented. +} + } // namespace base diff --git a/chromium/base/process/process_metrics_linux.cc b/chromium/base/process/process_metrics_linux.cc index afa88486a0b..0e12595f92e 100644 --- a/chromium/base/process/process_metrics_linux.cc +++ b/chromium/base/process/process_metrics_linux.cc @@ -36,7 +36,7 @@ static uint64 ReadFileToUint64(const base::FilePath file) { std::string file_as_string; if (!ReadFileToString(file, &file_as_string)) return 0; - TrimWhitespaceASCII(file_as_string, TRIM_ALL, &file_as_string); + base::TrimWhitespaceASCII(file_as_string, base::TRIM_ALL, &file_as_string); uint64 file_as_uint64 = 0; if (!base::StringToUint64(file_as_string, &file_as_uint64)) return 0; @@ -71,7 +71,8 @@ size_t ReadProcStatusAndGetFieldAsSizeT(pid_t pid, const std::string& field) { std::string value_str; tokenizer.token_piece().CopyToString(&value_str); std::string value_str_trimmed; - TrimWhitespaceASCII(value_str, TRIM_ALL, &value_str_trimmed); + base::TrimWhitespaceASCII(value_str, base::TRIM_ALL, + &value_str_trimmed); std::vector<std::string> split_value_str; SplitString(value_str_trimmed, ' ', &split_value_str); if (split_value_str.size() != 2 || split_value_str[1] != "kB") { @@ -181,20 +182,16 @@ bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const { } double ProcessMetrics::GetCPUUsage() { - struct timeval now; - int retval = gettimeofday(&now, NULL); - if (retval) - return 0; - int64 time = TimeValToMicroseconds(now); + TimeTicks time = TimeTicks::Now(); - if (last_time_ == 0) { + if (last_cpu_ == 0) { // First call, just set the last values. - last_time_ = time; + last_cpu_time_ = time; last_cpu_ = GetProcessCPU(process_); return 0; } - int64 time_delta = time - last_time_; + int64 time_delta = (time - last_cpu_time_).InMicroseconds(); DCHECK_NE(time_delta, 0); if (time_delta == 0) return 0; @@ -209,7 +206,7 @@ double ProcessMetrics::GetCPUUsage() { int percentage = 100 * (cpu_time - last_cpu_time).InSecondsF() / TimeDelta::FromMicroseconds(time_delta).InSecondsF(); - last_time_ = time; + last_cpu_time_ = time; last_cpu_ = cpu; return percentage; @@ -262,7 +259,6 @@ bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { ProcessMetrics::ProcessMetrics(ProcessHandle process) : process_(process), - last_time_(0), last_system_time_(0), last_cpu_(0) { processor_count_ = base::SysInfo::NumberOfProcessors(); @@ -391,16 +387,16 @@ int ParseProcStatCPU(const std::string& input) { if (proc_stats.size() <= internal::VM_STIME) return -1; - int utime = GetProcStatsFieldAsInt(proc_stats, internal::VM_UTIME); - int stime = GetProcStatsFieldAsInt(proc_stats, internal::VM_STIME); + int utime = GetProcStatsFieldAsInt64(proc_stats, internal::VM_UTIME); + int stime = GetProcStatsFieldAsInt64(proc_stats, internal::VM_STIME); return utime + stime; } const char kProcSelfExe[] = "/proc/self/exe"; int GetNumberOfThreads(ProcessHandle process) { - return internal::ReadProcStatsAndGetFieldAsInt(process, - internal::VM_NUMTHREADS); + return internal::ReadProcStatsAndGetFieldAsInt64(process, + internal::VM_NUMTHREADS); } namespace { diff --git a/chromium/base/process/process_metrics_mac.cc b/chromium/base/process/process_metrics_mac.cc index 048735ed36b..ada04e1427f 100644 --- a/chromium/base/process/process_metrics_mac.cc +++ b/chromium/base/process/process_metrics_mac.cc @@ -11,9 +11,27 @@ #include "base/containers/hash_tables.h" #include "base/logging.h" +#include "base/mac/mach_logging.h" #include "base/mac/scoped_mach_port.h" #include "base/sys_info.h" +#if !defined(TASK_POWER_INFO) +// Doesn't exist in the 10.6 or 10.7 SDKs. +#define TASK_POWER_INFO 21 +struct task_power_info { + uint64_t total_user; + uint64_t total_system; + uint64_t task_interrupt_wakeups; + uint64_t task_platform_idle_wakeups; + uint64_t task_timer_wakeups_bin_1; + uint64_t task_timer_wakeups_bin_2; +}; +typedef struct task_power_info task_power_info_data_t; +typedef struct task_power_info *task_power_info_t; +#define TASK_POWER_INFO_COUNT ((mach_msg_type_number_t) \ + (sizeof (task_power_info_data_t) / sizeof (natural_t))) +#endif + namespace base { namespace { @@ -99,7 +117,6 @@ size_t ProcessMetrics::GetPeakWorkingSetSize() const { // shared_bytes is the size of shared resident memory. bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes, size_t* shared_bytes) { - kern_return_t kr; size_t private_pages_count = 0; size_t shared_pages_count = 0; @@ -136,22 +153,26 @@ bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes, vm_region_top_info_data_t info; mach_msg_type_number_t info_count = VM_REGION_TOP_INFO_COUNT; mach_port_t object_name; - kr = mach_vm_region(task, - &address, - &size, - VM_REGION_TOP_INFO, - (vm_region_info_t)&info, - &info_count, - &object_name); + kern_return_t kr = mach_vm_region(task, + &address, + &size, + VM_REGION_TOP_INFO, + reinterpret_cast<vm_region_info_t>(&info), + &info_count, + &object_name); if (kr == KERN_INVALID_ADDRESS) { // We're at the end of the address space. break; } else if (kr != KERN_SUCCESS) { - DLOG(ERROR) << "Calling mach_vm_region failed with error: " - << mach_error_string(kr); + MACH_DLOG(ERROR, kr) << "mach_vm_region"; return false; } + // The kernel always returns a null object for VM_REGION_TOP_INFO, but + // balance it with a deallocate in case this ever changes. See 10.9.2 + // xnu-2422.90.20/osfmk/vm/vm_map.c vm_map_region. + mach_port_deallocate(mach_task_self(), object_name); + if (IsAddressInSharedRegion(address, cpu_type) && info.share_mode != SM_PRIVATE) continue; @@ -179,18 +200,10 @@ bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes, } } - vm_size_t page_size; - kr = host_page_size(task, &page_size); - if (kr != KERN_SUCCESS) { - DLOG(ERROR) << "Failed to fetch host page size, error: " - << mach_error_string(kr); - return false; - } - if (private_bytes) - *private_bytes = private_pages_count * page_size; + *private_bytes = private_pages_count * PAGE_SIZE; if (shared_bytes) - *shared_bytes = shared_pages_count * page_size; + *shared_bytes = shared_pages_count * PAGE_SIZE; return true; } @@ -218,16 +231,14 @@ double ProcessMetrics::GetCPUUsage() { if (task == MACH_PORT_NULL) return 0; - kern_return_t kr; - // Libtop explicitly loops over the threads (libtop_pinfo_update_cpu_usage() // in libtop.c), but this is more concise and gives the same results: task_thread_times_info thread_info_data; mach_msg_type_number_t thread_info_count = TASK_THREAD_TIMES_INFO_COUNT; - kr = task_info(task, - TASK_THREAD_TIMES_INFO, - reinterpret_cast<task_info_t>(&thread_info_data), - &thread_info_count); + kern_return_t kr = task_info(task, + TASK_THREAD_TIMES_INFO, + reinterpret_cast<task_info_t>(&thread_info_data), + &thread_info_count); if (kr != KERN_SUCCESS) { // Most likely cause: |task| is a zombie. return 0; @@ -250,33 +261,69 @@ double ProcessMetrics::GetCPUUsage() { timeradd(&user_timeval, &task_timeval, &task_timeval); timeradd(&system_timeval, &task_timeval, &task_timeval); - struct timeval now; - int retval = gettimeofday(&now, NULL); - if (retval) - return 0; - - int64 time = TimeValToMicroseconds(now); + TimeTicks time = TimeTicks::Now(); int64 task_time = TimeValToMicroseconds(task_timeval); - if ((last_system_time_ == 0) || (last_time_ == 0)) { + if (last_system_time_ == 0) { // First call, just set the last values. + last_cpu_time_ = time; last_system_time_ = task_time; - last_time_ = time; return 0; } int64 system_time_delta = task_time - last_system_time_; - int64 time_delta = time - last_time_; + int64 time_delta = (time - last_cpu_time_).InMicroseconds(); DCHECK_NE(0U, time_delta); if (time_delta == 0) return 0; + last_cpu_time_ = time; last_system_time_ = task_time; - last_time_ = time; return static_cast<double>(system_time_delta * 100.0) / time_delta; } +int ProcessMetrics::GetIdleWakeupsPerSecond() { + mach_port_t task = TaskForPid(process_); + if (task == MACH_PORT_NULL) + return 0; + + task_power_info power_info_data; + mach_msg_type_number_t power_info_count = TASK_POWER_INFO_COUNT; + kern_return_t kr = task_info(task, + TASK_POWER_INFO, + reinterpret_cast<task_info_t>(&power_info_data), + &power_info_count); + if (kr != KERN_SUCCESS) { + // Most likely cause: |task| is a zombie, or this is on a pre-10.8.4 system + // where TASK_POWER_INFO isn't supported yet. + return 0; + } + uint64_t absolute_idle_wakeups = power_info_data.task_platform_idle_wakeups; + + TimeTicks time = TimeTicks::Now(); + + if (last_absolute_idle_wakeups_ == 0) { + // First call, just set the last values. + last_idle_wakeups_time_ = time; + last_absolute_idle_wakeups_ = absolute_idle_wakeups; + return 0; + } + + int64 wakeups_delta = absolute_idle_wakeups - last_absolute_idle_wakeups_; + int64 time_delta = (time - last_idle_wakeups_time_).InMicroseconds(); + DCHECK_NE(0U, time_delta); + if (time_delta == 0) + return 0; + + last_idle_wakeups_time_ = time; + last_absolute_idle_wakeups_ = absolute_idle_wakeups; + + // Round to average wakeups per second. + const int kMicrosecondsPerSecond = 1000 * 1000; + return (wakeups_delta * kMicrosecondsPerSecond + time_delta/2) / time_delta; +} + bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { return false; } @@ -284,8 +331,8 @@ bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { ProcessMetrics::ProcessMetrics(ProcessHandle process, ProcessMetrics::PortProvider* port_provider) : process_(process), - last_time_(0), last_system_time_(0), + last_absolute_idle_wakeups_(0), port_provider_(port_provider) { processor_count_ = SysInfo::NumberOfProcessors(); } @@ -301,25 +348,18 @@ mach_port_t ProcessMetrics::TaskForPid(ProcessHandle process) const { // Bytes committed by the system. size_t GetSystemCommitCharge() { - base::mac::ScopedMachPort host(mach_host_self()); + base::mac::ScopedMachSendRight host(mach_host_self()); mach_msg_type_number_t count = HOST_VM_INFO_COUNT; vm_statistics_data_t data; kern_return_t kr = host_statistics(host, HOST_VM_INFO, reinterpret_cast<host_info_t>(&data), &count); - if (kr) { - DLOG(WARNING) << "Failed to fetch host statistics."; - return 0; - } - - vm_size_t page_size; - kr = host_page_size(host, &page_size); - if (kr) { - DLOG(ERROR) << "Failed to fetch host page size."; + if (kr != KERN_SUCCESS) { + MACH_DLOG(WARNING, kr) << "host_statistics"; return 0; } - return (data.active_count * page_size) / 1024; + return (data.active_count * PAGE_SIZE) / 1024; } } // namespace base diff --git a/chromium/base/process/process_metrics_openbsd.cc b/chromium/base/process/process_metrics_openbsd.cc index 36f607c0a9c..72927a1b578 100644 --- a/chromium/base/process/process_metrics_openbsd.cc +++ b/chromium/base/process/process_metrics_openbsd.cc @@ -106,22 +106,16 @@ static int GetProcessCPU(pid_t pid) { } double ProcessMetrics::GetCPUUsage() { - struct timeval now; + TimeTicks time = TimeTicks::Now(); - int retval = gettimeofday(&now, NULL); - if (retval) - return 0; - - int64 time = TimeValToMicroseconds(now); - - if (last_time_ == 0) { + if (last_cpu_ == 0) { // First call, just set the last values. - last_time_ = time; + last_cpu_time_ = time; last_cpu_ = GetProcessCPU(process_); return 0; } - int64 time_delta = time - last_time_; + int64 time_delta = (time - last_cpu_time_).InMicroseconds(); DCHECK_NE(time_delta, 0); if (time_delta == 0) @@ -129,7 +123,7 @@ double ProcessMetrics::GetCPUUsage() { int cpu = GetProcessCPU(process_); - last_time_ = time; + last_cpu_time_ = time; last_cpu_ = cpu; double percentage = static_cast<double>((cpu * 100.0) / FSCALE); @@ -139,7 +133,6 @@ double ProcessMetrics::GetCPUUsage() { ProcessMetrics::ProcessMetrics(ProcessHandle process) : process_(process), - last_time_(0), last_system_time_(0), last_cpu_(0) { diff --git a/chromium/base/process/process_metrics_posix.cc b/chromium/base/process/process_metrics_posix.cc index 531f6a40d70..ea79d7348ff 100644 --- a/chromium/base/process/process_metrics_posix.cc +++ b/chromium/base/process/process_metrics_posix.cc @@ -52,4 +52,21 @@ size_t GetMaxFds() { return static_cast<size_t>(max_fds); } + +void SetFdLimit(unsigned int max_descriptors) { + struct rlimit limits; + if (getrlimit(RLIMIT_NOFILE, &limits) == 0) { + unsigned int new_limit = max_descriptors; + if (limits.rlim_max > 0 && limits.rlim_max < max_descriptors) { + new_limit = limits.rlim_max; + } + limits.rlim_cur = new_limit; + if (setrlimit(RLIMIT_NOFILE, &limits) != 0) { + PLOG(INFO) << "Failed to set file descriptor limit"; + } + } else { + PLOG(INFO) << "Failed to get file descriptor limit"; + } +} + } // namespace base diff --git a/chromium/base/process/process_metrics_win.cc b/chromium/base/process/process_metrics_win.cc index f42ea86feb7..b1810b4c921 100644 --- a/chromium/base/process/process_metrics_win.cc +++ b/chromium/base/process/process_metrics_win.cc @@ -195,14 +195,11 @@ static uint64 FileTimeToUTC(const FILETIME& ftime) { } double ProcessMetrics::GetCPUUsage() { - FILETIME now; FILETIME creation_time; FILETIME exit_time; FILETIME kernel_time; FILETIME user_time; - GetSystemTimeAsFileTime(&now); - if (!GetProcessTimes(process_, &creation_time, &exit_time, &kernel_time, &user_time)) { // We don't assert here because in some cases (such as in the Task Manager) @@ -212,17 +209,18 @@ double ProcessMetrics::GetCPUUsage() { } int64 system_time = (FileTimeToUTC(kernel_time) + FileTimeToUTC(user_time)) / processor_count_; - int64 time = FileTimeToUTC(now); + TimeTicks time = TimeTicks::Now(); - if ((last_system_time_ == 0) || (last_time_ == 0)) { + if (last_system_time_ == 0) { // First call, just set the last values. last_system_time_ = system_time; - last_time_ = time; + last_cpu_time_ = time; return 0; } int64 system_time_delta = system_time - last_system_time_; - int64 time_delta = time - last_time_; + // FILETIME is in 100-nanosecond units, so this needs microseconds times 10. + int64 time_delta = (time - last_cpu_time_).InMicroseconds() * 10; DCHECK_NE(0U, time_delta); if (time_delta == 0) return 0; @@ -232,7 +230,7 @@ double ProcessMetrics::GetCPUUsage() { time_delta); last_system_time_ = system_time; - last_time_ = time; + last_cpu_time_ = time; return cpu; } @@ -269,7 +267,6 @@ bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { ProcessMetrics::ProcessMetrics(ProcessHandle process) : process_(process), processor_count_(base::SysInfo::NumberOfProcessors()), - last_time_(0), last_system_time_(0) { } diff --git a/chromium/base/process/process_util_unittest.cc b/chromium/base/process/process_util_unittest.cc index 6bfc1d07388..20623a60e18 100644 --- a/chromium/base/process/process_util_unittest.cc +++ b/chromium/base/process/process_util_unittest.cc @@ -144,8 +144,9 @@ MULTIPROCESS_TEST_MAIN(SimpleChildProcess) { return 0; } +// TODO(viettrungluu): This should be in a "MultiProcessTestTest". TEST_F(ProcessUtilTest, SpawnChild) { - base::ProcessHandle handle = this->SpawnChild("SimpleChildProcess", false); + base::ProcessHandle handle = SpawnChild("SimpleChildProcess"); ASSERT_NE(base::kNullProcessHandle, handle); EXPECT_TRUE(base::WaitForSingleProcess( handle, TestTimeouts::action_max_timeout())); @@ -161,7 +162,7 @@ TEST_F(ProcessUtilTest, KillSlowChild) { const std::string signal_file = ProcessUtilTest::GetSignalFilePath(kSignalFileSlow); remove(signal_file.c_str()); - base::ProcessHandle handle = this->SpawnChild("SlowChildProcess", false); + base::ProcessHandle handle = SpawnChild("SlowChildProcess"); ASSERT_NE(base::kNullProcessHandle, handle); SignalChildren(signal_file.c_str()); EXPECT_TRUE(base::WaitForSingleProcess( @@ -175,7 +176,7 @@ TEST_F(ProcessUtilTest, DISABLED_GetTerminationStatusExit) { const std::string signal_file = ProcessUtilTest::GetSignalFilePath(kSignalFileSlow); remove(signal_file.c_str()); - base::ProcessHandle handle = this->SpawnChild("SlowChildProcess", false); + base::ProcessHandle handle = SpawnChild("SlowChildProcess"); ASSERT_NE(base::kNullProcessHandle, handle); int exit_code = 42; @@ -198,7 +199,7 @@ TEST_F(ProcessUtilTest, DISABLED_GetTerminationStatusExit) { TEST_F(ProcessUtilTest, GetProcId) { base::ProcessId id1 = base::GetProcId(GetCurrentProcess()); EXPECT_NE(0ul, id1); - base::ProcessHandle handle = this->SpawnChild("SimpleChildProcess", false); + base::ProcessHandle handle = SpawnChild("SimpleChildProcess"); ASSERT_NE(base::kNullProcessHandle, handle); base::ProcessId id2 = base::GetProcId(handle); EXPECT_NE(0ul, id2); @@ -233,8 +234,7 @@ MULTIPROCESS_TEST_MAIN(CrashingChildProcess) { // This test intentionally crashes, so we don't need to run it under // AddressSanitizer. -// TODO(jschuh): crbug.com/175753 Fix this in Win64 bots. -#if defined(ADDRESS_SANITIZER) || (defined(OS_WIN) && defined(ARCH_CPU_X86_64)) +#if defined(ADDRESS_SANITIZER) || defined(SYZYASAN) #define MAYBE_GetTerminationStatusCrash DISABLED_GetTerminationStatusCrash #else #define MAYBE_GetTerminationStatusCrash GetTerminationStatusCrash @@ -243,8 +243,7 @@ TEST_F(ProcessUtilTest, MAYBE_GetTerminationStatusCrash) { const std::string signal_file = ProcessUtilTest::GetSignalFilePath(kSignalFileCrash); remove(signal_file.c_str()); - base::ProcessHandle handle = this->SpawnChild("CrashingChildProcess", - false); + base::ProcessHandle handle = SpawnChild("CrashingChildProcess"); ASSERT_NE(base::kNullProcessHandle, handle); int exit_code = 42; @@ -291,8 +290,7 @@ TEST_F(ProcessUtilTest, GetTerminationStatusKill) { const std::string signal_file = ProcessUtilTest::GetSignalFilePath(kSignalFileKill); remove(signal_file.c_str()); - base::ProcessHandle handle = this->SpawnChild("KilledChildProcess", - false); + base::ProcessHandle handle = SpawnChild("KilledChildProcess"); ASSERT_NE(base::kNullProcessHandle, handle); int exit_code = 42; @@ -322,7 +320,7 @@ TEST_F(ProcessUtilTest, GetTerminationStatusKill) { // Note: a platform may not be willing or able to lower the priority of // a process. The calls to SetProcessBackground should be noops then. TEST_F(ProcessUtilTest, SetProcessBackgrounded) { - base::ProcessHandle handle = this->SpawnChild("SimpleChildProcess", false); + base::ProcessHandle handle = SpawnChild("SimpleChildProcess"); base::Process process(handle); int old_priority = process.GetPriority(); #if defined(OS_WIN) @@ -393,8 +391,8 @@ TEST_F(ProcessUtilTest, LaunchAsUser) { ASSERT_TRUE(OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token)); base::LaunchOptions options; options.as_user = token; - EXPECT_TRUE(base::LaunchProcess( - this->MakeCmdLine("SimpleChildProcess", false), options, NULL)); + EXPECT_TRUE(base::LaunchProcess(MakeCmdLine("SimpleChildProcess"), options, + NULL)); } static const char kEventToTriggerHandleSwitch[] = "event-to-trigger-handle"; @@ -430,7 +428,7 @@ TEST_F(ProcessUtilTest, InheritSpecifiedHandles) { base::LaunchOptions options; options.handles_to_inherit = &handles_to_inherit; - CommandLine cmd_line = MakeCmdLine("TriggerEventChildProcess", false); + CommandLine cmd_line = MakeCmdLine("TriggerEventChildProcess"); cmd_line.AppendSwitchASCII(kEventToTriggerHandleSwitch, base::Uint64ToString(reinterpret_cast<uint64>(event.handle()))); @@ -505,8 +503,10 @@ int ProcessUtilTest::CountOpenFDsInChild() { base::FileHandleMappingVector fd_mapping_vec; fd_mapping_vec.push_back(std::pair<int, int>(fds[1], kChildPipe)); - base::ProcessHandle handle = this->SpawnChild( - "ProcessUtilsLeakFDChildProcess", fd_mapping_vec, false); + base::LaunchOptions options; + options.fds_to_remap = &fd_mapping_vec; + base::ProcessHandle handle = + SpawnChildWithOptions("ProcessUtilsLeakFDChildProcess", options); CHECK(handle); int ret = IGNORE_EINTR(close(fds[1])); DPCHECK(ret == 0); @@ -517,7 +517,7 @@ int ProcessUtilTest::CountOpenFDsInChild() { HANDLE_EINTR(read(fds[0], &num_open_files, sizeof(num_open_files))); CHECK_EQ(bytes_read, static_cast<ssize_t>(sizeof(num_open_files))); -#if defined(THREAD_SANITIZER) || defined(USE_HEAPCHECKER) +#if defined(THREAD_SANITIZER) // Compiler-based ThreadSanitizer makes this test slow. CHECK(base::WaitForSingleProcess(handle, base::TimeDelta::FromSeconds(3))); #else @@ -562,15 +562,12 @@ TEST_F(ProcessUtilTest, MAYBE_FDRemapping) { namespace { -std::string TestLaunchProcess(const base::EnvironmentMap& env_changes, +std::string TestLaunchProcess(const std::vector<std::string>& args, + const base::EnvironmentMap& env_changes, + const bool clear_environ, const int clone_flags) { - std::vector<std::string> args; base::FileHandleMappingVector fds_to_remap; - args.push_back(kPosixShell); - args.push_back("-c"); - args.push_back("echo $BASE_TEST"); - int fds[2]; PCHECK(pipe(fds) == 0); @@ -578,6 +575,7 @@ std::string TestLaunchProcess(const base::EnvironmentMap& env_changes, base::LaunchOptions options; options.wait = true; options.environ = env_changes; + options.clear_environ = clear_environ; options.fds_to_remap = &fds_to_remap; #if defined(OS_LINUX) options.clone_flags = clone_flags; @@ -589,7 +587,6 @@ std::string TestLaunchProcess(const base::EnvironmentMap& env_changes, char buf[512]; const ssize_t n = HANDLE_EINTR(read(fds[0], buf, sizeof(buf))); - PCHECK(n > 0); PCHECK(IGNORE_EINTR(close(fds[0])) == 0); @@ -609,37 +606,69 @@ const char kLargeString[] = TEST_F(ProcessUtilTest, LaunchProcess) { base::EnvironmentMap env_changes; + std::vector<std::string> echo_base_test; + echo_base_test.push_back(kPosixShell); + echo_base_test.push_back("-c"); + echo_base_test.push_back("echo $BASE_TEST"); + + std::vector<std::string> print_env; + print_env.push_back("/usr/bin/env"); const int no_clone_flags = 0; + const bool no_clear_environ = false; const char kBaseTest[] = "BASE_TEST"; env_changes[kBaseTest] = "bar"; - EXPECT_EQ("bar\n", TestLaunchProcess(env_changes, no_clone_flags)); + EXPECT_EQ("bar\n", + TestLaunchProcess( + echo_base_test, env_changes, no_clear_environ, no_clone_flags)); env_changes.clear(); EXPECT_EQ(0, setenv(kBaseTest, "testing", 1 /* override */)); - EXPECT_EQ("testing\n", TestLaunchProcess(env_changes, no_clone_flags)); + EXPECT_EQ("testing\n", + TestLaunchProcess( + echo_base_test, env_changes, no_clear_environ, no_clone_flags)); env_changes[kBaseTest] = std::string(); - EXPECT_EQ("\n", TestLaunchProcess(env_changes, no_clone_flags)); + EXPECT_EQ("\n", + TestLaunchProcess( + echo_base_test, env_changes, no_clear_environ, no_clone_flags)); env_changes[kBaseTest] = "foo"; - EXPECT_EQ("foo\n", TestLaunchProcess(env_changes, no_clone_flags)); + EXPECT_EQ("foo\n", + TestLaunchProcess( + echo_base_test, env_changes, no_clear_environ, no_clone_flags)); env_changes.clear(); EXPECT_EQ(0, setenv(kBaseTest, kLargeString, 1 /* override */)); EXPECT_EQ(std::string(kLargeString) + "\n", - TestLaunchProcess(env_changes, no_clone_flags)); + TestLaunchProcess( + echo_base_test, env_changes, no_clear_environ, no_clone_flags)); env_changes[kBaseTest] = "wibble"; - EXPECT_EQ("wibble\n", TestLaunchProcess(env_changes, no_clone_flags)); + EXPECT_EQ("wibble\n", + TestLaunchProcess( + echo_base_test, env_changes, no_clear_environ, no_clone_flags)); #if defined(OS_LINUX) // 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(env_changes, CLONE_FS | SIGCHLD)); + EXPECT_EQ( + "wibble\n", + TestLaunchProcess( + echo_base_test, env_changes, no_clear_environ, CLONE_FS | SIGCHLD)); } + + EXPECT_EQ( + "BASE_TEST=wibble\n", + TestLaunchProcess( + print_env, env_changes, true /* clear_environ */, no_clone_flags)); + env_changes.clear(); + EXPECT_EQ( + "", + TestLaunchProcess( + print_env, env_changes, true /* clear_environ */, no_clone_flags)); #endif } @@ -677,7 +706,13 @@ TEST_F(ProcessUtilTest, GetAppOutput) { #endif // defined(OS_ANDROID) } -TEST_F(ProcessUtilTest, GetAppOutputRestricted) { +// Flakes on Android, crbug.com/375840 +#if defined(OS_ANDROID) +#define MAYBE_GetAppOutputRestricted DISABLED_GetAppOutputRestricted +#else +#define MAYBE_GetAppOutputRestricted GetAppOutputRestricted +#endif +TEST_F(ProcessUtilTest, MAYBE_GetAppOutputRestricted) { // Unfortunately, since we can't rely on the path, we need to know where // everything is. So let's use /bin/sh, which is on every POSIX system, and // its built-ins. @@ -812,8 +847,7 @@ bool IsProcessDead(base::ProcessHandle child) { } TEST_F(ProcessUtilTest, DelayedTermination) { - base::ProcessHandle child_process = - SpawnChild("process_util_test_never_die", false); + 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)); @@ -832,7 +866,7 @@ MULTIPROCESS_TEST_MAIN(process_util_test_never_die) { TEST_F(ProcessUtilTest, ImmediateTermination) { base::ProcessHandle child_process = - SpawnChild("process_util_test_die_immediately", false); + SpawnChild("process_util_test_die_immediately"); ASSERT_TRUE(child_process); // Give it time to die. sleep(2); diff --git a/chromium/base/rand_util.cc b/chromium/base/rand_util.cc index 9641ff4f9c7..1525b91449d 100644 --- a/chromium/base/rand_util.cc +++ b/chromium/base/rand_util.cc @@ -61,16 +61,6 @@ uint64 RandGenerator(uint64 range) { return value % range; } -void RandBytes(void* output, size_t output_length) { - uint64 random_int; - size_t random_int_size = sizeof(random_int); - for (size_t i = 0; i < output_length; i += random_int_size) { - random_int = base::RandUint64(); - size_t copy_count = std::min(output_length - i, random_int_size); - memcpy(((uint8*)output) + i, &random_int, copy_count); - } -} - std::string RandBytesAsString(size_t length) { DCHECK_GT(length, 0u); std::string result; diff --git a/chromium/base/rand_util.h b/chromium/base/rand_util.h index bae8c311782..6130c129b7f 100644 --- a/chromium/base/rand_util.h +++ b/chromium/base/rand_util.h @@ -39,10 +39,11 @@ BASE_EXPORT double BitsToOpenEndedUnitInterval(uint64 bits); // See crypto/ for cryptographically secure random number generation APIs. BASE_EXPORT void RandBytes(void* output, size_t output_length); -// Fills a string of length |length| with with random data and returns it. +// Fills a string of length |length| with random data and returns it. // |length| should be nonzero. // // Note that this is a variation of |RandBytes| with a different return type. +// The returned string is likely not ASCII/UTF-8. Use with care. // // WARNING: // Do not use for security-sensitive purposes. diff --git a/chromium/base/rand_util_nacl.cc b/chromium/base/rand_util_nacl.cc index 47450aaba68..b771dc44547 100644 --- a/chromium/base/rand_util_nacl.cc +++ b/chromium/base/rand_util_nacl.cc @@ -4,50 +4,38 @@ #include "base/rand_util.h" +#include <nacl/nacl_random.h> + #include "base/basictypes.h" -#include "base/lazy_instance.h" #include "base/logging.h" -#include "native_client/src/untrusted/irt/irt.h" namespace { -class NaclRandom { - public: - NaclRandom() { - size_t result = nacl_interface_query(NACL_IRT_RANDOM_v0_1, - &random_, sizeof(random_)); - CHECK_EQ(result, sizeof(random_)); - } - - ~NaclRandom() { - } - - void GetRandomBytes(char* buffer, uint32_t num_bytes) { - while (num_bytes > 0) { - size_t nread; - int error = random_.get_random_bytes(buffer, num_bytes, &nread); - CHECK_EQ(error, 0); - CHECK_LE(nread, num_bytes); - buffer += nread; - num_bytes -= nread; - } +void GetRandomBytes(void* output, size_t num_bytes) { + char* output_ptr = static_cast<char*>(output); + while (num_bytes > 0) { + size_t nread; + const int error = nacl_secure_random(output_ptr, num_bytes, &nread); + CHECK_EQ(error, 0); + CHECK_LE(nread, num_bytes); + output_ptr += nread; + num_bytes -= nread; } - - private: - nacl_irt_random random_; -}; - -base::LazyInstance<NaclRandom>::Leaky g_nacl_random = LAZY_INSTANCE_INITIALIZER; +} } // namespace namespace base { +// NOTE: This function must be cryptographically secure. http://crbug.com/140076 uint64 RandUint64() { uint64 result; - g_nacl_random.Pointer()->GetRandomBytes( - reinterpret_cast<char*>(&result), sizeof(result)); + GetRandomBytes(&result, sizeof(result)); return result; } +void RandBytes(void* output, size_t output_length) { + GetRandomBytes(output, output_length); +} + } // namespace base diff --git a/chromium/base/rand_util_posix.cc b/chromium/base/rand_util_posix.cc index 082d64923d5..0a72a20d642 100644 --- a/chromium/base/rand_util_posix.cc +++ b/chromium/base/rand_util_posix.cc @@ -20,19 +20,16 @@ namespace { // we can use LazyInstance to handle opening it on the first access. class URandomFd { public: - URandomFd() { - fd_ = open("/dev/urandom", O_RDONLY); + URandomFd() : fd_(open("/dev/urandom", O_RDONLY)) { DCHECK_GE(fd_, 0) << "Cannot open /dev/urandom: " << errno; } - ~URandomFd() { - close(fd_); - } + ~URandomFd() { close(fd_); } int fd() const { return fd_; } private: - int fd_; + const int fd_; }; base::LazyInstance<URandomFd>::Leaky g_urandom_fd = LAZY_INSTANCE_INITIALIZER; @@ -44,13 +41,15 @@ namespace base { // NOTE: This function must be cryptographically secure. http://crbug.com/140076 uint64 RandUint64() { uint64 number; + RandBytes(&number, sizeof(number)); + return number; +} - int urandom_fd = g_urandom_fd.Pointer()->fd(); - bool success = ReadFromFD(urandom_fd, reinterpret_cast<char*>(&number), - sizeof(number)); +void RandBytes(void* output, size_t output_length) { + const int urandom_fd = g_urandom_fd.Pointer()->fd(); + const bool success = + ReadFromFD(urandom_fd, static_cast<char*>(output), output_length); CHECK(success); - - return number; } int GetUrandomFD(void) { diff --git a/chromium/base/rand_util_unittest.cc b/chromium/base/rand_util_unittest.cc index e0e85ecaa91..d26227505d0 100644 --- a/chromium/base/rand_util_unittest.cc +++ b/chromium/base/rand_util_unittest.cc @@ -7,6 +7,9 @@ #include <algorithm> #include <limits> +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/time/time.h" #include "testing/gtest/include/gtest/gtest.h" namespace { @@ -120,3 +123,22 @@ TEST(RandUtilTest, RandUint64ProducesBothValuesOfAllBits) { FAIL() << "Didn't achieve all bit values in maximum number of tries."; } + +// Benchmark test for RandBytes(). Disabled since it's intentionally slow and +// does not test anything that isn't already tested by the existing RandBytes() +// tests. +TEST(RandUtilTest, DISABLED_RandBytesPerf) { + // Benchmark the performance of |kTestIterations| of RandBytes() using a + // buffer size of |kTestBufferSize|. + const int kTestIterations = 10; + const size_t kTestBufferSize = 1 * 1024 * 1024; + + scoped_ptr<uint8[]> buffer(new uint8[kTestBufferSize]); + const base::TimeTicks now = base::TimeTicks::HighResNow(); + for (int i = 0; i < kTestIterations; ++i) + base::RandBytes(buffer.get(), kTestBufferSize); + const base::TimeTicks end = base::TimeTicks::HighResNow(); + + LOG(INFO) << "RandBytes(" << kTestBufferSize << ") took: " + << (end - now).InMicroseconds() << "µs"; +} diff --git a/chromium/base/rand_util_win.cc b/chromium/base/rand_util_win.cc index 391fe5b9e61..8573b6b601b 100644 --- a/chromium/base/rand_util_win.cc +++ b/chromium/base/rand_util_win.cc @@ -4,28 +4,40 @@ #include "base/rand_util.h" -#include <stdlib.h> +#include <windows.h> -#include "base/basictypes.h" -#include "base/logging.h" - -namespace { +// #define needed to link in RtlGenRandom(), a.k.a. SystemFunction036. See the +// "Community Additions" comment on MSDN here: +// http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx +#define SystemFunction036 NTAPI SystemFunction036 +#include <NTSecAPI.h> +#undef SystemFunction036 -uint32 RandUint32() { - uint32 number; - CHECK_EQ(rand_s(&number), 0); - return number; -} +#include <algorithm> +#include <limits> -} // namespace +#include "base/logging.h" namespace base { // NOTE: This function must be cryptographically secure. http://crbug.com/140076 uint64 RandUint64() { - uint32 first_half = RandUint32(); - uint32 second_half = RandUint32(); - return (static_cast<uint64>(first_half) << 32) + second_half; + uint64 number; + RandBytes(&number, sizeof(number)); + return number; +} + +void RandBytes(void* output, size_t output_length) { + char* output_ptr = static_cast<char*>(output); + while (output_length > 0) { + const ULONG output_bytes_this_pass = static_cast<ULONG>(std::min( + output_length, static_cast<size_t>(std::numeric_limits<ULONG>::max()))); + const bool success = + RtlGenRandom(output_ptr, output_bytes_this_pass) != FALSE; + CHECK(success); + output_length -= output_bytes_this_pass; + output_ptr += output_bytes_this_pass; + } } } // namespace base diff --git a/chromium/base/run_loop.cc b/chromium/base/run_loop.cc index 8666ee448fe..8344aa41b16 100644 --- a/chromium/base/run_loop.cc +++ b/chromium/base/run_loop.cc @@ -6,6 +6,10 @@ #include "base/bind.h" +#if defined(OS_WIN) +#include "base/message_loop/message_pump_dispatcher.h" +#endif + namespace base { RunLoop::RunLoop() @@ -17,15 +21,13 @@ RunLoop::RunLoop() running_(false), quit_when_idle_received_(false), weak_factory_(this) { -#if !defined(OS_MACOSX) && !defined(OS_ANDROID) && \ - !defined(USE_GTK_MESSAGE_PUMP) +#if defined(OS_WIN) dispatcher_ = NULL; #endif } -#if !defined(OS_MACOSX) && !defined(OS_ANDROID) && \ - !defined(USE_GTK_MESSAGE_PUMP) -RunLoop::RunLoop(MessageLoop::Dispatcher* dispatcher) +#if defined(OS_WIN) +RunLoop::RunLoop(MessagePumpDispatcher* dispatcher) : loop_(MessageLoop::current()), previous_run_loop_(NULL), dispatcher_(dispatcher), diff --git a/chromium/base/run_loop.h b/chromium/base/run_loop.h index 0dce6346e76..002410892f6 100644 --- a/chromium/base/run_loop.h +++ b/chromium/base/run_loop.h @@ -15,6 +15,10 @@ namespace base { class MessagePumpForUI; #endif +#if defined(OS_WIN) +class MessagePumpDispatcher; +#endif + #if defined(OS_IOS) class MessagePumpUIApplication; #endif @@ -27,19 +31,11 @@ class MessagePumpUIApplication; class BASE_EXPORT RunLoop { public: RunLoop(); -#if !defined(OS_MACOSX) && !defined(OS_ANDROID) && \ - !defined(USE_GTK_MESSAGE_PUMP) - explicit RunLoop(MessageLoop::Dispatcher* dispatcher); +#if defined(OS_WIN) + explicit RunLoop(MessagePumpDispatcher* dispatcher); #endif ~RunLoop(); -#if !defined(OS_MACOSX) && !defined(OS_ANDROID) && \ - !defined(USE_GTK_MESSAGE_PUMP) - void set_dispatcher(MessageLoop::Dispatcher* dispatcher) { - dispatcher_ = dispatcher; - } -#endif - // Run the current MessageLoop. This blocks until Quit is called. Before // calling Run, be sure to grab an AsWeakPtr or the QuitClosure in order to // stop the MessageLoop asynchronously. MessageLoop::Quit and QuitNow will @@ -97,9 +93,8 @@ class BASE_EXPORT RunLoop { // Parent RunLoop or NULL if this is the top-most RunLoop. RunLoop* previous_run_loop_; -#if !defined(OS_MACOSX) && !defined(OS_ANDROID) && \ - !defined(USE_GTK_MESSAGE_PUMP) - MessageLoop::Dispatcher* dispatcher_; +#if defined(OS_WIN) + MessagePumpDispatcher* dispatcher_; #endif // Used to count how many nested Run() invocations are on the stack. diff --git a/chromium/base/safe_numerics.h b/chromium/base/safe_numerics.h deleted file mode 100644 index ce5c72fcc77..00000000000 --- a/chromium/base/safe_numerics.h +++ /dev/null @@ -1,135 +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_SAFE_NUMERICS_H_ -#define BASE_SAFE_NUMERICS_H_ - -#include <limits> - -#include "base/logging.h" - -namespace base { -namespace internal { - -template <bool SameSize, bool DestLarger, - bool DestIsSigned, bool SourceIsSigned> -struct IsValidNumericCastImpl; - -#define BASE_NUMERIC_CAST_CASE_SPECIALIZATION(A, B, C, D, Code) \ -template <> struct IsValidNumericCastImpl<A, B, C, D> { \ - template <class Source, class DestBounds> static inline bool Test( \ - Source source, DestBounds min, DestBounds max) { \ - return Code; \ - } \ -} - -#define BASE_NUMERIC_CAST_CASE_SAME_SIZE(DestSigned, SourceSigned, Code) \ - BASE_NUMERIC_CAST_CASE_SPECIALIZATION( \ - true, true, DestSigned, SourceSigned, Code); \ - BASE_NUMERIC_CAST_CASE_SPECIALIZATION( \ - true, false, DestSigned, SourceSigned, Code) - -#define BASE_NUMERIC_CAST_CASE_SOURCE_LARGER(DestSigned, SourceSigned, Code) \ - BASE_NUMERIC_CAST_CASE_SPECIALIZATION( \ - false, false, DestSigned, SourceSigned, Code); \ - -#define BASE_NUMERIC_CAST_CASE_DEST_LARGER(DestSigned, SourceSigned, Code) \ - BASE_NUMERIC_CAST_CASE_SPECIALIZATION( \ - false, true, DestSigned, SourceSigned, Code); \ - -// The three top level cases are: -// - Same size -// - Source larger -// - Dest larger -// And for each of those three cases, we handle the 4 different possibilities -// of signed and unsigned. This gives 12 cases to handle, which we enumerate -// below. -// -// The last argument in each of the macros is the actual comparison code. It -// has three arguments available, source (the value), and min/max which are -// the ranges of the destination. - - -// These are the cases where both types have the same size. - -// Both signed. -BASE_NUMERIC_CAST_CASE_SAME_SIZE(true, true, true); -// Both unsigned. -BASE_NUMERIC_CAST_CASE_SAME_SIZE(false, false, true); -// Dest unsigned, Source signed. -BASE_NUMERIC_CAST_CASE_SAME_SIZE(false, true, source >= 0); -// Dest signed, Source unsigned. -// This cast is OK because Dest's max must be less than Source's. -BASE_NUMERIC_CAST_CASE_SAME_SIZE(true, false, - source <= static_cast<Source>(max)); - - -// These are the cases where Source is larger. - -// Both unsigned. -BASE_NUMERIC_CAST_CASE_SOURCE_LARGER(false, false, source <= max); -// Both signed. -BASE_NUMERIC_CAST_CASE_SOURCE_LARGER(true, true, - source >= min && source <= max); -// Dest is unsigned, Source is signed. -BASE_NUMERIC_CAST_CASE_SOURCE_LARGER(false, true, - source >= 0 && source <= max); -// Dest is signed, Source is unsigned. -// This cast is OK because Dest's max must be less than Source's. -BASE_NUMERIC_CAST_CASE_SOURCE_LARGER(true, false, - source <= static_cast<Source>(max)); - - -// These are the cases where Dest is larger. - -// Both unsigned. -BASE_NUMERIC_CAST_CASE_DEST_LARGER(false, false, true); -// Both signed. -BASE_NUMERIC_CAST_CASE_DEST_LARGER(true, true, true); -// Dest is unsigned, Source is signed. -BASE_NUMERIC_CAST_CASE_DEST_LARGER(false, true, source >= 0); -// Dest is signed, Source is unsigned. -BASE_NUMERIC_CAST_CASE_DEST_LARGER(true, false, true); - -#undef BASE_NUMERIC_CAST_CASE_SPECIALIZATION -#undef BASE_NUMERIC_CAST_CASE_SAME_SIZE -#undef BASE_NUMERIC_CAST_CASE_SOURCE_LARGER -#undef BASE_NUMERIC_CAST_CASE_DEST_LARGER - - -// The main test for whether the conversion will under or overflow. -template <class Dest, class Source> -inline bool IsValidNumericCast(Source source) { - typedef std::numeric_limits<Source> SourceLimits; - typedef std::numeric_limits<Dest> DestLimits; - COMPILE_ASSERT(SourceLimits::is_specialized, argument_must_be_numeric); - COMPILE_ASSERT(SourceLimits::is_integer, argument_must_be_integral); - COMPILE_ASSERT(DestLimits::is_specialized, result_must_be_numeric); - COMPILE_ASSERT(DestLimits::is_integer, result_must_be_integral); - - return IsValidNumericCastImpl< - sizeof(Dest) == sizeof(Source), - (sizeof(Dest) > sizeof(Source)), - DestLimits::is_signed, - SourceLimits::is_signed>::Test( - source, - DestLimits::min(), - DestLimits::max()); -} - -} // namespace internal - -// checked_numeric_cast<> is analogous to static_cast<> for numeric types, -// except that it CHECKs that the specified numeric conversion will not -// overflow or underflow. Floating point arguments are not currently allowed -// (this is COMPILE_ASSERTd), though this could be supported if necessary. -template <class Dest, class Source> -inline Dest checked_numeric_cast(Source source) { - CHECK(internal::IsValidNumericCast<Dest>(source)); - return static_cast<Dest>(source); -} - -} // namespace base - -#endif // BASE_SAFE_NUMERICS_H_ diff --git a/chromium/base/safe_numerics_unittest.cc b/chromium/base/safe_numerics_unittest.cc deleted file mode 100644 index c66e56f3cf7..00000000000 --- a/chromium/base/safe_numerics_unittest.cc +++ /dev/null @@ -1,151 +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 <gtest/gtest.h> - -#include <sstream> -#include <vector> - -#include "base/safe_numerics.h" - -namespace base { -namespace internal { - -// This is far (far, far) too slow to run normally, but if you're refactoring -// it might be useful. -// #define RUN_EXHAUSTIVE_TEST - -#ifdef RUN_EXHAUSTIVE_TEST - -template <class From, class To> void ExhaustiveCheckFromTo() { - fprintf(stderr, "."); - From i = std::numeric_limits<From>::min(); - for (;;) { - std::ostringstream str_from, str_to; - str_from << i; - To to = static_cast<To>(i); - str_to << to; - bool strings_equal = str_from.str() == str_to.str(); - EXPECT_EQ(IsValidNumericCast<To>(i), strings_equal); - fprintf(stderr, "\r%s vs %s\x1B[K", - str_from.str().c_str(), str_to.str().c_str()); - ++i; - // If we wrap, then we've tested everything. - if (i == std::numeric_limits<From>::min()) - break; - } -} - -template <class From> void ExhaustiveCheckFrom() { - ExhaustiveCheckFromTo<From, short>(); - ExhaustiveCheckFromTo<From, unsigned short>(); - ExhaustiveCheckFromTo<From, int>(); - ExhaustiveCheckFromTo<From, unsigned int>(); - ExhaustiveCheckFromTo<From, long long>(); - ExhaustiveCheckFromTo<From, unsigned long long>(); - ExhaustiveCheckFromTo<From, size_t>(); - fprintf(stderr, "\n"); -} - -#endif - - -TEST(SafeNumerics, NumericCast) { - int small_positive = 1; - int small_negative = -1; - int large_positive = INT_MAX; - int large_negative = INT_MIN; - size_t size_t_small = 1; - size_t size_t_large = UINT_MAX; - - // Narrow signed destination. - EXPECT_TRUE(IsValidNumericCast<signed char>(small_positive)); - EXPECT_TRUE(IsValidNumericCast<signed char>(small_negative)); - EXPECT_FALSE(IsValidNumericCast<signed char>(large_positive)); - EXPECT_FALSE(IsValidNumericCast<signed char>(large_negative)); - EXPECT_TRUE(IsValidNumericCast<signed short>(small_positive)); - EXPECT_TRUE(IsValidNumericCast<signed short>(small_negative)); - - // Narrow unsigned destination. - EXPECT_TRUE(IsValidNumericCast<unsigned char>(small_positive)); - EXPECT_FALSE(IsValidNumericCast<unsigned char>(small_negative)); - EXPECT_FALSE(IsValidNumericCast<unsigned char>(large_positive)); - EXPECT_FALSE(IsValidNumericCast<unsigned char>(large_negative)); - EXPECT_FALSE(IsValidNumericCast<unsigned short>(small_negative)); - EXPECT_FALSE(IsValidNumericCast<unsigned short>(large_negative)); - - // Same width signed destination. - EXPECT_TRUE(IsValidNumericCast<signed int>(small_positive)); - EXPECT_TRUE(IsValidNumericCast<signed int>(small_negative)); - EXPECT_TRUE(IsValidNumericCast<signed int>(large_positive)); - EXPECT_TRUE(IsValidNumericCast<signed int>(large_negative)); - - // Same width unsigned destination. - EXPECT_TRUE(IsValidNumericCast<unsigned int>(small_positive)); - EXPECT_FALSE(IsValidNumericCast<unsigned int>(small_negative)); - EXPECT_TRUE(IsValidNumericCast<unsigned int>(large_positive)); - EXPECT_FALSE(IsValidNumericCast<unsigned int>(large_negative)); - - // Wider signed destination. - EXPECT_TRUE(IsValidNumericCast<long long>(small_positive)); - EXPECT_TRUE(IsValidNumericCast<long long>(large_negative)); - EXPECT_TRUE(IsValidNumericCast<long long>(small_positive)); - EXPECT_TRUE(IsValidNumericCast<long long>(large_negative)); - - // Wider unsigned destination. - EXPECT_TRUE(IsValidNumericCast<unsigned long long>(small_positive)); - EXPECT_FALSE(IsValidNumericCast<unsigned long long>(small_negative)); - EXPECT_TRUE(IsValidNumericCast<unsigned long long>(large_positive)); - EXPECT_FALSE(IsValidNumericCast<unsigned long long>(large_negative)); - - // Negative to size_t. - EXPECT_FALSE(IsValidNumericCast<size_t>(small_negative)); - EXPECT_FALSE(IsValidNumericCast<size_t>(large_negative)); - - // From unsigned. - // Small. - EXPECT_TRUE(IsValidNumericCast<signed char>(size_t_small)); - EXPECT_TRUE(IsValidNumericCast<unsigned char>(size_t_small)); - EXPECT_TRUE(IsValidNumericCast<short>(size_t_small)); - EXPECT_TRUE(IsValidNumericCast<unsigned short>(size_t_small)); - EXPECT_TRUE(IsValidNumericCast<int>(size_t_small)); - EXPECT_TRUE(IsValidNumericCast<unsigned int>(size_t_small)); - EXPECT_TRUE(IsValidNumericCast<long long>(size_t_small)); - EXPECT_TRUE(IsValidNumericCast<unsigned long long>(size_t_small)); - - // Large. - EXPECT_FALSE(IsValidNumericCast<signed char>(size_t_large)); - EXPECT_FALSE(IsValidNumericCast<unsigned char>(size_t_large)); - EXPECT_FALSE(IsValidNumericCast<short>(size_t_large)); - EXPECT_FALSE(IsValidNumericCast<unsigned short>(size_t_large)); - EXPECT_FALSE(IsValidNumericCast<int>(size_t_large)); - EXPECT_TRUE(IsValidNumericCast<unsigned int>(size_t_large)); - EXPECT_TRUE(IsValidNumericCast<long long>(size_t_large)); - EXPECT_TRUE(IsValidNumericCast<unsigned long long>(size_t_large)); - - // Various edge cases. - EXPECT_TRUE(IsValidNumericCast<int>(static_cast<short>(SHRT_MIN))); - EXPECT_FALSE( - IsValidNumericCast<unsigned short>(static_cast<short>(SHRT_MIN))); - EXPECT_FALSE(IsValidNumericCast<unsigned short>(SHRT_MIN)); - - // Confirm that checked_numeric_cast<> actually compiles. - std::vector<int> v; - unsigned int checked_size = - base::checked_numeric_cast<unsigned int>(v.size()); - EXPECT_EQ(0u, checked_size); - -#ifdef RUN_EXHAUSTIVE_TEST - ExhaustiveCheckFrom<short>(); - ExhaustiveCheckFrom<unsigned short>(); - ExhaustiveCheckFrom<int>(); - ExhaustiveCheckFrom<unsigned int>(); - ExhaustiveCheckFrom<long long>(); - ExhaustiveCheckFrom<unsigned long long>(); - ExhaustiveCheckFrom<size_t>(); -#endif -} - -} // namespace internal -} // namespace base diff --git a/chromium/base/safe_numerics_unittest.nc b/chromium/base/safe_numerics_unittest.nc deleted file mode 100644 index 4219cd56a46..00000000000 --- a/chromium/base/safe_numerics_unittest.nc +++ /dev/null @@ -1,29 +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 <float.h> - -#include "base/safe_numerics.h" - -using base::internal::IsValidNumericCast; - -#if defined(NCTEST_NO_FLOATING_POINT_1) // [r"size of array is negative"] - -void WontCompile() { - IsValidNumericCast<float>(0.0); -} - -#elif defined(NCTEST_NO_FLOATING_POINT_2) // [r"size of array is negative"] - -void WontCompile() { - IsValidNumericCast<double>(0.0f); -} - -#elif defined(NCTEST_NO_FLOATING_POINT_3) // [r"size of array is negative"] - -void WontCompile() { - IsValidNumericCast<int>(DBL_MAX); -} - -#endif diff --git a/chromium/base/scoped_generic.h b/chromium/base/scoped_generic.h new file mode 100644 index 00000000000..da42609e5c4 --- /dev/null +++ b/chromium/base/scoped_generic.h @@ -0,0 +1,176 @@ +// 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_SCOPED_GENERIC_H_ +#define BASE_SCOPED_GENERIC_H_ + +#include <stdlib.h> + +#include <algorithm> + +#include "base/compiler_specific.h" +#include "base/move.h" + +namespace base { + +// This class acts like ScopedPtr with a custom deleter (although is slightly +// less fancy in some of the more escoteric respects) except that it keeps a +// copy of the object rather than a pointer, and we require that the contained +// object has some kind of "invalid" value. +// +// Defining a scoper based on this class allows you to get a scoper for +// non-pointer types without having to write custom code for set, reset, and +// move, etc. and get almost identical semantics that people are used to from +// scoped_ptr. +// +// It is intended that you will typedef this class with an appropriate deleter +// to implement clean up tasks for objects that act like pointers from a +// resource management standpoint but aren't, such as file descriptors and +// various types of operating system handles. Using scoped_ptr for these +// things requires that you keep a pointer to the handle valid for the lifetime +// of the scoper (which is easy to mess up). +// +// For an object to be able to be put into a ScopedGeneric, it must support +// standard copyable semantics and have a specific "invalid" value. The traits +// must define a free function and also the invalid value to assign for +// default-constructed and released objects. +// +// struct FooScopedTraits { +// // It's assumed that this is a fast inline function with little-to-no +// // penalty for duplicate calls. This must be a static function even +// // for stateful traits. +// static int InvalidValue() { +// return 0; +// } +// +// // This free function will not be called if f == InvalidValue()! +// static void Free(int f) { +// ::FreeFoo(f); +// } +// }; +// +// typedef ScopedGeneric<int, FooScopedTraits> ScopedFoo; +template<typename T, typename Traits> +class ScopedGeneric { + MOVE_ONLY_TYPE_FOR_CPP_03(ScopedGeneric, RValue) + + private: + // This must be first since it's used inline below. + // + // Use the empty base class optimization to allow us to have a D + // member, while avoiding any space overhead for it when D is an + // empty class. See e.g. http://www.cantrip.org/emptyopt.html for a good + // discussion of this technique. + struct Data : public Traits { + explicit Data(const T& in) : generic(in) {} + Data(const T& in, const Traits& other) : Traits(other), generic(in) {} + T generic; + }; + + public: + typedef T element_type; + typedef Traits traits_type; + + ScopedGeneric() : data_(traits_type::InvalidValue()) {} + + // Constructor. Takes responsibility for freeing the resource associated with + // the object T. + explicit ScopedGeneric(const element_type& value) : data_(value) {} + + // Constructor. Allows initialization of a stateful traits object. + ScopedGeneric(const element_type& value, const traits_type& traits) + : data_(value, traits) { + } + + // Move constructor for C++03 move emulation. + ScopedGeneric(RValue rvalue) + : data_(rvalue.object->release(), rvalue.object->get_traits()) { + } + + ~ScopedGeneric() { + FreeIfNecessary(); + } + + // 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 + void reset(const element_type& value = traits_type::InvalidValue()) { + if (data_.generic != traits_type::InvalidValue() && data_.generic == value) + abort(); + FreeIfNecessary(); + data_.generic = value; + } + + void swap(ScopedGeneric& other) { + // Standard swap idiom: 'using std::swap' ensures that std::swap is + // present in the overload set, but we call swap unqualified so that + // any more-specific overloads can be used, if available. + using std::swap; + swap(static_cast<Traits&>(data_), static_cast<Traits&>(other.data_)); + swap(data_.generic, other.data_.generic); + } + + // Release the object. The return value is the current object held by this + // object. After this operation, this object will hold a null value, and + // will not own the object any more. + element_type release() WARN_UNUSED_RESULT { + element_type old_generic = data_.generic; + data_.generic = traits_type::InvalidValue(); + return old_generic; + } + + const element_type& get() const { return data_.generic; } + + // Returns true if this object doesn't hold the special null value for the + // associated data type. + bool is_valid() const { return data_.generic != traits_type::InvalidValue(); } + + bool operator==(const element_type& value) const { + return data_.generic == value; + } + bool operator!=(const element_type& value) const { + return data_.generic != value; + } + + Traits& get_traits() { return data_; } + const Traits& get_traits() const { return data_; } + + private: + void FreeIfNecessary() { + if (data_.generic != traits_type::InvalidValue()) { + data_.Free(data_.generic); + data_.generic = traits_type::InvalidValue(); + } + } + + // Forbid comparison. If U != T, it totally doesn't make sense, and if U == + // T, it still doesn't make sense because you should never have the same + // object owned by two different ScopedGenerics. + template <typename T2, typename Traits2> bool operator==( + const ScopedGeneric<T2, Traits2>& p2) const; + template <typename T2, typename Traits2> bool operator!=( + const ScopedGeneric<T2, Traits2>& p2) const; + + Data data_; +}; + +template<class T, class Traits> +void swap(const ScopedGeneric<T, Traits>& a, + const ScopedGeneric<T, Traits>& b) { + a.swap(b); +} + +template<class T, class Traits> +bool operator==(const T& value, const ScopedGeneric<T, Traits>& scoped) { + return value == scoped.get(); +} + +template<class T, class Traits> +bool operator!=(const T& value, const ScopedGeneric<T, Traits>& scoped) { + return value != scoped.get(); +} + +} // namespace base + +#endif // BASE_SCOPED_GENERIC_H_ diff --git a/chromium/base/scoped_generic_unittest.cc b/chromium/base/scoped_generic_unittest.cc new file mode 100644 index 00000000000..f0dca22e693 --- /dev/null +++ b/chromium/base/scoped_generic_unittest.cc @@ -0,0 +1,153 @@ +// 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 <vector> + +#include "base/scoped_generic.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { + +namespace { + +struct IntTraits { + IntTraits(std::vector<int>* freed) : freed_ints(freed) {} + + static int InvalidValue() { + return -1; + } + void Free(int value) { + freed_ints->push_back(value); + } + + std::vector<int>* freed_ints; +}; + +typedef ScopedGeneric<int, IntTraits> ScopedInt; + +} // namespace + +TEST(ScopedGenericTest, ScopedGeneric) { + std::vector<int> values_freed; + IntTraits traits(&values_freed); + + // Invalid case, delete should not be called. + { + ScopedInt a(IntTraits::InvalidValue(), traits); + } + EXPECT_TRUE(values_freed.empty()); + + // Simple deleting case. + static const int kFirst = 0; + { + ScopedInt a(kFirst, traits); + } + ASSERT_EQ(1u, values_freed.size()); + ASSERT_EQ(kFirst, values_freed[0]); + values_freed.clear(); + + // Release should return the right value and leave the object empty. + { + ScopedInt a(kFirst, traits); + EXPECT_EQ(kFirst, a.release()); + + ScopedInt b(IntTraits::InvalidValue(), traits); + EXPECT_EQ(IntTraits::InvalidValue(), b.release()); + } + ASSERT_TRUE(values_freed.empty()); + + // Reset should free the old value, then the new one should go away when + // it goes out of scope. + static const int kSecond = 1; + { + ScopedInt b(kFirst, traits); + b.reset(kSecond); + ASSERT_EQ(1u, values_freed.size()); + ASSERT_EQ(kFirst, values_freed[0]); + } + ASSERT_EQ(2u, values_freed.size()); + ASSERT_EQ(kSecond, values_freed[1]); + values_freed.clear(); + + // Swap. + { + ScopedInt a(kFirst, traits); + ScopedInt b(kSecond, traits); + a.swap(b); + EXPECT_TRUE(values_freed.empty()); // Nothing should be freed. + EXPECT_EQ(kSecond, a.get()); + EXPECT_EQ(kFirst, b.get()); + } + // Values should be deleted in the opposite order. + ASSERT_EQ(2u, values_freed.size()); + EXPECT_EQ(kFirst, values_freed[0]); + EXPECT_EQ(kSecond, values_freed[1]); + values_freed.clear(); + + // Pass. + { + ScopedInt a(kFirst, traits); + ScopedInt b(a.Pass()); + EXPECT_TRUE(values_freed.empty()); // Nothing should be freed. + ASSERT_EQ(IntTraits::InvalidValue(), a.get()); + ASSERT_EQ(kFirst, b.get()); + } + ASSERT_EQ(1u, values_freed.size()); + ASSERT_EQ(kFirst, values_freed[0]); +} + +TEST(ScopedGenericTest, Operators) { + std::vector<int> values_freed; + IntTraits traits(&values_freed); + + static const int kFirst = 0; + static const int kSecond = 1; + { + ScopedInt a(kFirst, traits); + EXPECT_TRUE(a == kFirst); + EXPECT_FALSE(a != kFirst); + EXPECT_FALSE(a == kSecond); + EXPECT_TRUE(a != kSecond); + + EXPECT_TRUE(kFirst == a); + EXPECT_FALSE(kFirst != a); + EXPECT_FALSE(kSecond == a); + EXPECT_TRUE(kSecond != a); + } + + // is_valid(). + { + ScopedInt a(kFirst, traits); + EXPECT_TRUE(a.is_valid()); + a.reset(); + EXPECT_FALSE(a.is_valid()); + } +} + +// Cheesy manual "no compile" test for manually validating changes. +#if 0 +TEST(ScopedGenericTest, NoCompile) { + // Assignment shouldn't work. + /*{ + ScopedInt a(kFirst, traits); + ScopedInt b(a); + }*/ + + // Comparison shouldn't work. + /*{ + ScopedInt a(kFirst, traits); + ScopedInt b(kFirst, traits); + if (a == b) { + } + }*/ + + // Implicit conversion to bool shouldn't work. + /*{ + ScopedInt a(kFirst, traits); + bool result = a; + }*/ +} +#endif + +} // namespace base diff --git a/chromium/base/scoped_observer.h b/chromium/base/scoped_observer.h index eae6367204a..3754ed57e1d 100644 --- a/chromium/base/scoped_observer.h +++ b/chromium/base/scoped_observer.h @@ -19,8 +19,7 @@ class ScopedObserver { explicit ScopedObserver(Observer* observer) : observer_(observer) {} ~ScopedObserver() { - for (size_t i = 0; i < sources_.size(); ++i) - sources_[i]->RemoveObserver(observer_); + RemoveAll(); } // Adds the object passed to the constructor as an observer on |source|. @@ -35,12 +34,15 @@ class ScopedObserver { source->RemoveObserver(observer_); } + void RemoveAll() { + for (size_t i = 0; i < sources_.size(); ++i) + sources_[i]->RemoveObserver(observer_); + sources_.clear(); + } + bool IsObserving(Source* source) const { - for (size_t i = 0; i < sources_.size(); ++i) { - if (sources_[i] == source) - return true; - } - return false; + return std::find(sources_.begin(), sources_.end(), source) != + sources_.end(); } private: diff --git a/chromium/base/security_unittest.cc b/chromium/base/security_unittest.cc index cf3b1296667..960bc20276c 100644 --- a/chromium/base/security_unittest.cc +++ b/chromium/base/security_unittest.cc @@ -42,12 +42,12 @@ Type HideValueFromCompiler(volatile Type value) { return value; } -// - NO_TCMALLOC (should be defined if we compile with linux_use_tcmalloc=0) -// - ADDRESS_SANITIZER because it has its own memory allocator +// - 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(OS_IOS) && !defined(OS_MACOSX) && !defined(SYZYASAN) #define TCMALLOC_TEST(function) function #else #define TCMALLOC_TEST(function) DISABLED_##function @@ -59,7 +59,7 @@ const size_t kTooBigAllocSize = INT_MAX; // Detect runtime TCMalloc bypasses. bool IsTcMallocBypassed() { -#if defined(OS_LINUX) || defined(OS_CHROMEOS) +#if defined(OS_LINUX) // This should detect a TCMalloc bypass from Valgrind. char* g_slice = getenv("G_SLICE"); if (g_slice && !strcmp(g_slice, "always-malloc")) @@ -78,8 +78,10 @@ bool CallocDiesOnOOM() { // The sanitizers' calloc dies on OOM instead of returning NULL. // The wrapper function in base/process_util_linux.cc that is used when we // compile without TCMalloc will just die on OOM instead of returning NULL. -#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \ - defined(THREAD_SANITIZER) || (defined(OS_LINUX) && defined(NO_TCMALLOC)) +#if defined(ADDRESS_SANITIZER) || \ + defined(MEMORY_SANITIZER) || \ + defined(THREAD_SANITIZER) || \ + (defined(OS_LINUX) && defined(NO_TCMALLOC)) return true; #else return false; @@ -229,7 +231,7 @@ TEST(SecurityTest, CallocOverflow) { } } -#if (defined(OS_LINUX) || defined(OS_CHROMEOS)) && defined(__x86_64__) +#if defined(OS_LINUX) && defined(__x86_64__) // Check if ptr1 and ptr2 are separated by less than size chars. bool ArePointersToSameArea(void* ptr1, void* ptr2, size_t size) { ptrdiff_t ptr_diff = reinterpret_cast<char*>(std::max(ptr1, ptr2)) - @@ -285,6 +287,6 @@ TEST(SecurityTest, TCMALLOC_TEST(RandomMemoryAllocations)) { EXPECT_FALSE(impossible_random_address); } -#endif // (defined(OS_LINUX) || defined(OS_CHROMEOS)) && defined(__x86_64__) +#endif // defined(OS_LINUX) && defined(__x86_64__) } // namespace diff --git a/chromium/base/sequence_checker.h b/chromium/base/sequence_checker.h index 40d535e12d2..ad0182825c2 100644 --- a/chromium/base/sequence_checker.h +++ b/chromium/base/sequence_checker.h @@ -5,8 +5,6 @@ #ifndef BASE_SEQUENCE_CHECKER_H_ #define BASE_SEQUENCE_CHECKER_H_ -#include "base/memory/ref_counted.h" - // See comments for the similar block in thread_checker.h. #if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) #define ENABLE_SEQUENCE_CHECKER 1 @@ -14,14 +12,10 @@ #define ENABLE_SEQUENCE_CHECKER 0 #endif -#if ENABLE_SEQUENCE_CHECKER #include "base/sequence_checker_impl.h" -#endif namespace base { -class SequencedTaskRunner; - // Do nothing implementation, for use in release mode. // // Note: You should almost always use the SequenceChecker class to get @@ -52,7 +46,7 @@ class SequenceCheckerDoNothing { // SequenceChecker sequence_checker_; // } // -// In Release mode, CalledOnValidSequence will always return true. +// In Release mode, CalledOnValidSequencedThread() will always return true. #if ENABLE_SEQUENCE_CHECKER class SequenceChecker : public SequenceCheckerImpl { }; diff --git a/chromium/base/sequence_checker_unittest.cc b/chromium/base/sequence_checker_unittest.cc index 7df76149260..b8186408f60 100644 --- a/chromium/base/sequence_checker_unittest.cc +++ b/chromium/base/sequence_checker_unittest.cc @@ -261,8 +261,7 @@ TEST_F(SequenceCheckerTest, DifferentSequenceTokensDeathTestInDebug) { }, ""); } #else -TEST_F(SequenceCheckerTest, - DifferentSequenceTokensDeathTestInRelease) { +TEST_F(SequenceCheckerTest, DifferentSequenceTokensDeathTestInRelease) { DifferentSequenceTokensDeathTest(); } #endif // ENABLE_SEQUENCE_CHECKER @@ -289,8 +288,7 @@ TEST_F(SequenceCheckerTest, WorkerPoolAndSimpleThreadDeathTestInDebug) { }, ""); } #else -TEST_F(SequenceCheckerTest, - WorkerPoolAndSimpleThreadDeathTestInRelease) { +TEST_F(SequenceCheckerTest, WorkerPoolAndSimpleThreadDeathTestInRelease) { WorkerPoolAndSimpleThreadDeathTest(); } #endif // ENABLE_SEQUENCE_CHECKER @@ -323,8 +321,7 @@ TEST_F(SequenceCheckerTest, TwoDifferentWorkerPoolsDeathTestInDebug) { }, ""); } #else -TEST_F(SequenceCheckerTest, - TwoDifferentWorkerPoolsDeathTestInRelease) { +TEST_F(SequenceCheckerTest, TwoDifferentWorkerPoolsDeathTestInRelease) { TwoDifferentWorkerPoolsDeathTest(); } #endif // ENABLE_SEQUENCE_CHECKER diff --git a/chromium/base/sha1_win.cc b/chromium/base/sha1_win.cc index 98c3840f0e4..b64c9eb8583 100644 --- a/chromium/base/sha1_win.cc +++ b/chromium/base/sha1_win.cc @@ -18,20 +18,20 @@ std::string SHA1HashString(const std::string& str) { ScopedHCRYPTPROV provider; if (!CryptAcquireContext(provider.receive(), NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { - DLOG_GETLASTERROR(ERROR) << "CryptAcquireContext failed"; + DPLOG(ERROR) << "CryptAcquireContext failed"; return std::string(kSHA1Length, '\0'); } { ScopedHCRYPTHASH hash; if (!CryptCreateHash(provider, CALG_SHA1, 0, 0, hash.receive())) { - DLOG_GETLASTERROR(ERROR) << "CryptCreateHash failed"; + DPLOG(ERROR) << "CryptCreateHash failed"; return std::string(kSHA1Length, '\0'); } if (!CryptHashData(hash, reinterpret_cast<CONST BYTE*>(str.data()), static_cast<DWORD>(str.length()), 0)) { - DLOG_GETLASTERROR(ERROR) << "CryptHashData failed"; + DPLOG(ERROR) << "CryptHashData failed"; return std::string(kSHA1Length, '\0'); } @@ -40,7 +40,7 @@ std::string SHA1HashString(const std::string& str) { if (!CryptGetHashParam(hash, HP_HASHSIZE, reinterpret_cast<unsigned char*>(&hash_len), &buffer_size, 0)) { - DLOG_GETLASTERROR(ERROR) << "CryptGetHashParam(HP_HASHSIZE) failed"; + DPLOG(ERROR) << "CryptGetHashParam(HP_HASHSIZE) failed"; return std::string(kSHA1Length, '\0'); } @@ -50,7 +50,7 @@ std::string SHA1HashString(const std::string& str) { // but so that result.length() is correctly set to |hash_len|. reinterpret_cast<BYTE*>(WriteInto(&result, hash_len + 1)), &hash_len, 0))) { - DLOG_GETLASTERROR(ERROR) << "CryptGetHashParam(HP_HASHVAL) failed"; + DPLOG(ERROR) << "CryptGetHashParam(HP_HASHVAL) failed"; return std::string(kSHA1Length, '\0'); } diff --git a/chromium/base/stl_util.h b/chromium/base/stl_util.h index ccad09d7317..3602df0377a 100644 --- a/chromium/base/stl_util.h +++ b/chromium/base/stl_util.h @@ -201,9 +201,11 @@ namespace base { // Returns true if the container is sorted. template <typename Container> bool STLIsSorted(const Container& cont) { - return std::adjacent_find(cont.begin(), cont.end(), - std::greater<typename Container::value_type>()) - == cont.end(); + // Note: Use reverse iterator on container to ensure we only require + // value_type to implement operator<. + return std::adjacent_find(cont.rbegin(), cont.rend(), + std::less<typename Container::value_type>()) + == cont.rend(); } // Returns a new ResultType containing the difference of two sorted containers. @@ -218,6 +220,41 @@ ResultType STLSetDifference(const Arg1& a1, const Arg2& a2) { return difference; } +// Returns a new ResultType containing the union of two sorted containers. +template <typename ResultType, typename Arg1, typename Arg2> +ResultType STLSetUnion(const Arg1& a1, const Arg2& a2) { + DCHECK(STLIsSorted(a1)); + DCHECK(STLIsSorted(a2)); + ResultType result; + std::set_union(a1.begin(), a1.end(), + a2.begin(), a2.end(), + std::inserter(result, result.end())); + return result; +} + +// Returns a new ResultType containing the intersection of two sorted +// containers. +template <typename ResultType, typename Arg1, typename Arg2> +ResultType STLSetIntersection(const Arg1& a1, const Arg2& a2) { + DCHECK(STLIsSorted(a1)); + DCHECK(STLIsSorted(a2)); + ResultType result; + std::set_intersection(a1.begin(), a1.end(), + a2.begin(), a2.end(), + std::inserter(result, result.end())); + return result; +} + +// Returns true if the sorted container |a1| contains all elements of the sorted +// container |a2|. +template <typename Arg1, typename Arg2> +bool STLIncludes(const Arg1& a1, const Arg2& a2) { + DCHECK(STLIsSorted(a1)); + DCHECK(STLIsSorted(a2)); + return std::includes(a1.begin(), a1.end(), + a2.begin(), a2.end()); +} + } // namespace base #endif // BASE_STL_UTIL_H_ diff --git a/chromium/base/stl_util_unittest.cc b/chromium/base/stl_util_unittest.cc index 63d5c5c1ee7..a3f8e16f2ce 100644 --- a/chromium/base/stl_util_unittest.cc +++ b/chromium/base/stl_util_unittest.cc @@ -8,6 +8,28 @@ #include "testing/gtest/include/gtest/gtest.h" +namespace { + +// Used as test case to ensure the various base::STLXxx functions don't require +// more than operators "<" and "==" on values stored in containers. +class ComparableValue { + public: + explicit ComparableValue(int value) : value_(value) {} + + bool operator==(const ComparableValue& rhs) const { + return value_ == rhs.value_; + } + + bool operator<(const ComparableValue& rhs) const { + return value_ < rhs.value_; + } + + private: + int value_; +}; + +} + namespace base { namespace { @@ -21,6 +43,14 @@ TEST(STLUtilTest, STLIsSorted) { } { + std::set<ComparableValue> set; + set.insert(ComparableValue(24)); + set.insert(ComparableValue(1)); + set.insert(ComparableValue(12)); + EXPECT_TRUE(STLIsSorted(set)); + } + + { std::vector<int> vector; vector.push_back(1); vector.push_back(1); @@ -78,5 +108,135 @@ TEST(STLUtilTest, STLSetDifference) { } } +TEST(STLUtilTest, STLSetUnion) { + std::set<int> a1; + a1.insert(1); + a1.insert(2); + a1.insert(3); + a1.insert(4); + + std::set<int> a2; + a2.insert(3); + a2.insert(4); + a2.insert(5); + a2.insert(6); + a2.insert(7); + + { + std::set<int> result; + result.insert(1); + result.insert(2); + result.insert(3); + result.insert(4); + result.insert(5); + result.insert(6); + result.insert(7); + EXPECT_EQ(result, STLSetUnion<std::set<int> >(a1, a2)); + } + + { + std::set<int> result; + result.insert(1); + result.insert(2); + result.insert(3); + result.insert(4); + result.insert(5); + result.insert(6); + result.insert(7); + EXPECT_EQ(result, STLSetUnion<std::set<int> >(a2, a1)); + } + + { + std::vector<int> result; + result.push_back(1); + result.push_back(2); + result.push_back(3); + result.push_back(4); + result.push_back(5); + result.push_back(6); + result.push_back(7); + EXPECT_EQ(result, STLSetUnion<std::vector<int> >(a1, a2)); + } + + { + std::vector<int> result; + result.push_back(1); + result.push_back(2); + result.push_back(3); + result.push_back(4); + result.push_back(5); + result.push_back(6); + result.push_back(7); + EXPECT_EQ(result, STLSetUnion<std::vector<int> >(a2, a1)); + } +} + +TEST(STLUtilTest, STLSetIntersection) { + std::set<int> a1; + a1.insert(1); + a1.insert(2); + a1.insert(3); + a1.insert(4); + + std::set<int> a2; + a2.insert(3); + a2.insert(4); + a2.insert(5); + a2.insert(6); + a2.insert(7); + + { + std::set<int> result; + result.insert(3); + result.insert(4); + EXPECT_EQ(result, STLSetIntersection<std::set<int> >(a1, a2)); + } + + { + std::set<int> result; + result.insert(3); + result.insert(4); + EXPECT_EQ(result, STLSetIntersection<std::set<int> >(a2, a1)); + } + + { + std::vector<int> result; + result.push_back(3); + result.push_back(4); + EXPECT_EQ(result, STLSetIntersection<std::vector<int> >(a1, a2)); + } + + { + std::vector<int> result; + result.push_back(3); + result.push_back(4); + EXPECT_EQ(result, STLSetIntersection<std::vector<int> >(a2, a1)); + } +} + +TEST(STLUtilTest, STLIncludes) { + std::set<int> a1; + a1.insert(1); + a1.insert(2); + a1.insert(3); + a1.insert(4); + + std::set<int> a2; + a2.insert(3); + a2.insert(4); + + std::set<int> a3; + a3.insert(3); + a3.insert(4); + a3.insert(5); + + EXPECT_TRUE(STLIncludes<std::set<int> >(a1, a2)); + EXPECT_FALSE(STLIncludes<std::set<int> >(a1, a3)); + EXPECT_FALSE(STLIncludes<std::set<int> >(a2, a1)); + EXPECT_FALSE(STLIncludes<std::set<int> >(a2, a3)); + EXPECT_FALSE(STLIncludes<std::set<int> >(a3, a1)); + EXPECT_TRUE(STLIncludes<std::set<int> >(a3, a2)); +} + } // namespace } // namespace base diff --git a/chromium/base/strings/safe_sprintf_unittest.cc b/chromium/base/strings/safe_sprintf_unittest.cc index 937fa4eca23..d9fb64689cc 100644 --- a/chromium/base/strings/safe_sprintf_unittest.cc +++ b/chromium/base/strings/safe_sprintf_unittest.cc @@ -416,7 +416,7 @@ void PrintLongString(char* buf, size_t sz) { // The text that was generated by SafeSPrintf() should always match the // equivalent text generated by sprintf(). Please note that the format - // string for sprintf() is nor complicated, as it does not have the + // string for sprintf() is not complicated, as it does not have the // benefit of getting type information from the C++ compiler. // // N.B.: It would be so much cleaner to use snprintf(). But unfortunately, @@ -426,7 +426,8 @@ void PrintLongString(char* buf, size_t sz) { CHECK_LE(sz, sizeof(ref)); sprintf(ref, "A long string: %%d 00DEADBEEF %lld 0x%llX <NULL>", static_cast<long long>(std::numeric_limits<intptr_t>::min()), - (long long)PrintLongString); + static_cast<unsigned long long>( + reinterpret_cast<uintptr_t>(PrintLongString))); ref[sz-1] = '\000'; #if defined(NDEBUG) diff --git a/chromium/base/strings/string16.h b/chromium/base/strings/string16.h index fd98f1b5be7..804dca42e45 100644 --- a/chromium/base/strings/string16.h +++ b/chromium/base/strings/string16.h @@ -181,9 +181,4 @@ class BASE_EXPORT std::basic_string<base::char16, base::string16_char_traits>; #endif // WCHAR_T_IS_UTF32 -// TODO(brettw) update users of string16 to use the namespace and remove -// this "using". -using base::char16; -using base::string16; - #endif // BASE_STRINGS_STRING16_H_ diff --git a/chromium/base/strings/string_number_conversions.cc b/chromium/base/strings/string_number_conversions.cc index e3e71bef023..59dc93be8ad 100644 --- a/chromium/base/strings/string_number_conversions.cc +++ b/chromium/base/strings/string_number_conversions.cc @@ -78,24 +78,19 @@ struct IntToStringT { // unsigned, even the presence of the unary operation causes a warning. UINT res = ToUnsignedT<INT, UINT, NEG>::ToUnsigned(value); - for (typename STR::iterator it = outbuf.end();;) { + typename STR::iterator it(outbuf.end()); + do { --it; DCHECK(it != outbuf.begin()); *it = static_cast<typename STR::value_type>((res % 10) + '0'); res /= 10; - - // We're done.. - if (res == 0) { - if (is_neg) { - --it; - DCHECK(it != outbuf.begin()); - *it = static_cast<typename STR::value_type>('-'); - } - return STR(it, outbuf.end()); - } + } while (res != 0); + if (is_neg) { + --it; + DCHECK(it != outbuf.begin()); + *it = static_cast<typename STR::value_type>('-'); } - NOTREACHED(); - return STR(); + return STR(it, outbuf.end()); } }; diff --git a/chromium/base/strings/string_piece.cc b/chromium/base/strings/string_piece.cc index 79a42d78426..4c7f1122f2d 100644 --- a/chromium/base/strings/string_piece.cc +++ b/chromium/base/strings/string_piece.cc @@ -9,14 +9,30 @@ #include <ostream> namespace base { +namespace { + +// For each character in characters_wanted, sets the index corresponding +// to the ASCII code of that character to 1 in table. This is used by +// the find_.*_of methods below to tell whether or not a character is in +// the lookup table in constant time. +// The argument `table' must be an array that is large enough to hold all +// the possible values of an unsigned char. Thus it should be be declared +// as follows: +// bool table[UCHAR_MAX + 1] +inline void BuildLookupTable(const StringPiece& characters_wanted, + bool* table) { + const size_t length = characters_wanted.length(); + const char* const data = characters_wanted.data(); + for (size_t i = 0; i < length; ++i) { + table[static_cast<unsigned char>(data[i])] = true; + } +} + +} // namespace // MSVC doesn't like complex extern templates and DLLs. #if !defined(COMPILER_MSVC) -namespace internal { -template class StringPieceDetail<std::string>; -template class StringPieceDetail<string16>; -} // namespace internal - +template class BasicStringPiece<std::string>; template class BasicStringPiece<string16>; #endif @@ -33,101 +49,153 @@ std::ostream& operator<<(std::ostream& o, const StringPiece& piece) { } namespace internal { + +template<typename STR> +void CopyToStringT(const BasicStringPiece<STR>& self, STR* target) { + if (self.empty()) + target->clear(); + else + target->assign(self.data(), self.size()); +} + void CopyToString(const StringPiece& self, std::string* target) { - target->assign(!self.empty() ? self.data() : "", self.size()); + CopyToStringT(self, target); } -void AppendToString(const StringPiece& self, std::string* target) { +void CopyToString(const StringPiece16& self, string16* target) { + CopyToStringT(self, target); +} + +template<typename STR> +void AppendToStringT(const BasicStringPiece<STR>& self, STR* target) { if (!self.empty()) target->append(self.data(), self.size()); } -StringPiece::size_type copy(const StringPiece& self, - char* buf, - StringPiece::size_type n, - StringPiece::size_type pos) { - StringPiece::size_type ret = std::min(self.size() - pos, n); - memcpy(buf, self.data() + pos, ret); +void AppendToString(const StringPiece& self, std::string* target) { + AppendToStringT(self, target); +} + +void AppendToString(const StringPiece16& self, string16* target) { + AppendToStringT(self, target); +} + +template<typename STR> +size_t copyT(const BasicStringPiece<STR>& self, + typename STR::value_type* buf, + size_t n, + size_t pos) { + size_t ret = std::min(self.size() - pos, n); + memcpy(buf, self.data() + pos, ret * sizeof(typename STR::value_type)); return ret; } -StringPiece::size_type find(const StringPiece& self, - const StringPiece& s, - StringPiece::size_type pos) { +size_t copy(const StringPiece& self, char* buf, size_t n, size_t pos) { + return copyT(self, buf, n, pos); +} + +size_t copy(const StringPiece16& self, char16* buf, size_t n, size_t pos) { + return copyT(self, buf, n, pos); +} + +template<typename STR> +size_t findT(const BasicStringPiece<STR>& self, + const BasicStringPiece<STR>& s, + size_t pos) { if (pos > self.size()) - return StringPiece::npos; + return BasicStringPiece<STR>::npos; - StringPiece::const_iterator result = + typename BasicStringPiece<STR>::const_iterator result = std::search(self.begin() + pos, self.end(), s.begin(), s.end()); - const StringPiece::size_type xpos = + const size_t xpos = static_cast<size_t>(result - self.begin()); - return xpos + s.size() <= self.size() ? xpos : StringPiece::npos; + return xpos + s.size() <= self.size() ? xpos : BasicStringPiece<STR>::npos; +} + +size_t find(const StringPiece& self, const StringPiece& s, size_t pos) { + return findT(self, s, pos); } -StringPiece::size_type find(const StringPiece& self, - char c, - StringPiece::size_type pos) { +size_t find(const StringPiece16& self, const StringPiece16& s, size_t pos) { + return findT(self, s, pos); +} + +template<typename STR> +size_t findT(const BasicStringPiece<STR>& self, + typename STR::value_type c, + size_t pos) { if (pos >= self.size()) - return StringPiece::npos; + return BasicStringPiece<STR>::npos; - StringPiece::const_iterator result = + typename BasicStringPiece<STR>::const_iterator result = std::find(self.begin() + pos, self.end(), c); return result != self.end() ? - static_cast<size_t>(result - self.begin()) : StringPiece::npos; + static_cast<size_t>(result - self.begin()) : BasicStringPiece<STR>::npos; } -StringPiece::size_type rfind(const StringPiece& self, - const StringPiece& s, - StringPiece::size_type pos) { +size_t find(const StringPiece& self, char c, size_t pos) { + return findT(self, c, pos); +} + +size_t find(const StringPiece16& self, char16 c, size_t pos) { + return findT(self, c, pos); +} + +template<typename STR> +size_t rfindT(const BasicStringPiece<STR>& self, + const BasicStringPiece<STR>& s, + size_t pos) { if (self.size() < s.size()) - return StringPiece::npos; + return BasicStringPiece<STR>::npos; if (s.empty()) return std::min(self.size(), pos); - StringPiece::const_iterator last = + typename BasicStringPiece<STR>::const_iterator last = self.begin() + std::min(self.size() - s.size(), pos) + s.size(); - StringPiece::const_iterator result = + typename BasicStringPiece<STR>::const_iterator result = std::find_end(self.begin(), last, s.begin(), s.end()); return result != last ? - static_cast<size_t>(result - self.begin()) : StringPiece::npos; + static_cast<size_t>(result - self.begin()) : BasicStringPiece<STR>::npos; +} + +size_t rfind(const StringPiece& self, const StringPiece& s, size_t pos) { + return rfindT(self, s, pos); +} + +size_t rfind(const StringPiece16& self, const StringPiece16& s, size_t pos) { + return rfindT(self, s, pos); } -StringPiece::size_type rfind(const StringPiece& self, - char c, - StringPiece::size_type pos) { +template<typename STR> +size_t rfindT(const BasicStringPiece<STR>& self, + typename STR::value_type c, + size_t pos) { if (self.size() == 0) - return StringPiece::npos; + return BasicStringPiece<STR>::npos; - for (StringPiece::size_type i = std::min(pos, self.size() - 1); ; --i) { + for (size_t i = std::min(pos, self.size() - 1); ; + --i) { if (self.data()[i] == c) return i; if (i == 0) break; } - return StringPiece::npos; + return BasicStringPiece<STR>::npos; } -// For each character in characters_wanted, sets the index corresponding -// to the ASCII code of that character to 1 in table. This is used by -// the find_.*_of methods below to tell whether or not a character is in -// the lookup table in constant time. -// The argument `table' must be an array that is large enough to hold all -// the possible values of an unsigned char. Thus it should be be declared -// as follows: -// bool table[UCHAR_MAX + 1] -static inline void BuildLookupTable(const StringPiece& characters_wanted, - bool* table) { - const StringPiece::size_type length = characters_wanted.length(); - const char* const data = characters_wanted.data(); - for (StringPiece::size_type i = 0; i < length; ++i) { - table[static_cast<unsigned char>(data[i])] = true; - } +size_t rfind(const StringPiece& self, char c, size_t pos) { + return rfindT(self, c, pos); } -StringPiece::size_type find_first_of(const StringPiece& self, - const StringPiece& s, - StringPiece::size_type pos) { +size_t rfind(const StringPiece16& self, char16 c, size_t pos) { + return rfindT(self, c, pos); +} + +// 8-bit version using lookup table. +size_t find_first_of(const StringPiece& self, + const StringPiece& s, + size_t pos) { if (self.size() == 0 || s.size() == 0) return StringPiece::npos; @@ -137,7 +205,7 @@ StringPiece::size_type find_first_of(const StringPiece& self, bool lookup[UCHAR_MAX + 1] = { false }; BuildLookupTable(s, lookup); - for (StringPiece::size_type i = pos; i < self.size(); ++i) { + for (size_t i = pos; i < self.size(); ++i) { if (lookup[static_cast<unsigned char>(self.data()[i])]) { return i; } @@ -145,9 +213,21 @@ StringPiece::size_type find_first_of(const StringPiece& self, return StringPiece::npos; } -StringPiece::size_type find_first_not_of(const StringPiece& self, - const StringPiece& s, - StringPiece::size_type pos) { +// 16-bit brute force version. +size_t find_first_of(const StringPiece16& self, + const StringPiece16& s, + size_t pos) { + StringPiece16::const_iterator found = + std::find_first_of(self.begin() + pos, self.end(), s.begin(), s.end()); + if (found == self.end()) + return StringPiece16::npos; + return found - self.begin(); +} + +// 8-bit version using lookup table. +size_t find_first_not_of(const StringPiece& self, + const StringPiece& s, + size_t pos) { if (self.size() == 0) return StringPiece::npos; @@ -160,7 +240,7 @@ StringPiece::size_type find_first_not_of(const StringPiece& self, bool lookup[UCHAR_MAX + 1] = { false }; BuildLookupTable(s, lookup); - for (StringPiece::size_type i = pos; i < self.size(); ++i) { + for (size_t i = pos; i < self.size(); ++i) { if (!lookup[static_cast<unsigned char>(self.data()[i])]) { return i; } @@ -168,23 +248,56 @@ StringPiece::size_type find_first_not_of(const StringPiece& self, return StringPiece::npos; } -StringPiece::size_type find_first_not_of(const StringPiece& self, - char c, - StringPiece::size_type pos) { +// 16-bit brute-force version. +BASE_EXPORT size_t find_first_not_of(const StringPiece16& self, + const StringPiece16& s, + size_t pos) { if (self.size() == 0) - return StringPiece::npos; + return StringPiece16::npos; + + for (size_t self_i = pos; self_i < self.size(); ++self_i) { + bool found = false; + for (size_t s_i = 0; s_i < s.size(); ++s_i) { + if (self[self_i] == s[s_i]) { + found = true; + break; + } + } + if (!found) + return self_i; + } + return StringPiece16::npos; +} + +template<typename STR> +size_t find_first_not_ofT(const BasicStringPiece<STR>& self, + typename STR::value_type c, + size_t pos) { + if (self.size() == 0) + return BasicStringPiece<STR>::npos; for (; pos < self.size(); ++pos) { if (self.data()[pos] != c) { return pos; } } - return StringPiece::npos; + return BasicStringPiece<STR>::npos; +} + +size_t find_first_not_of(const StringPiece& self, + char c, + size_t pos) { + return find_first_not_ofT(self, c, pos); +} + +size_t find_first_not_of(const StringPiece16& self, + char16 c, + size_t pos) { + return find_first_not_ofT(self, c, pos); } -StringPiece::size_type find_last_of(const StringPiece& self, - const StringPiece& s, - StringPiece::size_type pos) { +// 8-bit version using lookup table. +size_t find_last_of(const StringPiece& self, const StringPiece& s, size_t pos) { if (self.size() == 0 || s.size() == 0) return StringPiece::npos; @@ -194,7 +307,7 @@ StringPiece::size_type find_last_of(const StringPiece& self, bool lookup[UCHAR_MAX + 1] = { false }; BuildLookupTable(s, lookup); - for (StringPiece::size_type i = std::min(pos, self.size() - 1); ; --i) { + for (size_t i = std::min(pos, self.size() - 1); ; --i) { if (lookup[static_cast<unsigned char>(self.data()[i])]) return i; if (i == 0) @@ -203,13 +316,33 @@ StringPiece::size_type find_last_of(const StringPiece& self, return StringPiece::npos; } -StringPiece::size_type find_last_not_of(const StringPiece& self, - const StringPiece& s, - StringPiece::size_type pos) { +// 16-bit brute-force version. +size_t find_last_of(const StringPiece16& self, + const StringPiece16& s, + size_t pos) { + if (self.size() == 0) + return StringPiece16::npos; + + for (size_t self_i = std::min(pos, self.size() - 1); ; + --self_i) { + for (size_t s_i = 0; s_i < s.size(); s_i++) { + if (self.data()[self_i] == s[s_i]) + return self_i; + } + if (self_i == 0) + break; + } + return StringPiece16::npos; +} + +// 8-bit version using lookup table. +size_t find_last_not_of(const StringPiece& self, + const StringPiece& s, + size_t pos) { if (self.size() == 0) return StringPiece::npos; - StringPiece::size_type i = std::min(pos, self.size() - 1); + size_t i = std::min(pos, self.size() - 1); if (s.size() == 0) return i; @@ -228,27 +361,76 @@ StringPiece::size_type find_last_not_of(const StringPiece& self, return StringPiece::npos; } -StringPiece::size_type find_last_not_of(const StringPiece& self, - char c, - StringPiece::size_type pos) { +// 16-bit brute-force version. +size_t find_last_not_of(const StringPiece16& self, + const StringPiece16& s, + size_t pos) { if (self.size() == 0) return StringPiece::npos; - for (StringPiece::size_type i = std::min(pos, self.size() - 1); ; --i) { + for (size_t self_i = std::min(pos, self.size() - 1); ; --self_i) { + bool found = false; + for (size_t s_i = 0; s_i < s.size(); s_i++) { + if (self.data()[self_i] == s[s_i]) { + found = true; + break; + } + } + if (!found) + return self_i; + if (self_i == 0) + break; + } + return StringPiece16::npos; +} + +template<typename STR> +size_t find_last_not_ofT(const BasicStringPiece<STR>& self, + typename STR::value_type c, + size_t pos) { + if (self.size() == 0) + return BasicStringPiece<STR>::npos; + + for (size_t i = std::min(pos, self.size() - 1); ; --i) { if (self.data()[i] != c) return i; if (i == 0) break; } - return StringPiece::npos; + return BasicStringPiece<STR>::npos; } -StringPiece substr(const StringPiece& self, - StringPiece::size_type pos, - StringPiece::size_type n) { +size_t find_last_not_of(const StringPiece& self, + char c, + size_t pos) { + return find_last_not_ofT(self, c, pos); +} + +size_t find_last_not_of(const StringPiece16& self, + char16 c, + size_t pos) { + return find_last_not_ofT(self, c, pos); +} + +template<typename STR> +BasicStringPiece<STR> substrT(const BasicStringPiece<STR>& self, + size_t pos, + size_t n) { if (pos > self.size()) pos = self.size(); if (n > self.size() - pos) n = self.size() - pos; - return StringPiece(self.data() + pos, n); + return BasicStringPiece<STR>(self.data() + pos, n); +} + +StringPiece substr(const StringPiece& self, + size_t pos, + size_t n) { + return substrT(self, pos, n); +} + +StringPiece16 substr(const StringPiece16& self, + size_t pos, + size_t n) { + return substrT(self, pos, n); } } // namespace internal diff --git a/chromium/base/strings/string_piece.h b/chromium/base/strings/string_piece.h index 818d6ca598b..38e2277e98c 100644 --- a/chromium/base/strings/string_piece.h +++ b/chromium/base/strings/string_piece.h @@ -39,14 +39,124 @@ template <typename STRING_TYPE> class BasicStringPiece; typedef BasicStringPiece<std::string> StringPiece; typedef BasicStringPiece<string16> StringPiece16; +// internal -------------------------------------------------------------------- + +// Many of the StringPiece functions use different implementations for the +// 8-bit and 16-bit versions, and we don't want lots of template expansions in +// this (very common) header that will slow down compilation. +// +// So here we define overloaded functions called by the StringPiece template. +// For those that share an implementation, the two versions will expand to a +// template internal to the .cc file. namespace internal { +BASE_EXPORT void CopyToString(const StringPiece& self, std::string* target); +BASE_EXPORT void CopyToString(const StringPiece16& self, string16* target); + +BASE_EXPORT void AppendToString(const StringPiece& self, std::string* target); +BASE_EXPORT void AppendToString(const StringPiece16& self, string16* target); + +BASE_EXPORT size_t copy(const StringPiece& self, + char* buf, + size_t n, + size_t pos); +BASE_EXPORT size_t copy(const StringPiece16& self, + char16* buf, + size_t n, + size_t pos); + +BASE_EXPORT size_t find(const StringPiece& self, + const StringPiece& s, + size_t pos); +BASE_EXPORT size_t find(const StringPiece16& self, + const StringPiece16& s, + size_t pos); +BASE_EXPORT size_t find(const StringPiece& self, + char c, + size_t pos); +BASE_EXPORT size_t find(const StringPiece16& self, + char16 c, + size_t pos); + +BASE_EXPORT size_t rfind(const StringPiece& self, + const StringPiece& s, + size_t pos); +BASE_EXPORT size_t rfind(const StringPiece16& self, + const StringPiece16& s, + size_t pos); +BASE_EXPORT size_t rfind(const StringPiece& self, + char c, + size_t pos); +BASE_EXPORT size_t rfind(const StringPiece16& self, + char16 c, + size_t pos); + +BASE_EXPORT size_t find_first_of(const StringPiece& self, + const StringPiece& s, + size_t pos); +BASE_EXPORT size_t find_first_of(const StringPiece16& self, + const StringPiece16& s, + size_t pos); + +BASE_EXPORT size_t find_first_not_of(const StringPiece& self, + const StringPiece& s, + size_t pos); +BASE_EXPORT size_t find_first_not_of(const StringPiece16& self, + const StringPiece16& s, + size_t pos); +BASE_EXPORT size_t find_first_not_of(const StringPiece& self, + char c, + size_t pos); +BASE_EXPORT size_t find_first_not_of(const StringPiece16& self, + char16 c, + size_t pos); + +BASE_EXPORT size_t find_last_of(const StringPiece& self, + const StringPiece& s, + size_t pos); +BASE_EXPORT size_t find_last_of(const StringPiece16& self, + const StringPiece16& s, + size_t pos); +BASE_EXPORT size_t find_last_of(const StringPiece& self, + char c, + size_t pos); +BASE_EXPORT size_t find_last_of(const StringPiece16& self, + char16 c, + size_t pos); + +BASE_EXPORT size_t find_last_not_of(const StringPiece& self, + const StringPiece& s, + size_t pos); +BASE_EXPORT size_t find_last_not_of(const StringPiece16& self, + const StringPiece16& s, + size_t pos); +BASE_EXPORT size_t find_last_not_of(const StringPiece16& self, + char16 c, + size_t pos); +BASE_EXPORT size_t find_last_not_of(const StringPiece& self, + char c, + size_t pos); + +BASE_EXPORT StringPiece substr(const StringPiece& self, + size_t pos, + size_t n); +BASE_EXPORT StringPiece16 substr(const StringPiece16& self, + size_t pos, + size_t n); + +} // namespace internal + +// BasicStringPiece ------------------------------------------------------------ + // Defines the types, methods, operators, and data members common to both // StringPiece and StringPiece16. Do not refer to this class directly, but // rather to BasicStringPiece, StringPiece, or StringPiece16. -template <typename STRING_TYPE> class StringPieceDetail { +// +// This is templatized by string class type rather than character type, so +// BasicStringPiece<std::string> or BasicStringPiece<base::string16>. +template <typename STRING_TYPE> class BasicStringPiece { public: - // standard STL container boilerplate + // Standard STL container boilerplate. typedef size_t size_type; typedef typename STRING_TYPE::value_type value_type; typedef const value_type* pointer; @@ -62,15 +172,15 @@ template <typename STRING_TYPE> class StringPieceDetail { // We provide non-explicit singleton constructors so users can pass // in a "const char*" or a "string" wherever a "StringPiece" is // expected (likewise for char16, string16, StringPiece16). - StringPieceDetail() : ptr_(NULL), length_(0) {} - StringPieceDetail(const value_type* str) + BasicStringPiece() : ptr_(NULL), length_(0) {} + BasicStringPiece(const value_type* str) : ptr_(str), length_((str == NULL) ? 0 : STRING_TYPE::traits_type::length(str)) {} - StringPieceDetail(const STRING_TYPE& str) + BasicStringPiece(const STRING_TYPE& str) : ptr_(str.data()), length_(str.size()) {} - StringPieceDetail(const value_type* offset, size_type len) + BasicStringPiece(const value_type* offset, size_type len) : ptr_(offset), length_(len) {} - StringPieceDetail(const typename STRING_TYPE::const_iterator& begin, + BasicStringPiece(const typename STRING_TYPE::const_iterator& begin, const typename STRING_TYPE::const_iterator& end) : ptr_((end > begin) ? &(*begin) : NULL), length_((end > begin) ? (size_type)(end - begin) : 0) {} @@ -141,213 +251,113 @@ template <typename STRING_TYPE> class StringPieceDetail { return STRING_TYPE::traits_type::compare(p, p2, N); } - protected: - const value_type* ptr_; - size_type length_; -}; - -template <typename STRING_TYPE> -const typename StringPieceDetail<STRING_TYPE>::size_type -StringPieceDetail<STRING_TYPE>::npos = - typename StringPieceDetail<STRING_TYPE>::size_type(-1); - -// MSVC doesn't like complex extern templates and DLLs. -#if !defined(COMPILER_MSVC) -extern template class BASE_EXPORT StringPieceDetail<std::string>; -extern template class BASE_EXPORT StringPieceDetail<string16>; -#endif - -BASE_EXPORT void CopyToString(const StringPiece& self, std::string* target); -BASE_EXPORT void AppendToString(const StringPiece& self, std::string* target); -BASE_EXPORT StringPieceDetail<std::string>::size_type copy( - const StringPiece& self, - char* buf, - StringPieceDetail<std::string>::size_type n, - StringPieceDetail<std::string>::size_type pos); -BASE_EXPORT StringPieceDetail<std::string>::size_type find( - const StringPiece& self, - const StringPiece& s, - StringPieceDetail<std::string>::size_type pos); -BASE_EXPORT StringPieceDetail<std::string>::size_type find( - const StringPiece& self, - char c, - StringPieceDetail<std::string>::size_type pos); -BASE_EXPORT StringPieceDetail<std::string>::size_type rfind( - const StringPiece& self, - const StringPiece& s, - StringPieceDetail<std::string>::size_type pos); -BASE_EXPORT StringPieceDetail<std::string>::size_type rfind( - const StringPiece& self, - char c, - StringPieceDetail<std::string>::size_type pos); -BASE_EXPORT StringPieceDetail<std::string>::size_type find_first_of( - const StringPiece& self, - const StringPiece& s, - StringPieceDetail<std::string>::size_type pos); -BASE_EXPORT StringPieceDetail<std::string>::size_type find_first_not_of( - const StringPiece& self, - const StringPiece& s, - StringPieceDetail<std::string>::size_type pos); -BASE_EXPORT StringPieceDetail<std::string>::size_type find_first_not_of( - const StringPiece& self, - char c, - StringPieceDetail<std::string>::size_type pos); -BASE_EXPORT StringPieceDetail<std::string>::size_type find_last_of( - const StringPiece& self, - const StringPiece& s, - StringPieceDetail<std::string>::size_type pos); -BASE_EXPORT StringPieceDetail<std::string>::size_type find_last_of( - const StringPiece& self, - char c, - StringPieceDetail<std::string>::size_type pos); -BASE_EXPORT StringPieceDetail<std::string>::size_type find_last_not_of( - const StringPiece& self, - const StringPiece& s, - StringPieceDetail<std::string>::size_type pos); -BASE_EXPORT StringPieceDetail<std::string>::size_type find_last_not_of( - const StringPiece& self, - char c, - StringPieceDetail<std::string>::size_type pos); -BASE_EXPORT StringPiece substr(const StringPiece& self, - StringPieceDetail<std::string>::size_type pos, - StringPieceDetail<std::string>::size_type n); -} // namespace internal - -// Defines the template type that is instantiated as either StringPiece or -// StringPiece16. -template <typename STRING_TYPE> class BasicStringPiece : - public internal::StringPieceDetail<STRING_TYPE> { - public: - typedef typename internal::StringPieceDetail<STRING_TYPE>::value_type - value_type; - typedef typename internal::StringPieceDetail<STRING_TYPE>::size_type - size_type; - - BasicStringPiece() {} - BasicStringPiece(const value_type*str) - : internal::StringPieceDetail<STRING_TYPE>(str) {} - BasicStringPiece(const STRING_TYPE& str) - : internal::StringPieceDetail<STRING_TYPE>(str) {} - BasicStringPiece(const value_type* offset, size_type len) - : internal::StringPieceDetail<STRING_TYPE>(offset, len) {} - BasicStringPiece(const typename STRING_TYPE::const_iterator& begin, - const typename STRING_TYPE::const_iterator& end) - : internal::StringPieceDetail<STRING_TYPE>(begin, end) {} -}; - -// Specializes BasicStringPiece for std::string to add a few operations that -// are not needed for string16. -template <> class BasicStringPiece<std::string> : - public internal::StringPieceDetail<std::string> { - public: - BasicStringPiece() {} - BasicStringPiece(const char* str) - : internal::StringPieceDetail<std::string>(str) {} - BasicStringPiece(const std::string& str) - : internal::StringPieceDetail<std::string>(str) {} - BasicStringPiece(const char* offset, size_type len) - : internal::StringPieceDetail<std::string>(offset, len) {} - BasicStringPiece(const std::string::const_iterator& begin, - const std::string::const_iterator& end) - : internal::StringPieceDetail<std::string>(begin, end) {} - - // Prevent the following overload of set() from hiding the definitions in the - // base class. - using internal::StringPieceDetail<std::string>::set; - - void set(const void* data, size_type len) { - ptr_ = reinterpret_cast<const value_type*>(data); - length_ = len; - } - - void CopyToString(std::string* target) const { + // Sets the value of the given string target type to be the current string. + // This saves a temporary over doing |a = b.as_string()| + void CopyToString(STRING_TYPE* target) const { internal::CopyToString(*this, target); } - void AppendToString(std::string* target) const { + void AppendToString(STRING_TYPE* target) const { internal::AppendToString(*this, target); } + size_type copy(value_type* buf, size_type n, size_type pos = 0) const { + return internal::copy(*this, buf, n, pos); + } + // Does "this" start with "x" bool starts_with(const BasicStringPiece& x) const { - return ((length_ >= x.length_) && - (wordmemcmp(ptr_, x.ptr_, x.length_) == 0)); + return ((this->length_ >= x.length_) && + (wordmemcmp(this->ptr_, x.ptr_, x.length_) == 0)); } // Does "this" end with "x" bool ends_with(const BasicStringPiece& x) const { - return ((length_ >= x.length_) && - (wordmemcmp(ptr_ + (length_-x.length_), x.ptr_, x.length_) == 0)); - } - - size_type copy(char* buf, size_type n, size_type pos = 0) const { - return internal::copy(*this, buf, n, pos); + return ((this->length_ >= x.length_) && + (wordmemcmp(this->ptr_ + (this->length_-x.length_), + x.ptr_, x.length_) == 0)); } - size_type find(const BasicStringPiece& s, size_type pos = 0) const { + // find: Search for a character or substring at a given offset. + size_type find(const BasicStringPiece<STRING_TYPE>& s, + size_type pos = 0) const { return internal::find(*this, s, pos); } - - size_type find(char c, size_type pos = 0) const { + size_type find(value_type c, size_type pos = 0) const { return internal::find(*this, c, pos); } - size_type rfind(const BasicStringPiece& s, size_type pos = npos) const { + // rfind: Reverse find. + size_type rfind(const BasicStringPiece& s, + size_type pos = BasicStringPiece::npos) const { return internal::rfind(*this, s, pos); } - - size_type rfind(char c, size_type pos = npos) const { + size_type rfind(value_type c, size_type pos = BasicStringPiece::npos) const { return internal::rfind(*this, c, pos); } - size_type find_first_of(const BasicStringPiece& s, size_type pos = 0) const { + // find_first_of: Find the first occurence of one of a set of characters. + size_type find_first_of(const BasicStringPiece& s, + size_type pos = 0) const { return internal::find_first_of(*this, s, pos); } - - size_type find_first_of(char c, size_type pos = 0) const { + size_type find_first_of(value_type c, size_type pos = 0) const { return find(c, pos); } + // find_first_not_of: Find the first occurence not of a set of characters. size_type find_first_not_of(const BasicStringPiece& s, size_type pos = 0) const { return internal::find_first_not_of(*this, s, pos); } - - size_type find_first_not_of(char c, size_type pos = 0) const { + size_type find_first_not_of(value_type c, size_type pos = 0) const { return internal::find_first_not_of(*this, c, pos); } + // find_last_of: Find the last occurence of one of a set of characters. size_type find_last_of(const BasicStringPiece& s, - size_type pos = npos) const { + size_type pos = BasicStringPiece::npos) const { return internal::find_last_of(*this, s, pos); } - - size_type find_last_of(char c, size_type pos = npos) const { + size_type find_last_of(value_type c, + size_type pos = BasicStringPiece::npos) const { return rfind(c, pos); } + // find_last_not_of: Find the last occurence not of a set of characters. size_type find_last_not_of(const BasicStringPiece& s, - size_type pos = npos) const { + size_type pos = BasicStringPiece::npos) const { return internal::find_last_not_of(*this, s, pos); } - - size_type find_last_not_of(char c, size_type pos = npos) const { + size_type find_last_not_of(value_type c, + size_type pos = BasicStringPiece::npos) const { return internal::find_last_not_of(*this, c, pos); } - BasicStringPiece substr(size_type pos, size_type n = npos) const { + // substr. + BasicStringPiece substr(size_type pos, + size_type n = BasicStringPiece::npos) const { return internal::substr(*this, pos, n); } + + protected: + const value_type* ptr_; + size_type length_; }; +template <typename STRING_TYPE> +const typename BasicStringPiece<STRING_TYPE>::size_type +BasicStringPiece<STRING_TYPE>::npos = + typename BasicStringPiece<STRING_TYPE>::size_type(-1); + // MSVC doesn't like complex extern templates and DLLs. #if !defined(COMPILER_MSVC) -// We can't explicitly declare the std::string instantiation here because it was -// already instantiated when specialized, above. Not only is it a no-op, but -// currently it also crashes Clang (see http://crbug.com/107412). +extern template class BASE_EXPORT BasicStringPiece<std::string>; extern template class BASE_EXPORT BasicStringPiece<string16>; #endif +// StingPiece operators -------------------------------------------------------- + BASE_EXPORT bool operator==(const StringPiece& x, const StringPiece& y); inline bool operator!=(const StringPiece& x, const StringPiece& y) { @@ -372,6 +382,8 @@ inline bool operator>=(const StringPiece& x, const StringPiece& y) { return !(x < y); } +// StringPiece16 operators ----------------------------------------------------- + inline bool operator==(const StringPiece16& x, const StringPiece16& y) { if (x.size() != y.size()) return false; @@ -406,6 +418,8 @@ BASE_EXPORT std::ostream& operator<<(std::ostream& o, } // namespace base +// Hashing --------------------------------------------------------------------- + // We provide appropriate hash functions so StringPiece and StringPiece16 can // be used as keys in hash sets and maps. diff --git a/chromium/base/strings/string_piece_unittest.cc b/chromium/base/strings/string_piece_unittest.cc index 84ed9efca8b..7f50cfbf679 100644 --- a/chromium/base/strings/string_piece_unittest.cc +++ b/chromium/base/strings/string_piece_unittest.cc @@ -192,20 +192,27 @@ TYPED_TEST(CommonStringPieceTest, CheckSTL) { ASSERT_GE(a.capacity(), a.size()); } -// STL stuff only supported by the std::string version -TEST(StringPieceTest, CheckSTL) { - StringPiece a("abcdefghijklmnopqrstuvwxyz"); - StringPiece b("abc"); - StringPiece c("xyz"); - StringPiece d("foobar"); +TYPED_TEST(CommonStringPieceTest, CheckFind) { + typedef BasicStringPiece<TypeParam> Piece; + + TypeParam alphabet(TestFixture::as_string("abcdefghijklmnopqrstuvwxyz")); + TypeParam abc(TestFixture::as_string("abc")); + TypeParam xyz(TestFixture::as_string("xyz")); + TypeParam foobar(TestFixture::as_string("foobar")); + + BasicStringPiece<TypeParam> a(alphabet); + BasicStringPiece<TypeParam> b(abc); + BasicStringPiece<TypeParam> c(xyz); + BasicStringPiece<TypeParam> d(foobar); + d.clear(); - StringPiece e; - std::string temp("123"); - temp += '\0'; - temp += "456"; - StringPiece f(temp); + Piece e; + TypeParam temp(TestFixture::as_string("123")); + temp.push_back('\0'); + temp += TestFixture::as_string("456"); + Piece f(temp); - char buf[4] = { '%', '%', '%', '%' }; + typename TypeParam::value_type buf[4] = { '%', '%', '%', '%' }; ASSERT_EQ(a.copy(buf, 4), 4U); ASSERT_EQ(buf[0], a[0]); ASSERT_EQ(buf[1], a[1]); @@ -222,28 +229,29 @@ TEST(StringPieceTest, CheckSTL) { ASSERT_EQ(buf[2], c[2]); ASSERT_EQ(buf[3], a[3]); - ASSERT_EQ(StringPiece::npos, std::string::npos); + ASSERT_EQ(Piece::npos, TypeParam::npos); ASSERT_EQ(a.find(b), 0U); - ASSERT_EQ(a.find(b, 1), StringPiece::npos); + ASSERT_EQ(a.find(b, 1), Piece::npos); ASSERT_EQ(a.find(c), 23U); ASSERT_EQ(a.find(c, 9), 23U); - ASSERT_EQ(a.find(c, StringPiece::npos), StringPiece::npos); - ASSERT_EQ(b.find(c), StringPiece::npos); - ASSERT_EQ(b.find(c, StringPiece::npos), StringPiece::npos); + ASSERT_EQ(a.find(c, Piece::npos), Piece::npos); + ASSERT_EQ(b.find(c), Piece::npos); + ASSERT_EQ(b.find(c, Piece::npos), Piece::npos); ASSERT_EQ(a.find(d), 0U); ASSERT_EQ(a.find(e), 0U); ASSERT_EQ(a.find(d, 12), 12U); ASSERT_EQ(a.find(e, 17), 17U); - StringPiece g("xx not found bb"); - ASSERT_EQ(a.find(g), StringPiece::npos); + TypeParam not_found(TestFixture::as_string("xx not found bb")); + Piece g(not_found); + ASSERT_EQ(a.find(g), Piece::npos); // empty string nonsense - ASSERT_EQ(d.find(b), StringPiece::npos); - ASSERT_EQ(e.find(b), StringPiece::npos); - ASSERT_EQ(d.find(b, 4), StringPiece::npos); - ASSERT_EQ(e.find(b, 7), StringPiece::npos); + ASSERT_EQ(d.find(b), Piece::npos); + ASSERT_EQ(e.find(b), Piece::npos); + ASSERT_EQ(d.find(b, 4), Piece::npos); + ASSERT_EQ(e.find(b, 7), Piece::npos); - size_t empty_search_pos = std::string().find(std::string()); + size_t empty_search_pos = TypeParam().find(TypeParam()); ASSERT_EQ(d.find(d), empty_search_pos); ASSERT_EQ(d.find(e), empty_search_pos); ASSERT_EQ(e.find(d), empty_search_pos); @@ -256,42 +264,42 @@ TEST(StringPieceTest, CheckSTL) { ASSERT_EQ(a.find('a'), 0U); ASSERT_EQ(a.find('c'), 2U); ASSERT_EQ(a.find('z'), 25U); - ASSERT_EQ(a.find('$'), StringPiece::npos); - ASSERT_EQ(a.find('\0'), StringPiece::npos); + ASSERT_EQ(a.find('$'), Piece::npos); + ASSERT_EQ(a.find('\0'), Piece::npos); ASSERT_EQ(f.find('\0'), 3U); ASSERT_EQ(f.find('3'), 2U); ASSERT_EQ(f.find('5'), 5U); ASSERT_EQ(g.find('o'), 4U); ASSERT_EQ(g.find('o', 4), 4U); ASSERT_EQ(g.find('o', 5), 8U); - ASSERT_EQ(a.find('b', 5), StringPiece::npos); + ASSERT_EQ(a.find('b', 5), Piece::npos); // empty string nonsense - ASSERT_EQ(d.find('\0'), StringPiece::npos); - ASSERT_EQ(e.find('\0'), StringPiece::npos); - ASSERT_EQ(d.find('\0', 4), StringPiece::npos); - ASSERT_EQ(e.find('\0', 7), StringPiece::npos); - ASSERT_EQ(d.find('x'), StringPiece::npos); - ASSERT_EQ(e.find('x'), StringPiece::npos); - ASSERT_EQ(d.find('x', 4), StringPiece::npos); - ASSERT_EQ(e.find('x', 7), StringPiece::npos); + ASSERT_EQ(d.find('\0'), Piece::npos); + ASSERT_EQ(e.find('\0'), Piece::npos); + ASSERT_EQ(d.find('\0', 4), Piece::npos); + ASSERT_EQ(e.find('\0', 7), Piece::npos); + ASSERT_EQ(d.find('x'), Piece::npos); + ASSERT_EQ(e.find('x'), Piece::npos); + ASSERT_EQ(d.find('x', 4), Piece::npos); + ASSERT_EQ(e.find('x', 7), Piece::npos); ASSERT_EQ(a.rfind(b), 0U); ASSERT_EQ(a.rfind(b, 1), 0U); ASSERT_EQ(a.rfind(c), 23U); - ASSERT_EQ(a.rfind(c, 22U), StringPiece::npos); - ASSERT_EQ(a.rfind(c, 1U), StringPiece::npos); - ASSERT_EQ(a.rfind(c, 0U), StringPiece::npos); - ASSERT_EQ(b.rfind(c), StringPiece::npos); - ASSERT_EQ(b.rfind(c, 0U), StringPiece::npos); - ASSERT_EQ(a.rfind(d), (size_t) a.as_string().rfind(std::string())); - ASSERT_EQ(a.rfind(e), a.as_string().rfind(std::string())); + ASSERT_EQ(a.rfind(c, 22U), Piece::npos); + ASSERT_EQ(a.rfind(c, 1U), Piece::npos); + ASSERT_EQ(a.rfind(c, 0U), Piece::npos); + ASSERT_EQ(b.rfind(c), Piece::npos); + ASSERT_EQ(b.rfind(c, 0U), Piece::npos); + ASSERT_EQ(a.rfind(d), static_cast<size_t>(a.as_string().rfind(TypeParam()))); + ASSERT_EQ(a.rfind(e), a.as_string().rfind(TypeParam())); ASSERT_EQ(a.rfind(d, 12), 12U); ASSERT_EQ(a.rfind(e, 17), 17U); - ASSERT_EQ(a.rfind(g), StringPiece::npos); - ASSERT_EQ(d.rfind(b), StringPiece::npos); - ASSERT_EQ(e.rfind(b), StringPiece::npos); - ASSERT_EQ(d.rfind(b, 4), StringPiece::npos); - ASSERT_EQ(e.rfind(b, 7), StringPiece::npos); + ASSERT_EQ(a.rfind(g), Piece::npos); + ASSERT_EQ(d.rfind(b), Piece::npos); + ASSERT_EQ(e.rfind(b), Piece::npos); + ASSERT_EQ(d.rfind(b, 4), Piece::npos); + ASSERT_EQ(e.rfind(b, 7), Piece::npos); // empty string nonsense ASSERT_EQ(d.rfind(d, 4), std::string().rfind(std::string())); ASSERT_EQ(e.rfind(d, 7), std::string().rfind(std::string())); @@ -303,80 +311,82 @@ TEST(StringPieceTest, CheckSTL) { ASSERT_EQ(e.rfind(e), std::string().rfind(std::string())); ASSERT_EQ(g.rfind('o'), 8U); - ASSERT_EQ(g.rfind('q'), StringPiece::npos); + ASSERT_EQ(g.rfind('q'), Piece::npos); ASSERT_EQ(g.rfind('o', 8), 8U); ASSERT_EQ(g.rfind('o', 7), 4U); - ASSERT_EQ(g.rfind('o', 3), StringPiece::npos); + ASSERT_EQ(g.rfind('o', 3), Piece::npos); ASSERT_EQ(f.rfind('\0'), 3U); ASSERT_EQ(f.rfind('\0', 12), 3U); ASSERT_EQ(f.rfind('3'), 2U); ASSERT_EQ(f.rfind('5'), 5U); // empty string nonsense - ASSERT_EQ(d.rfind('o'), StringPiece::npos); - ASSERT_EQ(e.rfind('o'), StringPiece::npos); - ASSERT_EQ(d.rfind('o', 4), StringPiece::npos); - ASSERT_EQ(e.rfind('o', 7), StringPiece::npos); - - ASSERT_EQ( - StringPiece("one,two:three;four").find_first_of(StringPiece(",:"), 1), - 3U); + ASSERT_EQ(d.rfind('o'), Piece::npos); + ASSERT_EQ(e.rfind('o'), Piece::npos); + ASSERT_EQ(d.rfind('o', 4), Piece::npos); + ASSERT_EQ(e.rfind('o', 7), Piece::npos); + + TypeParam one_two_three_four(TestFixture::as_string("one,two:three;four")); + TypeParam comma_colon(TestFixture::as_string(",:")); + ASSERT_EQ(3U, Piece(one_two_three_four).find_first_of(comma_colon)); ASSERT_EQ(a.find_first_of(b), 0U); ASSERT_EQ(a.find_first_of(b, 0), 0U); ASSERT_EQ(a.find_first_of(b, 1), 1U); ASSERT_EQ(a.find_first_of(b, 2), 2U); - ASSERT_EQ(a.find_first_of(b, 3), StringPiece::npos); + ASSERT_EQ(a.find_first_of(b, 3), Piece::npos); ASSERT_EQ(a.find_first_of(c), 23U); ASSERT_EQ(a.find_first_of(c, 23), 23U); ASSERT_EQ(a.find_first_of(c, 24), 24U); ASSERT_EQ(a.find_first_of(c, 25), 25U); - ASSERT_EQ(a.find_first_of(c, 26), StringPiece::npos); + ASSERT_EQ(a.find_first_of(c, 26), Piece::npos); ASSERT_EQ(g.find_first_of(b), 13U); ASSERT_EQ(g.find_first_of(c), 0U); - ASSERT_EQ(a.find_first_of(f), StringPiece::npos); - ASSERT_EQ(f.find_first_of(a), StringPiece::npos); + ASSERT_EQ(a.find_first_of(f), Piece::npos); + ASSERT_EQ(f.find_first_of(a), Piece::npos); // empty string nonsense - ASSERT_EQ(a.find_first_of(d), StringPiece::npos); - ASSERT_EQ(a.find_first_of(e), StringPiece::npos); - ASSERT_EQ(d.find_first_of(b), StringPiece::npos); - ASSERT_EQ(e.find_first_of(b), StringPiece::npos); - ASSERT_EQ(d.find_first_of(d), StringPiece::npos); - ASSERT_EQ(e.find_first_of(d), StringPiece::npos); - ASSERT_EQ(d.find_first_of(e), StringPiece::npos); - ASSERT_EQ(e.find_first_of(e), StringPiece::npos); + ASSERT_EQ(a.find_first_of(d), Piece::npos); + ASSERT_EQ(a.find_first_of(e), Piece::npos); + ASSERT_EQ(d.find_first_of(b), Piece::npos); + ASSERT_EQ(e.find_first_of(b), Piece::npos); + ASSERT_EQ(d.find_first_of(d), Piece::npos); + ASSERT_EQ(e.find_first_of(d), Piece::npos); + ASSERT_EQ(d.find_first_of(e), Piece::npos); + ASSERT_EQ(e.find_first_of(e), Piece::npos); ASSERT_EQ(a.find_first_not_of(b), 3U); ASSERT_EQ(a.find_first_not_of(c), 0U); - ASSERT_EQ(b.find_first_not_of(a), StringPiece::npos); - ASSERT_EQ(c.find_first_not_of(a), StringPiece::npos); + ASSERT_EQ(b.find_first_not_of(a), Piece::npos); + ASSERT_EQ(c.find_first_not_of(a), Piece::npos); ASSERT_EQ(f.find_first_not_of(a), 0U); ASSERT_EQ(a.find_first_not_of(f), 0U); ASSERT_EQ(a.find_first_not_of(d), 0U); ASSERT_EQ(a.find_first_not_of(e), 0U); // empty string nonsense - ASSERT_EQ(d.find_first_not_of(a), StringPiece::npos); - ASSERT_EQ(e.find_first_not_of(a), StringPiece::npos); - ASSERT_EQ(d.find_first_not_of(d), StringPiece::npos); - ASSERT_EQ(e.find_first_not_of(d), StringPiece::npos); - ASSERT_EQ(d.find_first_not_of(e), StringPiece::npos); - ASSERT_EQ(e.find_first_not_of(e), StringPiece::npos); - - StringPiece h("===="); - ASSERT_EQ(h.find_first_not_of('='), StringPiece::npos); - ASSERT_EQ(h.find_first_not_of('=', 3), StringPiece::npos); + ASSERT_EQ(d.find_first_not_of(a), Piece::npos); + ASSERT_EQ(e.find_first_not_of(a), Piece::npos); + ASSERT_EQ(d.find_first_not_of(d), Piece::npos); + ASSERT_EQ(e.find_first_not_of(d), Piece::npos); + ASSERT_EQ(d.find_first_not_of(e), Piece::npos); + ASSERT_EQ(e.find_first_not_of(e), Piece::npos); + + TypeParam equals(TestFixture::as_string("====")); + Piece h(equals); + ASSERT_EQ(h.find_first_not_of('='), Piece::npos); + ASSERT_EQ(h.find_first_not_of('=', 3), Piece::npos); ASSERT_EQ(h.find_first_not_of('\0'), 0U); ASSERT_EQ(g.find_first_not_of('x'), 2U); ASSERT_EQ(f.find_first_not_of('\0'), 0U); ASSERT_EQ(f.find_first_not_of('\0', 3), 4U); ASSERT_EQ(f.find_first_not_of('\0', 2), 2U); // empty string nonsense - ASSERT_EQ(d.find_first_not_of('x'), StringPiece::npos); - ASSERT_EQ(e.find_first_not_of('x'), StringPiece::npos); - ASSERT_EQ(d.find_first_not_of('\0'), StringPiece::npos); - ASSERT_EQ(e.find_first_not_of('\0'), StringPiece::npos); - - // StringPiece g("xx not found bb"); - StringPiece i("56"); - ASSERT_EQ(h.find_last_of(a), StringPiece::npos); + ASSERT_EQ(d.find_first_not_of('x'), Piece::npos); + ASSERT_EQ(e.find_first_not_of('x'), Piece::npos); + ASSERT_EQ(d.find_first_not_of('\0'), Piece::npos); + ASSERT_EQ(e.find_first_not_of('\0'), Piece::npos); + + // Piece g("xx not found bb"); + TypeParam fifty_six(TestFixture::as_string("56")); + Piece i(fifty_six); + ASSERT_EQ(h.find_last_of(a), Piece::npos); ASSERT_EQ(g.find_last_of(a), g.size()-1); ASSERT_EQ(a.find_last_of(b), 2U); ASSERT_EQ(a.find_last_of(c), a.size()-1); @@ -386,74 +396,74 @@ TEST(StringPieceTest, CheckSTL) { ASSERT_EQ(a.find_last_of('z'), 25U); ASSERT_EQ(a.find_last_of('a', 5), 0U); ASSERT_EQ(a.find_last_of('b', 5), 1U); - ASSERT_EQ(a.find_last_of('b', 0), StringPiece::npos); + ASSERT_EQ(a.find_last_of('b', 0), Piece::npos); ASSERT_EQ(a.find_last_of('z', 25), 25U); - ASSERT_EQ(a.find_last_of('z', 24), StringPiece::npos); + ASSERT_EQ(a.find_last_of('z', 24), Piece::npos); ASSERT_EQ(f.find_last_of(i, 5), 5U); ASSERT_EQ(f.find_last_of(i, 6), 6U); - ASSERT_EQ(f.find_last_of(a, 4), StringPiece::npos); + ASSERT_EQ(f.find_last_of(a, 4), Piece::npos); // empty string nonsense - ASSERT_EQ(f.find_last_of(d), StringPiece::npos); - ASSERT_EQ(f.find_last_of(e), StringPiece::npos); - ASSERT_EQ(f.find_last_of(d, 4), StringPiece::npos); - ASSERT_EQ(f.find_last_of(e, 4), StringPiece::npos); - ASSERT_EQ(d.find_last_of(d), StringPiece::npos); - ASSERT_EQ(d.find_last_of(e), StringPiece::npos); - ASSERT_EQ(e.find_last_of(d), StringPiece::npos); - ASSERT_EQ(e.find_last_of(e), StringPiece::npos); - ASSERT_EQ(d.find_last_of(f), StringPiece::npos); - ASSERT_EQ(e.find_last_of(f), StringPiece::npos); - ASSERT_EQ(d.find_last_of(d, 4), StringPiece::npos); - ASSERT_EQ(d.find_last_of(e, 4), StringPiece::npos); - ASSERT_EQ(e.find_last_of(d, 4), StringPiece::npos); - ASSERT_EQ(e.find_last_of(e, 4), StringPiece::npos); - ASSERT_EQ(d.find_last_of(f, 4), StringPiece::npos); - ASSERT_EQ(e.find_last_of(f, 4), StringPiece::npos); + ASSERT_EQ(f.find_last_of(d), Piece::npos); + ASSERT_EQ(f.find_last_of(e), Piece::npos); + ASSERT_EQ(f.find_last_of(d, 4), Piece::npos); + ASSERT_EQ(f.find_last_of(e, 4), Piece::npos); + ASSERT_EQ(d.find_last_of(d), Piece::npos); + ASSERT_EQ(d.find_last_of(e), Piece::npos); + ASSERT_EQ(e.find_last_of(d), Piece::npos); + ASSERT_EQ(e.find_last_of(e), Piece::npos); + ASSERT_EQ(d.find_last_of(f), Piece::npos); + ASSERT_EQ(e.find_last_of(f), Piece::npos); + ASSERT_EQ(d.find_last_of(d, 4), Piece::npos); + ASSERT_EQ(d.find_last_of(e, 4), Piece::npos); + ASSERT_EQ(e.find_last_of(d, 4), Piece::npos); + ASSERT_EQ(e.find_last_of(e, 4), Piece::npos); + ASSERT_EQ(d.find_last_of(f, 4), Piece::npos); + ASSERT_EQ(e.find_last_of(f, 4), Piece::npos); ASSERT_EQ(a.find_last_not_of(b), a.size()-1); ASSERT_EQ(a.find_last_not_of(c), 22U); - ASSERT_EQ(b.find_last_not_of(a), StringPiece::npos); - ASSERT_EQ(b.find_last_not_of(b), StringPiece::npos); + ASSERT_EQ(b.find_last_not_of(a), Piece::npos); + ASSERT_EQ(b.find_last_not_of(b), Piece::npos); ASSERT_EQ(f.find_last_not_of(i), 4U); ASSERT_EQ(a.find_last_not_of(c, 24), 22U); ASSERT_EQ(a.find_last_not_of(b, 3), 3U); - ASSERT_EQ(a.find_last_not_of(b, 2), StringPiece::npos); + ASSERT_EQ(a.find_last_not_of(b, 2), Piece::npos); // empty string nonsense ASSERT_EQ(f.find_last_not_of(d), f.size()-1); ASSERT_EQ(f.find_last_not_of(e), f.size()-1); ASSERT_EQ(f.find_last_not_of(d, 4), 4U); ASSERT_EQ(f.find_last_not_of(e, 4), 4U); - ASSERT_EQ(d.find_last_not_of(d), StringPiece::npos); - ASSERT_EQ(d.find_last_not_of(e), StringPiece::npos); - ASSERT_EQ(e.find_last_not_of(d), StringPiece::npos); - ASSERT_EQ(e.find_last_not_of(e), StringPiece::npos); - ASSERT_EQ(d.find_last_not_of(f), StringPiece::npos); - ASSERT_EQ(e.find_last_not_of(f), StringPiece::npos); - ASSERT_EQ(d.find_last_not_of(d, 4), StringPiece::npos); - ASSERT_EQ(d.find_last_not_of(e, 4), StringPiece::npos); - ASSERT_EQ(e.find_last_not_of(d, 4), StringPiece::npos); - ASSERT_EQ(e.find_last_not_of(e, 4), StringPiece::npos); - ASSERT_EQ(d.find_last_not_of(f, 4), StringPiece::npos); - ASSERT_EQ(e.find_last_not_of(f, 4), StringPiece::npos); + ASSERT_EQ(d.find_last_not_of(d), Piece::npos); + ASSERT_EQ(d.find_last_not_of(e), Piece::npos); + ASSERT_EQ(e.find_last_not_of(d), Piece::npos); + ASSERT_EQ(e.find_last_not_of(e), Piece::npos); + ASSERT_EQ(d.find_last_not_of(f), Piece::npos); + ASSERT_EQ(e.find_last_not_of(f), Piece::npos); + ASSERT_EQ(d.find_last_not_of(d, 4), Piece::npos); + ASSERT_EQ(d.find_last_not_of(e, 4), Piece::npos); + ASSERT_EQ(e.find_last_not_of(d, 4), Piece::npos); + ASSERT_EQ(e.find_last_not_of(e, 4), Piece::npos); + ASSERT_EQ(d.find_last_not_of(f, 4), Piece::npos); + ASSERT_EQ(e.find_last_not_of(f, 4), Piece::npos); ASSERT_EQ(h.find_last_not_of('x'), h.size() - 1); - ASSERT_EQ(h.find_last_not_of('='), StringPiece::npos); + ASSERT_EQ(h.find_last_not_of('='), Piece::npos); ASSERT_EQ(b.find_last_not_of('c'), 1U); ASSERT_EQ(h.find_last_not_of('x', 2), 2U); - ASSERT_EQ(h.find_last_not_of('=', 2), StringPiece::npos); + ASSERT_EQ(h.find_last_not_of('=', 2), Piece::npos); ASSERT_EQ(b.find_last_not_of('b', 1), 0U); // empty string nonsense - ASSERT_EQ(d.find_last_not_of('x'), StringPiece::npos); - ASSERT_EQ(e.find_last_not_of('x'), StringPiece::npos); - ASSERT_EQ(d.find_last_not_of('\0'), StringPiece::npos); - ASSERT_EQ(e.find_last_not_of('\0'), StringPiece::npos); + ASSERT_EQ(d.find_last_not_of('x'), Piece::npos); + ASSERT_EQ(e.find_last_not_of('x'), Piece::npos); + ASSERT_EQ(d.find_last_not_of('\0'), Piece::npos); + ASSERT_EQ(e.find_last_not_of('\0'), Piece::npos); ASSERT_EQ(a.substr(0, 3), b); ASSERT_EQ(a.substr(23), c); ASSERT_EQ(a.substr(23, 3), c); ASSERT_EQ(a.substr(23, 99), c); ASSERT_EQ(a.substr(0), a); - ASSERT_EQ(a.substr(3, 2), "de"); + ASSERT_EQ(a.substr(3, 2), TestFixture::as_string("de")); // empty string nonsense ASSERT_EQ(a.substr(99, 2), e); ASSERT_EQ(d.substr(99), e); @@ -561,11 +571,11 @@ TEST(StringPieceTest, CheckCustom) { ASSERT_TRUE(!e.ends_with(a)); StringPiece c; - c.set(static_cast<const void*>("foobar"), 6); + c.set("foobar", 6); ASSERT_EQ(c, a); - c.set(static_cast<const void*>("foobar"), 0); + c.set("foobar", 0); ASSERT_EQ(c, e); - c.set(static_cast<const void*>("foobar"), 7); + c.set("foobar", 7); ASSERT_NE(c, a); } @@ -664,9 +674,11 @@ TYPED_TEST(CommonStringPieceTest, CheckConstructors) { ASSERT_TRUE(str == BasicStringPiece<TypeParam>(str.c_str())); ASSERT_TRUE(TestFixture::as_string("hello") == BasicStringPiece<TypeParam>(str.c_str(), 5)); - ASSERT_TRUE(empty == BasicStringPiece<TypeParam>(str.c_str(), 0U)); + ASSERT_TRUE(empty == BasicStringPiece<TypeParam>(str.c_str(), + static_cast<typename BasicStringPiece<TypeParam>::size_type>(0))); ASSERT_TRUE(empty == BasicStringPiece<TypeParam>(NULL)); - ASSERT_TRUE(empty == BasicStringPiece<TypeParam>(NULL, 0U)); + ASSERT_TRUE(empty == BasicStringPiece<TypeParam>(NULL, + static_cast<typename BasicStringPiece<TypeParam>::size_type>(0))); ASSERT_TRUE(empty == BasicStringPiece<TypeParam>()); ASSERT_TRUE(str == BasicStringPiece<TypeParam>(str.begin(), str.end())); ASSERT_TRUE(empty == BasicStringPiece<TypeParam>(str.begin(), str.begin())); diff --git a/chromium/base/strings/string_split.cc b/chromium/base/strings/string_split.cc index 210789cd22d..7ef4760c58f 100644 --- a/chromium/base/strings/string_split.cc +++ b/chromium/base/strings/string_split.cc @@ -11,11 +11,13 @@ namespace base { -template<typename STR> -static void SplitStringT(const STR& str, - const typename STR::value_type s, - bool trim_whitespace, - std::vector<STR>* r) { +namespace { + +template <typename STR> +void SplitStringT(const STR& str, + const typename STR::value_type s, + bool trim_whitespace, + std::vector<STR>* r) { r->clear(); size_t last = 0; size_t c = str.size(); @@ -33,87 +35,34 @@ static void SplitStringT(const STR& str, } } -void SplitString(const string16& str, - char16 c, - std::vector<string16>* r) { - DCHECK(CBU16_IS_SINGLE(c)); - SplitStringT(str, c, true, r); -} - -void SplitString(const std::string& str, - char c, - std::vector<std::string>* r) { -#if CHAR_MIN < 0 - DCHECK(c >= 0); -#endif - DCHECK(c < 0x7F); - SplitStringT(str, c, true, r); -} - -bool SplitStringIntoKeyValues( - const std::string& line, - char key_value_delimiter, - std::string* key, std::vector<std::string>* values) { +bool SplitStringIntoKeyValue(const std::string& line, + char key_value_delimiter, + std::string* key, + std::string* value) { key->clear(); - values->clear(); + value->clear(); - // Find the key string. + // Find the delimiter. size_t end_key_pos = line.find_first_of(key_value_delimiter); if (end_key_pos == std::string::npos) { - DVLOG(1) << "cannot parse key from line: " << line; - return false; // no key + DVLOG(1) << "cannot find delimiter in: " << line; + return false; // no delimiter } key->assign(line, 0, end_key_pos); - // Find the values string. + // Find the value string. std::string remains(line, end_key_pos, line.size() - end_key_pos); - size_t begin_values_pos = remains.find_first_not_of(key_value_delimiter); - if (begin_values_pos == std::string::npos) { + size_t begin_value_pos = remains.find_first_not_of(key_value_delimiter); + if (begin_value_pos == std::string::npos) { DVLOG(1) << "cannot parse value from line: " << line; return false; // no value } - std::string values_string(remains, begin_values_pos, - remains.size() - begin_values_pos); - - // Construct the values vector. - values->push_back(values_string); + value->assign(remains, begin_value_pos, remains.size() - begin_value_pos); return true; } -bool SplitStringIntoKeyValuePairs(const std::string& line, - char key_value_delimiter, - char key_value_pair_delimiter, - StringPairs* key_value_pairs) { - key_value_pairs->clear(); - - std::vector<std::string> pairs; - SplitString(line, key_value_pair_delimiter, &pairs); - - bool success = true; - for (size_t i = 0; i < pairs.size(); ++i) { - // Empty pair. SplitStringIntoKeyValues is more strict about an empty pair - // line, so continue with the next pair. - if (pairs[i].empty()) - continue; - - std::string key; - std::vector<std::string> value; - if (!SplitStringIntoKeyValues(pairs[i], - key_value_delimiter, - &key, &value)) { - // Don't return here, to allow for keys without associated - // values; just record that our split failed. - success = false; - } - DCHECK_LE(value.size(), 1U); - key_value_pairs->push_back( - make_pair(key, value.empty() ? std::string() : value[0])); - } - return success; -} - template <typename STR> -static void SplitStringUsingSubstrT(const STR& str, +void SplitStringUsingSubstrT(const STR& str, const STR& s, std::vector<STR>* r) { r->clear(); @@ -135,36 +84,6 @@ static void SplitStringUsingSubstrT(const STR& str, } } -void SplitStringUsingSubstr(const string16& str, - const string16& s, - std::vector<string16>* r) { - SplitStringUsingSubstrT(str, s, r); -} - -void SplitStringUsingSubstr(const std::string& str, - const std::string& s, - std::vector<std::string>* r) { - SplitStringUsingSubstrT(str, s, r); -} - -void SplitStringDontTrim(const string16& str, - char16 c, - std::vector<string16>* r) { - DCHECK(CBU16_IS_SINGLE(c)); - SplitStringT(str, c, false, r); -} - -void SplitStringDontTrim(const std::string& str, - char c, - std::vector<std::string>* r) { - DCHECK(IsStringUTF8(str)); -#if CHAR_MIN < 0 - DCHECK(c >= 0); -#endif - DCHECK(c < 0x7F); - SplitStringT(str, c, false, r); -} - template<typename STR> void SplitStringAlongWhitespaceT(const STR& str, std::vector<STR>* result) { result->clear(); @@ -206,6 +125,82 @@ void SplitStringAlongWhitespaceT(const STR& str, std::vector<STR>* result) { } } +} // namespace + +void SplitString(const string16& str, + char16 c, + std::vector<string16>* r) { + DCHECK(CBU16_IS_SINGLE(c)); + SplitStringT(str, c, true, r); +} + +void SplitString(const std::string& str, + char c, + std::vector<std::string>* r) { +#if CHAR_MIN < 0 + DCHECK(c >= 0); +#endif + DCHECK(c < 0x7F); + SplitStringT(str, c, true, r); +} + +bool SplitStringIntoKeyValuePairs(const std::string& line, + char key_value_delimiter, + char key_value_pair_delimiter, + StringPairs* key_value_pairs) { + key_value_pairs->clear(); + + std::vector<std::string> pairs; + SplitString(line, key_value_pair_delimiter, &pairs); + + bool success = true; + for (size_t i = 0; i < pairs.size(); ++i) { + // Don't add empty pairs into the result. + if (pairs[i].empty()) + continue; + + std::string key; + std::string value; + if (!SplitStringIntoKeyValue(pairs[i], key_value_delimiter, &key, &value)) { + // Don't return here, to allow for pairs without associated + // value or key; just record that the split failed. + success = false; + } + key_value_pairs->push_back(make_pair(key, value)); + } + return success; +} + +void SplitStringUsingSubstr(const string16& str, + const string16& s, + std::vector<string16>* r) { + SplitStringUsingSubstrT(str, s, r); +} + +void SplitStringUsingSubstr(const std::string& str, + const std::string& s, + std::vector<std::string>* r) { + SplitStringUsingSubstrT(str, s, r); +} + +void SplitStringDontTrim(const string16& str, + char16 c, + std::vector<string16>* r) { + DCHECK(CBU16_IS_SINGLE(c)); + SplitStringT(str, c, false, r); +} + +void SplitStringDontTrim(const std::string& str, + char c, + std::vector<std::string>* r) { + DCHECK(IsStringUTF8(str)); +#if CHAR_MIN < 0 + DCHECK(c >= 0); +#endif + DCHECK(c < 0x7F); + SplitStringT(str, c, false, r); +} + void SplitStringAlongWhitespace(const string16& str, std::vector<string16>* result) { SplitStringAlongWhitespaceT(str, result); diff --git a/chromium/base/strings/string_split.h b/chromium/base/strings/string_split.h index 7c27e4899da..55d8cb377ed 100644 --- a/chromium/base/strings/string_split.h +++ b/chromium/base/strings/string_split.h @@ -23,6 +23,7 @@ namespace base { BASE_EXPORT void SplitString(const string16& str, char16 c, std::vector<string16>* r); + // |str| should not be in a multi-byte encoding like Shift-JIS or GBK in which // the trailing byte of a multi-byte character can be in the ASCII range. // UTF-8, and other single/multi-byte ASCII-compatible encodings are OK. @@ -31,18 +32,16 @@ BASE_EXPORT void SplitString(const std::string& str, char c, std::vector<std::string>* r); -BASE_EXPORT bool SplitStringIntoKeyValues(const std::string& line, - char key_value_delimiter, - std::string* key, - std::vector<std::string>* values); - -typedef std::vector<std::pair<std::string, std::string> > StringPairs;; +typedef std::vector<std::pair<std::string, std::string> > StringPairs; -BASE_EXPORT bool SplitStringIntoKeyValuePairs( - const std::string& line, - char key_value_delimiter, - char key_value_pair_delimiter, - StringPairs* key_value_pairs); +// Splits |line| into key value pairs according to the given delimiters and +// removes whitespace leading each key and trailing each value. Returns true +// only if each pair has a non-empty key and value. |key_value_pairs| will +// include ("","") pairs for entries without |key_value_delimiter|. +BASE_EXPORT bool SplitStringIntoKeyValuePairs(const std::string& line, + char key_value_delimiter, + char key_value_pair_delimiter, + StringPairs* key_value_pairs); // The same as SplitString, but use a substring delimiter instead of a char. BASE_EXPORT void SplitStringUsingSubstr(const string16& str, diff --git a/chromium/base/strings/string_split_unittest.cc b/chromium/base/strings/string_split_unittest.cc index eb69b68fcbd..9c932655e53 100644 --- a/chromium/base/strings/string_split_unittest.cc +++ b/chromium/base/strings/string_split_unittest.cc @@ -29,122 +29,131 @@ void SplitString(const std::wstring& str, } // anonymous namespace -class SplitStringIntoKeyValuesTest : public testing::Test { +class SplitStringIntoKeyValuePairsTest : public testing::Test { protected: - std::string key; - std::vector<std::string> values; + std::vector<std::pair<std::string, std::string> > kv_pairs; }; -TEST_F(SplitStringIntoKeyValuesTest, EmptyInputMultipleValues) { - EXPECT_FALSE(SplitStringIntoKeyValues(std::string(), // Empty input - '\t', // Key separators - &key, - &values)); - EXPECT_TRUE(key.empty()); - EXPECT_TRUE(values.empty()); -} - -TEST_F(SplitStringIntoKeyValuesTest, EmptyValueInputMultipleValues) { - EXPECT_FALSE(SplitStringIntoKeyValues("key_with_no_value\t", - '\t', // Key separators - &key, &values)); - EXPECT_EQ("key_with_no_value", key); - EXPECT_TRUE(values.empty()); -} - -TEST_F(SplitStringIntoKeyValuesTest, EmptyKeyInputMultipleValues) { - EXPECT_TRUE(SplitStringIntoKeyValues("\tvalue for empty key", - '\t', // Key separators - &key, &values)); - EXPECT_TRUE(key.empty()); - ASSERT_EQ(1U, values.size()); +TEST_F(SplitStringIntoKeyValuePairsTest, EmptyString) { + EXPECT_TRUE(SplitStringIntoKeyValuePairs(std::string(), + ':', // Key-value delimiter + ',', // Key-value pair delimiter + &kv_pairs)); + EXPECT_TRUE(kv_pairs.empty()); } -TEST_F(SplitStringIntoKeyValuesTest, KeyWithMultipleValues) { - EXPECT_TRUE(SplitStringIntoKeyValues("key1\tvalue1, value2 value3", - '\t', // Key separators - &key, &values)); - EXPECT_EQ("key1", key); - ASSERT_EQ(1U, values.size()); - EXPECT_EQ("value1, value2 value3", values[0]); +TEST_F(SplitStringIntoKeyValuePairsTest, MissingKeyValueDelimiter) { + EXPECT_FALSE(SplitStringIntoKeyValuePairs("key1,key2:value2", + ':', // Key-value delimiter + ',', // Key-value pair delimiter + &kv_pairs)); + ASSERT_EQ(2U, kv_pairs.size()); + EXPECT_TRUE(kv_pairs[0].first.empty()); + EXPECT_TRUE(kv_pairs[0].second.empty()); + EXPECT_EQ("key2", kv_pairs[1].first); + EXPECT_EQ("value2", kv_pairs[1].second); } -TEST_F(SplitStringIntoKeyValuesTest, EmptyInputSingleValue) { - EXPECT_FALSE(SplitStringIntoKeyValues(std::string(), // Empty input - '\t', // Key separators - &key, - &values)); - EXPECT_TRUE(key.empty()); - EXPECT_TRUE(values.empty()); +TEST_F(SplitStringIntoKeyValuePairsTest, EmptyKeyWithKeyValueDelimiter) { + EXPECT_TRUE(SplitStringIntoKeyValuePairs(":value1,key2:value2", + ':', // Key-value delimiter + ',', // Key-value pair delimiter + &kv_pairs)); + ASSERT_EQ(2U, kv_pairs.size()); + EXPECT_TRUE(kv_pairs[0].first.empty()); + EXPECT_EQ("value1", kv_pairs[0].second); + EXPECT_EQ("key2", kv_pairs[1].first); + EXPECT_EQ("value2", kv_pairs[1].second); } -TEST_F(SplitStringIntoKeyValuesTest, EmptyValueInputSingleValue) { - EXPECT_FALSE(SplitStringIntoKeyValues("key_with_no_value\t", - '\t', // Key separators - &key, &values)); - EXPECT_EQ("key_with_no_value", key); - EXPECT_TRUE(values.empty()); +TEST_F(SplitStringIntoKeyValuePairsTest, TrailingAndLeadingPairDelimiter) { + EXPECT_TRUE(SplitStringIntoKeyValuePairs(",key1:value1,key2:value2,", + ':', // Key-value delimiter + ',', // Key-value pair delimiter + &kv_pairs)); + ASSERT_EQ(2U, kv_pairs.size()); + EXPECT_EQ("key1", kv_pairs[0].first); + EXPECT_EQ("value1", kv_pairs[0].second); + EXPECT_EQ("key2", kv_pairs[1].first); + EXPECT_EQ("value2", kv_pairs[1].second); } -TEST_F(SplitStringIntoKeyValuesTest, EmptyKeyInputSingleValue) { - EXPECT_TRUE(SplitStringIntoKeyValues("\tvalue for empty key", - '\t', // Key separators - &key, &values)); - EXPECT_TRUE(key.empty()); - ASSERT_EQ(1U, values.size()); - EXPECT_EQ("value for empty key", values[0]); +TEST_F(SplitStringIntoKeyValuePairsTest, EmptyPair) { + EXPECT_TRUE(SplitStringIntoKeyValuePairs("key1:value1,,key3:value3", + ':', // Key-value delimiter + ',', // Key-value pair delimiter + &kv_pairs)); + ASSERT_EQ(2U, kv_pairs.size()); + EXPECT_EQ("key1", kv_pairs[0].first); + EXPECT_EQ("value1", kv_pairs[0].second); + EXPECT_EQ("key3", kv_pairs[1].first); + EXPECT_EQ("value3", kv_pairs[1].second); } -TEST_F(SplitStringIntoKeyValuesTest, KeyWithSingleValue) { - EXPECT_TRUE(SplitStringIntoKeyValues("key1\tvalue1, value2 value3", - '\t', // Key separators - &key, &values)); - EXPECT_EQ("key1", key); - ASSERT_EQ(1U, values.size()); - EXPECT_EQ("value1, value2 value3", values[0]); +TEST_F(SplitStringIntoKeyValuePairsTest, EmptyValue) { + EXPECT_FALSE(SplitStringIntoKeyValuePairs("key1:,key2:value2", + ':', // Key-value delimiter + ',', // Key-value pair delimiter + &kv_pairs)); + ASSERT_EQ(2U, kv_pairs.size()); + EXPECT_EQ("key1", kv_pairs[0].first); + EXPECT_EQ("", kv_pairs[0].second); + EXPECT_EQ("key2", kv_pairs[1].first); + EXPECT_EQ("value2", kv_pairs[1].second); } -class SplitStringIntoKeyValuePairsTest : public testing::Test { - protected: - std::vector<std::pair<std::string, std::string> > kv_pairs; -}; - -TEST_F(SplitStringIntoKeyValuePairsTest, EmptyString) { - EXPECT_TRUE(SplitStringIntoKeyValuePairs(std::string(), - ':', // Key-value delimiters - ',', // Key-value pair delims +TEST_F(SplitStringIntoKeyValuePairsTest, UntrimmedWhitespace) { + EXPECT_TRUE(SplitStringIntoKeyValuePairs("key1 : value1", + ':', // Key-value delimiter + ',', // Key-value pair delimiter &kv_pairs)); - EXPECT_TRUE(kv_pairs.empty()); + ASSERT_EQ(1U, kv_pairs.size()); + EXPECT_EQ("key1 ", kv_pairs[0].first); + EXPECT_EQ(" value1", kv_pairs[0].second); } -TEST_F(SplitStringIntoKeyValuePairsTest, EmptySecondPair) { - EXPECT_TRUE(SplitStringIntoKeyValuePairs("key1:value1,,key3:value3", - ':', // Key-value delimiters - ',', // Key-value pair delims +TEST_F(SplitStringIntoKeyValuePairsTest, TrimmedWhitespace) { + EXPECT_TRUE(SplitStringIntoKeyValuePairs("key1:value1 , key2:value2", + ':', // Key-value delimiter + ',', // Key-value pair delimiter &kv_pairs)); ASSERT_EQ(2U, kv_pairs.size()); EXPECT_EQ("key1", kv_pairs[0].first); EXPECT_EQ("value1", kv_pairs[0].second); - EXPECT_EQ("key3", kv_pairs[1].first); - EXPECT_EQ("value3", kv_pairs[1].second); + EXPECT_EQ("key2", kv_pairs[1].first); + EXPECT_EQ("value2", kv_pairs[1].second); } -TEST_F(SplitStringIntoKeyValuePairsTest, EmptySecondValue) { - EXPECT_FALSE(SplitStringIntoKeyValuePairs("key1:value1 , key2:", - ':', // Key-value delimiters - ',', // Key-value pair delims - &kv_pairs)); +TEST_F(SplitStringIntoKeyValuePairsTest, MultipleKeyValueDelimiters) { + EXPECT_TRUE(SplitStringIntoKeyValuePairs("key1:::value1,key2:value2", + ':', // Key-value delimiter + ',', // Key-value pair delimiter + &kv_pairs)); ASSERT_EQ(2U, kv_pairs.size()); EXPECT_EQ("key1", kv_pairs[0].first); EXPECT_EQ("value1", kv_pairs[0].second); EXPECT_EQ("key2", kv_pairs[1].first); - EXPECT_EQ("", kv_pairs[1].second); + EXPECT_EQ("value2", kv_pairs[1].second); } +TEST_F(SplitStringIntoKeyValuePairsTest, OnlySplitAtGivenSeparator) { + std::string a("a ?!@#$%^&*()_+:/{}\\\t\nb"); + EXPECT_TRUE(SplitStringIntoKeyValuePairs(a + "X" + a + "Y" + a + "X" + a, + 'X', // Key-value delimiter + 'Y', // Key-value pair delimiter + &kv_pairs)); + ASSERT_EQ(2U, kv_pairs.size()); + EXPECT_EQ(a, kv_pairs[0].first); + EXPECT_EQ(a, kv_pairs[0].second); + EXPECT_EQ(a, kv_pairs[1].first); + EXPECT_EQ(a, kv_pairs[1].second); +} + + TEST_F(SplitStringIntoKeyValuePairsTest, DelimiterInValue) { - EXPECT_TRUE(SplitStringIntoKeyValuePairs("key1:va:ue1 , key2:value2", - ':', // Key-value delimiters - ',', // Key-value pair delims + EXPECT_TRUE(SplitStringIntoKeyValuePairs("key1:va:ue1,key2:value2", + ':', // Key-value delimiter + ',', // Key-value pair delimiter &kv_pairs)); ASSERT_EQ(2U, kv_pairs.size()); EXPECT_EQ("key1", kv_pairs[0].first); @@ -160,7 +169,6 @@ TEST(SplitStringUsingSubstrTest, EmptyString) { EXPECT_THAT(results, ElementsAre("")); } -// Test for SplitString TEST(StringUtilTest, SplitString) { std::vector<std::wstring> r; diff --git a/chromium/base/strings/string_util.cc b/chromium/base/strings/string_util.cc index cee124b0c1a..e64b95f7635 100644 --- a/chromium/base/strings/string_util.cc +++ b/chromium/base/strings/string_util.cc @@ -32,13 +32,12 @@ using base::string16; namespace { -// Force the singleton used by Empty[W]String[16] to be a unique type. This +// Force the singleton used by EmptyString[16] to be a unique type. This // prevents other code that might accidentally use Singleton<string> from // getting our internal one. struct EmptyStrings { EmptyStrings() {} const std::string s; - const std::wstring ws; const string16 s16; static EmptyStrings* GetInstance() { @@ -108,17 +107,13 @@ const std::string& EmptyString() { return EmptyStrings::GetInstance()->s; } -const std::wstring& EmptyWString() { - return EmptyStrings::GetInstance()->ws; -} - const string16& EmptyString16() { return EmptyStrings::GetInstance()->s16; } template<typename STR> bool ReplaceCharsT(const STR& input, - const typename STR::value_type replace_chars[], + const STR& replace_chars, const STR& replace_with, STR* output) { bool removed = false; @@ -137,41 +132,41 @@ bool ReplaceCharsT(const STR& input, } bool ReplaceChars(const string16& input, - const char16 replace_chars[], + const base::StringPiece16& replace_chars, const string16& replace_with, string16* output) { - return ReplaceCharsT(input, replace_chars, replace_with, output); + return ReplaceCharsT(input, replace_chars.as_string(), replace_with, output); } bool ReplaceChars(const std::string& input, - const char replace_chars[], + const base::StringPiece& replace_chars, const std::string& replace_with, std::string* output) { - return ReplaceCharsT(input, replace_chars, replace_with, output); + return ReplaceCharsT(input, replace_chars.as_string(), replace_with, output); } bool RemoveChars(const string16& input, - const char16 remove_chars[], + const base::StringPiece16& remove_chars, string16* output) { - return ReplaceChars(input, remove_chars, string16(), output); + return ReplaceChars(input, remove_chars.as_string(), string16(), output); } bool RemoveChars(const std::string& input, - const char remove_chars[], + const base::StringPiece& remove_chars, std::string* output) { - return ReplaceChars(input, remove_chars, std::string(), output); + return ReplaceChars(input, remove_chars.as_string(), std::string(), output); } template<typename STR> TrimPositions TrimStringT(const STR& input, - const typename STR::value_type trim_chars[], + const STR& trim_chars, TrimPositions positions, STR* output) { // Find the edges of leading/trailing whitespace as desired. - const typename STR::size_type last_char = input.length() - 1; - const typename STR::size_type first_good_char = (positions & TRIM_LEADING) ? + const size_t last_char = input.length() - 1; + const size_t first_good_char = (positions & TRIM_LEADING) ? input.find_first_not_of(trim_chars) : 0; - const typename STR::size_type last_good_char = (positions & TRIM_TRAILING) ? + const size_t last_good_char = (positions & TRIM_TRAILING) ? input.find_last_not_of(trim_chars) : last_char; // When the string was all whitespace, report that we stripped off whitespace @@ -195,15 +190,17 @@ TrimPositions TrimStringT(const STR& input, } bool TrimString(const string16& input, - const char16 trim_chars[], + const base::StringPiece16& trim_chars, string16* output) { - return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE; + return TrimStringT(input, trim_chars.as_string(), TRIM_ALL, output) != + TRIM_NONE; } bool TrimString(const std::string& input, - const char trim_chars[], + const base::StringPiece& trim_chars, std::string* output) { - return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE; + return TrimStringT(input, trim_chars.as_string(), TRIM_ALL, output) != + TRIM_NONE; } void TruncateUTF8ToByteSize(const std::string& input, @@ -242,18 +239,17 @@ void TruncateUTF8ToByteSize(const std::string& input, output->clear(); } -} // namespace base - -TrimPositions TrimWhitespace(const base::string16& input, +TrimPositions TrimWhitespace(const string16& input, TrimPositions positions, - base::string16* output) { - return base::TrimStringT(input, base::kWhitespaceUTF16, positions, output); + string16* output) { + return TrimStringT(input, base::string16(kWhitespaceUTF16), positions, + output); } TrimPositions TrimWhitespaceASCII(const std::string& input, TrimPositions positions, std::string* output) { - return base::TrimStringT(input, base::kWhitespaceASCII, positions, output); + return TrimStringT(input, std::string(kWhitespaceASCII), positions, output); } // This function is only for backward-compatibility. @@ -316,49 +312,14 @@ std::string CollapseWhitespaceASCII(const std::string& text, return CollapseWhitespaceT(text, trim_sequences_with_line_breaks); } -bool ContainsOnlyWhitespaceASCII(const std::string& str) { - for (std::string::const_iterator i(str.begin()); i != str.end(); ++i) { - if (!IsAsciiWhitespace(*i)) - return false; - } - return true; -} - -bool ContainsOnlyWhitespace(const base::string16& str) { - return str.find_first_not_of(base::kWhitespaceUTF16) == string16::npos; -} - -template<typename STR> -static bool ContainsOnlyCharsT(const STR& input, const STR& characters) { - for (typename STR::const_iterator iter = input.begin(); - iter != input.end(); ++iter) { - if (characters.find(*iter) == STR::npos) - return false; - } - return true; -} - -bool ContainsOnlyChars(const string16& input, const string16& characters) { - return ContainsOnlyCharsT(input, characters); -} - -bool ContainsOnlyChars(const std::string& input, - const std::string& characters) { - return ContainsOnlyCharsT(input, characters); -} - -#if !defined(WCHAR_T_IS_UTF16) -bool IsStringASCII(const std::wstring& str); -#endif - -std::string WideToASCII(const std::wstring& wide) { - DCHECK(IsStringASCII(wide)) << wide; - return std::string(wide.begin(), wide.end()); +bool ContainsOnlyChars(const StringPiece& input, + const StringPiece& characters) { + return input.find_first_not_of(characters) == StringPiece::npos; } -std::string UTF16ToASCII(const string16& utf16) { - DCHECK(IsStringASCII(utf16)) << utf16; - return std::string(utf16.begin(), utf16.end()); +bool ContainsOnlyChars(const StringPiece16& input, + const StringPiece16& characters) { + return input.find_first_not_of(characters) == StringPiece16::npos; } template<class STR> @@ -371,20 +332,14 @@ static bool DoIsStringASCII(const STR& str) { return true; } -#if !defined(WCHAR_T_IS_UTF16) -bool IsStringASCII(const std::wstring& str) { +bool IsStringASCII(const StringPiece& str) { return DoIsStringASCII(str); } -#endif bool IsStringASCII(const string16& str) { return DoIsStringASCII(str); } -bool IsStringASCII(const base::StringPiece& str) { - return DoIsStringASCII(str); -} - bool IsStringUTF8(const std::string& str) { const char *src = str.data(); int32 src_len = static_cast<int32>(str.length()); @@ -393,12 +348,14 @@ bool IsStringUTF8(const std::string& str) { while (char_index < src_len) { int32 code_point; CBU8_NEXT(src, char_index, src_len, code_point); - if (!base::IsValidCharacter(code_point)) + if (!IsValidCharacter(code_point)) return false; } return true; } +} // namespace base + template<typename Iter> static inline bool DoLowerCaseEqualsASCII(Iter a_begin, Iter a_end, @@ -481,17 +438,15 @@ bool StartsWith(const string16& str, const string16& search, template <typename STR> bool EndsWithT(const STR& str, const STR& search, bool case_sensitive) { - typename STR::size_type str_length = str.length(); - typename STR::size_type search_length = search.length(); + size_t str_length = str.length(); + size_t search_length = search.length(); if (search_length > str_length) return false; - if (case_sensitive) { + if (case_sensitive) return str.compare(str_length - search_length, search_length, search) == 0; - } else { - return std::equal(search.begin(), search.end(), - str.begin() + (str_length - search_length), - base::CaseInsensitiveCompare<typename STR::value_type>()); - } + return std::equal(search.begin(), search.end(), + str.begin() + (str_length - search_length), + base::CaseInsensitiveCompare<typename STR::value_type>()); } bool EndsWith(const std::string& str, const std::string& search, @@ -532,12 +487,12 @@ string16 FormatBytesUnlocalized(int64 bytes) { kByteStringsUnlocalized[dimension]); } - return ASCIIToUTF16(buf); + return base::ASCIIToUTF16(buf); } template<class StringType> void DoReplaceSubstringsAfterOffset(StringType* str, - typename StringType::size_type start_offset, + size_t start_offset, const StringType& find_this, const StringType& replace_with, bool replace_all) { @@ -545,7 +500,7 @@ void DoReplaceSubstringsAfterOffset(StringType* str, return; DCHECK(!find_this.empty()); - for (typename StringType::size_type offs(str->find(find_this, start_offset)); + 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(); @@ -556,7 +511,7 @@ void DoReplaceSubstringsAfterOffset(StringType* str, } void ReplaceFirstSubstringAfterOffset(string16* str, - string16::size_type start_offset, + size_t start_offset, const string16& find_this, const string16& replace_with) { DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with, @@ -564,7 +519,7 @@ void ReplaceFirstSubstringAfterOffset(string16* str, } void ReplaceFirstSubstringAfterOffset(std::string* str, - std::string::size_type start_offset, + size_t start_offset, const std::string& find_this, const std::string& replace_with) { DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with, @@ -572,7 +527,7 @@ void ReplaceFirstSubstringAfterOffset(std::string* str, } void ReplaceSubstringsAfterOffset(string16* str, - string16::size_type start_offset, + size_t start_offset, const string16& find_this, const string16& replace_with) { DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with, @@ -580,7 +535,7 @@ void ReplaceSubstringsAfterOffset(string16* str, } void ReplaceSubstringsAfterOffset(std::string* str, - std::string::size_type start_offset, + size_t start_offset, const std::string& find_this, const std::string& replace_with) { DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with, @@ -594,9 +549,9 @@ static size_t TokenizeT(const STR& str, std::vector<STR>* tokens) { tokens->clear(); - typename STR::size_type start = str.find_first_not_of(delimiters); + size_t start = str.find_first_not_of(delimiters); while (start != STR::npos) { - typename STR::size_type end = str.find_first_of(delimiters, start + 1); + size_t end = str.find_first_of(delimiters, start + 1); if (end == STR::npos) { tokens->push_back(str.substr(start)); break; diff --git a/chromium/base/strings/string_util.h b/chromium/base/strings/string_util.h index 7b4b2193abf..a573e227df9 100644 --- a/chromium/base/strings/string_util.h +++ b/chromium/base/strings/string_util.h @@ -19,8 +19,6 @@ #include "base/strings/string16.h" #include "base/strings/string_piece.h" // For implicit conversions. -// Safe standard library wrappers for all platforms. - namespace base { // C standard-library functions like "strncasecmp" and "snprintf" that aren't @@ -152,10 +150,10 @@ BASE_EXPORT extern const char kUtf8ByteOrderMark[]; // if any characters were removed. |remove_chars| must be null-terminated. // NOTE: Safe to use the same variable for both |input| and |output|. BASE_EXPORT bool RemoveChars(const string16& input, - const char16 remove_chars[], + const base::StringPiece16& remove_chars, string16* output); BASE_EXPORT bool RemoveChars(const std::string& input, - const char remove_chars[], + const base::StringPiece& remove_chars, std::string* output); // Replaces characters in |replace_chars| from anywhere in |input| with @@ -164,11 +162,11 @@ BASE_EXPORT bool RemoveChars(const std::string& input, // |replace_chars| must be null-terminated. // NOTE: Safe to use the same variable for both |input| and |output|. BASE_EXPORT bool ReplaceChars(const string16& input, - const char16 replace_chars[], + const base::StringPiece16& replace_chars, const string16& replace_with, string16* output); BASE_EXPORT bool ReplaceChars(const std::string& input, - const char replace_chars[], + const base::StringPiece& replace_chars, const std::string& replace_with, std::string* output); @@ -176,10 +174,10 @@ BASE_EXPORT bool ReplaceChars(const std::string& input, // |trim_chars| must be null-terminated. // NOTE: Safe to use the same variable for both |input| and |output|. BASE_EXPORT bool TrimString(const string16& input, - const char16 trim_chars[], + const base::StringPiece16& trim_chars, string16* output); BASE_EXPORT bool TrimString(const std::string& input, - const char trim_chars[], + const base::StringPiece& trim_chars, std::string* output); // Truncates a string to the nearest UTF-8 character that will leave @@ -188,16 +186,6 @@ BASE_EXPORT void TruncateUTF8ToByteSize(const std::string& input, const size_t byte_size, std::string* output); -} // namespace base - -#if defined(OS_WIN) -#include "base/strings/string_util_win.h" -#elif defined(OS_POSIX) -#include "base/strings/string_util_posix.h" -#else -#error Define string operations appropriately for your platform -#endif - // Trims any whitespace from either end of the input string. Returns where // whitespace was found. // The non-wide version has two functions: @@ -211,7 +199,7 @@ enum TrimPositions { TRIM_TRAILING = 1 << 1, TRIM_ALL = TRIM_LEADING | TRIM_TRAILING, }; -BASE_EXPORT TrimPositions TrimWhitespace(const base::string16& input, +BASE_EXPORT TrimPositions TrimWhitespace(const string16& input, TrimPositions positions, base::string16* output); BASE_EXPORT TrimPositions TrimWhitespaceASCII(const std::string& input, @@ -232,29 +220,19 @@ BASE_EXPORT TrimPositions TrimWhitespace(const std::string& input, // (2) If |trim_sequences_with_line_breaks| is true, any other whitespace // sequences containing a CR or LF are trimmed. // (3) All other whitespace sequences are converted to single spaces. -BASE_EXPORT base::string16 CollapseWhitespace( - const base::string16& text, +BASE_EXPORT string16 CollapseWhitespace( + const string16& text, bool trim_sequences_with_line_breaks); BASE_EXPORT std::string CollapseWhitespaceASCII( const std::string& text, bool trim_sequences_with_line_breaks); -// Returns true if the passed string is empty or contains only white-space -// characters. -BASE_EXPORT bool ContainsOnlyWhitespaceASCII(const std::string& str); -BASE_EXPORT bool ContainsOnlyWhitespace(const base::string16& str); - // Returns true if |input| is empty or contains only characters found in // |characters|. -BASE_EXPORT bool ContainsOnlyChars(const base::string16& input, - const base::string16& characters); -BASE_EXPORT bool ContainsOnlyChars(const std::string& input, - const std::string& characters); - -// Converts to 7-bit ASCII by truncating. The result must be known to be ASCII -// beforehand. -BASE_EXPORT std::string WideToASCII(const std::wstring& wide); -BASE_EXPORT std::string UTF16ToASCII(const base::string16& utf16); +BASE_EXPORT bool ContainsOnlyChars(const StringPiece& input, + const StringPiece& characters); +BASE_EXPORT bool ContainsOnlyChars(const StringPiece16& input, + const StringPiece16& characters); // Returns true if the specified string matches the criteria. How can a wide // string be 8-bit or UTF8? It contains only characters that are < 256 (in the @@ -268,8 +246,18 @@ BASE_EXPORT std::string UTF16ToASCII(const base::string16& utf16); // there's a use case for just checking the structural validity, we have to // add a new function for that. BASE_EXPORT bool IsStringUTF8(const std::string& str); -BASE_EXPORT bool IsStringASCII(const base::StringPiece& str); -BASE_EXPORT bool IsStringASCII(const base::string16& str); +BASE_EXPORT bool IsStringASCII(const StringPiece& str); +BASE_EXPORT bool IsStringASCII(const string16& str); + +} // namespace base + +#if defined(OS_WIN) +#include "base/strings/string_util_win.h" +#elif defined(OS_POSIX) +#include "base/strings/string_util_posix.h" +#else +#error Define string operations appropriately for your platform +#endif // Converts the elements of the given string. This version uses a pointer to // clearly differentiate it from the non-pointer variant. @@ -390,12 +378,12 @@ BASE_EXPORT base::string16 FormatBytesUnlocalized(int64 bytes); // |find_this| with |replace_with|. BASE_EXPORT void ReplaceFirstSubstringAfterOffset( base::string16* str, - base::string16::size_type start_offset, + size_t start_offset, const base::string16& find_this, const base::string16& replace_with); BASE_EXPORT void ReplaceFirstSubstringAfterOffset( std::string* str, - std::string::size_type start_offset, + size_t start_offset, const std::string& find_this, const std::string& replace_with); @@ -407,14 +395,13 @@ BASE_EXPORT void ReplaceFirstSubstringAfterOffset( // std::replace(str.begin(), str.end(), 'a', 'b'); BASE_EXPORT void ReplaceSubstringsAfterOffset( base::string16* str, - base::string16::size_type start_offset, + size_t start_offset, const base::string16& find_this, const base::string16& replace_with); -BASE_EXPORT void ReplaceSubstringsAfterOffset( - std::string* str, - std::string::size_type start_offset, - const std::string& find_this, - const std::string& replace_with); +BASE_EXPORT void ReplaceSubstringsAfterOffset(std::string* str, + size_t start_offset, + const std::string& find_this, + const std::string& replace_with); // Reserves enough memory in |str| to accommodate |length_with_null| characters, // sets the size of |str| to |length_with_null - 1| characters, and returns a diff --git a/chromium/base/strings/string_util_constants.cc b/chromium/base/strings/string_util_constants.cc index 2a28a2b5126..146e5fd5355 100644 --- a/chromium/base/strings/string_util_constants.cc +++ b/chromium/base/strings/string_util_constants.cc @@ -7,33 +7,31 @@ namespace base { #define WHITESPACE_UNICODE \ - 0x0009, /* <control-0009> to <control-000D> */ \ - 0x000A, \ - 0x000B, \ - 0x000C, \ - 0x000D, \ - 0x0020, /* Space */ \ - 0x0085, /* <control-0085> */ \ - 0x00A0, /* No-Break Space */ \ - 0x1680, /* Ogham Space Mark */ \ - 0x180E, /* Mongolian Vowel Separator */ \ - 0x2000, /* En Quad to Hair Space */ \ - 0x2001, \ - 0x2002, \ - 0x2003, \ - 0x2004, \ - 0x2005, \ - 0x2006, \ - 0x2007, \ - 0x2008, \ - 0x2009, \ - 0x200A, \ - 0x200C, /* Zero Width Non-Joiner */ \ - 0x2028, /* Line Separator */ \ - 0x2029, /* Paragraph Separator */ \ - 0x202F, /* Narrow No-Break Space */ \ - 0x205F, /* Medium Mathematical Space */ \ - 0x3000, /* Ideographic Space */ \ + 0x0009, /* CHARACTER TABULATION */ \ + 0x000A, /* LINE FEED (LF) */ \ + 0x000B, /* LINE TABULATION */ \ + 0x000C, /* FORM FEED (FF) */ \ + 0x000D, /* CARRIAGE RETURN (CR) */ \ + 0x0020, /* SPACE */ \ + 0x0085, /* NEXT LINE (NEL) */ \ + 0x00A0, /* NO-BREAK SPACE */ \ + 0x1680, /* OGHAM SPACE MARK */ \ + 0x2000, /* EN QUAD */ \ + 0x2001, /* EM QUAD */ \ + 0x2002, /* EN SPACE */ \ + 0x2003, /* EM SPACE */ \ + 0x2004, /* THREE-PER-EM SPACE */ \ + 0x2005, /* FOUR-PER-EM SPACE */ \ + 0x2006, /* SIX-PER-EM SPACE */ \ + 0x2007, /* FIGURE SPACE */ \ + 0x2008, /* PUNCTUATION SPACE */ \ + 0x2009, /* THIN SPACE */ \ + 0x200A, /* HAIR SPACE */ \ + 0x2028, /* LINE SEPARATOR */ \ + 0x2029, /* PARAGRAPH SEPARATOR */ \ + 0x202F, /* NARROW NO-BREAK SPACE */ \ + 0x205F, /* MEDIUM MATHEMATICAL SPACE */ \ + 0x3000, /* IDEOGRAPHIC SPACE */ \ 0 const wchar_t kWhitespaceWide[] = { @@ -45,12 +43,12 @@ const char16 kWhitespaceUTF16[] = { }; const char kWhitespaceASCII[] = { - 0x09, // <control-0009> to <control-000D> - 0x0A, - 0x0B, - 0x0C, - 0x0D, - 0x20, // Space + 0x09, // CHARACTER TABULATION + 0x0A, // LINE FEED (LF) + 0x0B, // LINE TABULATION + 0x0C, // FORM FEED (FF) + 0x0D, // CARRIAGE RETURN (CR) + 0x20, // SPACE 0 }; diff --git a/chromium/base/strings/string_util_posix.h b/chromium/base/strings/string_util_posix.h index 34b14f178f8..f4009d4ae7f 100644 --- a/chromium/base/strings/string_util_posix.h +++ b/chromium/base/strings/string_util_posix.h @@ -11,7 +11,6 @@ #include <wchar.h> #include "base/logging.h" -#include "base/strings/string_util.h" namespace base { diff --git a/chromium/base/strings/string_util_unittest.cc b/chromium/base/strings/string_util_unittest.cc index e743bb41223..4ea9069a255 100644 --- a/chromium/base/strings/string_util_unittest.cc +++ b/chromium/base/strings/string_util_unittest.cc @@ -319,24 +319,6 @@ TEST(StringUtilTest, CollapseWhitespaceASCII) { } } -TEST(StringUtilTest, ContainsOnlyWhitespaceASCII) { - EXPECT_TRUE(ContainsOnlyWhitespaceASCII(std::string())); - EXPECT_TRUE(ContainsOnlyWhitespaceASCII(" ")); - EXPECT_TRUE(ContainsOnlyWhitespaceASCII("\t")); - EXPECT_TRUE(ContainsOnlyWhitespaceASCII("\t \r \n ")); - EXPECT_FALSE(ContainsOnlyWhitespaceASCII("a")); - EXPECT_FALSE(ContainsOnlyWhitespaceASCII("\thello\r \n ")); -} - -TEST(StringUtilTest, ContainsOnlyWhitespace) { - EXPECT_TRUE(ContainsOnlyWhitespace(string16())); - EXPECT_TRUE(ContainsOnlyWhitespace(ASCIIToUTF16(" "))); - EXPECT_TRUE(ContainsOnlyWhitespace(ASCIIToUTF16("\t"))); - EXPECT_TRUE(ContainsOnlyWhitespace(ASCIIToUTF16("\t \r \n "))); - EXPECT_FALSE(ContainsOnlyWhitespace(ASCIIToUTF16("a"))); - EXPECT_FALSE(ContainsOnlyWhitespace(ASCIIToUTF16("\thello\r \n "))); -} - TEST(StringUtilTest, IsStringUTF8) { EXPECT_TRUE(IsStringUTF8("abc")); EXPECT_TRUE(IsStringUTF8("\xc2\x81")); @@ -418,20 +400,20 @@ TEST(StringUtilTest, ConvertASCII) { for (size_t i = 0; i < arraysize(char_cases); ++i) { EXPECT_TRUE(IsStringASCII(char_cases[i])); - std::wstring wide = ASCIIToWide(char_cases[i]); - EXPECT_EQ(wchar_cases[i], wide); + string16 utf16 = ASCIIToUTF16(char_cases[i]); + EXPECT_EQ(WideToUTF16(wchar_cases[i]), utf16); - std::string ascii = WideToASCII(wchar_cases[i]); + std::string ascii = UTF16ToASCII(WideToUTF16(wchar_cases[i])); EXPECT_EQ(char_cases[i], ascii); } EXPECT_FALSE(IsStringASCII("Google \x80Video")); // Convert empty strings. - std::wstring wempty; + string16 empty16; std::string empty; - EXPECT_EQ(empty, WideToASCII(wempty)); - EXPECT_EQ(wempty, ASCIIToWide(empty)); + EXPECT_EQ(empty, UTF16ToASCII(empty16)); + EXPECT_EQ(empty16, ASCIIToUTF16(empty)); // Convert strings with an embedded NUL character. const char chars_with_nul[] = "test\0string"; @@ -440,7 +422,7 @@ TEST(StringUtilTest, ConvertASCII) { 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 = WideToASCII(wide_with_nul); + std::string narrow_with_nul = UTF16ToASCII(WideToUTF16(wide_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)); @@ -1029,13 +1011,8 @@ TEST(StringUtilTest, LcpyTest) { EXPECT_EQ(1, dst[0]); EXPECT_EQ(2, dst[1]); EXPECT_EQ(7U, base::wcslcpy(wdst, L"abcdefg", 0)); -#if defined(WCHAR_T_IS_UNSIGNED) - EXPECT_EQ(1U, wdst[0]); - EXPECT_EQ(2U, wdst[1]); -#else - EXPECT_EQ(1, wdst[0]); - EXPECT_EQ(2, wdst[1]); -#endif + EXPECT_EQ(static_cast<wchar_t>(1), wdst[0]); + EXPECT_EQ(static_cast<wchar_t>(2), wdst[1]); } // Test the case were we _just_ competely fit including the null. @@ -1161,6 +1138,21 @@ TEST(StringUtilTest, ContainsOnlyChars) { EXPECT_TRUE(ContainsOnlyChars("1", "4321")); EXPECT_TRUE(ContainsOnlyChars("123", "4321")); EXPECT_FALSE(ContainsOnlyChars("123a", "4321")); + + EXPECT_TRUE(ContainsOnlyChars(std::string(), kWhitespaceASCII)); + EXPECT_TRUE(ContainsOnlyChars(" ", kWhitespaceASCII)); + EXPECT_TRUE(ContainsOnlyChars("\t", kWhitespaceASCII)); + EXPECT_TRUE(ContainsOnlyChars("\t \r \n ", kWhitespaceASCII)); + EXPECT_FALSE(ContainsOnlyChars("a", kWhitespaceASCII)); + EXPECT_FALSE(ContainsOnlyChars("\thello\r \n ", kWhitespaceASCII)); + + EXPECT_TRUE(ContainsOnlyChars(string16(), kWhitespaceUTF16)); + EXPECT_TRUE(ContainsOnlyChars(ASCIIToUTF16(" "), kWhitespaceUTF16)); + EXPECT_TRUE(ContainsOnlyChars(ASCIIToUTF16("\t"), kWhitespaceUTF16)); + EXPECT_TRUE(ContainsOnlyChars(ASCIIToUTF16("\t \r \n "), kWhitespaceUTF16)); + EXPECT_FALSE(ContainsOnlyChars(ASCIIToUTF16("a"), kWhitespaceUTF16)); + EXPECT_FALSE(ContainsOnlyChars(ASCIIToUTF16("\thello\r \n "), + kWhitespaceUTF16)); } class WriteIntoTest : public testing::Test { diff --git a/chromium/base/strings/stringprintf.cc b/chromium/base/strings/stringprintf.cc index fe23daaa355..3d024fa65aa 100644 --- a/chromium/base/strings/stringprintf.cc +++ b/chromium/base/strings/stringprintf.cc @@ -66,19 +66,17 @@ static void StringAppendVT(StringType* dst, int mem_length = arraysize(stack_buf); while (true) { if (result < 0) { -#if !defined(OS_WIN) +#if defined(OS_WIN) // On Windows, vsnprintfT always returns the number of characters in a // fully-formatted string, so if we reach this point, something else is // wrong and no amount of buffer-doubling is going to fix it. + return; +#else if (errno != 0 && errno != EOVERFLOW) -#endif - { - // If an error other than overflow occurred, it's never going to work. - DLOG(WARNING) << "Unable to printf the requested string due to error."; return; - } // Try doubling the buffer size. mem_length *= 2; +#endif } else { // We need exactly "result + 1" characters. mem_length = result + 1; diff --git a/chromium/base/strings/utf_offset_string_conversions.cc b/chromium/base/strings/utf_offset_string_conversions.cc index 339bd5e1a75..c2270bfce2f 100644 --- a/chromium/base/strings/utf_offset_string_conversions.cc +++ b/chromium/base/strings/utf_offset_string_conversions.cc @@ -6,30 +6,188 @@ #include <algorithm> +#include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/strings/string_piece.h" #include "base/strings/utf_string_conversion_utils.h" namespace base { +OffsetAdjuster::Adjustment::Adjustment(size_t original_offset, + size_t original_length, + size_t output_length) + : original_offset(original_offset), + original_length(original_length), + output_length(output_length) { +} + +// static +void OffsetAdjuster::AdjustOffsets( + const Adjustments& adjustments, + std::vector<size_t>* offsets_for_adjustment) { + if (!offsets_for_adjustment || adjustments.empty()) + return; + for (std::vector<size_t>::iterator i(offsets_for_adjustment->begin()); + i != offsets_for_adjustment->end(); ++i) + AdjustOffset(adjustments, &(*i)); +} + +// static +void OffsetAdjuster::AdjustOffset(const Adjustments& adjustments, + size_t* offset) { + if (*offset == string16::npos) + return; + int adjustment = 0; + for (Adjustments::const_iterator i = adjustments.begin(); + i != adjustments.end(); ++i) { + if (*offset <= i->original_offset) + break; + if (*offset < (i->original_offset + i->original_length)) { + *offset = string16::npos; + return; + } + adjustment += static_cast<int>(i->original_length - i->output_length); + } + *offset -= adjustment; +} + +// static +void OffsetAdjuster::UnadjustOffsets( + const Adjustments& adjustments, + std::vector<size_t>* offsets_for_unadjustment) { + if (!offsets_for_unadjustment || adjustments.empty()) + return; + for (std::vector<size_t>::iterator i(offsets_for_unadjustment->begin()); + i != offsets_for_unadjustment->end(); ++i) + UnadjustOffset(adjustments, &(*i)); +} + +// static +void OffsetAdjuster::UnadjustOffset(const Adjustments& adjustments, + size_t* offset) { + if (*offset == string16::npos) + return; + int adjustment = 0; + for (Adjustments::const_iterator i = adjustments.begin(); + i != adjustments.end(); ++i) { + if (*offset + adjustment <= i->original_offset) + break; + adjustment += static_cast<int>(i->original_length - i->output_length); + if ((*offset + adjustment) < + (i->original_offset + i->original_length)) { + *offset = string16::npos; + return; + } + } + *offset += adjustment; +} + +// static +void OffsetAdjuster::MergeSequentialAdjustments( + const Adjustments& first_adjustments, + Adjustments* adjustments_on_adjusted_string) { + Adjustments::iterator adjusted_iter = adjustments_on_adjusted_string->begin(); + Adjustments::const_iterator first_iter = first_adjustments.begin(); + // Simultaneously iterate over all |adjustments_on_adjusted_string| and + // |first_adjustments|, adding adjustments to or correcting the adjustments + // in |adjustments_on_adjusted_string| as we go. |shift| keeps track of the + // current number of characters collapsed by |first_adjustments| up to this + // point. |currently_collapsing| keeps track of the number of characters + // collapsed by |first_adjustments| into the current |adjusted_iter|'s + // length. These are characters that will change |shift| as soon as we're + // done processing the current |adjusted_iter|; they are not yet reflected in + // |shift|. + size_t shift = 0; + size_t currently_collapsing = 0; + while (adjusted_iter != adjustments_on_adjusted_string->end()) { + if ((first_iter == first_adjustments.end()) || + ((adjusted_iter->original_offset + shift + + adjusted_iter->original_length) <= first_iter->original_offset)) { + // Entire |adjusted_iter| (accounting for its shift and including its + // whole original length) comes before |first_iter|. + // + // Correct the offset at |adjusted_iter| and move onto the next + // adjustment that needs revising. + adjusted_iter->original_offset += shift; + shift += currently_collapsing; + currently_collapsing = 0; + ++adjusted_iter; + } else if ((adjusted_iter->original_offset + shift) > + first_iter->original_offset) { + // |first_iter| comes before the |adjusted_iter| (as adjusted by |shift|). + + // It's not possible for the adjustments to overlap. (It shouldn't + // be possible that we have an |adjusted_iter->original_offset| that, + // when adjusted by the computed |shift|, is in the middle of + // |first_iter|'s output's length. After all, that would mean the + // current adjustment_on_adjusted_string somehow points to an offset + // that was supposed to have been eliminated by the first set of + // adjustments.) + DCHECK_LE(first_iter->original_offset + first_iter->output_length, + adjusted_iter->original_offset + shift); + + // Add the |first_adjustment_iter| to the full set of adjustments while + // making sure |adjusted_iter| continues pointing to the same element. + // We do this by inserting the |first_adjustment_iter| right before + // |adjusted_iter|, then incrementing |adjusted_iter| so it points to + // the following element. + shift += first_iter->original_length - first_iter->output_length; + adjusted_iter = adjustments_on_adjusted_string->insert( + adjusted_iter, *first_iter); + ++adjusted_iter; + ++first_iter; + } else { + // The first adjustment adjusted something that then got further adjusted + // by the second set of adjustments. In other words, |first_iter| points + // to something in the range covered by |adjusted_iter|'s length (after + // accounting for |shift|). Precisely, + // adjusted_iter->original_offset + shift + // <= + // first_iter->original_offset + // <= + // adjusted_iter->original_offset + shift + + // adjusted_iter->original_length + + // Modify the current |adjusted_iter| to include whatever collapsing + // happened in |first_iter|, then advance to the next |first_adjustments| + // because we dealt with the current one. + const int collapse = static_cast<int>(first_iter->original_length) - + static_cast<int>(first_iter->output_length); + // This function does not know how to deal with a string that expands and + // then gets modified, only strings that collapse and then get modified. + DCHECK_GT(collapse, 0); + adjusted_iter->original_length += collapse; + currently_collapsing += collapse; + ++first_iter; + } + } + DCHECK_EQ(0u, currently_collapsing); + if (first_iter != first_adjustments.end()) { + // Only first adjustments are left. These do not need to be modified. + // (Their offsets are already correct with respect to the original string.) + // Append them all. + DCHECK(adjusted_iter == adjustments_on_adjusted_string->end()); + adjustments_on_adjusted_string->insert( + adjustments_on_adjusted_string->end(), first_iter, + first_adjustments.end()); + } +} + // Converts the given source Unicode character type to the given destination // Unicode character type as a STL string. The given input buffer and size // determine the source, and the given output STL string will be replaced by -// the result. +// the result. If non-NULL, |adjustments| is set to reflect the all the +// alterations to the string that are not one-character-to-one-character. +// It will always be sorted by increasing offset. template<typename SrcChar, typename DestStdString> bool ConvertUnicode(const SrcChar* src, size_t src_len, DestStdString* output, - std::vector<size_t>* offsets_for_adjustment) { - if (offsets_for_adjustment) { - std::for_each(offsets_for_adjustment->begin(), - offsets_for_adjustment->end(), - LimitOffset<DestStdString>(src_len)); - } - + OffsetAdjuster::Adjustments* adjustments) { + if (adjustments) + adjustments->clear(); // ICU requires 32-bit numbers. bool success = true; - OffsetAdjuster offset_adjuster(offsets_for_adjustment); int32 src_len32 = static_cast<int32>(src_len); for (int32 i = 0; i < src_len32; i++) { uint32 code_point; @@ -41,122 +199,62 @@ bool ConvertUnicode(const SrcChar* src, chars_written = WriteUnicodeCharacter(0xFFFD, output); success = false; } - if (offsets_for_adjustment) { - // NOTE: ReadUnicodeCharacter() adjusts |i| to point _at_ the last - // character read, not after it (so that incrementing it in the loop - // increment will place it at the right location), so we need to account - // for that in determining the amount that was read. - offset_adjuster.Add(OffsetAdjuster::Adjustment(original_i, - i - original_i + 1, chars_written)); + + // Only bother writing an adjustment if this modification changed the + // length of this character. + // NOTE: ReadUnicodeCharacter() adjusts |i| to point _at_ the last + // character read, not after it (so that incrementing it in the loop + // increment will place it at the right location), so we need to account + // for that in determining the amount that was read. + if (adjustments && ((i - original_i + 1) != chars_written)) { + adjustments->push_back(OffsetAdjuster::Adjustment( + original_i, i - original_i + 1, chars_written)); } } return success; } -bool UTF8ToUTF16AndAdjustOffset(const char* src, - size_t src_len, - string16* output, - size_t* offset_for_adjustment) { - std::vector<size_t> offsets; - if (offset_for_adjustment) - offsets.push_back(*offset_for_adjustment); +bool UTF8ToUTF16WithAdjustments( + const char* src, + size_t src_len, + string16* output, + base::OffsetAdjuster::Adjustments* adjustments) { PrepareForUTF16Or32Output(src, src_len, output); - bool ret = ConvertUnicode(src, src_len, output, &offsets); - if (offset_for_adjustment) - *offset_for_adjustment = offsets[0]; - return ret; + return ConvertUnicode(src, src_len, output, adjustments); } -bool UTF8ToUTF16AndAdjustOffsets(const char* src, - size_t src_len, - string16* output, - std::vector<size_t>* offsets_for_adjustment) { - PrepareForUTF16Or32Output(src, src_len, output); - return ConvertUnicode(src, src_len, output, offsets_for_adjustment); -} - -string16 UTF8ToUTF16AndAdjustOffset(const base::StringPiece& utf8, - size_t* offset_for_adjustment) { - std::vector<size_t> offsets; - if (offset_for_adjustment) - offsets.push_back(*offset_for_adjustment); +string16 UTF8ToUTF16WithAdjustments( + const base::StringPiece& utf8, + base::OffsetAdjuster::Adjustments* adjustments) { string16 result; - UTF8ToUTF16AndAdjustOffsets(utf8.data(), utf8.length(), &result, - &offsets); - if (offset_for_adjustment) - *offset_for_adjustment = offsets[0]; + UTF8ToUTF16WithAdjustments(utf8.data(), utf8.length(), &result, adjustments); return result; } string16 UTF8ToUTF16AndAdjustOffsets( const base::StringPiece& utf8, std::vector<size_t>* offsets_for_adjustment) { - string16 result; - UTF8ToUTF16AndAdjustOffsets(utf8.data(), utf8.length(), &result, - offsets_for_adjustment); - return result; -} - -std::string UTF16ToUTF8AndAdjustOffset( - const base::StringPiece16& utf16, - size_t* offset_for_adjustment) { - std::vector<size_t> offsets; - if (offset_for_adjustment) - offsets.push_back(*offset_for_adjustment); - std::string result = UTF16ToUTF8AndAdjustOffsets(utf16, &offsets); - if (offset_for_adjustment) - *offset_for_adjustment = offsets[0]; + std::for_each(offsets_for_adjustment->begin(), + offsets_for_adjustment->end(), + LimitOffset<base::StringPiece>(utf8.length())); + OffsetAdjuster::Adjustments adjustments; + string16 result = UTF8ToUTF16WithAdjustments(utf8, &adjustments); + OffsetAdjuster::AdjustOffsets(adjustments, offsets_for_adjustment); return result; } std::string UTF16ToUTF8AndAdjustOffsets( const base::StringPiece16& utf16, std::vector<size_t>* offsets_for_adjustment) { + std::for_each(offsets_for_adjustment->begin(), + offsets_for_adjustment->end(), + LimitOffset<base::StringPiece16>(utf16.length())); std::string result; PrepareForUTF8Output(utf16.data(), utf16.length(), &result); - ConvertUnicode(utf16.data(), utf16.length(), &result, offsets_for_adjustment); + OffsetAdjuster::Adjustments adjustments; + ConvertUnicode(utf16.data(), utf16.length(), &result, &adjustments); + OffsetAdjuster::AdjustOffsets(adjustments, offsets_for_adjustment); return result; } -OffsetAdjuster::Adjustment::Adjustment(size_t original_offset, - size_t original_length, - size_t output_length) - : original_offset(original_offset), - original_length(original_length), - output_length(output_length) { -} - -OffsetAdjuster::OffsetAdjuster(std::vector<size_t>* offsets_for_adjustment) - : offsets_for_adjustment_(offsets_for_adjustment) { -} - -OffsetAdjuster::~OffsetAdjuster() { - if (!offsets_for_adjustment_ || adjustments_.empty()) - return; - for (std::vector<size_t>::iterator i(offsets_for_adjustment_->begin()); - i != offsets_for_adjustment_->end(); ++i) - AdjustOffset(i); -} - -void OffsetAdjuster::Add(const Adjustment& adjustment) { - adjustments_.push_back(adjustment); -} - -void OffsetAdjuster::AdjustOffset(std::vector<size_t>::iterator offset) { - if (*offset == string16::npos) - return; - size_t adjustment = 0; - for (std::vector<Adjustment>::const_iterator i = adjustments_.begin(); - i != adjustments_.end(); ++i) { - if (*offset <= i->original_offset) - break; - if (*offset < (i->original_offset + i->original_length)) { - *offset = string16::npos; - return; - } - adjustment += (i->original_length - i->output_length); - } - *offset -= adjustment; -} - } // namespace base diff --git a/chromium/base/strings/utf_offset_string_conversions.h b/chromium/base/strings/utf_offset_string_conversions.h index bdb7c111203..d4494894efb 100644 --- a/chromium/base/strings/utf_offset_string_conversions.h +++ b/chromium/base/strings/utf_offset_string_conversions.h @@ -14,35 +14,92 @@ namespace base { -// Like the conversions in utf_string_conversions.h, but also takes one or more -// |offset[s]_for_adjustment| representing insertion/selection points between -// characters: if |src| is "abcd", then 0 is before 'a', 2 is between 'b' and -// 'c', and 4 is at the end of the string. Valid input offsets range from 0 to -// |src_len|. On exit, each offset will have been modified to point at the same -// logical position in the output string. If an offset cannot be successfully -// adjusted (e.g. because it points into the middle of a multibyte sequence), it -// will be set to string16::npos. -// -// |offset[s]_for_adjustment| may be NULL. -BASE_EXPORT bool UTF8ToUTF16AndAdjustOffset(const char* src, - size_t src_len, - string16* output, - size_t* offset_for_adjustment); -BASE_EXPORT bool UTF8ToUTF16AndAdjustOffsets( +// A helper class and associated data structures to adjust offsets into a +// string in response to various adjustments one might do to that string +// (e.g., eliminating a range). For details on offsets, see the comments by +// the AdjustOffsets() function below. +class BASE_EXPORT OffsetAdjuster { + public: + struct BASE_EXPORT Adjustment { + Adjustment(size_t original_offset, + size_t original_length, + size_t output_length); + + size_t original_offset; + size_t original_length; + size_t output_length; + }; + typedef std::vector<Adjustment> Adjustments; + + // Adjusts all offsets in |offsets_for_adjustment| to reflect the adjustments + // recorded in |adjustments|. + // + // Offsets represents insertion/selection points between characters: if |src| + // is "abcd", then 0 is before 'a', 2 is between 'b' and 'c', and 4 is at the + // end of the string. Valid input offsets range from 0 to |src_len|. On + // exit, each offset will have been modified to point at the same logical + // position in the output string. If an offset cannot be successfully + // adjusted (e.g., because it points into the middle of a multibyte sequence), + // it will be set to string16::npos. + static void AdjustOffsets(const Adjustments& adjustments, + std::vector<size_t>* offsets_for_adjustment); + + // Adjusts the single |offset| to reflect the adjustments recorded in + // |adjustments|. + static void AdjustOffset(const Adjustments& adjustments, + size_t* offset); + + // Adjusts all offsets in |offsets_for_unadjustment| to reflect the reverse + // of the adjustments recorded in |adjustments|. In other words, the offsets + // provided represent offsets into an adjusted string and the caller wants + // to know the offsets they correspond to in the original string. If an + // offset cannot be successfully unadjusted (e.g., because it points into + // the middle of a multibyte sequence), it will be set to string16::npos. + static void UnadjustOffsets(const Adjustments& adjustments, + std::vector<size_t>* offsets_for_unadjustment); + + // Adjusts the single |offset| to reflect the reverse of the adjustments + // recorded in |adjustments|. + static void UnadjustOffset(const Adjustments& adjustments, + size_t* offset); + + // Combines two sequential sets of adjustments, storing the combined revised + // adjustments in |adjustments_on_adjusted_string|. That is, suppose a + // string was altered in some way, with the alterations recorded as + // adjustments in |first_adjustments|. Then suppose the resulting string is + // further altered, with the alterations recorded as adjustments scored in + // |adjustments_on_adjusted_string|, with the offsets recorded in these + // adjustments being with respect to the intermediate string. This function + // combines the two sets of adjustments into one, storing the result in + // |adjustments_on_adjusted_string|, whose offsets are correct with respect + // to the original string. + // + // Assumes both parameters are sorted by increasing offset. + // + // WARNING: Only supports |first_adjustments| that involve collapsing ranges + // of text, not expanding ranges. + static void MergeSequentialAdjustments( + const Adjustments& first_adjustments, + Adjustments* adjustments_on_adjusted_string); +}; + +// Like the conversions in utf_string_conversions.h, but also fills in an +// |adjustments| parameter that reflects the alterations done to the string. +// It may be NULL. +BASE_EXPORT bool UTF8ToUTF16WithAdjustments( const char* src, size_t src_len, string16* output, - std::vector<size_t>* offsets_for_adjustment); - -BASE_EXPORT string16 UTF8ToUTF16AndAdjustOffset(const base::StringPiece& utf8, - size_t* offset_for_adjustment); + base::OffsetAdjuster::Adjustments* adjustments); +BASE_EXPORT string16 UTF8ToUTF16WithAdjustments( + const base::StringPiece& utf8, + base::OffsetAdjuster::Adjustments* adjustments); +// As above, but instead internally examines the adjustments and applies them +// to |offsets_for_adjustment|. See comments by AdjustOffsets(). BASE_EXPORT string16 UTF8ToUTF16AndAdjustOffsets( const base::StringPiece& utf8, std::vector<size_t>* offsets_for_adjustment); -BASE_EXPORT std::string UTF16ToUTF8AndAdjustOffset( - const base::StringPiece16& utf16, - size_t* offset_for_adjustment); BASE_EXPORT std::string UTF16ToUTF8AndAdjustOffsets( const base::StringPiece16& utf16, std::vector<size_t>* offsets_for_adjustment); @@ -64,36 +121,6 @@ struct LimitOffset { size_t limit_; }; -// Stack object which, on destruction, will update a vector of offsets based on -// any supplied adjustments. To use, declare one of these, providing the -// address of the offset vector to adjust. Then Add() any number of Adjustments -// (each Adjustment gives the |original_offset| of a substring and the lengths -// of the substring before and after transforming). When the OffsetAdjuster -// goes out of scope, all the offsets in the provided vector will be updated. -class BASE_EXPORT OffsetAdjuster { - public: - struct BASE_EXPORT Adjustment { - Adjustment(size_t original_offset, - size_t original_length, - size_t output_length); - - size_t original_offset; - size_t original_length; - size_t output_length; - }; - - explicit OffsetAdjuster(std::vector<size_t>* offsets_for_adjustment); - ~OffsetAdjuster(); - - void Add(const Adjustment& adjustment); - - private: - void AdjustOffset(std::vector<size_t>::iterator offset); - - std::vector<size_t>* offsets_for_adjustment_; - std::vector<Adjustment> adjustments_; -}; - } // namespace base #endif // BASE_STRINGS_UTF_OFFSET_STRING_CONVERSIONS_H_ diff --git a/chromium/base/strings/utf_offset_string_conversions_unittest.cc b/chromium/base/strings/utf_offset_string_conversions_unittest.cc index 7626e4c0674..b50e1b69bfc 100644 --- a/chromium/base/strings/utf_offset_string_conversions_unittest.cc +++ b/chromium/base/strings/utf_offset_string_conversions_unittest.cc @@ -35,9 +35,11 @@ TEST(UTFOffsetStringConversionsTest, AdjustOffset) { {"A\xF0\x90\x8C\x80z", kNpos, kNpos}, }; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(utf8_to_utf16_cases); ++i) { - size_t offset = utf8_to_utf16_cases[i].input_offset; - UTF8ToUTF16AndAdjustOffset(utf8_to_utf16_cases[i].utf8, &offset); - EXPECT_EQ(utf8_to_utf16_cases[i].output_offset, offset); + const size_t offset = utf8_to_utf16_cases[i].input_offset; + std::vector<size_t> offsets; + offsets.push_back(offset); + UTF8ToUTF16AndAdjustOffsets(utf8_to_utf16_cases[i].utf8, &offsets); + EXPECT_EQ(utf8_to_utf16_cases[i].output_offset, offsets[0]); } struct UTF16ToUTF8Case { @@ -64,8 +66,10 @@ TEST(UTFOffsetStringConversionsTest, AdjustOffset) { }; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(utf16_to_utf8_cases); ++i) { size_t offset = utf16_to_utf8_cases[i].input_offset; - UTF16ToUTF8AndAdjustOffset(utf16_to_utf8_cases[i].utf16, &offset); - EXPECT_EQ(utf16_to_utf8_cases[i].output_offset, offset); + std::vector<size_t> offsets; + offsets.push_back(offset); + UTF16ToUTF8AndAdjustOffsets(utf16_to_utf8_cases[i].utf16, &offsets); + EXPECT_EQ(utf16_to_utf8_cases[i].output_offset, offsets[0]) << i; } } @@ -108,10 +112,9 @@ TEST(UTFOffsetStringConversionsTest, AdjustOffsets) { std::vector<size_t> offsets; for (size_t t = 0; t <= 9; ++t) offsets.push_back(t); - { - OffsetAdjuster offset_adjuster(&offsets); - offset_adjuster.Add(OffsetAdjuster::Adjustment(3, 3, 1)); - } + OffsetAdjuster::Adjustments adjustments; + adjustments.push_back(OffsetAdjuster::Adjustment(3, 3, 1)); + OffsetAdjuster::AdjustOffsets(adjustments, &offsets); size_t expected_1[] = {0, 1, 2, 3, kNpos, kNpos, 4, 5, 6, 7}; EXPECT_EQ(offsets.size(), arraysize(expected_1)); for (size_t i = 0; i < arraysize(expected_1); ++i) @@ -123,13 +126,12 @@ TEST(UTFOffsetStringConversionsTest, AdjustOffsets) { std::vector<size_t> offsets; for (size_t t = 0; t <= 23; ++t) offsets.push_back(t); - { - OffsetAdjuster offset_adjuster(&offsets); - offset_adjuster.Add(OffsetAdjuster::Adjustment(0, 3, 1)); - offset_adjuster.Add(OffsetAdjuster::Adjustment(4, 4, 2)); - offset_adjuster.Add(OffsetAdjuster::Adjustment(10, 7, 4)); - offset_adjuster.Add(OffsetAdjuster::Adjustment(20, 3, 1)); - } + OffsetAdjuster::Adjustments adjustments; + adjustments.push_back(OffsetAdjuster::Adjustment(0, 3, 1)); + adjustments.push_back(OffsetAdjuster::Adjustment(4, 4, 2)); + adjustments.push_back(OffsetAdjuster::Adjustment(10, 7, 4)); + adjustments.push_back(OffsetAdjuster::Adjustment(20, 3, 1)); + OffsetAdjuster::AdjustOffsets(adjustments, &offsets); size_t expected_2[] = { 0, kNpos, kNpos, 1, 2, kNpos, kNpos, kNpos, 4, 5, 6, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, 10, 11, 12, 13, kNpos, kNpos, 14 @@ -144,13 +146,12 @@ TEST(UTFOffsetStringConversionsTest, AdjustOffsets) { std::vector<size_t> offsets; for (size_t t = 0; t <= 17; ++t) offsets.push_back(t); - { - OffsetAdjuster offset_adjuster(&offsets); - offset_adjuster.Add(OffsetAdjuster::Adjustment(0, 3, 0)); - offset_adjuster.Add(OffsetAdjuster::Adjustment(4, 4, 4)); - offset_adjuster.Add(OffsetAdjuster::Adjustment(11, 3, 3)); - offset_adjuster.Add(OffsetAdjuster::Adjustment(15, 2, 0)); - } + OffsetAdjuster::Adjustments adjustments; + adjustments.push_back(OffsetAdjuster::Adjustment(0, 3, 0)); + adjustments.push_back(OffsetAdjuster::Adjustment(4, 4, 4)); + adjustments.push_back(OffsetAdjuster::Adjustment(11, 3, 3)); + adjustments.push_back(OffsetAdjuster::Adjustment(15, 2, 0)); + OffsetAdjuster::AdjustOffsets(adjustments, &offsets); size_t expected_3[] = { 0, kNpos, kNpos, 0, 1, kNpos, kNpos, kNpos, 5, 6, 7, 8, kNpos, kNpos, 11, 12, kNpos, 12 @@ -161,4 +162,135 @@ TEST(UTFOffsetStringConversionsTest, AdjustOffsets) { } } +TEST(UTFOffsetStringConversionsTest, UnadjustOffsets) { + // Imagine we have strings as shown in the following cases where the + // X's represent encoded characters. + // 1: abcXXXdef ==> abcXdef + { + std::vector<size_t> offsets; + for (size_t t = 0; t <= 7; ++t) + offsets.push_back(t); + OffsetAdjuster::Adjustments adjustments; + adjustments.push_back(OffsetAdjuster::Adjustment(3, 3, 1)); + OffsetAdjuster::UnadjustOffsets(adjustments, &offsets); + size_t expected_1[] = {0, 1, 2, 3, 6, 7, 8, 9}; + EXPECT_EQ(offsets.size(), arraysize(expected_1)); + for (size_t i = 0; i < arraysize(expected_1); ++i) + EXPECT_EQ(expected_1[i], offsets[i]); + } + + // 2: XXXaXXXXbcXXXXXXXdefXXX ==> XaXXbcXXXXdefX + { + std::vector<size_t> offsets; + for (size_t t = 0; t <= 14; ++t) + offsets.push_back(t); + OffsetAdjuster::Adjustments adjustments; + adjustments.push_back(OffsetAdjuster::Adjustment(0, 3, 1)); + adjustments.push_back(OffsetAdjuster::Adjustment(4, 4, 2)); + adjustments.push_back(OffsetAdjuster::Adjustment(10, 7, 4)); + adjustments.push_back(OffsetAdjuster::Adjustment(20, 3, 1)); + OffsetAdjuster::UnadjustOffsets(adjustments, &offsets); + size_t expected_2[] = { + 0, 3, 4, kNpos, 8, 9, 10, kNpos, kNpos, kNpos, 17, 18, 19, 20, 23 + }; + EXPECT_EQ(offsets.size(), arraysize(expected_2)); + for (size_t i = 0; i < arraysize(expected_2); ++i) + EXPECT_EQ(expected_2[i], offsets[i]); + } + + // 3: XXXaXXXXbcdXXXeXX ==> aXXXXbcdXXXe + { + std::vector<size_t> offsets; + for (size_t t = 0; t <= 12; ++t) + offsets.push_back(t); + OffsetAdjuster::Adjustments adjustments; + adjustments.push_back(OffsetAdjuster::Adjustment(0, 3, 0)); + adjustments.push_back(OffsetAdjuster::Adjustment(4, 4, 4)); + adjustments.push_back(OffsetAdjuster::Adjustment(11, 3, 3)); + adjustments.push_back(OffsetAdjuster::Adjustment(15, 2, 0)); + OffsetAdjuster::UnadjustOffsets(adjustments, &offsets); + size_t expected_3[] = { + 0, // this could just as easily be 3 + 4, kNpos, kNpos, kNpos, 8, 9, 10, 11, kNpos, kNpos, 14, + 15 // this could just as easily be 17 + }; + EXPECT_EQ(offsets.size(), arraysize(expected_3)); + for (size_t i = 0; i < arraysize(expected_3); ++i) + EXPECT_EQ(expected_3[i], offsets[i]); + } +} + +// MergeSequentialAdjustments is used by net/base/escape.{h,cc} and +// net/base/net_util.{h,cc}. The two tests EscapeTest.AdjustOffset and +// NetUtilTest.FormatUrlWithOffsets test its behavior extensively. This +// is simply a short, additional test. +TEST(UTFOffsetStringConversionsTest, MergeSequentialAdjustments) { + // Pretend the input string is "abcdefghijklmnopqrstuvwxyz". + + // Set up |first_adjustments| to + // - remove the leading "a" + // - combine the "bc" into one character (call it ".") + // - remove the "f" + // - remove the "tuv" + // The resulting string should be ".deghijklmnopqrswxyz". + OffsetAdjuster::Adjustments first_adjustments; + first_adjustments.push_back(OffsetAdjuster::Adjustment(0, 1, 0)); + first_adjustments.push_back(OffsetAdjuster::Adjustment(1, 2, 1)); + first_adjustments.push_back(OffsetAdjuster::Adjustment(5, 1, 0)); + first_adjustments.push_back(OffsetAdjuster::Adjustment(19, 3, 0)); + + // Set up |adjustments_on_adjusted_string| to + // - combine the "." character that replaced "bc" with "d" into one character + // (call it "?") + // - remove the "egh" + // - expand the "i" into two characters (call them "12") + // - combine the "jkl" into one character (call it "@") + // - expand the "z" into two characters (call it "34") + // The resulting string should be "?12@mnopqrswxy34". + OffsetAdjuster::Adjustments adjustments_on_adjusted_string; + adjustments_on_adjusted_string.push_back(OffsetAdjuster::Adjustment( + 0, 2, 1)); + adjustments_on_adjusted_string.push_back(OffsetAdjuster::Adjustment( + 2, 3, 0)); + adjustments_on_adjusted_string.push_back(OffsetAdjuster::Adjustment( + 5, 1, 2)); + adjustments_on_adjusted_string.push_back(OffsetAdjuster::Adjustment( + 6, 3, 1)); + adjustments_on_adjusted_string.push_back(OffsetAdjuster::Adjustment( + 19, 1, 2)); + + // Now merge the adjustments and check the results. + OffsetAdjuster::MergeSequentialAdjustments(first_adjustments, + &adjustments_on_adjusted_string); + // The merged adjustments should look like + // - combine abcd into "?" + // - note: it's also reasonable for the Merge function to instead produce + // two adjustments instead of this, one to remove a and another to + // combine bcd into "?". This test verifies the current behavior. + // - remove efgh + // - expand i into "12" + // - combine jkl into "@" + // - remove tuv + // - expand z into "34" + ASSERT_EQ(6u, adjustments_on_adjusted_string.size()); + EXPECT_EQ(0u, adjustments_on_adjusted_string[0].original_offset); + EXPECT_EQ(4u, adjustments_on_adjusted_string[0].original_length); + EXPECT_EQ(1u, adjustments_on_adjusted_string[0].output_length); + EXPECT_EQ(4u, adjustments_on_adjusted_string[1].original_offset); + EXPECT_EQ(4u, adjustments_on_adjusted_string[1].original_length); + EXPECT_EQ(0u, adjustments_on_adjusted_string[1].output_length); + EXPECT_EQ(8u, adjustments_on_adjusted_string[2].original_offset); + EXPECT_EQ(1u, adjustments_on_adjusted_string[2].original_length); + EXPECT_EQ(2u, adjustments_on_adjusted_string[2].output_length); + EXPECT_EQ(9u, adjustments_on_adjusted_string[3].original_offset); + EXPECT_EQ(3u, adjustments_on_adjusted_string[3].original_length); + EXPECT_EQ(1u, adjustments_on_adjusted_string[3].output_length); + EXPECT_EQ(19u, adjustments_on_adjusted_string[4].original_offset); + EXPECT_EQ(3u, adjustments_on_adjusted_string[4].original_length); + EXPECT_EQ(0u, adjustments_on_adjusted_string[4].output_length); + EXPECT_EQ(25u, adjustments_on_adjusted_string[5].original_offset); + EXPECT_EQ(1u, adjustments_on_adjusted_string[5].original_length); + EXPECT_EQ(2u, adjustments_on_adjusted_string[5].output_length); +} + } // namaspace base diff --git a/chromium/base/strings/utf_string_conversions.cc b/chromium/base/strings/utf_string_conversions.cc index c3ea4f253e5..f13ed1bb9c6 100644 --- a/chromium/base/strings/utf_string_conversions.cc +++ b/chromium/base/strings/utf_string_conversions.cc @@ -182,4 +182,9 @@ string16 ASCIIToUTF16(const StringPiece& ascii) { return string16(ascii.begin(), ascii.end()); } +std::string UTF16ToASCII(const string16& utf16) { + DCHECK(IsStringASCII(utf16)) << UTF16ToUTF8(utf16); + return std::string(utf16.begin(), utf16.end()); +} + } // namespace base diff --git a/chromium/base/strings/utf_string_conversions.h b/chromium/base/strings/utf_string_conversions.h index 3461aa491df..13e0b7193be 100644 --- a/chromium/base/strings/utf_string_conversions.h +++ b/chromium/base/strings/utf_string_conversions.h @@ -39,34 +39,15 @@ BASE_EXPORT bool UTF16ToUTF8(const char16* src, size_t src_len, std::string* output); BASE_EXPORT std::string UTF16ToUTF8(const string16& utf16); -// We are trying to get rid of wstring as much as possible, but it's too big -// a mess to do it all at once. These conversions should be used when we -// really should just be passing a string16 around, but we haven't finished -// porting whatever module uses wstring and the conversion is being used as a -// stopcock. This makes it easy to grep for the ones that should be removed. -#if defined(OS_WIN) -# define WideToUTF16Hack -# define UTF16ToWideHack -#else -# define WideToUTF16Hack WideToUTF16 -# define UTF16ToWideHack UTF16ToWide -#endif - // These convert an ASCII string, typically a hardcoded constant, to a // UTF16/Wide string. BASE_EXPORT std::wstring ASCIIToWide(const StringPiece& ascii); BASE_EXPORT string16 ASCIIToUTF16(const StringPiece& ascii); -} // namespace base +// Converts to 7-bit ASCII by truncating. The result must be known to be ASCII +// beforehand. +BASE_EXPORT std::string UTF16ToASCII(const string16& utf16); -// TODO(brettw) remove these when callers are fixed up. -using base::WideToUTF8; -using base::UTF8ToWide; -using base::WideToUTF16; -using base::UTF16ToWide; -using base::UTF8ToUTF16; -using base::UTF16ToUTF8; -using base::ASCIIToWide; -using base::ASCIIToUTF16; +} // namespace base #endif // BASE_STRINGS_UTF_STRING_CONVERSIONS_H_ diff --git a/chromium/base/supports_user_data.cc b/chromium/base/supports_user_data.cc index 2a0263ed0d1..9689014d4ad 100644 --- a/chromium/base/supports_user_data.cc +++ b/chromium/base/supports_user_data.cc @@ -35,6 +35,11 @@ void SupportsUserData::DetachUserDataThread() { SupportsUserData::~SupportsUserData() { DCHECK(thread_checker_.CalledOnValidThread() || user_data_.empty()); + DataMap local_user_data; + user_data_.swap(local_user_data); + // Now this->user_data_ is empty, and any destructors called transitively from + // the destruction of |local_user_data| will see it that way instead of + // examining a being-destroyed object. } } // namespace base diff --git a/chromium/base/supports_user_data_unittest.cc b/chromium/base/supports_user_data_unittest.cc new file mode 100644 index 00000000000..f6afc37dc28 --- /dev/null +++ b/chromium/base/supports_user_data_unittest.cc @@ -0,0 +1,39 @@ +// 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/supports_user_data.h" + +#include <vector> + +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { +namespace { + +struct TestSupportsUserData : public SupportsUserData {}; + +struct UsesItself : public SupportsUserData::Data { + UsesItself(SupportsUserData* supports_user_data, const void* key) + : supports_user_data_(supports_user_data), + key_(key) { + } + + virtual ~UsesItself() { + EXPECT_EQ(NULL, supports_user_data_->GetUserData(key_)); + } + + SupportsUserData* supports_user_data_; + const void* key_; +}; + +TEST(SupportsUserDataTest, ClearWorksRecursively) { + TestSupportsUserData supports_user_data; + char key = 0; + supports_user_data.SetUserData(&key, + new UsesItself(&supports_user_data, &key)); + // Destruction of supports_user_data runs the actual test. +} + +} // namespace +} // namespace base diff --git a/chromium/base/sync_socket_posix.cc b/chromium/base/sync_socket_posix.cc index 5bb893aae87..6d397ffa2ec 100644 --- a/chromium/base/sync_socket_posix.cc +++ b/chromium/base/sync_socket_posix.cc @@ -36,7 +36,7 @@ size_t SendHelper(SyncSocket::Handle handle, DCHECK_LE(length, kMaxMessageLength); DCHECK_NE(handle, SyncSocket::kInvalidHandle); const char* charbuffer = static_cast<const char*>(buffer); - const int len = file_util::WriteFileDescriptor(handle, charbuffer, length); + const int len = WriteFileDescriptor(handle, charbuffer, length); return len < 0 ? 0 : static_cast<size_t>(len); } diff --git a/chromium/base/synchronization/condition_variable_posix.cc b/chromium/base/synchronization/condition_variable_posix.cc index e70a301cb2a..8492a01fbc5 100644 --- a/chromium/base/synchronization/condition_variable_posix.cc +++ b/chromium/base/synchronization/condition_variable_posix.cc @@ -23,10 +23,12 @@ ConditionVariable::ConditionVariable(Lock* user_lock) int rv = 0; // http://crbug.com/293736 // NaCl doesn't support monotonic clock based absolute deadlines. - // Android supports it through the non-standard - // pthread_cond_timedwait_monotonic_np. + // On older Android platform versions, it's supported through the + // non-standard pthread_cond_timedwait_monotonic_np. Newer platform + // versions have pthread_condattr_setclock. // Mac can use relative time deadlines. -#if !defined(OS_MACOSX) && !defined(OS_NACL) && !defined(OS_ANDROID) +#if !defined(OS_MACOSX) && !defined(OS_NACL) && \ + !(defined(OS_ANDROID) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC)) pthread_condattr_t attrs; rv = pthread_condattr_init(&attrs); DCHECK_EQ(0, rv); @@ -93,12 +95,12 @@ void ConditionVariable::TimedWait(const TimeDelta& max_time) { absolute_time.tv_nsec %= Time::kNanosecondsPerSecond; DCHECK_GE(absolute_time.tv_sec, now.tv_sec); // Overflow paranoia -#if defined(OS_ANDROID) +#if defined(OS_ANDROID) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC) int rv = pthread_cond_timedwait_monotonic_np( &condition_, user_mutex_, &absolute_time); #else int rv = pthread_cond_timedwait(&condition_, user_mutex_, &absolute_time); -#endif // OS_ANDROID +#endif // OS_ANDROID && HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC #endif // OS_MACOSX DCHECK(rv == 0 || rv == ETIMEDOUT); diff --git a/chromium/base/synchronization/condition_variable_unittest.cc b/chromium/base/synchronization/condition_variable_unittest.cc index ee554ff329f..5f947a9567f 100644 --- a/chromium/base/synchronization/condition_variable_unittest.cc +++ b/chromium/base/synchronization/condition_variable_unittest.cc @@ -101,7 +101,6 @@ class WorkQueue : public PlatformThread::Delegate { int GetNumThreadsTakingAssignments() const; int GetNumThreadsCompletingTasks() const; int GetNumberOfCompletedTasks() const; - TimeDelta GetWorkTime() const; void SetWorkTime(TimeDelta delay); void SetTaskCount(int count); @@ -651,10 +650,6 @@ int WorkQueue::GetNumberOfCompletedTasks() const { return total; } -TimeDelta WorkQueue::GetWorkTime() const { - return worker_delay_; -} - void WorkQueue::SetWorkTime(TimeDelta delay) { worker_delay_ = delay; } diff --git a/chromium/base/synchronization/lock.cc b/chromium/base/synchronization/lock.cc index 49efbe92651..7c7ee9dc5a9 100644 --- a/chromium/base/synchronization/lock.cc +++ b/chromium/base/synchronization/lock.cc @@ -13,34 +13,25 @@ namespace base { -const PlatformThreadId kNoThreadId = static_cast<PlatformThreadId>(0); - Lock::Lock() : lock_() { - owned_by_thread_ = false; - owning_thread_id_ = kNoThreadId; } Lock::~Lock() { - DCHECK(!owned_by_thread_); - DCHECK_EQ(kNoThreadId, owning_thread_id_); + DCHECK(owning_thread_ref_.is_null()); } void Lock::AssertAcquired() const { - DCHECK(owned_by_thread_); - DCHECK_EQ(owning_thread_id_, PlatformThread::CurrentId()); + DCHECK(owning_thread_ref_ == PlatformThread::CurrentRef()); } void Lock::CheckHeldAndUnmark() { - DCHECK(owned_by_thread_); - DCHECK_EQ(owning_thread_id_, PlatformThread::CurrentId()); - owned_by_thread_ = false; - owning_thread_id_ = kNoThreadId; + DCHECK(owning_thread_ref_ == PlatformThread::CurrentRef()); + owning_thread_ref_ = PlatformThreadRef(); } void Lock::CheckUnheldAndMark() { - DCHECK(!owned_by_thread_); - owned_by_thread_ = true; - owning_thread_id_ = PlatformThread::CurrentId(); + DCHECK(owning_thread_ref_.is_null()); + owning_thread_ref_ = PlatformThread::CurrentRef(); } } // namespace base diff --git a/chromium/base/synchronization/lock.h b/chromium/base/synchronization/lock.h index 7e8ffe7b741..9c0d062a900 100644 --- a/chromium/base/synchronization/lock.h +++ b/chromium/base/synchronization/lock.h @@ -80,11 +80,7 @@ class BASE_EXPORT Lock { // All private data is implicitly protected by lock_. // Be VERY careful to only access members under that lock. - - // Determines validity of owning_thread_id_. Needed as we don't have - // a null owning_thread_id_ value. - bool owned_by_thread_; - base::PlatformThreadId owning_thread_id_; + base::PlatformThreadRef owning_thread_ref_; #endif // NDEBUG // Platform specific underlying lock implementation. diff --git a/chromium/base/synchronization/waitable_event_win.cc b/chromium/base/synchronization/waitable_event_win.cc index 8259cb74cfc..04746b597b4 100644 --- a/chromium/base/synchronization/waitable_event_win.cc +++ b/chromium/base/synchronization/waitable_event_win.cc @@ -92,7 +92,7 @@ size_t WaitableEvent::WaitMany(WaitableEvent** events, size_t count) { FALSE, // don't wait for all the objects INFINITE); // no timeout if (result >= WAIT_OBJECT_0 + count) { - DLOG_GETLASTERROR(FATAL) << "WaitForMultipleObjects failed"; + DPLOG(FATAL) << "WaitForMultipleObjects failed"; return 0; } diff --git a/chromium/base/sys_byteorder.h b/chromium/base/sys_byteorder.h index 17b662c58f0..704ed568b09 100644 --- a/chromium/base/sys_byteorder.h +++ b/chromium/base/sys_byteorder.h @@ -20,59 +20,27 @@ #include <arpa/inet.h> #endif -// Include headers to provide byteswap for all platforms. -#if defined(COMPILER_MSVC) -#include <stdlib.h> -#elif defined(OS_MACOSX) -#include <libkern/OSByteOrder.h> -#elif defined(OS_BSD) -#include <sys/endian.h> -#else -#include <byteswap.h> -#endif - - namespace base { // Returns a value with all bytes in |x| swapped, i.e. reverses the endianness. inline uint16 ByteSwap(uint16 x) { -#if defined(COMPILER_MSVC) - return _byteswap_ushort(x); -#elif defined(OS_MACOSX) - return OSSwapInt16(x); -#elif defined(OS_OPENBSD) - return swap16(x); -#elif defined(OS_FREEBSD) - return bswap16(x); -#else - return bswap_16(x); -#endif + return ((x & 0x00ff) << 8) | ((x & 0xff00) >> 8); } + inline uint32 ByteSwap(uint32 x) { -#if defined(COMPILER_MSVC) - return _byteswap_ulong(x); -#elif defined(OS_MACOSX) - return OSSwapInt32(x); -#elif defined(OS_OPENBSD) - return swap32(x); -#elif defined(OS_FREEBSD) - return bswap32(x); -#else - return bswap_32(x); -#endif + return ((x & 0x000000fful) << 24) | ((x & 0x0000ff00ul) << 8) | + ((x & 0x00ff0000ul) >> 8) | ((x & 0xff000000ul) >> 24); } + inline uint64 ByteSwap(uint64 x) { -#if defined(COMPILER_MSVC) - return _byteswap_uint64(x); -#elif defined(OS_MACOSX) - return OSSwapInt64(x); -#elif defined(OS_OPENBSD) - return swap64(x); -#elif defined(OS_FREEBSD) - return bswap64(x); -#else - return bswap_64(x); -#endif + return ((x & 0x00000000000000ffull) << 56) | + ((x & 0x000000000000ff00ull) << 40) | + ((x & 0x0000000000ff0000ull) << 24) | + ((x & 0x00000000ff000000ull) << 8) | + ((x & 0x000000ff00000000ull) >> 8) | + ((x & 0x0000ff0000000000ull) >> 24) | + ((x & 0x00ff000000000000ull) >> 40) | + ((x & 0xff00000000000000ull) >> 56); } // Converts the bytes in |x| from host order (endianness) to little endian, and @@ -149,5 +117,4 @@ inline uint64 HostToNet64(uint64 x) { } // namespace base - #endif // BASE_SYS_BYTEORDER_H_ diff --git a/chromium/base/sys_info.h b/chromium/base/sys_info.h index aa40cadfbc9..7ce4e659f28 100644 --- a/chromium/base/sys_info.h +++ b/chromium/base/sys_info.h @@ -28,11 +28,22 @@ class BASE_EXPORT SysInfo { // machine. static int64 AmountOfAvailablePhysicalMemory(); + // Return the number of bytes of virtual memory of this process. A return + // value of zero means that there is no limit on the available virtual + // memory. + static int64 AmountOfVirtualMemory(); + // Return the number of megabytes of physical memory on the current machine. static int AmountOfPhysicalMemoryMB() { return static_cast<int>(AmountOfPhysicalMemory() / 1024 / 1024); } + // Return the number of megabytes of available virtual memory, or zero if it + // is unlimited. + static int AmountOfVirtualMemoryMB() { + return static_cast<int>(AmountOfVirtualMemory() / 1024 / 1024); + } + // Return the available disk space in bytes on the volume containing |path|, // or -1 on failure. static int64 AmountOfFreeDiskSpace(const FilePath& path); diff --git a/chromium/base/sys_info_android.cc b/chromium/base/sys_info_android.cc index 577ca0e6bd1..8884e15d464 100644 --- a/chromium/base/sys_info_android.cc +++ b/chromium/base/sys_info_android.cc @@ -4,22 +4,64 @@ #include "base/sys_info.h" +#include <dlfcn.h> #include <sys/system_properties.h> +#include "base/lazy_instance.h" #include "base/logging.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_piece.h" #include "base/strings/stringprintf.h" +#include "base/sys_info_internal.h" + +// TODO(rmcilroy): Update API level when 'L' gets an official API level. +#if (__ANDROID_API__ >= 9999 /* 'L' */) + +namespace { + +typedef int (SystemPropertyGetFunction)(const char*, char*); + +SystemPropertyGetFunction* DynamicallyLoadRealSystemPropertyGet() { + // libc.so should already be open, get a handle to it. + void* handle = dlopen("libc.so", RTLD_NOLOAD); + if (!handle) { + LOG(FATAL) << "Cannot dlopen libc.so: " << dlerror(); + } + SystemPropertyGetFunction* real_system_property_get = + reinterpret_cast<SystemPropertyGetFunction*>( + dlsym(handle, "__system_property_get")); + if (!real_system_property_get) { + LOG(FATAL) << "Cannot resolve __system_property_get(): " << dlerror(); + } + return real_system_property_get; +} + +static base::LazyInstance<base::internal::LazySysInfoValue< + SystemPropertyGetFunction*, DynamicallyLoadRealSystemPropertyGet> >::Leaky + g_lazy_real_system_property_get = LAZY_INSTANCE_INITIALIZER; + +} // namespace + +// Android 'L' removes __system_property_get from the NDK, however it is still +// a hidden symbol in libc. Until we remove all calls of __system_property_get +// 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) { + return g_lazy_real_system_property_get.Get().value()(name, value); +} + +#endif namespace { // Default version of Android to fall back to when actual version numbers -// cannot be acquired. -// TODO(dfalcantara): Keep this reasonably up to date with the latest publicly -// available version of Android. +// 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 = 3; -const int kDefaultAndroidBugfixVersion = 0; +const int kDefaultAndroidMinorVersion = 4; +const int kDefaultAndroidBugfixVersion = 99; // Parse out the OS version numbers from the system properties. void ParseOSVersionNumbers(const char* os_version_str, diff --git a/chromium/base/sys_info_chromeos.cc b/chromium/base/sys_info_chromeos.cc index 7cf69753460..4f32f9bfe63 100644 --- a/chromium/base/sys_info_chromeos.cc +++ b/chromium/base/sys_info_chromeos.cc @@ -7,6 +7,7 @@ #include "base/basictypes.h" #include "base/environment.h" #include "base/file_util.h" +#include "base/files/file.h" #include "base/files/file_path.h" #include "base/lazy_instance.h" #include "base/strings/string_number_conversions.h" @@ -71,7 +72,7 @@ class ChromeOSVersionInfo { ThreadRestrictions::ScopedAllowIO allow_io; FilePath path(kLinuxStandardBaseReleaseFile); ReadFileToString(path, &lsb_release); - PlatformFileInfo fileinfo; + File::Info fileinfo; if (GetFileInfo(path, &fileinfo)) lsb_release_time_ = fileinfo.creation_time; } diff --git a/chromium/base/sys_info_ios.mm b/chromium/base/sys_info_ios.mm index de68fcf37c1..49d618c416f 100644 --- a/chromium/base/sys_info_ios.mm +++ b/chromium/base/sys_info_ios.mm @@ -65,7 +65,7 @@ void SysInfo::OperatingSystemVersionNumbers(int32* major_version, int64 SysInfo::AmountOfPhysicalMemory() { struct host_basic_info hostinfo; mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT; - base::mac::ScopedMachPort host(mach_host_self()); + base::mac::ScopedMachSendRight host(mach_host_self()); int result = host_info(host, HOST_BASIC_INFO, reinterpret_cast<host_info_t>(&hostinfo), @@ -79,6 +79,23 @@ int64 SysInfo::AmountOfPhysicalMemory() { } // static +int64 SysInfo::AmountOfAvailablePhysicalMemory() { + base::mac::ScopedMachSendRight host(mach_host_self()); + vm_statistics_data_t vm_info; + mach_msg_type_number_t count = HOST_VM_INFO_COUNT; + if (host_statistics(host.get(), + HOST_VM_INFO, + reinterpret_cast<host_info_t>(&vm_info), + &count) != KERN_SUCCESS) { + NOTREACHED(); + return 0; + } + + return static_cast<int64>( + vm_info.free_count - vm_info.speculative_count) * PAGE_SIZE; +} + +// static std::string SysInfo::CPUModelName() { char name[256]; size_t len = arraysize(name); diff --git a/chromium/base/sys_info_mac.cc b/chromium/base/sys_info_mac.cc index 2911c7c4ce3..57e1f835e32 100644 --- a/chromium/base/sys_info_mac.cc +++ b/chromium/base/sys_info_mac.cc @@ -45,7 +45,7 @@ void SysInfo::OperatingSystemVersionNumbers(int32* major_version, int64 SysInfo::AmountOfPhysicalMemory() { struct host_basic_info hostinfo; mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT; - base::mac::ScopedMachPort host(mach_host_self()); + base::mac::ScopedMachSendRight host(mach_host_self()); int result = host_info(host, HOST_BASIC_INFO, reinterpret_cast<host_info_t>(&hostinfo), @@ -60,7 +60,7 @@ int64 SysInfo::AmountOfPhysicalMemory() { // static int64 SysInfo::AmountOfAvailablePhysicalMemory() { - base::mac::ScopedMachPort host(mach_host_self()); + base::mac::ScopedMachSendRight host(mach_host_self()); vm_statistics_data_t vm_info; mach_msg_type_number_t count = HOST_VM_INFO_COUNT; diff --git a/chromium/base/sys_info_posix.cc b/chromium/base/sys_info_posix.cc index 07d08b72bbc..90baa69a2f6 100644 --- a/chromium/base/sys_info_posix.cc +++ b/chromium/base/sys_info_posix.cc @@ -7,6 +7,7 @@ #include <errno.h> #include <string.h> #include <sys/param.h> +#include <sys/resource.h> #include <sys/utsname.h> #include <unistd.h> @@ -45,6 +46,20 @@ base::LazyInstance< g_lazy_number_of_processors = LAZY_INSTANCE_INITIALIZER; #endif +int64 AmountOfVirtualMemory() { + struct rlimit limit; + int result = getrlimit(RLIMIT_DATA, &limit); + if (result != 0) { + NOTREACHED(); + return 0; + } + return limit.rlim_cur == RLIM_INFINITY ? 0 : limit.rlim_cur; +} + +base::LazyInstance< + base::internal::LazySysInfoValue<int64, AmountOfVirtualMemory> >::Leaky + g_lazy_virtual_memory = LAZY_INSTANCE_INITIALIZER; + } // namespace namespace base { @@ -56,6 +71,11 @@ int SysInfo::NumberOfProcessors() { #endif // static +int64 SysInfo::AmountOfVirtualMemory() { + return g_lazy_virtual_memory.Get().value(); +} + +// static int64 SysInfo::AmountOfFreeDiskSpace(const FilePath& path) { base::ThreadRestrictions::AssertIOAllowed(); diff --git a/chromium/base/sys_info_unittest.cc b/chromium/base/sys_info_unittest.cc index 93eff777c9b..e771b2b9879 100644 --- a/chromium/base/sys_info_unittest.cc +++ b/chromium/base/sys_info_unittest.cc @@ -29,6 +29,8 @@ TEST_F(SysInfoTest, AmountOfMem) { // We aren't actually testing that it's correct, just that it's sane. EXPECT_GT(base::SysInfo::AmountOfPhysicalMemory(), 0); EXPECT_GT(base::SysInfo::AmountOfPhysicalMemoryMB(), 0); + // The maxmimal amount of virtual memory can be zero which means unlimited. + EXPECT_GE(base::SysInfo::AmountOfVirtualMemory(), 0); } TEST_F(SysInfoTest, AmountOfFreeDiskSpace) { diff --git a/chromium/base/sys_info_win.cc b/chromium/base/sys_info_win.cc index 191fd94ff5e..9cc0cfa48a3 100644 --- a/chromium/base/sys_info_win.cc +++ b/chromium/base/sys_info_win.cc @@ -49,6 +49,11 @@ int64 SysInfo::AmountOfAvailablePhysicalMemory() { } // static +int64 SysInfo::AmountOfVirtualMemory() { + return 0; +} + +// static int64 SysInfo::AmountOfFreeDiskSpace(const FilePath& path) { base::ThreadRestrictions::AssertIOAllowed(); diff --git a/chromium/base/task/cancelable_task_tracker.cc b/chromium/base/task/cancelable_task_tracker.cc new file mode 100644 index 00000000000..801223e2806 --- /dev/null +++ b/chromium/base/task/cancelable_task_tracker.cc @@ -0,0 +1,187 @@ +// 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/task/cancelable_task_tracker.h" + +#include <utility> + +#include "base/bind.h" +#include "base/callback_helpers.h" +#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/synchronization/cancellation_flag.h" +#include "base/task_runner.h" + +using base::Bind; +using base::CancellationFlag; +using base::Closure; +using base::hash_map; +using base::TaskRunner; + +namespace { + +void RunIfNotCanceled(const CancellationFlag* flag, const Closure& task) { + if (!flag->IsSet()) + task.Run(); +} + +void RunIfNotCanceledThenUntrack(const CancellationFlag* flag, + const Closure& task, + const Closure& untrack) { + RunIfNotCanceled(flag, task); + untrack.Run(); +} + +bool IsCanceled(const CancellationFlag* flag, + base::ScopedClosureRunner* cleanup_runner) { + return flag->IsSet(); +} + +void RunAndDeleteFlag(const Closure& closure, const CancellationFlag* flag) { + closure.Run(); + delete flag; +} + +void RunOrPostToTaskRunner(TaskRunner* task_runner, const Closure& closure) { + if (task_runner->RunsTasksOnCurrentThread()) + closure.Run(); + else + task_runner->PostTask(FROM_HERE, closure); +} + +} // namespace + +namespace base { + +// static +const CancelableTaskTracker::TaskId CancelableTaskTracker::kBadTaskId = 0; + +CancelableTaskTracker::CancelableTaskTracker() + : weak_factory_(this), next_id_(1) {} + +CancelableTaskTracker::~CancelableTaskTracker() { + DCHECK(thread_checker_.CalledOnValidThread()); + + TryCancelAll(); +} + +CancelableTaskTracker::TaskId CancelableTaskTracker::PostTask( + TaskRunner* task_runner, + const tracked_objects::Location& from_here, + const Closure& task) { + DCHECK(thread_checker_.CalledOnValidThread()); + + return PostTaskAndReply(task_runner, from_here, task, Bind(&base::DoNothing)); +} + +CancelableTaskTracker::TaskId CancelableTaskTracker::PostTaskAndReply( + TaskRunner* task_runner, + const tracked_objects::Location& from_here, + const Closure& task, + const Closure& reply) { + DCHECK(thread_checker_.CalledOnValidThread()); + + // We need a MessageLoop to run reply. + DCHECK(base::MessageLoopProxy::current().get()); + + // Owned by reply callback below. + CancellationFlag* flag = new CancellationFlag(); + + TaskId id = next_id_; + next_id_++; // int64 is big enough that we ignore the potential overflow. + + const Closure& untrack_closure = + Bind(&CancelableTaskTracker::Untrack, weak_factory_.GetWeakPtr(), id); + bool success = + task_runner->PostTaskAndReply(from_here, + Bind(&RunIfNotCanceled, flag, task), + Bind(&RunIfNotCanceledThenUntrack, + base::Owned(flag), + reply, + untrack_closure)); + + if (!success) + return kBadTaskId; + + Track(id, flag); + return id; +} + +CancelableTaskTracker::TaskId CancelableTaskTracker::NewTrackedTaskId( + IsCanceledCallback* is_canceled_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(base::MessageLoopProxy::current().get()); + + TaskId id = next_id_; + next_id_++; // int64 is big enough that we ignore the potential overflow. + + // Will be deleted by |untrack_and_delete_flag| after Untrack(). + CancellationFlag* flag = new CancellationFlag(); + + Closure untrack_and_delete_flag = Bind( + &RunAndDeleteFlag, + Bind(&CancelableTaskTracker::Untrack, weak_factory_.GetWeakPtr(), id), + flag); + + // 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(), + untrack_and_delete_flag)); + + *is_canceled_cb = + Bind(&IsCanceled, flag, base::Owned(untrack_and_delete_flag_runner)); + + Track(id, flag); + return id; +} + +void CancelableTaskTracker::TryCancel(TaskId id) { + DCHECK(thread_checker_.CalledOnValidThread()); + + hash_map<TaskId, CancellationFlag*>::const_iterator it = task_flags_.find(id); + if (it == task_flags_.end()) { + // Two possibilities: + // + // 1. The task has already been untracked. + // 2. The TaskId is bad or unknown. + // + // Since this function is best-effort, it's OK to ignore these. + return; + } + it->second->Set(); +} + +void CancelableTaskTracker::TryCancelAll() { + DCHECK(thread_checker_.CalledOnValidThread()); + + for (hash_map<TaskId, CancellationFlag*>::const_iterator it = + task_flags_.begin(); + it != task_flags_.end(); + ++it) { + it->second->Set(); + } +} + +bool CancelableTaskTracker::HasTrackedTasks() const { + DCHECK(thread_checker_.CalledOnValidThread()); + return !task_flags_.empty(); +} + +void CancelableTaskTracker::Track(TaskId id, CancellationFlag* flag) { + DCHECK(thread_checker_.CalledOnValidThread()); + + bool success = task_flags_.insert(std::make_pair(id, flag)).second; + DCHECK(success); +} + +void CancelableTaskTracker::Untrack(TaskId id) { + DCHECK(thread_checker_.CalledOnValidThread()); + size_t num = task_flags_.erase(id); + DCHECK_EQ(1u, num); +} + +} // namespace base diff --git a/chromium/base/task/cancelable_task_tracker.h b/chromium/base/task/cancelable_task_tracker.h new file mode 100644 index 00000000000..21cb45df1c0 --- /dev/null +++ b/chromium/base/task/cancelable_task_tracker.h @@ -0,0 +1,120 @@ +// 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. + +// CancelableTaskTracker posts tasks (in the form of a Closure) to a +// TaskRunner, and is able to cancel the task later if it's not needed +// anymore. On destruction, CancelableTaskTracker will cancel all +// tracked tasks. +// +// Each cancelable task can be associated with a reply (also a Closure). After +// the task is run on the TaskRunner, |reply| will be posted back to +// originating TaskRunner. +// +// NOTE: +// +// CancelableCallback (base/cancelable_callback.h) and WeakPtr binding are +// preferred solutions for canceling a task. However, they don't support +// cancelation from another thread. This is sometimes a performance critical +// requirement. E.g. We need to cancel database lookup task on DB thread when +// user changes inputed text. If it is performance critical to do a best effort +// cancelation of a task, then CancelableTaskTracker is appropriate, +// otherwise use one of the other mechanisms. +// +// THREAD-SAFETY: +// +// 1. CancelableTaskTracker objects are not thread safe. They must +// be created, used, and destroyed on the originating thread that posts the +// task. It's safe to destroy a CancelableTaskTracker while there +// are outstanding tasks. This is commonly used to cancel all outstanding +// tasks. +// +// 2. Both task and reply are deleted on the originating thread. +// +// 3. IsCanceledCallback is thread safe and can be run or deleted on any +// thread. +#ifndef BASE_TASK_CANCELABLE_TASK_TRACKER_H_ +#define BASE_TASK_CANCELABLE_TASK_TRACKER_H_ + +#include "base/base_export.h" +#include "base/basictypes.h" +#include "base/callback.h" +#include "base/containers/hash_tables.h" +#include "base/memory/weak_ptr.h" +#include "base/threading/thread_checker.h" + +namespace tracked_objects { +class Location; +} // namespace tracked_objects + +namespace base { + +class CancellationFlag; +class TaskRunner; + +class BASE_EXPORT CancelableTaskTracker { + public: + // All values except kBadTaskId are valid. + typedef int64 TaskId; + static const TaskId kBadTaskId; + + typedef base::Callback<bool()> IsCanceledCallback; + + CancelableTaskTracker(); + + // Cancels all tracked tasks. + ~CancelableTaskTracker(); + + TaskId PostTask(base::TaskRunner* task_runner, + const tracked_objects::Location& from_here, + const base::Closure& task); + + TaskId PostTaskAndReply(base::TaskRunner* task_runner, + const tracked_objects::Location& from_here, + const base::Closure& task, + const base::Closure& reply); + + // Creates a tracked TaskId and an associated IsCanceledCallback. Client can + // later call TryCancel() with the returned TaskId, and run |is_canceled_cb| + // from any thread to check whether the TaskId is canceled. + // + // The returned task ID is tracked until the last copy of + // |is_canceled_cb| is destroyed. + // + // Note. This function is used to address some special cancelation requirement + // in existing code. You SHOULD NOT need this function in new code. + TaskId NewTrackedTaskId(IsCanceledCallback* is_canceled_cb); + + // After calling this function, |task| and |reply| will not run. If the + // cancelation happens when |task| is running or has finished running, |reply| + // will not run. If |reply| is running or has finished running, cancellation + // is a noop. + // + // Note. It's OK to cancel a |task| for more than once. The later calls are + // noops. + void TryCancel(TaskId id); + + // It's OK to call this function for more than once. The later calls are + // noops. + void TryCancelAll(); + + // Returns true iff there are in-flight tasks that are still being + // tracked. + bool HasTrackedTasks() const; + + private: + void Track(TaskId id, base::CancellationFlag* flag); + void Untrack(TaskId id); + + base::hash_map<TaskId, base::CancellationFlag*> task_flags_; + base::WeakPtrFactory<CancelableTaskTracker> weak_factory_; + + TaskId next_id_; + base::ThreadChecker thread_checker_; + + DISALLOW_COPY_AND_ASSIGN(CancelableTaskTracker); +}; + +} // namespace base + +#endif // BASE_TASK_CANCELABLE_TASK_TRACKER_H_ diff --git a/chromium/base/task/cancelable_task_tracker_unittest.cc b/chromium/base/task/cancelable_task_tracker_unittest.cc new file mode 100644 index 00000000000..e122e8deef0 --- /dev/null +++ b/chromium/base/task/cancelable_task_tracker_unittest.cc @@ -0,0 +1,435 @@ +// 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/task/cancelable_task_tracker.h" + +#include <cstddef> +#include <deque> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/location.h" +#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/test/test_simple_task_runner.h" +#include "base/threading/thread.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { + +namespace { + +class CancelableTaskTrackerTest : public testing::Test { + protected: + virtual ~CancelableTaskTrackerTest() { RunCurrentLoopUntilIdle(); } + + void RunCurrentLoopUntilIdle() { + RunLoop run_loop; + run_loop.RunUntilIdle(); + } + + CancelableTaskTracker task_tracker_; + + private: + // Needed by CancelableTaskTracker methods. + MessageLoop message_loop_; +}; + +void AddFailureAt(const tracked_objects::Location& location) { + ADD_FAILURE_AT(location.file_name(), location.line_number()); +} + +// Returns a closure that fails if run. +Closure MakeExpectedNotRunClosure(const tracked_objects::Location& location) { + return Bind(&AddFailureAt, location); +} + +// A helper class for MakeExpectedRunClosure() that fails if it is +// destroyed without Run() having been called. This class may be used +// from multiple threads as long as Run() is called at most once +// before destruction. +class RunChecker { + public: + explicit RunChecker(const tracked_objects::Location& location) + : location_(location), called_(false) {} + + ~RunChecker() { + if (!called_) { + ADD_FAILURE_AT(location_.file_name(), location_.line_number()); + } + } + + void Run() { called_ = true; } + + private: + tracked_objects::Location location_; + bool called_; +}; + +// Returns a closure that fails on destruction if it hasn't been run. +Closure MakeExpectedRunClosure(const tracked_objects::Location& location) { + return Bind(&RunChecker::Run, Owned(new RunChecker(location))); +} + +} // namespace + +// With the task tracker, post a task, a task with a reply, and get a +// new task id without canceling any of them. The tasks and the reply +// should run and the "is canceled" callback should return false. +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(), + FROM_HERE, + MakeExpectedRunClosure(FROM_HERE))); + + ignore_result( + task_tracker_.PostTaskAndReply(worker_thread.message_loop_proxy().get(), + FROM_HERE, + MakeExpectedRunClosure(FROM_HERE), + MakeExpectedRunClosure(FROM_HERE))); + + CancelableTaskTracker::IsCanceledCallback is_canceled; + ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled)); + + worker_thread.Stop(); + + RunCurrentLoopUntilIdle(); + + EXPECT_FALSE(is_canceled.Run()); +} + +// Post a task with the task tracker but cancel it before running the +// task runner. The task should not run. +TEST_F(CancelableTaskTrackerTest, CancelPostedTask) { + scoped_refptr<TestSimpleTaskRunner> test_task_runner( + new TestSimpleTaskRunner()); + + CancelableTaskTracker::TaskId task_id = task_tracker_.PostTask( + test_task_runner.get(), FROM_HERE, MakeExpectedNotRunClosure(FROM_HERE)); + EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); + + EXPECT_EQ(1U, test_task_runner->GetPendingTasks().size()); + + task_tracker_.TryCancel(task_id); + + test_task_runner->RunUntilIdle(); +} + +// Post a task with reply with the task tracker and cancel it before +// running the task runner. Neither the task nor the reply should +// run. +TEST_F(CancelableTaskTrackerTest, CancelPostedTaskAndReply) { + scoped_refptr<TestSimpleTaskRunner> test_task_runner( + new TestSimpleTaskRunner()); + + CancelableTaskTracker::TaskId task_id = + task_tracker_.PostTaskAndReply(test_task_runner.get(), + FROM_HERE, + MakeExpectedNotRunClosure(FROM_HERE), + MakeExpectedNotRunClosure(FROM_HERE)); + EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); + + task_tracker_.TryCancel(task_id); + + test_task_runner->RunUntilIdle(); +} + +// Post a task with reply with the task tracker and cancel it after +// running the task runner but before running the current message +// loop. The task should run but the reply should not. +TEST_F(CancelableTaskTrackerTest, CancelReply) { + scoped_refptr<TestSimpleTaskRunner> test_task_runner( + new TestSimpleTaskRunner()); + + CancelableTaskTracker::TaskId task_id = + task_tracker_.PostTaskAndReply(test_task_runner.get(), + FROM_HERE, + MakeExpectedRunClosure(FROM_HERE), + MakeExpectedNotRunClosure(FROM_HERE)); + EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); + + test_task_runner->RunUntilIdle(); + + task_tracker_.TryCancel(task_id); +} + +// Post a task with reply with the task tracker on a worker thread and +// cancel it before running the current message loop. The task should +// run but the reply should not. +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)); + EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); + + task_tracker_.TryCancel(task_id); + + worker_thread.Stop(); +} + +void ExpectIsCanceled( + const CancelableTaskTracker::IsCanceledCallback& is_canceled, + bool expected_is_canceled) { + EXPECT_EQ(expected_is_canceled, is_canceled.Run()); +} + +// Create a new task ID and check its status on a separate thread +// before and after canceling. The is-canceled callback should be +// thread-safe (i.e., nothing should blow up). +TEST_F(CancelableTaskTrackerTest, NewTrackedTaskIdDifferentThread) { + CancelableTaskTracker::IsCanceledCallback is_canceled; + CancelableTaskTracker::TaskId task_id = + task_tracker_.NewTrackedTaskId(&is_canceled); + + EXPECT_FALSE(is_canceled.Run()); + + Thread other_thread("other thread"); + ASSERT_TRUE(other_thread.Start()); + other_thread.message_loop_proxy()->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( + FROM_HERE, Bind(&ExpectIsCanceled, is_canceled, true)); + other_thread.Stop(); +} + +// With the task tracker, post a task, a task with a reply, get a new +// task id, and then cancel all of them. None of the tasks nor the +// reply should run and the "is canceled" callback should return +// true. +TEST_F(CancelableTaskTrackerTest, CancelAll) { + scoped_refptr<TestSimpleTaskRunner> test_task_runner( + new TestSimpleTaskRunner()); + + ignore_result(task_tracker_.PostTask( + test_task_runner.get(), FROM_HERE, MakeExpectedNotRunClosure(FROM_HERE))); + + ignore_result( + task_tracker_.PostTaskAndReply(test_task_runner.get(), + FROM_HERE, + MakeExpectedNotRunClosure(FROM_HERE), + MakeExpectedNotRunClosure(FROM_HERE))); + + CancelableTaskTracker::IsCanceledCallback is_canceled; + ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled)); + + task_tracker_.TryCancelAll(); + + test_task_runner->RunUntilIdle(); + + RunCurrentLoopUntilIdle(); + + EXPECT_TRUE(is_canceled.Run()); +} + +// With the task tracker, post a task, a task with a reply, get a new +// task id, and then cancel all of them. None of the tasks nor the +// reply should run and the "is canceled" callback should return +// true. +TEST_F(CancelableTaskTrackerTest, DestructionCancelsAll) { + scoped_refptr<TestSimpleTaskRunner> test_task_runner( + new TestSimpleTaskRunner()); + + CancelableTaskTracker::IsCanceledCallback is_canceled; + + { + // Create another task tracker with a smaller scope. + CancelableTaskTracker task_tracker; + + ignore_result(task_tracker.PostTask(test_task_runner.get(), + FROM_HERE, + MakeExpectedNotRunClosure(FROM_HERE))); + + ignore_result( + task_tracker.PostTaskAndReply(test_task_runner.get(), + FROM_HERE, + MakeExpectedNotRunClosure(FROM_HERE), + MakeExpectedNotRunClosure(FROM_HERE))); + + ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled)); + } + + test_task_runner->RunUntilIdle(); + + RunCurrentLoopUntilIdle(); + + EXPECT_FALSE(is_canceled.Run()); +} + +// Post a task and cancel it. HasTrackedTasks() should return true +// from when the task is posted until the (do-nothing) reply task is +// flushed. +TEST_F(CancelableTaskTrackerTest, HasTrackedTasksPost) { + scoped_refptr<TestSimpleTaskRunner> test_task_runner( + new TestSimpleTaskRunner()); + + EXPECT_FALSE(task_tracker_.HasTrackedTasks()); + + ignore_result(task_tracker_.PostTask( + test_task_runner.get(), FROM_HERE, MakeExpectedNotRunClosure(FROM_HERE))); + + task_tracker_.TryCancelAll(); + + test_task_runner->RunUntilIdle(); + + EXPECT_TRUE(task_tracker_.HasTrackedTasks()); + + RunCurrentLoopUntilIdle(); + + EXPECT_FALSE(task_tracker_.HasTrackedTasks()); +} + +// Post a task with a reply and cancel it. HasTrackedTasks() should +// return true from when the task is posted until it is canceled. +TEST_F(CancelableTaskTrackerTest, HasTrackedTasksPostWithReply) { + scoped_refptr<TestSimpleTaskRunner> test_task_runner( + new TestSimpleTaskRunner()); + + EXPECT_FALSE(task_tracker_.HasTrackedTasks()); + + ignore_result( + task_tracker_.PostTaskAndReply(test_task_runner.get(), + FROM_HERE, + MakeExpectedNotRunClosure(FROM_HERE), + MakeExpectedNotRunClosure(FROM_HERE))); + + task_tracker_.TryCancelAll(); + + test_task_runner->RunUntilIdle(); + + EXPECT_TRUE(task_tracker_.HasTrackedTasks()); + + RunCurrentLoopUntilIdle(); + + EXPECT_FALSE(task_tracker_.HasTrackedTasks()); +} + +// Create a new tracked task ID. HasTrackedTasks() should return true +// until the IsCanceledCallback is destroyed. +TEST_F(CancelableTaskTrackerTest, HasTrackedTasksIsCancelled) { + EXPECT_FALSE(task_tracker_.HasTrackedTasks()); + + CancelableTaskTracker::IsCanceledCallback is_canceled; + ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled)); + + task_tracker_.TryCancelAll(); + + EXPECT_TRUE(task_tracker_.HasTrackedTasks()); + + is_canceled.Reset(); + + EXPECT_FALSE(task_tracker_.HasTrackedTasks()); +} + +// The death tests below make sure that calling task tracker member +// functions from a thread different from its owner thread DCHECKs in +// debug mode. + +class CancelableTaskTrackerDeathTest : public CancelableTaskTrackerTest { + protected: + CancelableTaskTrackerDeathTest() { + // 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 +// good citizens there and undef the macro. +#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON) +#define ENABLE_THREAD_CHECKER 1 +#else +#define ENABLE_THREAD_CHECKER 0 +#endif + +// Runs |fn| with |task_tracker|, expecting it to crash in debug mode. +void MaybeRunDeadlyTaskTrackerMemberFunction( + CancelableTaskTracker* task_tracker, + const Callback<void(CancelableTaskTracker*)>& fn) { +// CancelableTask uses DCHECKs with its ThreadChecker (itself only +// enabled in debug mode). +#if ENABLE_THREAD_CHECKER + EXPECT_DEATH_IF_SUPPORTED(fn.Run(task_tracker), ""); +#endif +} + +void PostDoNothingTask(CancelableTaskTracker* task_tracker) { + ignore_result(task_tracker->PostTask( + scoped_refptr<TestSimpleTaskRunner>(new TestSimpleTaskRunner()).get(), + FROM_HERE, + Bind(&DoNothing))); +} + +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))); +} + +void TryCancel(CancelableTaskTracker::TaskId task_id, + CancelableTaskTracker* task_tracker) { + task_tracker->TryCancel(task_id); +} + +TEST_F(CancelableTaskTrackerDeathTest, CancelOnDifferentThread) { + scoped_refptr<TestSimpleTaskRunner> test_task_runner( + new TestSimpleTaskRunner()); + + Thread bad_thread("bad thread"); + ASSERT_TRUE(bad_thread.Start()); + + CancelableTaskTracker::TaskId task_id = task_tracker_.PostTask( + 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))); + + test_task_runner->RunUntilIdle(); +} + +TEST_F(CancelableTaskTrackerDeathTest, CancelAllOnDifferentThread) { + scoped_refptr<TestSimpleTaskRunner> test_task_runner( + new TestSimpleTaskRunner()); + + Thread bad_thread("bad thread"); + ASSERT_TRUE(bad_thread.Start()); + + CancelableTaskTracker::TaskId task_id = task_tracker_.PostTask( + 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(&CancelableTaskTracker::TryCancelAll))); + + test_task_runner->RunUntilIdle(); +} + +} // namespace base diff --git a/chromium/base/task_runner_util_unittest.cc b/chromium/base/task_runner_util_unittest.cc index 04743944d63..481f09ebd19 100644 --- a/chromium/base/task_runner_util_unittest.cc +++ b/chromium/base/task_runner_util_unittest.cc @@ -45,20 +45,20 @@ void ExpectFoo(scoped_ptr<Foo> foo) { EXPECT_FALSE(foo.get()); } -struct FreeFooFunctor { +struct FooDeleter { void operator()(Foo* foo) const { ++g_foo_free_count; delete foo; }; }; -scoped_ptr_malloc<Foo, FreeFooFunctor> CreateScopedFoo() { - return scoped_ptr_malloc<Foo, FreeFooFunctor>(new Foo); +scoped_ptr<Foo, FooDeleter> CreateScopedFoo() { + return scoped_ptr<Foo, FooDeleter>(new Foo); } -void ExpectScopedFoo(scoped_ptr_malloc<Foo, FreeFooFunctor> foo) { +void ExpectScopedFoo(scoped_ptr<Foo, FooDeleter> foo) { EXPECT_TRUE(foo.get()); - scoped_ptr_malloc<Foo, FreeFooFunctor> local_foo(foo.Pass()); + scoped_ptr<Foo, FooDeleter> local_foo(foo.Pass()); EXPECT_TRUE(local_foo.get()); EXPECT_FALSE(foo.get()); } diff --git a/chromium/base/third_party/dmg_fp/dtoa.cc b/chromium/base/third_party/dmg_fp/dtoa.cc index b03ccff569f..4eb9f0efd94 100644 --- a/chromium/base/third_party/dmg_fp/dtoa.cc +++ b/chromium/base/third_party/dmg_fp/dtoa.cc @@ -179,12 +179,6 @@ * used for input more than STRTOD_DIGLIM digits long (default 40). */ -#if defined _MSC_VER && _MSC_VER == 1800 -// TODO(scottmg): VS2013 RC ICEs on a bunch of functions in this file. -// This should be removed after RTM. See http://crbug.com/288948. -#pragma optimize("", off) -#endif - #define IEEE_8087 #define NO_HEX_FP diff --git a/chromium/base/third_party/dynamic_annotations/BUILD.gn b/chromium/base/third_party/dynamic_annotations/BUILD.gn new file mode 100644 index 00000000000..fdaf44d3b3d --- /dev/null +++ b/chromium/base/third_party/dynamic_annotations/BUILD.gn @@ -0,0 +1,11 @@ +# 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("dynamic_annotations") { + sources = [ + "dynamic_annotations.c", + "dynamic_annotations.h", + "../valgrind/valgrind.h", + ] +} diff --git a/chromium/base/third_party/nspr/BUILD.gn b/chromium/base/third_party/nspr/BUILD.gn new file mode 100644 index 00000000000..eb758d500b8 --- /dev/null +++ b/chromium/base/third_party/nspr/BUILD.gn @@ -0,0 +1,15 @@ +# 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" ] +} diff --git a/chromium/base/third_party/nspr/nspr.gyp b/chromium/base/third_party/nspr/nspr.gyp deleted file mode 100644 index 681e6ae9075..00000000000 --- a/chromium/base/third_party/nspr/nspr.gyp +++ /dev/null @@ -1,41 +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. - -{ - 'conditions': [ - ['use_system_nspr==1', { - 'targets': [ - { - 'target_name': 'nspr', - 'type': 'none', - 'toolsets': ['host', 'target'], - 'variables': { - 'headers_root_path': '.', - 'header_filenames': [ - 'prcpucfg.h', - 'prtime.h', - 'prtypes.h', - ], - }, - 'includes': [ - '../../../build/shim_headers.gypi', - ], - 'direct_dependent_settings': { - 'cflags': [ - '<!@(pkg-config --cflags nspr)', - ], - }, - 'link_settings': { - 'ldflags': [ - '<!@(pkg-config --libs-only-L --libs-only-other nspr)', - ], - 'libraries': [ - '<!@(pkg-config --libs-only-l nspr)', - ], - }, - } - ], - }], - ], -} diff --git a/chromium/base/third_party/nspr/prcpucfg.h b/chromium/base/third_party/nspr/prcpucfg.h deleted file mode 100644 index d6ebb47abd0..00000000000 --- a/chromium/base/third_party/nspr/prcpucfg.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2008, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef BASE_THIRD_PARTY_NSPR_PRCPUCFG_H__ -#define BASE_THIRD_PARTY_NSPR_PRCPUCFG_H__ - -#if defined(WIN32) -#include "base/third_party/nspr/prcpucfg_win.h" -#elif defined(__APPLE__) -#include "base/third_party/nspr/prcpucfg_mac.h" -#elif defined(__native_client__) -#include "base/third_party/nspr/prcpucfg_nacl.h" -#elif defined(__linux__) || defined(ANDROID) -#include "base/third_party/nspr/prcpucfg_linux.h" -#elif defined(__FreeBSD__) -#include "base/third_party/nspr/prcpucfg_freebsd.h" -#elif defined(__OpenBSD__) -#include "base/third_party/nspr/prcpucfg_openbsd.h" -#elif defined(__sun) -#include "base/third_party/nspr/prcpucfg_solaris.h" -#else -#error Provide a prcpucfg.h appropriate for your platform -#endif - -#endif // BASE_THIRD_PARTY_NSPR_PRCPUCFG_H__ diff --git a/chromium/base/third_party/nspr/prcpucfg_freebsd.h b/chromium/base/third_party/nspr/prcpucfg_freebsd.h deleted file mode 100644 index 76d35420271..00000000000 --- a/chromium/base/third_party/nspr/prcpucfg_freebsd.h +++ /dev/null @@ -1,337 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is the Netscape Portable Runtime (NSPR). - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998-2000 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef nspr_cpucfg___ -#define nspr_cpucfg___ - -#ifndef XP_UNIX -#define XP_UNIX -#endif - -#ifndef FREEBSD -#define FREEBSD -#endif - -#define PR_AF_INET6 28 /* same as AF_INET6 */ - -#ifndef HAVE_LONG_LONG -#define HAVE_LONG_LONG -#endif - -#if defined(__i386__) - -#define IS_LITTLE_ENDIAN 1 -#undef IS_BIG_ENDIAN -#undef HAVE_ALIGNED_DOUBLES -#undef HAVE_ALIGNED_LONGLONGS - -#define PR_BYTES_PER_BYTE 1 -#define PR_BYTES_PER_SHORT 2 -#define PR_BYTES_PER_INT 4 -#define PR_BYTES_PER_INT64 8 -#define PR_BYTES_PER_LONG 4 -#define PR_BYTES_PER_FLOAT 4 -#define PR_BYTES_PER_DOUBLE 8 -#define PR_BYTES_PER_WORD 4 -#define PR_BYTES_PER_DWORD 8 -#define PR_BYTES_PER_WORD_LOG2 2 -#define PR_BYTES_PER_DWORD_LOG2 3 - -#define PR_BITS_PER_BYTE 8 -#define PR_BITS_PER_SHORT 16 -#define PR_BITS_PER_INT 32 -#define PR_BITS_PER_INT64 64 -#define PR_BITS_PER_LONG 32 -#define PR_BITS_PER_FLOAT 32 -#define PR_BITS_PER_DOUBLE 64 -#define PR_BITS_PER_WORD 32 - -#define PR_BITS_PER_BYTE_LOG2 3 -#define PR_BITS_PER_SHORT_LOG2 4 -#define PR_BITS_PER_INT_LOG2 5 -#define PR_BITS_PER_INT64_LOG2 6 -#define PR_BITS_PER_LONG_LOG2 5 -#define PR_BITS_PER_FLOAT_LOG2 5 -#define PR_BITS_PER_DOUBLE_LOG2 6 -#define PR_BITS_PER_WORD_LOG2 5 - -#define PR_ALIGN_OF_SHORT 2 -#define PR_ALIGN_OF_INT 4 -#define PR_ALIGN_OF_LONG 4 -#define PR_ALIGN_OF_INT64 4 -#define PR_ALIGN_OF_FLOAT 4 -#define PR_ALIGN_OF_DOUBLE 4 -#define PR_ALIGN_OF_POINTER 4 - -#elif defined(__alpha__) - -#define IS_LITTLE_ENDIAN 1 -#undef IS_BIG_ENDIAN -#define HAVE_ALIGNED_DOUBLES -#define HAVE_ALIGNED_LONGLONGS -#define IS_64 - -#define PR_BYTES_PER_BYTE 1 -#define PR_BYTES_PER_SHORT 2 -#define PR_BYTES_PER_INT 4 -#define PR_BYTES_PER_INT64 8 -#define PR_BYTES_PER_LONG 8 -#define PR_BYTES_PER_FLOAT 4 -#define PR_BYTES_PER_DOUBLE 8 -#define PR_BYTES_PER_WORD 8 -#define PR_BYTES_PER_DWORD 8 -#define PR_BYTES_PER_WORD_LOG2 3 -#define PR_BYTES_PER_DWORD_LOG2 3 - -#define PR_BITS_PER_BYTE 8 -#define PR_BITS_PER_SHORT 16 -#define PR_BITS_PER_INT 32 -#define PR_BITS_PER_INT64 64 -#define PR_BITS_PER_LONG 64 -#define PR_BITS_PER_FLOAT 32 -#define PR_BITS_PER_DOUBLE 64 -#define PR_BITS_PER_WORD 64 - -#define PR_BITS_PER_BYTE_LOG2 3 -#define PR_BITS_PER_SHORT_LOG2 4 -#define PR_BITS_PER_INT_LOG2 5 -#define PR_BITS_PER_INT64_LOG2 6 -#define PR_BITS_PER_LONG_LOG2 6 -#define PR_BITS_PER_FLOAT_LOG2 5 -#define PR_BITS_PER_DOUBLE_LOG2 6 -#define PR_BITS_PER_WORD_LOG2 6 - -#define PR_ALIGN_OF_SHORT 2 -#define PR_ALIGN_OF_INT 4 -#define PR_ALIGN_OF_LONG 8 -#define PR_ALIGN_OF_INT64 8 -#define PR_ALIGN_OF_FLOAT 4 -#define PR_ALIGN_OF_DOUBLE 8 -#define PR_ALIGN_OF_POINTER 8 - -#elif defined(__sparc__) - -#undef IS_LITTLE_ENDIAN -#define IS_BIG_ENDIAN 1 -#define HAVE_ALIGNED_DOUBLES -#define HAVE_ALIGNED_LONGLONGS -#define IS_64 - -#define PR_BYTES_PER_BYTE 1 -#define PR_BYTES_PER_SHORT 2 -#define PR_BYTES_PER_INT 4 -#define PR_BYTES_PER_INT64 8 -#define PR_BYTES_PER_LONG 8 -#define PR_BYTES_PER_FLOAT 4 -#define PR_BYTES_PER_DOUBLE 8 -#define PR_BYTES_PER_WORD 8 -#define PR_BYTES_PER_DWORD 8 -#define PR_BYTES_PER_WORD_LOG2 3 -#define PR_BYTES_PER_DWORD_LOG2 3 - -#define PR_BITS_PER_BYTE 8 -#define PR_BITS_PER_SHORT 16 -#define PR_BITS_PER_INT 32 -#define PR_BITS_PER_INT64 64 -#define PR_BITS_PER_LONG 64 -#define PR_BITS_PER_FLOAT 32 -#define PR_BITS_PER_DOUBLE 64 -#define PR_BITS_PER_WORD 64 - -#define PR_BITS_PER_BYTE_LOG2 3 -#define PR_BITS_PER_SHORT_LOG2 4 -#define PR_BITS_PER_INT_LOG2 5 -#define PR_BITS_PER_INT64_LOG2 6 -#define PR_BITS_PER_LONG_LOG2 6 -#define PR_BITS_PER_FLOAT_LOG2 5 -#define PR_BITS_PER_DOUBLE_LOG2 6 -#define PR_BITS_PER_WORD_LOG2 6 - -#define PR_ALIGN_OF_SHORT 2 -#define PR_ALIGN_OF_INT 4 -#define PR_ALIGN_OF_LONG 8 -#define PR_ALIGN_OF_INT64 8 -#define PR_ALIGN_OF_FLOAT 4 -#define PR_ALIGN_OF_DOUBLE 8 -#define PR_ALIGN_OF_POINTER 8 - -#elif defined(__ia64__) - -#define IS_LITTLE_ENDIAN 1 -#undef IS_BIG_ENDIAN -#define HAVE_ALIGNED_DOUBLES -#define HAVE_ALIGNED_LONGLONGS -#define IS_64 - -#define PR_BYTES_PER_BYTE 1 -#define PR_BYTES_PER_SHORT 2 -#define PR_BYTES_PER_INT 4 -#define PR_BYTES_PER_INT64 8 -#define PR_BYTES_PER_LONG 8 -#define PR_BYTES_PER_FLOAT 4 -#define PR_BYTES_PER_DOUBLE 8 -#define PR_BYTES_PER_WORD 8 -#define PR_BYTES_PER_DWORD 8 -#define PR_BYTES_PER_WORD_LOG2 3 -#define PR_BYTES_PER_DWORD_LOG2 3 - -#define PR_BITS_PER_BYTE 8 -#define PR_BITS_PER_SHORT 16 -#define PR_BITS_PER_INT 32 -#define PR_BITS_PER_INT64 64 -#define PR_BITS_PER_LONG 64 -#define PR_BITS_PER_FLOAT 32 -#define PR_BITS_PER_DOUBLE 64 -#define PR_BITS_PER_WORD 64 - -#define PR_BITS_PER_BYTE_LOG2 3 -#define PR_BITS_PER_SHORT_LOG2 4 -#define PR_BITS_PER_INT_LOG2 5 -#define PR_BITS_PER_INT64_LOG2 6 -#define PR_BITS_PER_LONG_LOG2 6 -#define PR_BITS_PER_FLOAT_LOG2 5 -#define PR_BITS_PER_DOUBLE_LOG2 6 -#define PR_BITS_PER_WORD_LOG2 6 - -#define PR_ALIGN_OF_SHORT 2 -#define PR_ALIGN_OF_INT 4 -#define PR_ALIGN_OF_LONG 8 -#define PR_ALIGN_OF_INT64 8 -#define PR_ALIGN_OF_FLOAT 4 -#define PR_ALIGN_OF_DOUBLE 8 -#define PR_ALIGN_OF_POINTER 8 -#define PR_ALIGN_OF_WORD 8 - -#elif defined(__amd64__) - -#define IS_LITTLE_ENDIAN 1 -#undef IS_BIG_ENDIAN -#define HAVE_ALIGNED_DOUBLES -#define HAVE_ALIGNED_LONGLONGS -#define IS_64 - -#define PR_BYTES_PER_BYTE 1 -#define PR_BYTES_PER_SHORT 2 -#define PR_BYTES_PER_INT 4 -#define PR_BYTES_PER_INT64 8 -#define PR_BYTES_PER_LONG 8 -#define PR_BYTES_PER_FLOAT 4 -#define PR_BYTES_PER_DOUBLE 8 -#define PR_BYTES_PER_WORD 8 -#define PR_BYTES_PER_DWORD 8 -#define PR_BYTES_PER_WORD_LOG2 3 -#define PR_BYTES_PER_DWORD_LOG2 3 - -#define PR_BITS_PER_BYTE 8 -#define PR_BITS_PER_SHORT 16 -#define PR_BITS_PER_INT 32 -#define PR_BITS_PER_INT64 64 -#define PR_BITS_PER_LONG 64 -#define PR_BITS_PER_FLOAT 32 -#define PR_BITS_PER_DOUBLE 64 -#define PR_BITS_PER_WORD 64 - -#define PR_BITS_PER_BYTE_LOG2 3 -#define PR_BITS_PER_SHORT_LOG2 4 -#define PR_BITS_PER_INT_LOG2 5 -#define PR_BITS_PER_INT64_LOG2 6 -#define PR_BITS_PER_LONG_LOG2 6 -#define PR_BITS_PER_FLOAT_LOG2 5 -#define PR_BITS_PER_DOUBLE_LOG2 6 -#define PR_BITS_PER_WORD_LOG2 6 - -#define PR_ALIGN_OF_SHORT 2 -#define PR_ALIGN_OF_INT 4 -#define PR_ALIGN_OF_LONG 8 -#define PR_ALIGN_OF_INT64 8 -#define PR_ALIGN_OF_FLOAT 4 -#define PR_ALIGN_OF_DOUBLE 8 -#define PR_ALIGN_OF_POINTER 8 -#define PR_ALIGN_OF_WORD 8 - -#else - -#error "Unknown CPU architecture" - -#endif - -#ifndef NO_NSPR_10_SUPPORT - -#define BYTES_PER_BYTE PR_BYTES_PER_BYTE -#define BYTES_PER_SHORT PR_BYTES_PER_SHORT -#define BYTES_PER_INT PR_BYTES_PER_INT -#define BYTES_PER_INT64 PR_BYTES_PER_INT64 -#define BYTES_PER_LONG PR_BYTES_PER_LONG -#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT -#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE -#define BYTES_PER_WORD PR_BYTES_PER_WORD -#define BYTES_PER_DWORD PR_BYTES_PER_DWORD - -#define BITS_PER_BYTE PR_BITS_PER_BYTE -#define BITS_PER_SHORT PR_BITS_PER_SHORT -#define BITS_PER_INT PR_BITS_PER_INT -#define BITS_PER_INT64 PR_BITS_PER_INT64 -#define BITS_PER_LONG PR_BITS_PER_LONG -#define BITS_PER_FLOAT PR_BITS_PER_FLOAT -#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE -#define BITS_PER_WORD PR_BITS_PER_WORD - -#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2 -#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2 -#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2 -#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2 -#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2 -#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2 -#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2 -#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2 - -#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT -#define ALIGN_OF_INT PR_ALIGN_OF_INT -#define ALIGN_OF_LONG PR_ALIGN_OF_LONG -#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64 -#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT -#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE -#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER -#define ALIGN_OF_WORD PR_ALIGN_OF_WORD - -#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2 -#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2 -#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2 - -#endif /* NO_NSPR_10_SUPPORT */ - -#endif /* nspr_cpucfg___ */ diff --git a/chromium/base/third_party/nspr/prcpucfg_linux.h b/chromium/base/third_party/nspr/prcpucfg_linux.h deleted file mode 100644 index e4336da9e89..00000000000 --- a/chromium/base/third_party/nspr/prcpucfg_linux.h +++ /dev/null @@ -1,756 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is the Netscape Portable Runtime (NSPR). - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998-2000 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef nspr_cpucfg___ -#define nspr_cpucfg___ - -#ifndef XP_UNIX -#define XP_UNIX -#endif - -#ifndef LINUX -#define LINUX -#endif - -#define PR_AF_INET6 10 /* same as AF_INET6 */ - -#ifdef __powerpc64__ - -#undef IS_LITTLE_ENDIAN -#define IS_BIG_ENDIAN 1 -#define IS_64 - -#define PR_BYTES_PER_BYTE 1 -#define PR_BYTES_PER_SHORT 2 -#define PR_BYTES_PER_INT 4 -#define PR_BYTES_PER_INT64 8 -#define PR_BYTES_PER_LONG 8 -#define PR_BYTES_PER_FLOAT 4 -#define PR_BYTES_PER_DOUBLE 8 -#define PR_BYTES_PER_WORD 8 -#define PR_BYTES_PER_DWORD 8 - -#define PR_BITS_PER_BYTE 8 -#define PR_BITS_PER_SHORT 16 -#define PR_BITS_PER_INT 32 -#define PR_BITS_PER_INT64 64 -#define PR_BITS_PER_LONG 64 -#define PR_BITS_PER_FLOAT 32 -#define PR_BITS_PER_DOUBLE 64 -#define PR_BITS_PER_WORD 64 - -#define PR_BITS_PER_BYTE_LOG2 3 -#define PR_BITS_PER_SHORT_LOG2 4 -#define PR_BITS_PER_INT_LOG2 5 -#define PR_BITS_PER_INT64_LOG2 6 -#define PR_BITS_PER_LONG_LOG2 6 -#define PR_BITS_PER_FLOAT_LOG2 5 -#define PR_BITS_PER_DOUBLE_LOG2 6 -#define PR_BITS_PER_WORD_LOG2 6 - -#define PR_ALIGN_OF_SHORT 2 -#define PR_ALIGN_OF_INT 4 -#define PR_ALIGN_OF_LONG 8 -#define PR_ALIGN_OF_INT64 8 -#define PR_ALIGN_OF_FLOAT 4 -#define PR_ALIGN_OF_DOUBLE 8 -#define PR_ALIGN_OF_POINTER 8 -#define PR_ALIGN_OF_WORD 8 - -#define PR_BYTES_PER_WORD_LOG2 3 -#define PR_BYTES_PER_DWORD_LOG2 3 - -#elif defined(__powerpc__) - -#undef IS_LITTLE_ENDIAN -#define IS_BIG_ENDIAN 1 - -#define PR_BYTES_PER_BYTE 1 -#define PR_BYTES_PER_SHORT 2 -#define PR_BYTES_PER_INT 4 -#define PR_BYTES_PER_INT64 8 -#define PR_BYTES_PER_LONG 4 -#define PR_BYTES_PER_FLOAT 4 -#define PR_BYTES_PER_DOUBLE 8 -#define PR_BYTES_PER_WORD 4 -#define PR_BYTES_PER_DWORD 8 - -#define PR_BITS_PER_BYTE 8 -#define PR_BITS_PER_SHORT 16 -#define PR_BITS_PER_INT 32 -#define PR_BITS_PER_INT64 64 -#define PR_BITS_PER_LONG 32 -#define PR_BITS_PER_FLOAT 32 -#define PR_BITS_PER_DOUBLE 64 -#define PR_BITS_PER_WORD 32 - -#define PR_BITS_PER_BYTE_LOG2 3 -#define PR_BITS_PER_SHORT_LOG2 4 -#define PR_BITS_PER_INT_LOG2 5 -#define PR_BITS_PER_INT64_LOG2 6 -#define PR_BITS_PER_LONG_LOG2 5 -#define PR_BITS_PER_FLOAT_LOG2 5 -#define PR_BITS_PER_DOUBLE_LOG2 6 -#define PR_BITS_PER_WORD_LOG2 5 - -#define PR_ALIGN_OF_SHORT 2 -#define PR_ALIGN_OF_INT 4 -#define PR_ALIGN_OF_LONG 4 -#define PR_ALIGN_OF_INT64 8 -#define PR_ALIGN_OF_FLOAT 4 -#define PR_ALIGN_OF_DOUBLE 8 -#define PR_ALIGN_OF_POINTER 4 -#define PR_ALIGN_OF_WORD 4 - -#define PR_BYTES_PER_WORD_LOG2 2 -#define PR_BYTES_PER_DWORD_LOG2 3 - -#elif defined(__alpha) - -#define IS_LITTLE_ENDIAN 1 -#undef IS_BIG_ENDIAN -#define IS_64 - -#define PR_BYTES_PER_BYTE 1 -#define PR_BYTES_PER_SHORT 2 -#define PR_BYTES_PER_INT 4 -#define PR_BYTES_PER_INT64 8 -#define PR_BYTES_PER_LONG 8 -#define PR_BYTES_PER_FLOAT 4 -#define PR_BYTES_PER_DOUBLE 8 -#define PR_BYTES_PER_WORD 8 -#define PR_BYTES_PER_DWORD 8 - -#define PR_BITS_PER_BYTE 8 -#define PR_BITS_PER_SHORT 16 -#define PR_BITS_PER_INT 32 -#define PR_BITS_PER_INT64 64 -#define PR_BITS_PER_LONG 64 -#define PR_BITS_PER_FLOAT 32 -#define PR_BITS_PER_DOUBLE 64 -#define PR_BITS_PER_WORD 64 - -#define PR_BITS_PER_BYTE_LOG2 3 -#define PR_BITS_PER_SHORT_LOG2 4 -#define PR_BITS_PER_INT_LOG2 5 -#define PR_BITS_PER_INT64_LOG2 6 -#define PR_BITS_PER_LONG_LOG2 6 -#define PR_BITS_PER_FLOAT_LOG2 5 -#define PR_BITS_PER_DOUBLE_LOG2 6 -#define PR_BITS_PER_WORD_LOG2 6 - -#define PR_ALIGN_OF_SHORT 2 -#define PR_ALIGN_OF_INT 4 -#define PR_ALIGN_OF_LONG 8 -#define PR_ALIGN_OF_INT64 8 -#define PR_ALIGN_OF_FLOAT 4 -#define PR_ALIGN_OF_DOUBLE 8 -#define PR_ALIGN_OF_POINTER 8 -#define PR_ALIGN_OF_WORD 8 - -#define PR_BYTES_PER_WORD_LOG2 3 -#define PR_BYTES_PER_DWORD_LOG2 3 - -#elif defined(__ia64__) - -#define IS_LITTLE_ENDIAN 1 -#undef IS_BIG_ENDIAN -#define IS_64 - -#define PR_BYTES_PER_BYTE 1 -#define PR_BYTES_PER_SHORT 2 -#define PR_BYTES_PER_INT 4 -#define PR_BYTES_PER_INT64 8 -#define PR_BYTES_PER_LONG 8 -#define PR_BYTES_PER_FLOAT 4 -#define PR_BYTES_PER_DOUBLE 8 -#define PR_BYTES_PER_WORD 8 -#define PR_BYTES_PER_DWORD 8 - -#define PR_BITS_PER_BYTE 8 -#define PR_BITS_PER_SHORT 16 -#define PR_BITS_PER_INT 32 -#define PR_BITS_PER_INT64 64 -#define PR_BITS_PER_LONG 64 -#define PR_BITS_PER_FLOAT 32 -#define PR_BITS_PER_DOUBLE 64 -#define PR_BITS_PER_WORD 64 - -#define PR_BITS_PER_BYTE_LOG2 3 -#define PR_BITS_PER_SHORT_LOG2 4 -#define PR_BITS_PER_INT_LOG2 5 -#define PR_BITS_PER_INT64_LOG2 6 -#define PR_BITS_PER_LONG_LOG2 6 -#define PR_BITS_PER_FLOAT_LOG2 5 -#define PR_BITS_PER_DOUBLE_LOG2 6 -#define PR_BITS_PER_WORD_LOG2 6 - -#define PR_ALIGN_OF_SHORT 2 -#define PR_ALIGN_OF_INT 4 -#define PR_ALIGN_OF_LONG 8 -#define PR_ALIGN_OF_INT64 8 -#define PR_ALIGN_OF_FLOAT 4 -#define PR_ALIGN_OF_DOUBLE 8 -#define PR_ALIGN_OF_POINTER 8 -#define PR_ALIGN_OF_WORD 8 - -#define PR_BYTES_PER_WORD_LOG2 3 -#define PR_BYTES_PER_DWORD_LOG2 3 - -#elif defined(__x86_64__) - -#ifdef __ILP32__ - -#define IS_LITTLE_ENDIAN 1 -#undef IS_BIG_ENDIAN - -#define PR_BYTES_PER_BYTE 1 -#define PR_BYTES_PER_SHORT 2 -#define PR_BYTES_PER_INT 4 -#define PR_BYTES_PER_INT64 8 -#define PR_BYTES_PER_LONG 4 -#define PR_BYTES_PER_FLOAT 4 -#define PR_BYTES_PER_DOUBLE 8 -#define PR_BYTES_PER_WORD 4 -#define PR_BYTES_PER_DWORD 8 - -#define PR_BITS_PER_BYTE 8 -#define PR_BITS_PER_SHORT 16 -#define PR_BITS_PER_INT 32 -#define PR_BITS_PER_INT64 64 -#define PR_BITS_PER_LONG 32 -#define PR_BITS_PER_FLOAT 32 -#define PR_BITS_PER_DOUBLE 64 -#define PR_BITS_PER_WORD 32 - -#define PR_BITS_PER_BYTE_LOG2 3 -#define PR_BITS_PER_SHORT_LOG2 4 -#define PR_BITS_PER_INT_LOG2 5 -#define PR_BITS_PER_INT64_LOG2 6 -#define PR_BITS_PER_LONG_LOG2 5 -#define PR_BITS_PER_FLOAT_LOG2 5 -#define PR_BITS_PER_DOUBLE_LOG2 6 -#define PR_BITS_PER_WORD_LOG2 5 - -#define PR_ALIGN_OF_SHORT 2 -#define PR_ALIGN_OF_INT 4 -#define PR_ALIGN_OF_LONG 4 -#define PR_ALIGN_OF_INT64 4 -#define PR_ALIGN_OF_FLOAT 4 -#define PR_ALIGN_OF_DOUBLE 4 -#define PR_ALIGN_OF_POINTER 4 -#define PR_ALIGN_OF_WORD 4 - -#define PR_BYTES_PER_WORD_LOG2 2 -#define PR_BYTES_PER_DWORD_LOG2 3 - -#else - -#define IS_LITTLE_ENDIAN 1 -#undef IS_BIG_ENDIAN -#define IS_64 - -#define PR_BYTES_PER_BYTE 1 -#define PR_BYTES_PER_SHORT 2 -#define PR_BYTES_PER_INT 4 -#define PR_BYTES_PER_INT64 8 -#define PR_BYTES_PER_LONG 8 -#define PR_BYTES_PER_FLOAT 4 -#define PR_BYTES_PER_DOUBLE 8 -#define PR_BYTES_PER_WORD 8 -#define PR_BYTES_PER_DWORD 8 - -#define PR_BITS_PER_BYTE 8 -#define PR_BITS_PER_SHORT 16 -#define PR_BITS_PER_INT 32 -#define PR_BITS_PER_INT64 64 -#define PR_BITS_PER_LONG 64 -#define PR_BITS_PER_FLOAT 32 -#define PR_BITS_PER_DOUBLE 64 -#define PR_BITS_PER_WORD 64 - -#define PR_BITS_PER_BYTE_LOG2 3 -#define PR_BITS_PER_SHORT_LOG2 4 -#define PR_BITS_PER_INT_LOG2 5 -#define PR_BITS_PER_INT64_LOG2 6 -#define PR_BITS_PER_LONG_LOG2 6 -#define PR_BITS_PER_FLOAT_LOG2 5 -#define PR_BITS_PER_DOUBLE_LOG2 6 -#define PR_BITS_PER_WORD_LOG2 6 - -#define PR_ALIGN_OF_SHORT 2 -#define PR_ALIGN_OF_INT 4 -#define PR_ALIGN_OF_LONG 8 -#define PR_ALIGN_OF_INT64 8 -#define PR_ALIGN_OF_FLOAT 4 -#define PR_ALIGN_OF_DOUBLE 8 -#define PR_ALIGN_OF_POINTER 8 -#define PR_ALIGN_OF_WORD 8 - -#define PR_BYTES_PER_WORD_LOG2 3 -#define PR_BYTES_PER_DWORD_LOG2 3 - -#endif - -#elif defined(__mc68000__) - -#undef IS_LITTLE_ENDIAN -#define IS_BIG_ENDIAN 1 - -#define PR_BYTES_PER_BYTE 1 -#define PR_BYTES_PER_SHORT 2 -#define PR_BYTES_PER_INT 4 -#define PR_BYTES_PER_INT64 8 -#define PR_BYTES_PER_LONG 4 -#define PR_BYTES_PER_FLOAT 4 -#define PR_BYTES_PER_DOUBLE 8 -#define PR_BYTES_PER_WORD 4 -#define PR_BYTES_PER_DWORD 8 - -#define PR_BITS_PER_BYTE 8 -#define PR_BITS_PER_SHORT 16 -#define PR_BITS_PER_INT 32 -#define PR_BITS_PER_INT64 64 -#define PR_BITS_PER_LONG 32 -#define PR_BITS_PER_FLOAT 32 -#define PR_BITS_PER_DOUBLE 64 -#define PR_BITS_PER_WORD 32 - -#define PR_BITS_PER_BYTE_LOG2 3 -#define PR_BITS_PER_SHORT_LOG2 4 -#define PR_BITS_PER_INT_LOG2 5 -#define PR_BITS_PER_INT64_LOG2 6 -#define PR_BITS_PER_LONG_LOG2 5 -#define PR_BITS_PER_FLOAT_LOG2 5 -#define PR_BITS_PER_DOUBLE_LOG2 6 -#define PR_BITS_PER_WORD_LOG2 5 - -#define PR_ALIGN_OF_SHORT 2 -#define PR_ALIGN_OF_INT 2 -#define PR_ALIGN_OF_LONG 2 -#define PR_ALIGN_OF_INT64 2 -#define PR_ALIGN_OF_FLOAT 2 -#define PR_ALIGN_OF_DOUBLE 2 -#define PR_ALIGN_OF_POINTER 2 -#define PR_ALIGN_OF_WORD 2 - -#define PR_BYTES_PER_WORD_LOG2 2 -#define PR_BYTES_PER_DWORD_LOG2 3 - -#elif defined(__sparc__) - -#undef IS_LITTLE_ENDIAN -#define IS_BIG_ENDIAN 1 - -#define PR_BYTES_PER_BYTE 1 -#define PR_BYTES_PER_SHORT 2 -#define PR_BYTES_PER_INT 4 -#define PR_BYTES_PER_INT64 8 -#define PR_BYTES_PER_LONG 4 -#define PR_BYTES_PER_FLOAT 4 -#define PR_BYTES_PER_DOUBLE 8 -#define PR_BYTES_PER_WORD 4 -#define PR_BYTES_PER_DWORD 8 - -#define PR_BITS_PER_BYTE 8 -#define PR_BITS_PER_SHORT 16 -#define PR_BITS_PER_INT 32 -#define PR_BITS_PER_INT64 64 -#define PR_BITS_PER_LONG 32 -#define PR_BITS_PER_FLOAT 32 -#define PR_BITS_PER_DOUBLE 64 -#define PR_BITS_PER_WORD 32 - -#define PR_BITS_PER_BYTE_LOG2 3 -#define PR_BITS_PER_SHORT_LOG2 4 -#define PR_BITS_PER_INT_LOG2 5 -#define PR_BITS_PER_INT64_LOG2 6 -#define PR_BITS_PER_LONG_LOG2 5 -#define PR_BITS_PER_FLOAT_LOG2 5 -#define PR_BITS_PER_DOUBLE_LOG2 6 -#define PR_BITS_PER_WORD_LOG2 5 - -#define PR_ALIGN_OF_SHORT 2 -#define PR_ALIGN_OF_INT 4 -#define PR_ALIGN_OF_LONG 4 -#define PR_ALIGN_OF_INT64 8 -#define PR_ALIGN_OF_FLOAT 4 -#define PR_ALIGN_OF_DOUBLE 8 -#define PR_ALIGN_OF_POINTER 4 -#define PR_ALIGN_OF_WORD 4 - -#define PR_BYTES_PER_WORD_LOG2 2 -#define PR_BYTES_PER_DWORD_LOG2 3 - -#elif defined(__i386__) - -#define IS_LITTLE_ENDIAN 1 -#undef IS_BIG_ENDIAN - -#define PR_BYTES_PER_BYTE 1 -#define PR_BYTES_PER_SHORT 2 -#define PR_BYTES_PER_INT 4 -#define PR_BYTES_PER_INT64 8 -#define PR_BYTES_PER_LONG 4 -#define PR_BYTES_PER_FLOAT 4 -#define PR_BYTES_PER_DOUBLE 8 -#define PR_BYTES_PER_WORD 4 -#define PR_BYTES_PER_DWORD 8 - -#define PR_BITS_PER_BYTE 8 -#define PR_BITS_PER_SHORT 16 -#define PR_BITS_PER_INT 32 -#define PR_BITS_PER_INT64 64 -#define PR_BITS_PER_LONG 32 -#define PR_BITS_PER_FLOAT 32 -#define PR_BITS_PER_DOUBLE 64 -#define PR_BITS_PER_WORD 32 - -#define PR_BITS_PER_BYTE_LOG2 3 -#define PR_BITS_PER_SHORT_LOG2 4 -#define PR_BITS_PER_INT_LOG2 5 -#define PR_BITS_PER_INT64_LOG2 6 -#define PR_BITS_PER_LONG_LOG2 5 -#define PR_BITS_PER_FLOAT_LOG2 5 -#define PR_BITS_PER_DOUBLE_LOG2 6 -#define PR_BITS_PER_WORD_LOG2 5 - -#define PR_ALIGN_OF_SHORT 2 -#define PR_ALIGN_OF_INT 4 -#define PR_ALIGN_OF_LONG 4 -#define PR_ALIGN_OF_INT64 4 -#define PR_ALIGN_OF_FLOAT 4 -#define PR_ALIGN_OF_DOUBLE 4 -#define PR_ALIGN_OF_POINTER 4 -#define PR_ALIGN_OF_WORD 4 - -#define PR_BYTES_PER_WORD_LOG2 2 -#define PR_BYTES_PER_DWORD_LOG2 3 - -#elif defined(__mips__) - -#ifdef __MIPSEB__ -#define IS_BIG_ENDIAN 1 -#undef IS_LITTLE_ENDIAN -#elif defined(__MIPSEL__) -#define IS_LITTLE_ENDIAN 1 -#undef IS_BIG_ENDIAN -#else -#error "Unknown MIPS endianness." -#endif - -#define PR_BYTES_PER_BYTE 1 -#define PR_BYTES_PER_SHORT 2 -#define PR_BYTES_PER_INT 4 -#define PR_BYTES_PER_INT64 8 -#define PR_BYTES_PER_LONG 4 -#define PR_BYTES_PER_FLOAT 4 -#define PR_BYTES_PER_DOUBLE 8 -#define PR_BYTES_PER_WORD 4 -#define PR_BYTES_PER_DWORD 8 - -#define PR_BITS_PER_BYTE 8 -#define PR_BITS_PER_SHORT 16 -#define PR_BITS_PER_INT 32 -#define PR_BITS_PER_INT64 64 -#define PR_BITS_PER_LONG 32 -#define PR_BITS_PER_FLOAT 32 -#define PR_BITS_PER_DOUBLE 64 -#define PR_BITS_PER_WORD 32 - -#define PR_BITS_PER_BYTE_LOG2 3 -#define PR_BITS_PER_SHORT_LOG2 4 -#define PR_BITS_PER_INT_LOG2 5 -#define PR_BITS_PER_INT64_LOG2 6 -#define PR_BITS_PER_LONG_LOG2 5 -#define PR_BITS_PER_FLOAT_LOG2 5 -#define PR_BITS_PER_DOUBLE_LOG2 6 -#define PR_BITS_PER_WORD_LOG2 5 - -#define PR_ALIGN_OF_SHORT 2 -#define PR_ALIGN_OF_INT 4 -#define PR_ALIGN_OF_LONG 4 -#define PR_ALIGN_OF_INT64 8 -#define PR_ALIGN_OF_FLOAT 4 -#define PR_ALIGN_OF_DOUBLE 8 -#define PR_ALIGN_OF_POINTER 4 -#define PR_ALIGN_OF_WORD 4 - -#define PR_BYTES_PER_WORD_LOG2 2 -#define PR_BYTES_PER_DWORD_LOG2 3 - -#elif defined(__arm__) - -#define IS_LITTLE_ENDIAN 1 -#undef IS_BIG_ENDIAN - -#define PR_BYTES_PER_BYTE 1 -#define PR_BYTES_PER_SHORT 2 -#define PR_BYTES_PER_INT 4 -#define PR_BYTES_PER_INT64 8 -#define PR_BYTES_PER_LONG 4 -#define PR_BYTES_PER_FLOAT 4 -#define PR_BYTES_PER_DOUBLE 8 -#define PR_BYTES_PER_WORD 4 -#define PR_BYTES_PER_DWORD 8 - -#define PR_BITS_PER_BYTE 8 -#define PR_BITS_PER_SHORT 16 -#define PR_BITS_PER_INT 32 -#define PR_BITS_PER_INT64 64 -#define PR_BITS_PER_LONG 32 -#define PR_BITS_PER_FLOAT 32 -#define PR_BITS_PER_DOUBLE 64 -#define PR_BITS_PER_WORD 32 - -#define PR_BITS_PER_BYTE_LOG2 3 -#define PR_BITS_PER_SHORT_LOG2 4 -#define PR_BITS_PER_INT_LOG2 5 -#define PR_BITS_PER_INT64_LOG2 6 -#define PR_BITS_PER_LONG_LOG2 5 -#define PR_BITS_PER_FLOAT_LOG2 5 -#define PR_BITS_PER_DOUBLE_LOG2 6 -#define PR_BITS_PER_WORD_LOG2 5 - -#define PR_ALIGN_OF_SHORT 2 -#define PR_ALIGN_OF_INT 4 -#define PR_ALIGN_OF_LONG 4 -#define PR_ALIGN_OF_INT64 4 -#define PR_ALIGN_OF_FLOAT 4 -#define PR_ALIGN_OF_DOUBLE 4 -#define PR_ALIGN_OF_POINTER 4 -#define PR_ALIGN_OF_WORD 4 - -#define PR_BYTES_PER_WORD_LOG2 2 -#define PR_BYTES_PER_DWORD_LOG2 3 - -#elif defined(__hppa__) - -#undef IS_LITTLE_ENDIAN -#define IS_BIG_ENDIAN 1 - -#define PR_BYTES_PER_BYTE 1 -#define PR_BYTES_PER_SHORT 2 -#define PR_BYTES_PER_INT 4 -#define PR_BYTES_PER_INT64 8 -#define PR_BYTES_PER_LONG 4 -#define PR_BYTES_PER_FLOAT 4 -#define PR_BYTES_PER_DOUBLE 8 -#define PR_BYTES_PER_WORD 4 -#define PR_BYTES_PER_DWORD 8 - -#define PR_BITS_PER_BYTE 8 -#define PR_BITS_PER_SHORT 16 -#define PR_BITS_PER_INT 32 -#define PR_BITS_PER_INT64 64 -#define PR_BITS_PER_LONG 32 -#define PR_BITS_PER_FLOAT 32 -#define PR_BITS_PER_DOUBLE 64 -#define PR_BITS_PER_WORD 32 - -#define PR_BITS_PER_BYTE_LOG2 3 -#define PR_BITS_PER_SHORT_LOG2 4 -#define PR_BITS_PER_INT_LOG2 5 -#define PR_BITS_PER_INT64_LOG2 6 -#define PR_BITS_PER_LONG_LOG2 5 -#define PR_BITS_PER_FLOAT_LOG2 5 -#define PR_BITS_PER_DOUBLE_LOG2 6 -#define PR_BITS_PER_WORD_LOG2 5 - -#define PR_ALIGN_OF_SHORT 2 -#define PR_ALIGN_OF_INT 4 -#define PR_ALIGN_OF_LONG 4 -#define PR_ALIGN_OF_INT64 8 -#define PR_ALIGN_OF_FLOAT 4 -#define PR_ALIGN_OF_DOUBLE 8 -#define PR_ALIGN_OF_POINTER 4 -#define PR_ALIGN_OF_WORD 4 - -#define PR_BYTES_PER_WORD_LOG2 2 -#define PR_BYTES_PER_DWORD_LOG2 3 - -#elif defined(__s390x__) - -#define IS_BIG_ENDIAN 1 -#undef IS_LITTLE_ENDIAN -#define IS_64 - -#define PR_BYTES_PER_BYTE 1 -#define PR_BYTES_PER_SHORT 2 -#define PR_BYTES_PER_INT 4 -#define PR_BYTES_PER_INT64 8 -#define PR_BYTES_PER_LONG 8 -#define PR_BYTES_PER_FLOAT 4 -#define PR_BYTES_PER_DOUBLE 8 -#define PR_BYTES_PER_WORD 8 -#define PR_BYTES_PER_DWORD 8 - -#define PR_BITS_PER_BYTE 8 -#define PR_BITS_PER_SHORT 16 -#define PR_BITS_PER_INT 32 -#define PR_BITS_PER_INT64 64 -#define PR_BITS_PER_LONG 64 -#define PR_BITS_PER_FLOAT 32 -#define PR_BITS_PER_DOUBLE 64 -#define PR_BITS_PER_WORD 64 - -#define PR_BITS_PER_BYTE_LOG2 3 -#define PR_BITS_PER_SHORT_LOG2 4 -#define PR_BITS_PER_INT_LOG2 5 -#define PR_BITS_PER_INT64_LOG2 6 -#define PR_BITS_PER_LONG_LOG2 6 -#define PR_BITS_PER_FLOAT_LOG2 5 -#define PR_BITS_PER_DOUBLE_LOG2 6 -#define PR_BITS_PER_WORD_LOG2 6 - -#define PR_ALIGN_OF_SHORT 2 -#define PR_ALIGN_OF_INT 4 -#define PR_ALIGN_OF_LONG 8 -#define PR_ALIGN_OF_INT64 8 -#define PR_ALIGN_OF_FLOAT 4 -#define PR_ALIGN_OF_DOUBLE 8 -#define PR_ALIGN_OF_POINTER 8 -#define PR_ALIGN_OF_WORD 8 - -#define PR_BYTES_PER_WORD_LOG2 3 -#define PR_BYTES_PER_DWORD_LOG2 3 - -#elif defined(__s390__) - -#define IS_BIG_ENDIAN 1 -#undef IS_LITTLE_ENDIAN - -#define PR_BYTES_PER_BYTE 1 -#define PR_BYTES_PER_SHORT 2 -#define PR_BYTES_PER_INT 4 -#define PR_BYTES_PER_INT64 8 -#define PR_BYTES_PER_LONG 4 -#define PR_BYTES_PER_FLOAT 4 -#define PR_BYTES_PER_DOUBLE 8 -#define PR_BYTES_PER_WORD 4 -#define PR_BYTES_PER_DWORD 8 - -#define PR_BITS_PER_BYTE 8 -#define PR_BITS_PER_SHORT 16 -#define PR_BITS_PER_INT 32 -#define PR_BITS_PER_INT64 64 -#define PR_BITS_PER_LONG 32 -#define PR_BITS_PER_FLOAT 32 -#define PR_BITS_PER_DOUBLE 64 -#define PR_BITS_PER_WORD 32 - -#define PR_BITS_PER_BYTE_LOG2 3 -#define PR_BITS_PER_SHORT_LOG2 4 -#define PR_BITS_PER_INT_LOG2 5 -#define PR_BITS_PER_INT64_LOG2 6 -#define PR_BITS_PER_LONG_LOG2 5 -#define PR_BITS_PER_FLOAT_LOG2 5 -#define PR_BITS_PER_DOUBLE_LOG2 6 -#define PR_BITS_PER_WORD_LOG2 5 - -#define PR_ALIGN_OF_SHORT 2 -#define PR_ALIGN_OF_INT 4 -#define PR_ALIGN_OF_LONG 4 -#define PR_ALIGN_OF_INT64 4 -#define PR_ALIGN_OF_FLOAT 4 -#define PR_ALIGN_OF_DOUBLE 4 -#define PR_ALIGN_OF_POINTER 4 -#define PR_ALIGN_OF_WORD 4 - -#define PR_BYTES_PER_WORD_LOG2 2 -#define PR_BYTES_PER_DWORD_LOG2 3 - -#else - -#error "Unknown CPU architecture" - -#endif - -#define HAVE_LONG_LONG -#if PR_ALIGN_OF_DOUBLE == 8 -#define HAVE_ALIGNED_DOUBLES -#endif -#if PR_ALIGN_OF_INT64 == 8 -#define HAVE_ALIGNED_LONGLONGS -#endif - -#ifndef NO_NSPR_10_SUPPORT - -#define BYTES_PER_BYTE PR_BYTES_PER_BYTE -#define BYTES_PER_SHORT PR_BYTES_PER_SHORT -#define BYTES_PER_INT PR_BYTES_PER_INT -#define BYTES_PER_INT64 PR_BYTES_PER_INT64 -#define BYTES_PER_LONG PR_BYTES_PER_LONG -#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT -#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE -#define BYTES_PER_WORD PR_BYTES_PER_WORD -#define BYTES_PER_DWORD PR_BYTES_PER_DWORD - -#define BITS_PER_BYTE PR_BITS_PER_BYTE -#define BITS_PER_SHORT PR_BITS_PER_SHORT -#define BITS_PER_INT PR_BITS_PER_INT -#define BITS_PER_INT64 PR_BITS_PER_INT64 -#define BITS_PER_LONG PR_BITS_PER_LONG -#define BITS_PER_FLOAT PR_BITS_PER_FLOAT -#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE -#define BITS_PER_WORD PR_BITS_PER_WORD - -#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2 -#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2 -#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2 -#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2 -#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2 -#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2 -#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2 -#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2 - -#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT -#define ALIGN_OF_INT PR_ALIGN_OF_INT -#define ALIGN_OF_LONG PR_ALIGN_OF_LONG -#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64 -#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT -#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE -#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER -#define ALIGN_OF_WORD PR_ALIGN_OF_WORD - -#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2 -#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2 -#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2 - -#endif /* NO_NSPR_10_SUPPORT */ - -#endif /* nspr_cpucfg___ */ diff --git a/chromium/base/third_party/nspr/prcpucfg_mac.h b/chromium/base/third_party/nspr/prcpucfg_mac.h deleted file mode 100644 index 60bea8e5c38..00000000000 --- a/chromium/base/third_party/nspr/prcpucfg_mac.h +++ /dev/null @@ -1,197 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is the Netscape Portable Runtime (NSPR). - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998-2000 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef nspr_cpucfg___ -#define nspr_cpucfg___ - -#ifndef XP_UNIX -#define XP_UNIX -#endif - -#define PR_AF_INET6 30 /* same as AF_INET6 */ - -#ifdef __LITTLE_ENDIAN__ -#undef IS_BIG_ENDIAN -#define IS_LITTLE_ENDIAN 1 -#else -#undef IS_LITTLE_ENDIAN -#define IS_BIG_ENDIAN 1 -#endif - -#ifdef __x86_64__ -#define IS_64 -#endif - -#ifndef HAVE_LONG_LONG -#define HAVE_LONG_LONG -#endif -#undef HAVE_ALIGNED_DOUBLES -#define HAVE_ALIGNED_LONGLONGS 1 - -#ifdef IS_64 - -#define PR_BYTES_PER_BYTE 1 -#define PR_BYTES_PER_SHORT 2 -#define PR_BYTES_PER_INT 4 -#define PR_BYTES_PER_INT64 8 -#define PR_BYTES_PER_LONG 8 -#define PR_BYTES_PER_FLOAT 4 -#define PR_BYTES_PER_DOUBLE 8 -#define PR_BYTES_PER_WORD 8 -#define PR_BYTES_PER_DWORD 8 - -#define PR_BITS_PER_BYTE 8 -#define PR_BITS_PER_SHORT 16 -#define PR_BITS_PER_INT 32 -#define PR_BITS_PER_INT64 64 -#define PR_BITS_PER_LONG 64 -#define PR_BITS_PER_FLOAT 32 -#define PR_BITS_PER_DOUBLE 64 -#define PR_BITS_PER_WORD 64 -#define PR_BITS_PER_DWORD 64 - -#define PR_BITS_PER_BYTE_LOG2 3 -#define PR_BITS_PER_SHORT_LOG2 4 -#define PR_BITS_PER_INT_LOG2 5 -#define PR_BITS_PER_INT64_LOG2 6 -#define PR_BITS_PER_LONG_LOG2 6 -#define PR_BITS_PER_FLOAT_LOG2 5 -#define PR_BITS_PER_DOUBLE_LOG2 6 -#define PR_BITS_PER_WORD_LOG2 6 -#define PR_BITS_PER_DWORD_LOG2 6 - -#define PR_ALIGN_OF_SHORT 2 -#define PR_ALIGN_OF_INT 4 -#define PR_ALIGN_OF_LONG 8 -#define PR_ALIGN_OF_INT64 8 -#define PR_ALIGN_OF_FLOAT 4 -#define PR_ALIGN_OF_DOUBLE 8 -#define PR_ALIGN_OF_POINTER 8 -#define PR_ALIGN_OF_WORD 8 -#define PR_ALIGN_OF_DWORD 8 - -#else /* IS_64 */ - -#define PR_BYTES_PER_BYTE 1 -#define PR_BYTES_PER_SHORT 2 -#define PR_BYTES_PER_INT 4 -#define PR_BYTES_PER_INT64 8 -#define PR_BYTES_PER_LONG 4 -#define PR_BYTES_PER_FLOAT 4 -#define PR_BYTES_PER_DOUBLE 8 -#define PR_BYTES_PER_WORD 4 -#define PR_BYTES_PER_DWORD 8 -#define PR_BYTES_PER_WORD_LOG2 2 -#define PR_BYTES_PER_DWORD_LOG2 3 - -#define PR_BITS_PER_BYTE 8 -#define PR_BITS_PER_SHORT 16 -#define PR_BITS_PER_INT 32 -#define PR_BITS_PER_INT64 64 -#define PR_BITS_PER_LONG 32 -#define PR_BITS_PER_FLOAT 32 -#define PR_BITS_PER_DOUBLE 64 -#define PR_BITS_PER_WORD 32 -#define PR_BITS_PER_DWORD 64 - -#define PR_BITS_PER_BYTE_LOG2 3 -#define PR_BITS_PER_SHORT_LOG2 4 -#define PR_BITS_PER_INT_LOG2 5 -#define PR_BITS_PER_INT64_LOG2 6 -#define PR_BITS_PER_LONG_LOG2 5 -#define PR_BITS_PER_FLOAT_LOG2 5 -#define PR_BITS_PER_DOUBLE_LOG2 6 -#define PR_BITS_PER_WORD_LOG2 5 - -#define PR_ALIGN_OF_SHORT 2 -#define PR_ALIGN_OF_INT 4 -#define PR_ALIGN_OF_LONG 4 -#define PR_ALIGN_OF_INT64 4 -#define PR_ALIGN_OF_FLOAT 4 -#define PR_ALIGN_OF_DOUBLE 4 -#define PR_ALIGN_OF_POINTER 4 -#define PR_ALIGN_OF_WORD 4 - -#endif /* IS_64 */ - -#ifndef NO_NSPR_10_SUPPORT - -#define BYTES_PER_BYTE PR_BYTES_PER_BYTE -#define BYTES_PER_SHORT PR_BYTES_PER_SHORT -#define BYTES_PER_INT PR_BYTES_PER_INT -#define BYTES_PER_INT64 PR_BYTES_PER_INT64 -#define BYTES_PER_LONG PR_BYTES_PER_LONG -#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT -#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE -#define BYTES_PER_WORD PR_BYTES_PER_WORD -#define BYTES_PER_DWORD PR_BYTES_PER_DWORD - -#define BITS_PER_BYTE PR_BITS_PER_BYTE -#define BITS_PER_SHORT PR_BITS_PER_SHORT -#define BITS_PER_INT PR_BITS_PER_INT -#define BITS_PER_INT64 PR_BITS_PER_INT64 -#define BITS_PER_LONG PR_BITS_PER_LONG -#define BITS_PER_FLOAT PR_BITS_PER_FLOAT -#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE -#define BITS_PER_WORD PR_BITS_PER_WORD - -#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2 -#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2 -#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2 -#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2 -#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2 -#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2 -#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2 -#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2 - -#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT -#define ALIGN_OF_INT PR_ALIGN_OF_INT -#define ALIGN_OF_LONG PR_ALIGN_OF_LONG -#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64 -#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT -#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE -#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER -#define ALIGN_OF_WORD PR_ALIGN_OF_WORD - -#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2 -#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2 -#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2 - -#endif /* NO_NSPR_10_SUPPORT */ - -#endif /* nspr_cpucfg___ */ - diff --git a/chromium/base/third_party/nspr/prcpucfg_nacl.h b/chromium/base/third_party/nspr/prcpucfg_nacl.h deleted file mode 100644 index d8602d35111..00000000000 --- a/chromium/base/third_party/nspr/prcpucfg_nacl.h +++ /dev/null @@ -1,246 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is the Netscape Portable Runtime (NSPR). - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998-2000 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef nspr_cpucfg___ -#define nspr_cpucfg___ - -#ifndef XP_UNIX -#define XP_UNIX -#endif - -#ifndef LINUX -#define LINUX -#endif - -#define PR_AF_INET6 10 /* same as AF_INET6 */ - -#if defined(__x86_64__) - -#define IS_LITTLE_ENDIAN 1 -#undef IS_BIG_ENDIAN -#define IS_64 - -#define PR_BYTES_PER_BYTE 1 -#define PR_BYTES_PER_SHORT 2 -#define PR_BYTES_PER_INT 4 -#define PR_BYTES_PER_INT64 8 -#define PR_BYTES_PER_LONG 4 -#define PR_BYTES_PER_FLOAT 4 -#define PR_BYTES_PER_DOUBLE 8 -#define PR_BYTES_PER_WORD 8 -#define PR_BYTES_PER_DWORD 8 - -#define PR_BITS_PER_BYTE 8 -#define PR_BITS_PER_SHORT 16 -#define PR_BITS_PER_INT 32 -#define PR_BITS_PER_INT64 64 -#define PR_BITS_PER_LONG 32 -#define PR_BITS_PER_FLOAT 32 -#define PR_BITS_PER_DOUBLE 64 -#define PR_BITS_PER_WORD 64 - -#define PR_BITS_PER_BYTE_LOG2 3 -#define PR_BITS_PER_SHORT_LOG2 4 -#define PR_BITS_PER_INT_LOG2 5 -#define PR_BITS_PER_INT64_LOG2 6 -#define PR_BITS_PER_LONG_LOG2 5 -#define PR_BITS_PER_FLOAT_LOG2 5 -#define PR_BITS_PER_DOUBLE_LOG2 6 -#define PR_BITS_PER_WORD_LOG2 6 - -#define PR_ALIGN_OF_SHORT 2 -#define PR_ALIGN_OF_INT 4 -#define PR_ALIGN_OF_LONG 4 -#define PR_ALIGN_OF_INT64 8 -#define PR_ALIGN_OF_FLOAT 4 -#define PR_ALIGN_OF_DOUBLE 8 -#define PR_ALIGN_OF_POINTER 8 -#define PR_ALIGN_OF_WORD 8 - -#define PR_BYTES_PER_WORD_LOG2 3 -#define PR_BYTES_PER_DWORD_LOG2 3 - -#elif defined(__i386__) - -#define IS_LITTLE_ENDIAN 1 -#undef IS_BIG_ENDIAN - -#define PR_BYTES_PER_BYTE 1 -#define PR_BYTES_PER_SHORT 2 -#define PR_BYTES_PER_INT 4 -#define PR_BYTES_PER_INT64 8 -#define PR_BYTES_PER_LONG 4 -#define PR_BYTES_PER_FLOAT 4 -#define PR_BYTES_PER_DOUBLE 8 -#define PR_BYTES_PER_WORD 4 -#define PR_BYTES_PER_DWORD 8 - -#define PR_BITS_PER_BYTE 8 -#define PR_BITS_PER_SHORT 16 -#define PR_BITS_PER_INT 32 -#define PR_BITS_PER_INT64 64 -#define PR_BITS_PER_LONG 32 -#define PR_BITS_PER_FLOAT 32 -#define PR_BITS_PER_DOUBLE 64 -#define PR_BITS_PER_WORD 32 - -#define PR_BITS_PER_BYTE_LOG2 3 -#define PR_BITS_PER_SHORT_LOG2 4 -#define PR_BITS_PER_INT_LOG2 5 -#define PR_BITS_PER_INT64_LOG2 6 -#define PR_BITS_PER_LONG_LOG2 5 -#define PR_BITS_PER_FLOAT_LOG2 5 -#define PR_BITS_PER_DOUBLE_LOG2 6 -#define PR_BITS_PER_WORD_LOG2 5 - -#define PR_ALIGN_OF_SHORT 2 -#define PR_ALIGN_OF_INT 4 -#define PR_ALIGN_OF_LONG 4 -#define PR_ALIGN_OF_INT64 4 -#define PR_ALIGN_OF_FLOAT 4 -#define PR_ALIGN_OF_DOUBLE 4 -#define PR_ALIGN_OF_POINTER 4 -#define PR_ALIGN_OF_WORD 4 - -#define PR_BYTES_PER_WORD_LOG2 2 -#define PR_BYTES_PER_DWORD_LOG2 3 - -#elif defined(__arm__) - -#define IS_LITTLE_ENDIAN 1 -#undef IS_BIG_ENDIAN - -#define PR_BYTES_PER_BYTE 1 -#define PR_BYTES_PER_SHORT 2 -#define PR_BYTES_PER_INT 4 -#define PR_BYTES_PER_INT64 8 -#define PR_BYTES_PER_LONG 4 -#define PR_BYTES_PER_FLOAT 4 -#define PR_BYTES_PER_DOUBLE 8 -#define PR_BYTES_PER_WORD 4 -#define PR_BYTES_PER_DWORD 8 - -#define PR_BITS_PER_BYTE 8 -#define PR_BITS_PER_SHORT 16 -#define PR_BITS_PER_INT 32 -#define PR_BITS_PER_INT64 64 -#define PR_BITS_PER_LONG 32 -#define PR_BITS_PER_FLOAT 32 -#define PR_BITS_PER_DOUBLE 64 -#define PR_BITS_PER_WORD 32 - -#define PR_BITS_PER_BYTE_LOG2 3 -#define PR_BITS_PER_SHORT_LOG2 4 -#define PR_BITS_PER_INT_LOG2 5 -#define PR_BITS_PER_INT64_LOG2 6 -#define PR_BITS_PER_LONG_LOG2 5 -#define PR_BITS_PER_FLOAT_LOG2 5 -#define PR_BITS_PER_DOUBLE_LOG2 6 -#define PR_BITS_PER_WORD_LOG2 5 - -#define PR_ALIGN_OF_SHORT 2 -#define PR_ALIGN_OF_INT 4 -#define PR_ALIGN_OF_LONG 4 -#define PR_ALIGN_OF_INT64 4 -#define PR_ALIGN_OF_FLOAT 4 -#define PR_ALIGN_OF_DOUBLE 4 -#define PR_ALIGN_OF_POINTER 4 -#define PR_ALIGN_OF_WORD 4 - -#define PR_BYTES_PER_WORD_LOG2 2 -#define PR_BYTES_PER_DWORD_LOG2 3 - -#else - -#error "Unknown CPU architecture" - -#endif - -#define HAVE_LONG_LONG -#if PR_ALIGN_OF_DOUBLE == 8 -#define HAVE_ALIGNED_DOUBLES -#endif -#if PR_ALIGN_OF_INT64 == 8 -#define HAVE_ALIGNED_LONGLONGS -#endif - -#ifndef NO_NSPR_10_SUPPORT - -#define BYTES_PER_BYTE PR_BYTES_PER_BYTE -#define BYTES_PER_SHORT PR_BYTES_PER_SHORT -#define BYTES_PER_INT PR_BYTES_PER_INT -#define BYTES_PER_INT64 PR_BYTES_PER_INT64 -#define BYTES_PER_LONG PR_BYTES_PER_LONG -#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT -#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE -#define BYTES_PER_WORD PR_BYTES_PER_WORD -#define BYTES_PER_DWORD PR_BYTES_PER_DWORD - -#define BITS_PER_BYTE PR_BITS_PER_BYTE -#define BITS_PER_SHORT PR_BITS_PER_SHORT -#define BITS_PER_INT PR_BITS_PER_INT -#define BITS_PER_INT64 PR_BITS_PER_INT64 -#define BITS_PER_LONG PR_BITS_PER_LONG -#define BITS_PER_FLOAT PR_BITS_PER_FLOAT -#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE -#define BITS_PER_WORD PR_BITS_PER_WORD - -#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2 -#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2 -#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2 -#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2 -#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2 -#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2 -#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2 -#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2 - -#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT -#define ALIGN_OF_INT PR_ALIGN_OF_INT -#define ALIGN_OF_LONG PR_ALIGN_OF_LONG -#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64 -#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT -#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE -#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER -#define ALIGN_OF_WORD PR_ALIGN_OF_WORD - -#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2 -#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2 -#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2 - -#endif /* NO_NSPR_10_SUPPORT */ - -#endif /* nspr_cpucfg___ */ diff --git a/chromium/base/third_party/nspr/prcpucfg_openbsd.h b/chromium/base/third_party/nspr/prcpucfg_openbsd.h deleted file mode 100644 index 93c5b32bc53..00000000000 --- a/chromium/base/third_party/nspr/prcpucfg_openbsd.h +++ /dev/null @@ -1,337 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is the Netscape Portable Runtime (NSPR). - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998-2000 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef nspr_cpucfg___ -#define nspr_cpucfg___ - -#ifndef XP_UNIX -#define XP_UNIX -#endif - -#ifndef OPENBSD -#define OPENBSD -#endif - -#define PR_AF_INET6 28 /* same as AF_INET6 */ - -#ifndef HAVE_LONG_LONG -#define HAVE_LONG_LONG -#endif - -#if defined(__i386__) - -#define IS_LITTLE_ENDIAN 1 -#undef IS_BIG_ENDIAN -#undef HAVE_ALIGNED_DOUBLES -#undef HAVE_ALIGNED_LONGLONGS - -#define PR_BYTES_PER_BYTE 1 -#define PR_BYTES_PER_SHORT 2 -#define PR_BYTES_PER_INT 4 -#define PR_BYTES_PER_INT64 8 -#define PR_BYTES_PER_LONG 4 -#define PR_BYTES_PER_FLOAT 4 -#define PR_BYTES_PER_DOUBLE 8 -#define PR_BYTES_PER_WORD 4 -#define PR_BYTES_PER_DWORD 8 -#define PR_BYTES_PER_WORD_LOG2 2 -#define PR_BYTES_PER_DWORD_LOG2 3 - -#define PR_BITS_PER_BYTE 8 -#define PR_BITS_PER_SHORT 16 -#define PR_BITS_PER_INT 32 -#define PR_BITS_PER_INT64 64 -#define PR_BITS_PER_LONG 32 -#define PR_BITS_PER_FLOAT 32 -#define PR_BITS_PER_DOUBLE 64 -#define PR_BITS_PER_WORD 32 - -#define PR_BITS_PER_BYTE_LOG2 3 -#define PR_BITS_PER_SHORT_LOG2 4 -#define PR_BITS_PER_INT_LOG2 5 -#define PR_BITS_PER_INT64_LOG2 6 -#define PR_BITS_PER_LONG_LOG2 5 -#define PR_BITS_PER_FLOAT_LOG2 5 -#define PR_BITS_PER_DOUBLE_LOG2 6 -#define PR_BITS_PER_WORD_LOG2 5 - -#define PR_ALIGN_OF_SHORT 2 -#define PR_ALIGN_OF_INT 4 -#define PR_ALIGN_OF_LONG 4 -#define PR_ALIGN_OF_INT64 4 -#define PR_ALIGN_OF_FLOAT 4 -#define PR_ALIGN_OF_DOUBLE 4 -#define PR_ALIGN_OF_POINTER 4 - -#elif defined(__alpha__) - -#define IS_LITTLE_ENDIAN 1 -#undef IS_BIG_ENDIAN -#define HAVE_ALIGNED_DOUBLES -#define HAVE_ALIGNED_LONGLONGS -#define IS_64 - -#define PR_BYTES_PER_BYTE 1 -#define PR_BYTES_PER_SHORT 2 -#define PR_BYTES_PER_INT 4 -#define PR_BYTES_PER_INT64 8 -#define PR_BYTES_PER_LONG 8 -#define PR_BYTES_PER_FLOAT 4 -#define PR_BYTES_PER_DOUBLE 8 -#define PR_BYTES_PER_WORD 8 -#define PR_BYTES_PER_DWORD 8 -#define PR_BYTES_PER_WORD_LOG2 3 -#define PR_BYTES_PER_DWORD_LOG2 3 - -#define PR_BITS_PER_BYTE 8 -#define PR_BITS_PER_SHORT 16 -#define PR_BITS_PER_INT 32 -#define PR_BITS_PER_INT64 64 -#define PR_BITS_PER_LONG 64 -#define PR_BITS_PER_FLOAT 32 -#define PR_BITS_PER_DOUBLE 64 -#define PR_BITS_PER_WORD 64 - -#define PR_BITS_PER_BYTE_LOG2 3 -#define PR_BITS_PER_SHORT_LOG2 4 -#define PR_BITS_PER_INT_LOG2 5 -#define PR_BITS_PER_INT64_LOG2 6 -#define PR_BITS_PER_LONG_LOG2 6 -#define PR_BITS_PER_FLOAT_LOG2 5 -#define PR_BITS_PER_DOUBLE_LOG2 6 -#define PR_BITS_PER_WORD_LOG2 6 - -#define PR_ALIGN_OF_SHORT 2 -#define PR_ALIGN_OF_INT 4 -#define PR_ALIGN_OF_LONG 8 -#define PR_ALIGN_OF_INT64 8 -#define PR_ALIGN_OF_FLOAT 4 -#define PR_ALIGN_OF_DOUBLE 8 -#define PR_ALIGN_OF_POINTER 8 - -#elif defined(__sparc__) - -#undef IS_LITTLE_ENDIAN -#define IS_BIG_ENDIAN 1 -#define HAVE_ALIGNED_DOUBLES -#define HAVE_ALIGNED_LONGLONGS -#define IS_64 - -#define PR_BYTES_PER_BYTE 1 -#define PR_BYTES_PER_SHORT 2 -#define PR_BYTES_PER_INT 4 -#define PR_BYTES_PER_INT64 8 -#define PR_BYTES_PER_LONG 8 -#define PR_BYTES_PER_FLOAT 4 -#define PR_BYTES_PER_DOUBLE 8 -#define PR_BYTES_PER_WORD 8 -#define PR_BYTES_PER_DWORD 8 -#define PR_BYTES_PER_WORD_LOG2 3 -#define PR_BYTES_PER_DWORD_LOG2 3 - -#define PR_BITS_PER_BYTE 8 -#define PR_BITS_PER_SHORT 16 -#define PR_BITS_PER_INT 32 -#define PR_BITS_PER_INT64 64 -#define PR_BITS_PER_LONG 64 -#define PR_BITS_PER_FLOAT 32 -#define PR_BITS_PER_DOUBLE 64 -#define PR_BITS_PER_WORD 64 - -#define PR_BITS_PER_BYTE_LOG2 3 -#define PR_BITS_PER_SHORT_LOG2 4 -#define PR_BITS_PER_INT_LOG2 5 -#define PR_BITS_PER_INT64_LOG2 6 -#define PR_BITS_PER_LONG_LOG2 6 -#define PR_BITS_PER_FLOAT_LOG2 5 -#define PR_BITS_PER_DOUBLE_LOG2 6 -#define PR_BITS_PER_WORD_LOG2 6 - -#define PR_ALIGN_OF_SHORT 2 -#define PR_ALIGN_OF_INT 4 -#define PR_ALIGN_OF_LONG 8 -#define PR_ALIGN_OF_INT64 8 -#define PR_ALIGN_OF_FLOAT 4 -#define PR_ALIGN_OF_DOUBLE 8 -#define PR_ALIGN_OF_POINTER 8 - -#elif defined(__ia64__) - -#define IS_LITTLE_ENDIAN 1 -#undef IS_BIG_ENDIAN -#define HAVE_ALIGNED_DOUBLES -#define HAVE_ALIGNED_LONGLONGS -#define IS_64 - -#define PR_BYTES_PER_BYTE 1 -#define PR_BYTES_PER_SHORT 2 -#define PR_BYTES_PER_INT 4 -#define PR_BYTES_PER_INT64 8 -#define PR_BYTES_PER_LONG 8 -#define PR_BYTES_PER_FLOAT 4 -#define PR_BYTES_PER_DOUBLE 8 -#define PR_BYTES_PER_WORD 8 -#define PR_BYTES_PER_DWORD 8 -#define PR_BYTES_PER_WORD_LOG2 3 -#define PR_BYTES_PER_DWORD_LOG2 3 - -#define PR_BITS_PER_BYTE 8 -#define PR_BITS_PER_SHORT 16 -#define PR_BITS_PER_INT 32 -#define PR_BITS_PER_INT64 64 -#define PR_BITS_PER_LONG 64 -#define PR_BITS_PER_FLOAT 32 -#define PR_BITS_PER_DOUBLE 64 -#define PR_BITS_PER_WORD 64 - -#define PR_BITS_PER_BYTE_LOG2 3 -#define PR_BITS_PER_SHORT_LOG2 4 -#define PR_BITS_PER_INT_LOG2 5 -#define PR_BITS_PER_INT64_LOG2 6 -#define PR_BITS_PER_LONG_LOG2 6 -#define PR_BITS_PER_FLOAT_LOG2 5 -#define PR_BITS_PER_DOUBLE_LOG2 6 -#define PR_BITS_PER_WORD_LOG2 6 - -#define PR_ALIGN_OF_SHORT 2 -#define PR_ALIGN_OF_INT 4 -#define PR_ALIGN_OF_LONG 8 -#define PR_ALIGN_OF_INT64 8 -#define PR_ALIGN_OF_FLOAT 4 -#define PR_ALIGN_OF_DOUBLE 8 -#define PR_ALIGN_OF_POINTER 8 -#define PR_ALIGN_OF_WORD 8 - -#elif defined(__amd64__) - -#define IS_LITTLE_ENDIAN 1 -#undef IS_BIG_ENDIAN -#define HAVE_ALIGNED_DOUBLES -#define HAVE_ALIGNED_LONGLONGS -#define IS_64 - -#define PR_BYTES_PER_BYTE 1 -#define PR_BYTES_PER_SHORT 2 -#define PR_BYTES_PER_INT 4 -#define PR_BYTES_PER_INT64 8 -#define PR_BYTES_PER_LONG 8 -#define PR_BYTES_PER_FLOAT 4 -#define PR_BYTES_PER_DOUBLE 8 -#define PR_BYTES_PER_WORD 8 -#define PR_BYTES_PER_DWORD 8 -#define PR_BYTES_PER_WORD_LOG2 3 -#define PR_BYTES_PER_DWORD_LOG2 3 - -#define PR_BITS_PER_BYTE 8 -#define PR_BITS_PER_SHORT 16 -#define PR_BITS_PER_INT 32 -#define PR_BITS_PER_INT64 64 -#define PR_BITS_PER_LONG 64 -#define PR_BITS_PER_FLOAT 32 -#define PR_BITS_PER_DOUBLE 64 -#define PR_BITS_PER_WORD 64 - -#define PR_BITS_PER_BYTE_LOG2 3 -#define PR_BITS_PER_SHORT_LOG2 4 -#define PR_BITS_PER_INT_LOG2 5 -#define PR_BITS_PER_INT64_LOG2 6 -#define PR_BITS_PER_LONG_LOG2 6 -#define PR_BITS_PER_FLOAT_LOG2 5 -#define PR_BITS_PER_DOUBLE_LOG2 6 -#define PR_BITS_PER_WORD_LOG2 6 - -#define PR_ALIGN_OF_SHORT 2 -#define PR_ALIGN_OF_INT 4 -#define PR_ALIGN_OF_LONG 8 -#define PR_ALIGN_OF_INT64 8 -#define PR_ALIGN_OF_FLOAT 4 -#define PR_ALIGN_OF_DOUBLE 8 -#define PR_ALIGN_OF_POINTER 8 -#define PR_ALIGN_OF_WORD 8 - -#else - -#error "Unknown CPU architecture" - -#endif - -#ifndef NO_NSPR_10_SUPPORT - -#define BYTES_PER_BYTE PR_BYTES_PER_BYTE -#define BYTES_PER_SHORT PR_BYTES_PER_SHORT -#define BYTES_PER_INT PR_BYTES_PER_INT -#define BYTES_PER_INT64 PR_BYTES_PER_INT64 -#define BYTES_PER_LONG PR_BYTES_PER_LONG -#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT -#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE -#define BYTES_PER_WORD PR_BYTES_PER_WORD -#define BYTES_PER_DWORD PR_BYTES_PER_DWORD - -#define BITS_PER_BYTE PR_BITS_PER_BYTE -#define BITS_PER_SHORT PR_BITS_PER_SHORT -#define BITS_PER_INT PR_BITS_PER_INT -#define BITS_PER_INT64 PR_BITS_PER_INT64 -#define BITS_PER_LONG PR_BITS_PER_LONG -#define BITS_PER_FLOAT PR_BITS_PER_FLOAT -#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE -#define BITS_PER_WORD PR_BITS_PER_WORD - -#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2 -#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2 -#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2 -#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2 -#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2 -#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2 -#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2 -#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2 - -#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT -#define ALIGN_OF_INT PR_ALIGN_OF_INT -#define ALIGN_OF_LONG PR_ALIGN_OF_LONG -#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64 -#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT -#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE -#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER -#define ALIGN_OF_WORD PR_ALIGN_OF_WORD - -#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2 -#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2 -#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2 - -#endif /* NO_NSPR_10_SUPPORT */ - -#endif /* nspr_cpucfg___ */ diff --git a/chromium/base/third_party/nspr/prcpucfg_solaris.h b/chromium/base/third_party/nspr/prcpucfg_solaris.h deleted file mode 100644 index 81313e553db..00000000000 --- a/chromium/base/third_party/nspr/prcpucfg_solaris.h +++ /dev/null @@ -1,203 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is the Netscape Portable Runtime (NSPR). - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998-2000 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef nspr_cpucfg___ -#define nspr_cpucfg___ - -#ifndef XP_UNIX -#define XP_UNIX -#endif - -#ifndef SOLARIS -#define SOLARIS -#endif - -#define PR_AF_INET6 26 /* same as AF_INET6 */ - -#if defined(sparc) || defined(__sparc) -#undef IS_LITTLE_ENDIAN -#define IS_BIG_ENDIAN 1 -#define PR_ALIGN_OF_INT64 8 -#define PR_ALIGN_OF_DOUBLE 8 -#if defined(__sparcv9) -#define IS_64 -#endif -#elif defined(__x86_64) -#define IS_LITTLE_ENDIAN 1 -#undef IS_BIG_ENDIAN -#define PR_ALIGN_OF_INT64 8 -#define PR_ALIGN_OF_DOUBLE 8 -#define IS_64 -#elif defined(i386) || defined(__i386) -#define IS_LITTLE_ENDIAN 1 -#undef IS_BIG_ENDIAN -#define PR_ALIGN_OF_INT64 4 -#define PR_ALIGN_OF_DOUBLE 4 -#else -#error unknown processor -#endif - -#ifdef IS_64 - -#define PR_BYTES_PER_BYTE 1 -#define PR_BYTES_PER_SHORT 2 -#define PR_BYTES_PER_INT 4 -#define PR_BYTES_PER_INT64 8 -#define PR_BYTES_PER_LONG 8 -#define PR_BYTES_PER_FLOAT 4 -#define PR_BYTES_PER_DOUBLE 8 -#define PR_BYTES_PER_WORD 8 -#define PR_BYTES_PER_DWORD 8 -#define PR_BYTES_PER_WORD_LOG2 3 -#define PR_BYTES_PER_DWORD_LOG2 3 - -#define PR_BITS_PER_BYTE 8 -#define PR_BITS_PER_SHORT 16 -#define PR_BITS_PER_INT 32 -#define PR_BITS_PER_INT64 64 -#define PR_BITS_PER_LONG 64 -#define PR_BITS_PER_FLOAT 32 -#define PR_BITS_PER_DOUBLE 64 -#define PR_BITS_PER_WORD 64 - -#define PR_BITS_PER_BYTE_LOG2 3 -#define PR_BITS_PER_SHORT_LOG2 4 -#define PR_BITS_PER_INT_LOG2 5 -#define PR_BITS_PER_INT64_LOG2 6 -#define PR_BITS_PER_LONG_LOG2 6 -#define PR_BITS_PER_FLOAT_LOG2 5 -#define PR_BITS_PER_DOUBLE_LOG2 6 -#define PR_BITS_PER_WORD_LOG2 6 - -#define PR_ALIGN_OF_SHORT 2 -#define PR_ALIGN_OF_INT 4 -#define PR_ALIGN_OF_LONG 8 -#define PR_ALIGN_OF_FLOAT 4 -#define PR_ALIGN_OF_POINTER 8 - -#else /* IS_64 */ - -#define PR_BYTES_PER_BYTE 1 -#define PR_BYTES_PER_SHORT 2 -#define PR_BYTES_PER_INT 4 -#define PR_BYTES_PER_INT64 8 -#define PR_BYTES_PER_LONG 4 -#define PR_BYTES_PER_FLOAT 4 -#define PR_BYTES_PER_DOUBLE 8 -#define PR_BYTES_PER_WORD 4 -#define PR_BYTES_PER_DWORD 8 -#define PR_BYTES_PER_WORD_LOG2 2 -#define PR_BYTES_PER_DWORD_LOG2 3 - -#define PR_BITS_PER_BYTE 8 -#define PR_BITS_PER_SHORT 16 -#define PR_BITS_PER_INT 32 -#define PR_BITS_PER_INT64 64 -#define PR_BITS_PER_LONG 32 -#define PR_BITS_PER_FLOAT 32 -#define PR_BITS_PER_DOUBLE 64 -#define PR_BITS_PER_WORD 32 - -#define PR_BITS_PER_BYTE_LOG2 3 -#define PR_BITS_PER_SHORT_LOG2 4 -#define PR_BITS_PER_INT_LOG2 5 -#define PR_BITS_PER_INT64_LOG2 6 -#define PR_BITS_PER_LONG_LOG2 5 -#define PR_BITS_PER_FLOAT_LOG2 5 -#define PR_BITS_PER_DOUBLE_LOG2 6 -#define PR_BITS_PER_WORD_LOG2 5 - -#define PR_ALIGN_OF_SHORT 2 -#define PR_ALIGN_OF_INT 4 -#define PR_ALIGN_OF_LONG 4 -#define PR_ALIGN_OF_FLOAT 4 -#define PR_ALIGN_OF_POINTER 4 - -#endif /* IS_64 */ - -#ifndef HAVE_LONG_LONG -#define HAVE_LONG_LONG -#endif -#define HAVE_ALIGNED_DOUBLES -#define HAVE_ALIGNED_LONGLONGS - -#ifndef NO_NSPR_10_SUPPORT - -#define BYTES_PER_BYTE PR_BYTES_PER_BYTE -#define BYTES_PER_SHORT PR_BYTES_PER_SHORT -#define BYTES_PER_INT PR_BYTES_PER_INT -#define BYTES_PER_INT64 PR_BYTES_PER_INT64 -#define BYTES_PER_LONG PR_BYTES_PER_LONG -#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT -#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE -#define BYTES_PER_WORD PR_BYTES_PER_WORD -#define BYTES_PER_DWORD PR_BYTES_PER_DWORD - -#define BITS_PER_BYTE PR_BITS_PER_BYTE -#define BITS_PER_SHORT PR_BITS_PER_SHORT -#define BITS_PER_INT PR_BITS_PER_INT -#define BITS_PER_INT64 PR_BITS_PER_INT64 -#define BITS_PER_LONG PR_BITS_PER_LONG -#define BITS_PER_FLOAT PR_BITS_PER_FLOAT -#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE -#define BITS_PER_WORD PR_BITS_PER_WORD - -#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2 -#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2 -#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2 -#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2 -#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2 -#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2 -#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2 -#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2 - -#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT -#define ALIGN_OF_INT PR_ALIGN_OF_INT -#define ALIGN_OF_LONG PR_ALIGN_OF_LONG -#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64 -#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT -#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE -#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER -#define ALIGN_OF_WORD PR_ALIGN_OF_WORD - -#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2 -#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2 -#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2 - -#endif /* NO_NSPR_10_SUPPORT */ - -#endif /* ifndef nspr_cpucfg___ */ diff --git a/chromium/base/third_party/nspr/prcpucfg_win.h b/chromium/base/third_party/nspr/prcpucfg_win.h deleted file mode 100644 index 4ad534580a9..00000000000 --- a/chromium/base/third_party/nspr/prcpucfg_win.h +++ /dev/null @@ -1,256 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is the Netscape Portable Runtime (NSPR). - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998-2000 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef nspr_cpucfg___ -#define nspr_cpucfg___ - -#ifndef XP_PC -#define XP_PC -#endif - -#ifndef WIN32 -#define WIN32 -#endif - -#ifndef WIN95 -#define WIN95 -#endif - -#define PR_AF_INET6 23 /* same as AF_INET6 */ - -#if defined(_M_IX86) || defined(_X86_) - -#define IS_LITTLE_ENDIAN 1 -#undef IS_BIG_ENDIAN - -#define PR_BYTES_PER_BYTE 1 -#define PR_BYTES_PER_SHORT 2 -#define PR_BYTES_PER_INT 4 -#define PR_BYTES_PER_INT64 8 -#define PR_BYTES_PER_LONG 4 -#define PR_BYTES_PER_FLOAT 4 -#define PR_BYTES_PER_WORD 4 -#define PR_BYTES_PER_DWORD 8 -#define PR_BYTES_PER_DOUBLE 8 - -#define PR_BITS_PER_BYTE 8 -#define PR_BITS_PER_SHORT 16 -#define PR_BITS_PER_INT 32 -#define PR_BITS_PER_INT64 64 -#define PR_BITS_PER_LONG 32 -#define PR_BITS_PER_FLOAT 32 -#define PR_BITS_PER_WORD 32 -#define PR_BITS_PER_DWORD 64 -#define PR_BITS_PER_DOUBLE 64 - -#define PR_BITS_PER_BYTE_LOG2 3 -#define PR_BITS_PER_SHORT_LOG2 4 -#define PR_BITS_PER_INT_LOG2 5 -#define PR_BITS_PER_INT64_LOG2 6 -#define PR_BITS_PER_LONG_LOG2 5 -#define PR_BITS_PER_FLOAT_LOG2 5 -#define PR_BITS_PER_WORD_LOG2 5 -#define PR_BITS_PER_DWORD_LOG2 6 -#define PR_BITS_PER_DOUBLE_LOG2 6 - -#define PR_ALIGN_OF_SHORT 2 -#define PR_ALIGN_OF_INT 4 -#define PR_ALIGN_OF_LONG 4 -#define PR_ALIGN_OF_INT64 8 -#define PR_ALIGN_OF_FLOAT 4 -#define PR_ALIGN_OF_WORD 4 -#define PR_ALIGN_OF_DWORD 8 -#define PR_ALIGN_OF_DOUBLE 4 -#define PR_ALIGN_OF_POINTER 4 - -#define PR_BYTES_PER_WORD_LOG2 2 -#define PR_BYTES_PER_DWORD_LOG2 2 - -#elif defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_) - -#define IS_LITTLE_ENDIAN 1 -#undef IS_BIG_ENDIAN -#define IS_64 - -#define PR_BYTES_PER_BYTE 1 -#define PR_BYTES_PER_SHORT 2 -#define PR_BYTES_PER_INT 4 -#define PR_BYTES_PER_INT64 8 -#define PR_BYTES_PER_LONG 4 -#define PR_BYTES_PER_FLOAT 4 -#define PR_BYTES_PER_WORD 8 -#define PR_BYTES_PER_DWORD 8 -#define PR_BYTES_PER_DOUBLE 8 - -#define PR_BITS_PER_BYTE 8 -#define PR_BITS_PER_SHORT 16 -#define PR_BITS_PER_INT 32 -#define PR_BITS_PER_INT64 64 -#define PR_BITS_PER_LONG 32 -#define PR_BITS_PER_FLOAT 32 -#define PR_BITS_PER_WORD 64 -#define PR_BITS_PER_DWORD 64 -#define PR_BITS_PER_DOUBLE 64 - -#define PR_BITS_PER_BYTE_LOG2 3 -#define PR_BITS_PER_SHORT_LOG2 4 -#define PR_BITS_PER_INT_LOG2 5 -#define PR_BITS_PER_INT64_LOG2 6 -#define PR_BITS_PER_LONG_LOG2 5 -#define PR_BITS_PER_FLOAT_LOG2 5 -#define PR_BITS_PER_WORD_LOG2 6 -#define PR_BITS_PER_DWORD_LOG2 6 -#define PR_BITS_PER_DOUBLE_LOG2 6 - -#define PR_ALIGN_OF_SHORT 2 -#define PR_ALIGN_OF_INT 4 -#define PR_ALIGN_OF_LONG 4 -#define PR_ALIGN_OF_INT64 8 -#define PR_ALIGN_OF_FLOAT 4 -#define PR_ALIGN_OF_WORD 8 -#define PR_ALIGN_OF_DWORD 8 -#define PR_ALIGN_OF_DOUBLE 8 -#define PR_ALIGN_OF_POINTER 8 - -#define PR_BYTES_PER_WORD_LOG2 3 -#define PR_BYTES_PER_DWORD_LOG2 3 - -#elif defined(_M_IA64) || defined(_IA64_) - -#define IS_LITTLE_ENDIAN 1 -#undef IS_BIG_ENDIAN -#define IS_64 - -#define PR_BYTES_PER_BYTE 1 -#define PR_BYTES_PER_SHORT 2 -#define PR_BYTES_PER_INT 4 -#define PR_BYTES_PER_INT64 8 -#define PR_BYTES_PER_LONG 4 -#define PR_BYTES_PER_FLOAT 4 -#define PR_BYTES_PER_WORD 8 -#define PR_BYTES_PER_DWORD 8 -#define PR_BYTES_PER_DOUBLE 8 - -#define PR_BITS_PER_BYTE 8 -#define PR_BITS_PER_SHORT 16 -#define PR_BITS_PER_INT 32 -#define PR_BITS_PER_INT64 64 -#define PR_BITS_PER_LONG 32 -#define PR_BITS_PER_FLOAT 32 -#define PR_BITS_PER_WORD 64 -#define PR_BITS_PER_DWORD 64 -#define PR_BITS_PER_DOUBLE 64 - -#define PR_BITS_PER_BYTE_LOG2 3 -#define PR_BITS_PER_SHORT_LOG2 4 -#define PR_BITS_PER_INT_LOG2 5 -#define PR_BITS_PER_INT64_LOG2 6 -#define PR_BITS_PER_LONG_LOG2 5 -#define PR_BITS_PER_FLOAT_LOG2 5 -#define PR_BITS_PER_WORD_LOG2 6 -#define PR_BITS_PER_DWORD_LOG2 6 -#define PR_BITS_PER_DOUBLE_LOG2 6 - -#define PR_ALIGN_OF_SHORT 2 -#define PR_ALIGN_OF_INT 4 -#define PR_ALIGN_OF_LONG 4 -#define PR_ALIGN_OF_INT64 8 -#define PR_ALIGN_OF_FLOAT 4 -#define PR_ALIGN_OF_WORD 8 -#define PR_ALIGN_OF_DWORD 8 -#define PR_ALIGN_OF_DOUBLE 8 -#define PR_ALIGN_OF_POINTER 8 - -#define PR_BYTES_PER_WORD_LOG2 3 -#define PR_BYTES_PER_DWORD_LOG2 3 - -#else /* defined(_M_IX86) || defined(_X86_) */ - -#error unknown processor architecture - -#endif /* defined(_M_IX86) || defined(_X86_) */ - -#ifndef HAVE_LONG_LONG -#define HAVE_LONG_LONG -#endif - -#ifndef NO_NSPR_10_SUPPORT - -#define BYTES_PER_BYTE PR_BYTES_PER_BYTE -#define BYTES_PER_SHORT PR_BYTES_PER_SHORT -#define BYTES_PER_INT PR_BYTES_PER_INT -#define BYTES_PER_INT64 PR_BYTES_PER_INT64 -#define BYTES_PER_LONG PR_BYTES_PER_LONG -#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT -#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE -#define BYTES_PER_WORD PR_BYTES_PER_WORD -#define BYTES_PER_DWORD PR_BYTES_PER_DWORD - -#define BITS_PER_BYTE PR_BITS_PER_BYTE -#define BITS_PER_SHORT PR_BITS_PER_SHORT -#define BITS_PER_INT PR_BITS_PER_INT -#define BITS_PER_INT64 PR_BITS_PER_INT64 -#define BITS_PER_LONG PR_BITS_PER_LONG -#define BITS_PER_FLOAT PR_BITS_PER_FLOAT -#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE -#define BITS_PER_WORD PR_BITS_PER_WORD - -#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2 -#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2 -#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2 -#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2 -#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2 -#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2 -#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2 -#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2 - -#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT -#define ALIGN_OF_INT PR_ALIGN_OF_INT -#define ALIGN_OF_LONG PR_ALIGN_OF_LONG -#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64 -#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT -#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE -#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER -#define ALIGN_OF_WORD PR_ALIGN_OF_WORD - -#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2 -#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2 -#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2 - -#endif /* NO_NSPR_10_SUPPORT */ - -#endif /* nspr_cpucfg___ */ diff --git a/chromium/base/third_party/nspr/prtime.cc b/chromium/base/third_party/nspr/prtime.cc index 5fc89c5e449..2029660c6f7 100644 --- a/chromium/base/third_party/nspr/prtime.cc +++ b/chromium/base/third_party/nspr/prtime.cc @@ -61,6 +61,8 @@ * 1. prtime.h * 2. prtypes.h * 3. prlong.h + * + * Unit tests are in base/time/pr_time_unittest.cc. */ #include "base/logging.h" @@ -132,6 +134,8 @@ PR_ImplodeTime(const PRExplodedTime *exploded) // Adjust for time zone and dst. Convert from seconds to microseconds. result -= (exploded->tm_params.tp_gmt_offset + exploded->tm_params.tp_dst_offset) * kSecondsToMicroseconds; + // Add microseconds that cannot be represented in |st|. + result += exploded->tm_usec % 1000; return result; #elif defined(OS_MACOSX) // Create the system struct representing our exploded time. @@ -443,10 +447,6 @@ PR_NormalizeTime(PRExplodedTime *time, PRTimeParamFn params) PRTimeParameters PR_GMTParameters(const PRExplodedTime *gmt) { -#if defined(XP_MAC) -#pragma unused (gmt) -#endif - PRTimeParameters retVal = { 0, 0 }; return retVal; } @@ -509,6 +509,7 @@ typedef enum * 06/21/95 04:24:34 PM * 20/06/95 21:07 * 95-06-08 19:32:48 EDT + * 1995-06-17T23:11:25.342156Z * * If the input string doesn't contain a description of the timezone, * we consult the `default_to_gmt' to decide whether the string should @@ -535,6 +536,7 @@ PR_ParseTimeString( int hour = -1; int min = -1; int sec = -1; + int usec = -1; const char *rest = string; @@ -778,6 +780,7 @@ PR_ParseTimeString( int tmp_hour = -1; int tmp_min = -1; int tmp_sec = -1; + int tmp_usec = -1; const char *end = rest + 1; while (*end >= '0' && *end <= '9') end++; @@ -837,14 +840,38 @@ PR_ParseTimeString( else tmp_sec = (rest[0]-'0'); - /* If we made it here, we've parsed hour and min, - and possibly sec, so it worked as a unit. */ + /* fractional second */ + rest = end; + if (*rest == '.') + { + rest++; + end++; + tmp_usec = 0; + /* use up to 6 digits, skip over the rest */ + while (*end >= '0' && *end <= '9') + { + if (end - rest < 6) + tmp_usec = tmp_usec * 10 + *end - '0'; + end++; + } + int ndigits = end - rest; + while (ndigits++ < 6) + tmp_usec *= 10; + rest = end; + } - /* skip over whitespace and see if there's an AM or PM - directly following the time. - */ - if (tmp_hour <= 12) + if (*rest == 'Z') { + zone = TT_GMT; + rest++; + } + else if (tmp_hour <= 12) + { + /* If we made it here, we've parsed hour and min, + and possibly sec, so the current token is a time. + Now skip over whitespace and see if there's an AM + or PM directly following the time. + */ const char *s = end; while (*s && (*s == ' ' || *s == '\t')) s++; @@ -862,6 +889,7 @@ PR_ParseTimeString( hour = tmp_hour; min = tmp_min; sec = tmp_sec; + usec = tmp_usec; rest = end; break; } @@ -869,8 +897,7 @@ PR_ParseTimeString( end[1] >= '0' && end[1] <= '9') { /* Perhaps this is 6/16/95, 16/6/95, 6-16-95, or 16-6-95 - or even 95-06-05... - #### But it doesn't handle 1995-06-22. + or even 95-06-05 or 1995-06-22. */ int n1, n2, n3; const char *s; @@ -881,9 +908,19 @@ PR_ParseTimeString( s = rest; - n1 = (*s++ - '0'); /* first 1 or 2 digits */ + n1 = (*s++ - '0'); /* first 1, 2 or 4 digits */ if (*s >= '0' && *s <= '9') - n1 = n1*10 + (*s++ - '0'); + { + n1 = n1*10 + (*s++ - '0'); + + if (*s >= '0' && *s <= '9') /* optional digits 3 and 4 */ + { + n1 = n1*10 + (*s++ - '0'); + if (*s < '0' || *s > '9') + break; + n1 = n1*10 + (*s++ - '0'); + } + } if (*s != '/' && *s != '-') /* slash */ break; @@ -915,17 +952,21 @@ PR_ParseTimeString( n3 = n3*10 + (*s++ - '0'); } - if ((*s >= '0' && *s <= '9') || /* followed by non-alphanum */ - (*s >= 'A' && *s <= 'Z') || - (*s >= 'a' && *s <= 'z')) + if (*s == 'T' && s[1] >= '0' && s[1] <= '9') + /* followed by ISO 8601 T delimiter and number is ok */ + ; + else if ((*s >= '0' && *s <= '9') || + (*s >= 'A' && *s <= 'Z') || + (*s >= 'a' && *s <= 'z')) + /* but other alphanumerics are not ok */ break; - /* Ok, we parsed three 1-2 digit numbers, with / or - + /* Ok, we parsed three multi-digit numbers, with / or - between them. Now decide what the hell they are - (DD/MM/YY or MM/DD/YY or YY/MM/DD.) + (DD/MM/YY or MM/DD/YY or [YY]YY/MM/DD.) */ - if (n1 > 31 || n1 == 0) /* must be YY/MM/DD */ + if (n1 > 31 || n1 == 0) /* must be [YY]YY/MM/DD */ { if (n2 > 12) break; if (n3 > 31) break; @@ -1018,26 +1059,27 @@ PR_ParseTimeString( /* else, three or more than five digits - what's that? */ break; - } - } + } /* case '0' .. '9' */ + } /* switch */ /* Skip to the end of this token, whether we parsed it or not. - Tokens are delimited by whitespace, or ,;-/ - But explicitly not :+-. + Tokens are delimited by whitespace, or ,;-+/()[] but explicitly not .: + 'T' is also treated as delimiter when followed by a digit (ISO 8601). */ while (*rest && *rest != ' ' && *rest != '\t' && *rest != ',' && *rest != ';' && *rest != '-' && *rest != '+' && *rest != '/' && - *rest != '(' && *rest != ')' && *rest != '[' && *rest != ']') + *rest != '(' && *rest != ')' && *rest != '[' && *rest != ']' && + !(*rest == 'T' && rest[1] >= '0' && rest[1] <= '9') + ) rest++; /* skip over uninteresting chars. */ SKIP_MORE: - while (*rest && - (*rest == ' ' || *rest == '\t' || - *rest == ',' || *rest == ';' || *rest == '/' || - *rest == '(' || *rest == ')' || *rest == '[' || *rest == ']')) + while (*rest == ' ' || *rest == '\t' || + *rest == ',' || *rest == ';' || *rest == '/' || + *rest == '(' || *rest == ')' || *rest == '[' || *rest == ']') rest++; /* "-" is ignored at the beginning of a token if we have not yet @@ -1051,7 +1093,10 @@ PR_ParseTimeString( goto SKIP_MORE; } - } + /* Skip T that may precede ISO 8601 time. */ + if (*rest == 'T' && rest[1] >= '0' && rest[1] <= '9') + rest++; + } /* while */ if (zone != TT_UNKNOWN && zone_offset == -1) { @@ -1086,6 +1131,8 @@ PR_ParseTimeString( return PR_FAILURE; memset(result, 0, sizeof(*result)); + if (usec != -1) + result->tm_usec = usec; if (sec != -1) result->tm_sec = sec; if (min != -1) @@ -1139,11 +1186,9 @@ PR_ParseTimeString( */ /* month, day, hours, mins and secs are always non-negative - so we dont need to worry about them. */ - if(result->tm_year >= 1970) + so we dont need to worry about them. */ + if (result->tm_year >= 1970) { - PRInt64 usec_per_sec; - localTime.tm_sec = result->tm_sec; localTime.tm_min = result->tm_min; localTime.tm_hour = result->tm_hour; @@ -1183,11 +1228,8 @@ PR_ParseTimeString( #endif if (secs != (time_t) -1) { - PRTime usecs64; - LL_I2L(usecs64, secs); - LL_I2L(usec_per_sec, PR_USEC_PER_SEC); - LL_MUL(usecs64, usecs64, usec_per_sec); - *result_imploded = usecs64; + *result_imploded = (PRInt64)secs * PR_USEC_PER_SEC; + *result_imploded += result->tm_usec; return PR_SUCCESS; } } diff --git a/chromium/base/third_party/nspr/prtime.h b/chromium/base/third_party/nspr/prtime.h index ffbedec8253..01a4e540782 100644 --- a/chromium/base/third_party/nspr/prtime.h +++ b/chromium/base/third_party/nspr/prtime.h @@ -52,13 +52,26 @@ #ifndef BASE_PRTIME_H__ #define BASE_PRTIME_H__ +#include <stdint.h> + #include "base/base_export.h" -#include "base/third_party/nspr/prtypes.h" -#define PR_ASSERT DCHECK +typedef int8_t PRInt8; +typedef int16_t PRInt16; +typedef int32_t PRInt32; +typedef int64_t PRInt64; +typedef int PRIntn; + +typedef PRIntn PRBool; +#define PR_TRUE 1 +#define PR_FALSE 0 -#define LL_I2L(l, i) ((l) = (PRInt64)(i)) -#define LL_MUL(r, a, b) ((r) = (a) * (b)) +typedef enum { PR_FAILURE = -1, PR_SUCCESS = 0 } PRStatus; + +#define PR_ASSERT DCHECK +#define PR_CALLBACK +#define PR_INT16_MAX 32767 +#define NSPR_API(__type) extern __type /**********************************************************************/ /************************* TYPES AND CONSTANTS ************************/ @@ -217,6 +230,7 @@ NSPR_API(PRTimeParameters) PR_GMTParameters(const PRExplodedTime *gmt); * 06/21/95 04:24:34 PM * 20/06/95 21:07 * 95-06-08 19:32:48 EDT + * 1995-06-17T23:11:25.342156Z * * If the input string doesn't contain a description of the timezone, * we consult the `default_to_gmt' to decide whether the string should diff --git a/chromium/base/third_party/nspr/prtypes.h b/chromium/base/third_party/nspr/prtypes.h deleted file mode 100644 index 630df81a761..00000000000 --- a/chromium/base/third_party/nspr/prtypes.h +++ /dev/null @@ -1,558 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is the Netscape Portable Runtime (NSPR). - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998-2000 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* -** File: prtypes.h -** Description: Definitions of NSPR's basic types -** -** Prototypes and macros used to make up for deficiencies that we have found -** in ANSI environments. -** -** Since we do not wrap <stdlib.h> and all the other standard headers, authors -** of portable code will not know in general that they need these definitions. -** Instead of requiring these authors to find the dependent uses in their code -** and take the following steps only in those C files, we take steps once here -** for all C files. -**/ - -#ifndef prtypes_h___ -#define prtypes_h___ - -#ifdef MDCPUCFG -#include MDCPUCFG -#else -#include "base/third_party/nspr/prcpucfg.h" -#endif - -#include <stddef.h> - -/*********************************************************************** -** MACROS: PR_EXTERN -** PR_IMPLEMENT -** DESCRIPTION: -** These are only for externally visible routines and globals. For -** internal routines, just use "extern" for type checking and that -** will not export internal cross-file or forward-declared symbols. -** Define a macro for declaring procedures return types. We use this to -** deal with windoze specific type hackery for DLL definitions. Use -** PR_EXTERN when the prototype for the method is declared. Use -** PR_IMPLEMENT for the implementation of the method. -** -** Example: -** in dowhim.h -** PR_EXTERN( void ) DoWhatIMean( void ); -** in dowhim.c -** PR_IMPLEMENT( void ) DoWhatIMean( void ) { return; } -** -** -***********************************************************************/ -#if 1 - -/* -** Local change: the portions of NSPR used by the base module are -** implementation details. NSPR symbols do not need to be exported beyond -** the base module. For all platforms, avoid decorating functions with -** specific visibility and access keywords. -*/ - -#define PR_EXPORT(__type) extern __type -#define PR_EXPORT_DATA(__type) extern __type -#define PR_IMPORT(__type) extern __type -#define PR_IMPORT_DATA(__type) extern __type - -#define PR_EXTERN(__type) extern __type -#define PR_IMPLEMENT(__type) __type -#define PR_EXTERN_DATA(__type) extern __type -#define PR_IMPLEMENT_DATA(__type) __type - -#define PR_CALLBACK -#define PR_CALLBACK_DECL -#define PR_STATIC_CALLBACK(__x) static __x - -#elif defined(WIN32) - -#define PR_EXPORT(__type) extern __declspec(dllexport) __type -#define PR_EXPORT_DATA(__type) extern __declspec(dllexport) __type -#define PR_IMPORT(__type) __declspec(dllimport) __type -#define PR_IMPORT_DATA(__type) __declspec(dllimport) __type - -#define PR_EXTERN(__type) extern __declspec(dllexport) __type -#define PR_IMPLEMENT(__type) __declspec(dllexport) __type -#define PR_EXTERN_DATA(__type) extern __declspec(dllexport) __type -#define PR_IMPLEMENT_DATA(__type) __declspec(dllexport) __type - -#define PR_CALLBACK -#define PR_CALLBACK_DECL -#define PR_STATIC_CALLBACK(__x) static __x - -#elif defined(XP_BEOS) - -#define PR_EXPORT(__type) extern __declspec(dllexport) __type -#define PR_EXPORT_DATA(__type) extern __declspec(dllexport) __type -#define PR_IMPORT(__type) extern __declspec(dllexport) __type -#define PR_IMPORT_DATA(__type) extern __declspec(dllexport) __type - -#define PR_EXTERN(__type) extern __declspec(dllexport) __type -#define PR_IMPLEMENT(__type) __declspec(dllexport) __type -#define PR_EXTERN_DATA(__type) extern __declspec(dllexport) __type -#define PR_IMPLEMENT_DATA(__type) __declspec(dllexport) __type - -#define PR_CALLBACK -#define PR_CALLBACK_DECL -#define PR_STATIC_CALLBACK(__x) static __x - -#elif defined(XP_OS2) && defined(__declspec) - -#define PR_EXPORT(__type) extern __declspec(dllexport) __type -#define PR_EXPORT_DATA(__type) extern __declspec(dllexport) __type -#define PR_IMPORT(__type) extern __declspec(dllimport) __type -#define PR_IMPORT_DATA(__type) extern __declspec(dllimport) __type - -#define PR_EXTERN(__type) extern __declspec(dllexport) __type -#define PR_IMPLEMENT(__type) __declspec(dllexport) __type -#define PR_EXTERN_DATA(__type) extern __declspec(dllexport) __type -#define PR_IMPLEMENT_DATA(__type) __declspec(dllexport) __type - -#define PR_CALLBACK -#define PR_CALLBACK_DECL -#define PR_STATIC_CALLBACK(__x) static __x - -#elif defined(SYMBIAN) - -#define PR_EXPORT(__type) extern __declspec(dllexport) __type -#define PR_EXPORT_DATA(__type) extern __declspec(dllexport) __type -#ifdef __WINS__ -#define PR_IMPORT(__type) extern __declspec(dllexport) __type -#define PR_IMPORT_DATA(__type) extern __declspec(dllexport) __type -#else -#define PR_IMPORT(__type) extern __declspec(dllimport) __type -#define PR_IMPORT_DATA(__type) extern __declspec(dllimport) __type -#endif - -#define PR_EXTERN(__type) extern __type -#define PR_IMPLEMENT(__type) __type -#define PR_EXTERN_DATA(__type) extern __type -#define PR_IMPLEMENT_DATA(__type) __type - -#define PR_CALLBACK -#define PR_CALLBACK_DECL -#define PR_STATIC_CALLBACK(__x) static __x - -#else /* Unix */ - -/* GCC 3.3 and later support the visibility attribute. */ -#if (__GNUC__ >= 4) || \ - (__GNUC__ == 3 && __GNUC_MINOR__ >= 3) -#define PR_VISIBILITY_DEFAULT __attribute__((visibility("default"))) -#else -#define PR_VISIBILITY_DEFAULT -#endif - -#define PR_EXPORT(__type) extern PR_VISIBILITY_DEFAULT __type -#define PR_EXPORT_DATA(__type) extern PR_VISIBILITY_DEFAULT __type -#define PR_IMPORT(__type) extern PR_VISIBILITY_DEFAULT __type -#define PR_IMPORT_DATA(__type) extern PR_VISIBILITY_DEFAULT __type - -#define PR_EXTERN(__type) extern PR_VISIBILITY_DEFAULT __type -#define PR_IMPLEMENT(__type) PR_VISIBILITY_DEFAULT __type -#define PR_EXTERN_DATA(__type) extern PR_VISIBILITY_DEFAULT __type -#define PR_IMPLEMENT_DATA(__type) PR_VISIBILITY_DEFAULT __type -#define PR_CALLBACK -#define PR_CALLBACK_DECL -#define PR_STATIC_CALLBACK(__x) static __x - -#endif - -#if defined(_NSPR_BUILD_) -#define NSPR_API(__type) PR_EXPORT(__type) -#define NSPR_DATA_API(__type) PR_EXPORT_DATA(__type) -#else -#define NSPR_API(__type) PR_IMPORT(__type) -#define NSPR_DATA_API(__type) PR_IMPORT_DATA(__type) -#endif - -/*********************************************************************** -** MACROS: PR_BEGIN_MACRO -** PR_END_MACRO -** DESCRIPTION: -** Macro body brackets so that macros with compound statement definitions -** behave syntactically more like functions when called. -***********************************************************************/ -#define PR_BEGIN_MACRO do { -#define PR_END_MACRO } while (0) - -/*********************************************************************** -** MACROS: PR_BEGIN_EXTERN_C -** PR_END_EXTERN_C -** DESCRIPTION: -** Macro shorthands for conditional C++ extern block delimiters. -***********************************************************************/ -#ifdef __cplusplus -#define PR_BEGIN_EXTERN_C extern "C" { -#define PR_END_EXTERN_C } -#else -#define PR_BEGIN_EXTERN_C -#define PR_END_EXTERN_C -#endif - -/*********************************************************************** -** MACROS: PR_BIT -** PR_BITMASK -** DESCRIPTION: -** Bit masking macros. XXX n must be <= 31 to be portable -***********************************************************************/ -#define PR_BIT(n) ((PRUint32)1 << (n)) -#define PR_BITMASK(n) (PR_BIT(n) - 1) - -/*********************************************************************** -** MACROS: PR_ROUNDUP -** PR_MIN -** PR_MAX -** PR_ABS -** DESCRIPTION: -** Commonly used macros for operations on compatible types. -***********************************************************************/ -#define PR_ROUNDUP(x,y) ((((x)+((y)-1))/(y))*(y)) -#define PR_MIN(x,y) ((x)<(y)?(x):(y)) -#define PR_MAX(x,y) ((x)>(y)?(x):(y)) -#define PR_ABS(x) ((x)<0?-(x):(x)) - -PR_BEGIN_EXTERN_C - -/************************************************************************ -** TYPES: PRUint8 -** PRInt8 -** DESCRIPTION: -** The int8 types are known to be 8 bits each. There is no type that -** is equivalent to a plain "char". -************************************************************************/ -#if PR_BYTES_PER_BYTE == 1 -typedef unsigned char PRUint8; -/* -** Some cfront-based C++ compilers do not like 'signed char' and -** issue the warning message: -** warning: "signed" not implemented (ignored) -** For these compilers, we have to define PRInt8 as plain 'char'. -** Make sure that plain 'char' is indeed signed under these compilers. -*/ -#if (defined(HPUX) && defined(__cplusplus) \ - && !defined(__GNUC__) && __cplusplus < 199707L) \ - || (defined(SCO) && defined(__cplusplus) \ - && !defined(__GNUC__) && __cplusplus == 1L) -typedef char PRInt8; -#else -typedef signed char PRInt8; -#endif -#else -#error No suitable type for PRInt8/PRUint8 -#endif - -/************************************************************************ - * MACROS: PR_INT8_MAX - * PR_INT8_MIN - * PR_UINT8_MAX - * DESCRIPTION: - * The maximum and minimum values of a PRInt8 or PRUint8. -************************************************************************/ - -#define PR_INT8_MAX 127 -#define PR_INT8_MIN (-128) -#define PR_UINT8_MAX 255U - -/************************************************************************ -** TYPES: PRUint16 -** PRInt16 -** DESCRIPTION: -** The int16 types are known to be 16 bits each. -************************************************************************/ -#if PR_BYTES_PER_SHORT == 2 -typedef unsigned short PRUint16; -typedef short PRInt16; -#else -#error No suitable type for PRInt16/PRUint16 -#endif - -/************************************************************************ - * MACROS: PR_INT16_MAX - * PR_INT16_MIN - * PR_UINT16_MAX - * DESCRIPTION: - * The maximum and minimum values of a PRInt16 or PRUint16. -************************************************************************/ - -#define PR_INT16_MAX 32767 -#define PR_INT16_MIN (-32768) -#define PR_UINT16_MAX 65535U - -/************************************************************************ -** TYPES: PRUint32 -** PRInt32 -** DESCRIPTION: -** The int32 types are known to be 32 bits each. -************************************************************************/ -#if PR_BYTES_PER_INT == 4 -typedef unsigned int PRUint32; -typedef int PRInt32; -#define PR_INT32(x) x -#define PR_UINT32(x) x ## U -#elif PR_BYTES_PER_LONG == 4 -typedef unsigned long PRUint32; -typedef long PRInt32; -#define PR_INT32(x) x ## L -#define PR_UINT32(x) x ## UL -#else -#error No suitable type for PRInt32/PRUint32 -#endif - -/************************************************************************ - * MACROS: PR_INT32_MAX - * PR_INT32_MIN - * PR_UINT32_MAX - * DESCRIPTION: - * The maximum and minimum values of a PRInt32 or PRUint32. -************************************************************************/ - -#define PR_INT32_MAX PR_INT32(2147483647) -#define PR_INT32_MIN (-PR_INT32_MAX - 1) -#define PR_UINT32_MAX PR_UINT32(4294967295) - -/************************************************************************ -** TYPES: PRUint64 -** PRInt64 -** DESCRIPTION: -** The int64 types are known to be 64 bits each. Care must be used when -** declaring variables of type PRUint64 or PRInt64. Different hardware -** architectures and even different compilers have varying support for -** 64 bit values. The only guaranteed portability requires the use of -** the LL_ macros (see prlong.h). -************************************************************************/ -#ifdef HAVE_LONG_LONG -/* Keep this in sync with prlong.h. */ -/* - * On 64-bit Mac OS X, uint64 needs to be defined as unsigned long long to - * match uint64_t, otherwise our uint64 typedef conflicts with the uint64 - * typedef in cssmconfig.h, which CoreServices.h includes indirectly. - */ -#if PR_BYTES_PER_LONG == 8 && !defined(__APPLE__) -typedef long PRInt64; -typedef unsigned long PRUint64; -#elif defined(WIN32) && !defined(__GNUC__) -typedef __int64 PRInt64; -typedef unsigned __int64 PRUint64; -#else -typedef long long PRInt64; -typedef unsigned long long PRUint64; -#endif /* PR_BYTES_PER_LONG == 8 */ -#else /* !HAVE_LONG_LONG */ -typedef struct { -#ifdef IS_LITTLE_ENDIAN - PRUint32 lo, hi; -#else - PRUint32 hi, lo; -#endif -} PRInt64; -typedef PRInt64 PRUint64; -#endif /* !HAVE_LONG_LONG */ - -/************************************************************************ -** TYPES: PRUintn -** PRIntn -** DESCRIPTION: -** The PRIntn types are most appropriate for automatic variables. They are -** guaranteed to be at least 16 bits, though various architectures may -** define them to be wider (e.g., 32 or even 64 bits). These types are -** never valid for fields of a structure. -************************************************************************/ -#if PR_BYTES_PER_INT >= 2 -typedef int PRIntn; -typedef unsigned int PRUintn; -#else -#error 'sizeof(int)' not sufficient for platform use -#endif - -/************************************************************************ -** TYPES: PRFloat64 -** DESCRIPTION: -** NSPR's floating point type is always 64 bits. -************************************************************************/ -typedef double PRFloat64; - -/************************************************************************ -** TYPES: PRSize -** DESCRIPTION: -** A type for representing the size of objects. -************************************************************************/ -typedef size_t PRSize; - - -/************************************************************************ -** TYPES: PROffset32, PROffset64 -** DESCRIPTION: -** A type for representing byte offsets from some location. -************************************************************************/ -typedef PRInt32 PROffset32; -typedef PRInt64 PROffset64; - -/************************************************************************ -** TYPES: PRPtrDiff -** DESCRIPTION: -** A type for pointer difference. Variables of this type are suitable -** for storing a pointer or pointer subtraction. -************************************************************************/ -typedef ptrdiff_t PRPtrdiff; - -/************************************************************************ -** TYPES: PRUptrdiff -** DESCRIPTION: -** A type for pointer difference. Variables of this type are suitable -** for storing a pointer or pointer sutraction. -************************************************************************/ -#ifdef _WIN64 -typedef PRUint64 PRUptrdiff; -#else -typedef unsigned long PRUptrdiff; -#endif - -/************************************************************************ -** TYPES: PRBool -** DESCRIPTION: -** Use PRBool for variables and parameter types. Use PR_FALSE and PR_TRUE -** for clarity of target type in assignments and actual arguments. Use -** 'if (bool)', 'while (!bool)', '(bool) ? x : y' etc., to test booleans -** just as you would C int-valued conditions. -************************************************************************/ -typedef PRIntn PRBool; -#define PR_TRUE 1 -#define PR_FALSE 0 - -/************************************************************************ -** TYPES: PRPackedBool -** DESCRIPTION: -** Use PRPackedBool within structs where bitfields are not desirable -** but minimum and consistant overhead matters. -************************************************************************/ -typedef PRUint8 PRPackedBool; - -/* -** Status code used by some routines that have a single point of failure or -** special status return. -*/ -typedef enum { PR_FAILURE = -1, PR_SUCCESS = 0 } PRStatus; - -#ifndef __PRUNICHAR__ -#define __PRUNICHAR__ -#ifdef WIN32 -typedef wchar_t PRUnichar; -#else -typedef PRUint16 PRUnichar; -#endif -#endif - -/* -** WARNING: The undocumented data types PRWord and PRUword are -** only used in the garbage collection and arena code. Do not -** use PRWord and PRUword in new code. -** -** A PRWord is an integer that is the same size as a void*. -** It implements the notion of a "word" in the Java Virtual -** Machine. (See Sec. 3.4 "Words", The Java Virtual Machine -** Specification, Addison-Wesley, September 1996. -** http://java.sun.com/docs/books/vmspec/index.html.) -*/ -#ifdef _WIN64 -typedef PRInt64 PRWord; -typedef PRUint64 PRUword; -#else -typedef long PRWord; -typedef unsigned long PRUword; -#endif - -#if defined(NO_NSPR_10_SUPPORT) -#else -/********* ???????????????? FIX ME ??????????????????????????? *****/ -/********************** Some old definitions until pr=>ds transition is done ***/ -/********************** Also, we are still using NSPR 1.0. GC ******************/ -/* -** Fundamental NSPR macros, used nearly everywhere. -*/ - -#define PR_PUBLIC_API PR_IMPLEMENT - -/* -** Macro body brackets so that macros with compound statement definitions -** behave syntactically more like functions when called. -*/ -#define NSPR_BEGIN_MACRO do { -#define NSPR_END_MACRO } while (0) - -/* -** Macro shorthands for conditional C++ extern block delimiters. -*/ -#ifdef NSPR_BEGIN_EXTERN_C -#undef NSPR_BEGIN_EXTERN_C -#endif -#ifdef NSPR_END_EXTERN_C -#undef NSPR_END_EXTERN_C -#endif - -#ifdef __cplusplus -#define NSPR_BEGIN_EXTERN_C extern "C" { -#define NSPR_END_EXTERN_C } -#else -#define NSPR_BEGIN_EXTERN_C -#define NSPR_END_EXTERN_C -#endif - -/********* ????????????? End Fix me ?????????????????????????????? *****/ -#endif /* NO_NSPR_10_SUPPORT */ - -/* -** Compile-time assert. "condition" must be a constant expression. -** The macro can be used only in places where an "extern" declaration is -** allowed. -*/ -#define PR_STATIC_ASSERT(condition) \ - extern void pr_static_assert(int arg[(condition) ? 1 : -1]) - -PR_END_EXTERN_C - -#if !defined(NO_NSPR_10_SUPPORT) -#include "base/basictypes.h" -#endif - -#endif /* prtypes_h___ */ - diff --git a/chromium/base/third_party/superfasthash/LICENSE b/chromium/base/third_party/superfasthash/LICENSE new file mode 100644 index 00000000000..3c40a3ecd79 --- /dev/null +++ b/chromium/base/third_party/superfasthash/LICENSE @@ -0,0 +1,27 @@ +Paul Hsieh OLD BSD license + +Copyright (c) 2010, Paul Hsieh +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. +* Neither my name, Paul Hsieh, nor the names of any other contributors to the + code use may not be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/chromium/base/third_party/superfasthash/OWNERS b/chromium/base/third_party/superfasthash/OWNERS new file mode 100644 index 00000000000..f34cfb1c929 --- /dev/null +++ b/chromium/base/third_party/superfasthash/OWNERS @@ -0,0 +1,2 @@ +mgiuca@chromium.org +rvargas@chromium.org diff --git a/chromium/base/third_party/superfasthash/README.chromium b/chromium/base/third_party/superfasthash/README.chromium new file mode 100644 index 00000000000..d41ed7724a2 --- /dev/null +++ b/chromium/base/third_party/superfasthash/README.chromium @@ -0,0 +1,29 @@ +Name: Paul Hsieh's SuperFastHash +Short Name: SuperFastHash +URL: http://www.azillionmonkeys.com/qed/hash.html +Version: 0 +Date: 2012-02-21 +License: BSD +License File: LICENSE +Security Critical: yes + +Description: +A fast string hashing algorithm. + +Local Modifications: +- Added LICENSE. +- Added license text as a comment to the top of superfasthash.c. +- #include <stdint.h> instead of "pstdint.h". +- #include <stdlib.h>. + +The license is a standard 3-clause BSD license with the following minor changes: + +"nor the names of its contributors may be used" +is replaced with: +"nor the names of any other contributors to the code use may not be used" + +and + +"IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE" +is replaced with: +"IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE" diff --git a/chromium/base/third_party/superfasthash/superfasthash.c b/chromium/base/third_party/superfasthash/superfasthash.c new file mode 100644 index 00000000000..6e7687e1319 --- /dev/null +++ b/chromium/base/third_party/superfasthash/superfasthash.c @@ -0,0 +1,84 @@ +// Copyright (c) 2010, Paul Hsieh +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither my name, Paul Hsieh, nor the names of any other contributors to the +// code use may not be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#include <stdint.h> +#include <stdlib.h> +#undef get16bits +#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ + || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) +#define get16bits(d) (*((const uint16_t *) (d))) +#endif + +#if !defined (get16bits) +#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\ + +(uint32_t)(((const uint8_t *)(d))[0]) ) +#endif + +uint32_t SuperFastHash (const char * data, int len) { +uint32_t hash = len, tmp; +int rem; + + if (len <= 0 || data == NULL) return 0; + + rem = len & 3; + len >>= 2; + + /* Main loop */ + for (;len > 0; len--) { + hash += get16bits (data); + tmp = (get16bits (data+2) << 11) ^ hash; + hash = (hash << 16) ^ tmp; + data += 2*sizeof (uint16_t); + hash += hash >> 11; + } + + /* Handle end cases */ + switch (rem) { + case 3: hash += get16bits (data); + hash ^= hash << 16; + hash ^= ((signed char)data[sizeof (uint16_t)]) << 18; + hash += hash >> 11; + break; + case 2: hash += get16bits (data); + hash ^= hash << 11; + hash += hash >> 17; + break; + case 1: hash += (signed char)*data; + hash ^= hash << 10; + hash += hash >> 1; + } + + /* Force "avalanching" of final 127 bits */ + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + + return hash; +} diff --git a/chromium/base/third_party/symbolize/BUILD.gn b/chromium/base/third_party/symbolize/BUILD.gn new file mode 100644 index 00000000000..0b4dd952bac --- /dev/null +++ b/chromium/base/third_party/symbolize/BUILD.gn @@ -0,0 +1,20 @@ +# 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("symbolize") { + visibility = "//base/*" + sources = [ + "config.h", + "demangle.cc", + "demangle.h", + "glog/logging.h", + "glog/raw_logging.h", + "symbolize.cc", + "symbolize.h", + "utilities.h", + ] + + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] +} diff --git a/chromium/base/third_party/symbolize/README.chromium b/chromium/base/third_party/symbolize/README.chromium index 520a3db5775..de92794c812 100644 --- a/chromium/base/third_party/symbolize/README.chromium +++ b/chromium/base/third_party/symbolize/README.chromium @@ -3,16 +3,13 @@ URL: http://code.google.com/p/google-glog/ License: BSD The following files are copied AS-IS from: -http://code.google.com/p/google-glog/source/browse/#svn/trunk/src (r76) +http://code.google.com/p/google-glog/source/browse/#svn/trunk/src (r141) - demangle.cc - demangle.h - symbolize.cc - symbolize.h -r137 (https://code.google.com/p/google-glog/source/detail?r=137) was merged -after that. - The following files are minimal stubs created for use in Chromium: - config.h diff --git a/chromium/base/third_party/symbolize/demangle.cc b/chromium/base/third_party/symbolize/demangle.cc index 46556bf3c13..e858181a68f 100644 --- a/chromium/base/third_party/symbolize/demangle.cc +++ b/chromium/base/third_party/symbolize/demangle.cc @@ -28,6 +28,11 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Author: Satoru Takabayashi +// +// For reference check out: +// http://www.codesourcery.com/public/cxx-abi/abi.html#mangling +// +// Note that we only have partial C++0x support yet. #include <stdio.h> // for NULL #include "demangle.h" @@ -138,14 +143,12 @@ static const AbbrevPair kSubstitutionList[] = { // State needed for demangling. typedef struct { const char *mangled_cur; // Cursor of mangled name. - const char *mangled_end; // End of mangled name. char *out_cur; // Cursor of output string. const char *out_begin; // Beginning of output string. const char *out_end; // End of output string. const char *prev_name; // For constructors/destructors. int prev_name_length; // For constructors/destructors. - int nest_level; // For nested names. - int number; // Remember the previous number. + short nest_level; // For nested names. bool append; // Append flag. bool overflowed; // True if output gets overflowed. } State; @@ -161,6 +164,16 @@ static size_t StrLen(const char *str) { return len; } +// Returns true if "str" has at least "n" characters remaining. +static bool AtLeastNumCharsRemaining(const char *str, int n) { + for (int i = 0; i < n; ++i) { + if (str[i] == '\0') { + return false; + } + } + return true; +} + // Returns true if "str" has "prefix" as a prefix. static bool StrPrefix(const char *str, const char *prefix) { size_t i = 0; @@ -174,39 +187,33 @@ static bool StrPrefix(const char *str, const char *prefix) { static void InitState(State *state, const char *mangled, char *out, int out_size) { state->mangled_cur = mangled; - state->mangled_end = mangled + StrLen(mangled); state->out_cur = out; state->out_begin = out; state->out_end = out + out_size; state->prev_name = NULL; state->prev_name_length = -1; state->nest_level = -1; - state->number = -1; state->append = true; state->overflowed = false; } -// Calculates the remaining length of the mangled name. -static int RemainingLength(State *state) { - return state->mangled_end - state->mangled_cur; -} - -// Returns true and advances "mangled_cur" if we find "c" at -// "mangled_cur" position. -static bool ParseChar(State *state, const char c) { - if (RemainingLength(state) >= 1 && *state->mangled_cur == c) { +// Returns true and advances "mangled_cur" if we find "one_char_token" +// at "mangled_cur" position. It is assumed that "one_char_token" does +// not contain '\0'. +static bool ParseOneCharToken(State *state, const char one_char_token) { + if (state->mangled_cur[0] == one_char_token) { ++state->mangled_cur; return true; } return false; } -// Returns true and advances "mangled_cur" if we find "two_chars" at -// "mangled_cur" position. -static bool ParseTwoChar(State *state, const char *two_chars) { - if (RemainingLength(state) >= 2 && - state->mangled_cur[0] == two_chars[0] && - state->mangled_cur[1] == two_chars[1]) { +// Returns true and advances "mangled_cur" if we find "two_char_token" +// at "mangled_cur" position. It is assumed that "two_char_token" does +// not contain '\0'. +static bool ParseTwoCharToken(State *state, const char *two_char_token) { + if (state->mangled_cur[0] == two_char_token[0] && + state->mangled_cur[1] == two_char_token[1]) { state->mangled_cur += 2; return true; } @@ -216,13 +223,10 @@ static bool ParseTwoChar(State *state, const char *two_chars) { // Returns true and advances "mangled_cur" if we find any character in // "char_class" at "mangled_cur" position. static bool ParseCharClass(State *state, const char *char_class) { - if (state->mangled_cur == state->mangled_end) { - return false; - } const char *p = char_class; for (; *p != '\0'; ++p) { - if (*state->mangled_cur == *p) { - state->mangled_cur += 1; + if (state->mangled_cur[0] == *p) { + ++state->mangled_cur; return true; } } @@ -230,7 +234,7 @@ static bool ParseCharClass(State *state, const char *char_class) { } // This function is used for handling an optional non-terminal. -static bool Optional(bool status) { +static bool Optional(bool) { return true; } @@ -245,6 +249,16 @@ static bool OneOrMore(ParseFunc parse_func, State *state) { return false; } +// This function is used for handling <non-terminal>* syntax. The function +// always returns true and must be followed by a termination token or a +// terminating sequence not handled by parse_func (e.g. +// ParseOneCharToken(state, 'E')). +static bool ZeroOrMore(ParseFunc parse_func, State *state) { + while (parse_func(state)) { + } + return true; +} + // Append "str" at "out_cur". If there is an overflow, "overflowed" // is set to true for later use. The output string is ensured to // always terminate with '\0' as long as there is no overflow. @@ -270,7 +284,37 @@ static bool IsLower(char c) { } static bool IsAlpha(char c) { - return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); +} + +static bool IsDigit(char c) { + return c >= '0' && c <= '9'; +} + +// Returns true if "str" is a function clone suffix. These suffixes are used +// by GCC 4.5.x and later versions to indicate functions which have been +// cloned during optimization. We treat any sequence (.<alpha>+.<digit>+)+ as +// a function clone suffix. +static bool IsFunctionCloneSuffix(const char *str) { + size_t i = 0; + while (str[i] != '\0') { + // Consume a single .<alpha>+.<digit>+ sequence. + if (str[i] != '.' || !IsAlpha(str[i + 1])) { + return false; + } + i += 2; + while (IsAlpha(str[i])) { + ++i; + } + if (str[i] != '.' || !IsDigit(str[i + 1])) { + return false; + } + i += 2; + while (IsDigit(str[i])) { + ++i; + } + } + return true; // Consumed everything in "str". } // Append "str" with some tweaks, iff "append" state is true. @@ -309,7 +353,7 @@ static bool EnterNestedName(State *state) { } // This function is used for handling nested names. -static bool LeaveNestedName(State *state, int prev_value) { +static bool LeaveNestedName(State *state, short prev_value) { state->nest_level = prev_value; return true; } @@ -349,11 +393,11 @@ static void MaybeCancelLastSeparator(State *state) { } } -// Returns true if identifier pointed by "mangled_cur" is anonymous -// namespace. -static bool IdentifierIsAnonymousNamespace(State *state) { - const char anon_prefix[] = "_GLOBAL__N_"; - return (state->number > sizeof(anon_prefix) - 1 && // Should be longer. +// Returns true if the identifier of the given length pointed to by +// "mangled_cur" is anonymous namespace. +static bool IdentifierIsAnonymousNamespace(State *state, int length) { + static const char anon_prefix[] = "_GLOBAL__N_"; + return (length > (int)sizeof(anon_prefix) - 1 && // Should be longer. StrPrefix(state->mangled_cur, anon_prefix)); } @@ -368,10 +412,10 @@ static bool ParsePrefix(State *state); static bool ParseUnqualifiedName(State *state); static bool ParseSourceName(State *state); static bool ParseLocalSourceName(State *state); -static bool ParseNumber(State *state); +static bool ParseNumber(State *state, int *number_out); static bool ParseFloatNumber(State *state); static bool ParseSeqId(State *state); -static bool ParseIdentifier(State *state); +static bool ParseIdentifier(State *state, int length); static bool ParseOperatorName(State *state); static bool ParseSpecialName(State *state); static bool ParseCallOffset(State *state); @@ -428,17 +472,7 @@ static bool ParseSubstitution(State *state); // <mangled-name> ::= _Z <encoding> static bool ParseMangledName(State *state) { - if (ParseTwoChar(state, "_Z") && ParseEncoding(state)) { - // Append trailing version suffix if any. - // ex. _Z3foo@@GLIBCXX_3.4 - if (state->mangled_cur < state->mangled_end && - state->mangled_cur[0] == '@') { - MaybeAppend(state, state->mangled_cur); - state->mangled_cur = state->mangled_end; - } - return true; - } - return false; + return ParseTwoCharToken(state, "_Z") && ParseEncoding(state); } // <encoding> ::= <(function) name> <bare-function-type> @@ -488,7 +522,7 @@ static bool ParseUnscopedName(State *state) { } State copy = *state; - if (ParseTwoChar(state, "St") && + if (ParseTwoCharToken(state, "St") && MaybeAppend(state, "std::") && ParseUnqualifiedName(state)) { return true; @@ -507,12 +541,12 @@ static bool ParseUnscopedTemplateName(State *state) { // ::= N [<CV-qualifiers>] <template-prefix> <template-args> E static bool ParseNestedName(State *state) { State copy = *state; - if (ParseChar(state, 'N') && + if (ParseOneCharToken(state, 'N') && EnterNestedName(state) && Optional(ParseCVQualifiers(state)) && ParsePrefix(state) && LeaveNestedName(state, copy.nest_level) && - ParseChar(state, 'E')) { + ParseOneCharToken(state, 'E')) { return true; } *state = copy; @@ -565,7 +599,8 @@ static bool ParseUnqualifiedName(State *state) { // <source-name> ::= <positive length number> <identifier> static bool ParseSourceName(State *state) { State copy = *state; - if (ParseNumber(state) && ParseIdentifier(state)) { + int length = -1; + if (ParseNumber(state, &length) && ParseIdentifier(state, length)) { return true; } *state = copy; @@ -579,7 +614,7 @@ static bool ParseSourceName(State *state) { // http://gcc.gnu.org/viewcvs?view=rev&revision=124467 static bool ParseLocalSourceName(State *state) { State copy = *state; - if (ParseChar(state, 'L') && ParseSourceName(state) && + if (ParseOneCharToken(state, 'L') && ParseSourceName(state) && Optional(ParseDiscriminator(state))) { return true; } @@ -588,15 +623,17 @@ static bool ParseLocalSourceName(State *state) { } // <number> ::= [n] <non-negative decimal integer> -static bool ParseNumber(State *state) { +// If "number_out" is non-null, then *number_out is set to the value of the +// parsed number on success. +static bool ParseNumber(State *state, int *number_out) { int sign = 1; - if (ParseChar(state, 'n')) { + if (ParseOneCharToken(state, 'n')) { sign = -1; } const char *p = state->mangled_cur; int number = 0; - for (;p < state->mangled_end; ++p) { - if ((*p >= '0' && *p <= '9')) { + for (;*p != '\0'; ++p) { + if (IsDigit(*p)) { number = number * 10 + (*p - '0'); } else { break; @@ -604,7 +641,9 @@ static bool ParseNumber(State *state) { } if (p != state->mangled_cur) { // Conversion succeeded. state->mangled_cur = p; - state->number = number * sign; + if (number_out != NULL) { + *number_out = number * sign; + } return true; } return false; @@ -614,19 +653,13 @@ static bool ParseNumber(State *state) { // hexadecimal string. static bool ParseFloatNumber(State *state) { const char *p = state->mangled_cur; - int number = 0; - for (;p < state->mangled_end; ++p) { - if ((*p >= '0' && *p <= '9')) { - number = number * 16 + (*p - '0'); - } else if (*p >= 'a' && *p <= 'f') { - number = number * 16 + (*p - 'a' + 10); - } else { + for (;*p != '\0'; ++p) { + if (!IsDigit(*p) && !(*p >= 'a' && *p <= 'f')) { break; } } if (p != state->mangled_cur) { // Conversion succeeded. state->mangled_cur = p; - state->number = number; return true; } return false; @@ -636,37 +669,30 @@ static bool ParseFloatNumber(State *state) { // using digits and upper case letters static bool ParseSeqId(State *state) { const char *p = state->mangled_cur; - int number = 0; - for (;p < state->mangled_end; ++p) { - if ((*p >= '0' && *p <= '9')) { - number = number * 36 + (*p - '0'); - } else if (*p >= 'A' && *p <= 'Z') { - number = number * 36 + (*p - 'A' + 10); - } else { + for (;*p != '\0'; ++p) { + if (!IsDigit(*p) && !(*p >= 'A' && *p <= 'Z')) { break; } } if (p != state->mangled_cur) { // Conversion succeeded. state->mangled_cur = p; - state->number = number; return true; } return false; } -// <identifier> ::= <unqualified source code identifier> -static bool ParseIdentifier(State *state) { - if (state->number == -1 || - RemainingLength(state) < state->number) { +// <identifier> ::= <unqualified source code identifier> (of given length) +static bool ParseIdentifier(State *state, int length) { + if (length == -1 || + !AtLeastNumCharsRemaining(state->mangled_cur, length)) { return false; } - if (IdentifierIsAnonymousNamespace(state)) { + if (IdentifierIsAnonymousNamespace(state, length)) { MaybeAppend(state, "(anonymous namespace)"); } else { - MaybeAppendWithLength(state, state->mangled_cur, state->number); + MaybeAppendWithLength(state, state->mangled_cur, length); } - state->mangled_cur += state->number; - state->number = -1; // Reset the number. + state->mangled_cur += length; return true; } @@ -674,12 +700,12 @@ static bool ParseIdentifier(State *state) { // ::= cv <type> # (cast) // ::= v <digit> <source-name> # vendor extended operator static bool ParseOperatorName(State *state) { - if (RemainingLength(state) < 2) { + if (!AtLeastNumCharsRemaining(state->mangled_cur, 2)) { return false; } // First check with "cv" (cast) case. State copy = *state; - if (ParseTwoChar(state, "cv") && + if (ParseTwoCharToken(state, "cv") && MaybeAppend(state, "operator ") && EnterNestedName(state) && ParseType(state) && @@ -689,7 +715,7 @@ static bool ParseOperatorName(State *state) { *state = copy; // Then vendor extended operators. - if (ParseChar(state, 'v') && ParseCharClass(state, "0123456789") && + if (ParseOneCharToken(state, 'v') && ParseCharClass(state, "0123456789") && ParseSourceName(state)) { return true; } @@ -738,34 +764,34 @@ static bool ParseOperatorName(State *state) { // stack traces. The are special data. static bool ParseSpecialName(State *state) { State copy = *state; - if (ParseChar(state, 'T') && + if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "VTIS") && ParseType(state)) { return true; } *state = copy; - if (ParseTwoChar(state, "Tc") && ParseCallOffset(state) && + if (ParseTwoCharToken(state, "Tc") && ParseCallOffset(state) && ParseCallOffset(state) && ParseEncoding(state)) { return true; } *state = copy; - if (ParseTwoChar(state, "GV") && + if (ParseTwoCharToken(state, "GV") && ParseName(state)) { return true; } *state = copy; - if (ParseChar(state, 'T') && ParseCallOffset(state) && + if (ParseOneCharToken(state, 'T') && ParseCallOffset(state) && ParseEncoding(state)) { return true; } *state = copy; // G++ extensions - if (ParseTwoChar(state, "TC") && ParseType(state) && - ParseNumber(state) && ParseChar(state, '_') && + if (ParseTwoCharToken(state, "TC") && ParseType(state) && + ParseNumber(state, NULL) && ParseOneCharToken(state, '_') && DisableAppend(state) && ParseType(state)) { RestoreAppend(state, copy.append); @@ -773,23 +799,23 @@ static bool ParseSpecialName(State *state) { } *state = copy; - if (ParseChar(state, 'T') && ParseCharClass(state, "FJ") && + if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "FJ") && ParseType(state)) { return true; } *state = copy; - if (ParseTwoChar(state, "GR") && ParseName(state)) { + if (ParseTwoCharToken(state, "GR") && ParseName(state)) { return true; } *state = copy; - if (ParseTwoChar(state, "GA") && ParseEncoding(state)) { + if (ParseTwoCharToken(state, "GA") && ParseEncoding(state)) { return true; } *state = copy; - if (ParseChar(state, 'T') && ParseCharClass(state, "hv") && + if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "hv") && ParseCallOffset(state) && ParseEncoding(state)) { return true; } @@ -801,14 +827,14 @@ static bool ParseSpecialName(State *state) { // ::= v <v-offset> _ static bool ParseCallOffset(State *state) { State copy = *state; - if (ParseChar(state, 'h') && - ParseNVOffset(state) && ParseChar(state, '_')) { + if (ParseOneCharToken(state, 'h') && + ParseNVOffset(state) && ParseOneCharToken(state, '_')) { return true; } *state = copy; - if (ParseChar(state, 'v') && - ParseVOffset(state) && ParseChar(state, '_')) { + if (ParseOneCharToken(state, 'v') && + ParseVOffset(state) && ParseOneCharToken(state, '_')) { return true; } *state = copy; @@ -818,14 +844,14 @@ static bool ParseCallOffset(State *state) { // <nv-offset> ::= <(offset) number> static bool ParseNVOffset(State *state) { - return ParseNumber(state); + return ParseNumber(state, NULL); } // <v-offset> ::= <(offset) number> _ <(virtual offset) number> static bool ParseVOffset(State *state) { State copy = *state; - if (ParseNumber(state) && ParseChar(state, '_') && - ParseNumber(state)) { + if (ParseNumber(state, NULL) && ParseOneCharToken(state, '_') && + ParseNumber(state, NULL)) { return true; } *state = copy; @@ -836,7 +862,7 @@ static bool ParseVOffset(State *state) { // ::= D0 | D1 | D2 static bool ParseCtorDtorName(State *state) { State copy = *state; - if (ParseChar(state, 'C') && + if (ParseOneCharToken(state, 'C') && ParseCharClass(state, "123")) { const char * const prev_name = state->prev_name; const int prev_name_length = state->prev_name_length; @@ -845,7 +871,7 @@ static bool ParseCtorDtorName(State *state) { } *state = copy; - if (ParseChar(state, 'D') && + if (ParseOneCharToken(state, 'D') && ParseCharClass(state, "012")) { const char * const prev_name = state->prev_name; const int prev_name_length = state->prev_name_length; @@ -858,11 +884,12 @@ static bool ParseCtorDtorName(State *state) { } // <type> ::= <CV-qualifiers> <type> -// ::= P <type> -// ::= R <type> -// ::= C <type> -// ::= G <type> -// ::= U <source-name> <type> +// ::= P <type> # pointer-to +// ::= R <type> # reference-to +// ::= O <type> # rvalue reference-to (C++0x) +// ::= C <type> # complex pair (C 2000) +// ::= G <type> # imaginary (C 2000) +// ::= U <source-name> <type> # vendor extended type qualifier // ::= <builtin-type> // ::= <function-type> // ::= <class-enum-type> @@ -871,6 +898,11 @@ static bool ParseCtorDtorName(State *state) { // ::= <template-template-param> <template-args> // ::= <template-param> // ::= <substitution> +// ::= Dp <type> # pack expansion of (C++0x) +// ::= Dt <expression> E # decltype of an id-expression or class +// # member access (C++0x) +// ::= DT <expression> E # decltype of an expression (C++0x) +// static bool ParseType(State *state) { // We should check CV-qualifers, and PRGC things first. State copy = *state; @@ -879,12 +911,23 @@ static bool ParseType(State *state) { } *state = copy; - if (ParseCharClass(state, "PRCG") && ParseType(state)) { + if (ParseCharClass(state, "OPRCG") && ParseType(state)) { return true; } *state = copy; - if (ParseChar(state, 'U') && ParseSourceName(state) && + if (ParseTwoCharToken(state, "Dp") && ParseType(state)) { + return true; + } + *state = copy; + + if (ParseOneCharToken(state, 'D') && ParseCharClass(state, "tT") && + ParseExpression(state) && ParseOneCharToken(state, 'E')) { + return true; + } + *state = copy; + + if (ParseOneCharToken(state, 'U') && ParseSourceName(state) && ParseType(state)) { return true; } @@ -918,9 +961,9 @@ static bool ParseType(State *state) { // ParseType(). static bool ParseCVQualifiers(State *state) { int num_cv_qualifiers = 0; - num_cv_qualifiers += ParseChar(state, 'r'); - num_cv_qualifiers += ParseChar(state, 'V'); - num_cv_qualifiers += ParseChar(state, 'K'); + num_cv_qualifiers += ParseOneCharToken(state, 'r'); + num_cv_qualifiers += ParseOneCharToken(state, 'V'); + num_cv_qualifiers += ParseOneCharToken(state, 'K'); return num_cv_qualifiers > 0; } @@ -937,7 +980,7 @@ static bool ParseBuiltinType(State *state) { } State copy = *state; - if (ParseChar(state, 'u') && ParseSourceName(state)) { + if (ParseOneCharToken(state, 'u') && ParseSourceName(state)) { return true; } *state = copy; @@ -947,8 +990,9 @@ static bool ParseBuiltinType(State *state) { // <function-type> ::= F [Y] <bare-function-type> E static bool ParseFunctionType(State *state) { State copy = *state; - if (ParseChar(state, 'F') && Optional(ParseChar(state, 'Y')) && - ParseBareFunctionType(state) && ParseChar(state, 'E')) { + if (ParseOneCharToken(state, 'F') && + Optional(ParseOneCharToken(state, 'Y')) && + ParseBareFunctionType(state) && ParseOneCharToken(state, 'E')) { return true; } *state = copy; @@ -977,14 +1021,14 @@ static bool ParseClassEnumType(State *state) { // ::= A [<(dimension) expression>] _ <(element) type> static bool ParseArrayType(State *state) { State copy = *state; - if (ParseChar(state, 'A') && ParseNumber(state) && - ParseChar(state, '_') && ParseType(state)) { + if (ParseOneCharToken(state, 'A') && ParseNumber(state, NULL) && + ParseOneCharToken(state, '_') && ParseType(state)) { return true; } *state = copy; - if (ParseChar(state, 'A') && Optional(ParseExpression(state)) && - ParseChar(state, '_') && ParseType(state)) { + if (ParseOneCharToken(state, 'A') && Optional(ParseExpression(state)) && + ParseOneCharToken(state, '_') && ParseType(state)) { return true; } *state = copy; @@ -994,7 +1038,7 @@ static bool ParseArrayType(State *state) { // <pointer-to-member-type> ::= M <(class) type> <(member) type> static bool ParsePointerToMemberType(State *state) { State copy = *state; - if (ParseChar(state, 'M') && ParseType(state) && + if (ParseOneCharToken(state, 'M') && ParseType(state) && ParseType(state)) { return true; } @@ -1005,14 +1049,14 @@ static bool ParsePointerToMemberType(State *state) { // <template-param> ::= T_ // ::= T <parameter-2 non-negative number> _ static bool ParseTemplateParam(State *state) { - if (ParseTwoChar(state, "T_")) { + if (ParseTwoCharToken(state, "T_")) { MaybeAppend(state, "?"); // We don't support template substitutions. return true; } State copy = *state; - if (ParseChar(state, 'T') && ParseNumber(state) && - ParseChar(state, '_')) { + if (ParseOneCharToken(state, 'T') && ParseNumber(state, NULL) && + ParseOneCharToken(state, '_')) { MaybeAppend(state, "?"); // We don't support template substitutions. return true; } @@ -1032,9 +1076,9 @@ static bool ParseTemplateTemplateParam(State *state) { static bool ParseTemplateArgs(State *state) { State copy = *state; DisableAppend(state); - if (ParseChar(state, 'I') && + if (ParseOneCharToken(state, 'I') && OneOrMore(ParseTemplateArg, state) && - ParseChar(state, 'E')) { + ParseOneCharToken(state, 'E')) { RestoreAppend(state, copy.append); MaybeAppend(state, "<>"); return true; @@ -1045,16 +1089,25 @@ static bool ParseTemplateArgs(State *state) { // <template-arg> ::= <type> // ::= <expr-primary> +// ::= I <template-arg>* E # argument pack // ::= X <expression> E static bool ParseTemplateArg(State *state) { + State copy = *state; + if (ParseOneCharToken(state, 'I') && + ZeroOrMore(ParseTemplateArg, state) && + ParseOneCharToken(state, 'E')) { + return true; + } + *state = copy; + if (ParseType(state) || ParseExprPrimary(state)) { return true; } + *state = copy; - State copy = *state; - if (ParseChar(state, 'X') && ParseExpression(state) && - ParseChar(state, 'E')) { + if (ParseOneCharToken(state, 'X') && ParseExpression(state) && + ParseOneCharToken(state, 'E')) { return true; } *state = copy; @@ -1097,19 +1150,19 @@ static bool ParseExpression(State *state) { } *state = copy; - if (ParseTwoChar(state, "st") && ParseType(state)) { + if (ParseTwoCharToken(state, "st") && ParseType(state)) { return true; } *state = copy; - if (ParseTwoChar(state, "sr") && ParseType(state) && + if (ParseTwoCharToken(state, "sr") && ParseType(state) && ParseUnqualifiedName(state) && ParseTemplateArgs(state)) { return true; } *state = copy; - if (ParseTwoChar(state, "sr") && ParseType(state) && + if (ParseTwoCharToken(state, "sr") && ParseType(state) && ParseUnqualifiedName(state)) { return true; } @@ -1124,28 +1177,28 @@ static bool ParseExpression(State *state) { // ::= LZ <encoding> E static bool ParseExprPrimary(State *state) { State copy = *state; - if (ParseChar(state, 'L') && ParseType(state) && - ParseNumber(state) && - ParseChar(state, 'E')) { + if (ParseOneCharToken(state, 'L') && ParseType(state) && + ParseNumber(state, NULL) && + ParseOneCharToken(state, 'E')) { return true; } *state = copy; - if (ParseChar(state, 'L') && ParseType(state) && + if (ParseOneCharToken(state, 'L') && ParseType(state) && ParseFloatNumber(state) && - ParseChar(state, 'E')) { + ParseOneCharToken(state, 'E')) { return true; } *state = copy; - if (ParseChar(state, 'L') && ParseMangledName(state) && - ParseChar(state, 'E')) { + if (ParseOneCharToken(state, 'L') && ParseMangledName(state) && + ParseOneCharToken(state, 'E')) { return true; } *state = copy; - if (ParseTwoChar(state, "LZ") && ParseEncoding(state) && - ParseChar(state, 'E')) { + if (ParseTwoCharToken(state, "LZ") && ParseEncoding(state) && + ParseOneCharToken(state, 'E')) { return true; } *state = copy; @@ -1158,15 +1211,15 @@ static bool ParseExprPrimary(State *state) { // := Z <(function) encoding> E s [<discriminator>] static bool ParseLocalName(State *state) { State copy = *state; - if (ParseChar(state, 'Z') && ParseEncoding(state) && - ParseChar(state, 'E') && MaybeAppend(state, "::") && + if (ParseOneCharToken(state, 'Z') && ParseEncoding(state) && + ParseOneCharToken(state, 'E') && MaybeAppend(state, "::") && ParseName(state) && Optional(ParseDiscriminator(state))) { return true; } *state = copy; - if (ParseChar(state, 'Z') && ParseEncoding(state) && - ParseTwoChar(state, "Es") && Optional(ParseDiscriminator(state))) { + if (ParseOneCharToken(state, 'Z') && ParseEncoding(state) && + ParseTwoCharToken(state, "Es") && Optional(ParseDiscriminator(state))) { return true; } *state = copy; @@ -1176,7 +1229,7 @@ static bool ParseLocalName(State *state) { // <discriminator> := _ <(non-negative) number> static bool ParseDiscriminator(State *state) { State copy = *state; - if (ParseChar(state, '_') && ParseNumber(state)) { + if (ParseOneCharToken(state, '_') && ParseNumber(state, NULL)) { return true; } *state = copy; @@ -1187,21 +1240,21 @@ static bool ParseDiscriminator(State *state) { // ::= S <seq-id> _ // ::= St, etc. static bool ParseSubstitution(State *state) { - if (ParseTwoChar(state, "S_")) { + if (ParseTwoCharToken(state, "S_")) { MaybeAppend(state, "?"); // We don't support substitutions. return true; } State copy = *state; - if (ParseChar(state, 'S') && ParseSeqId(state) && - ParseChar(state, '_')) { + if (ParseOneCharToken(state, 'S') && ParseSeqId(state) && + ParseOneCharToken(state, '_')) { MaybeAppend(state, "?"); // We don't support substitutions. return true; } *state = copy; // Expand abbreviations like "St" => "std". - if (ParseChar(state, 'S')) { + if (ParseOneCharToken(state, 'S')) { const AbbrevPair *p; for (p = kSubstitutionList; p->abbrev != NULL; ++p) { if (state->mangled_cur[0] == p->abbrev[1]) { @@ -1210,7 +1263,7 @@ static bool ParseSubstitution(State *state) { MaybeAppend(state, "::"); MaybeAppend(state, p->real_name); } - state->mangled_cur += 1; + ++state->mangled_cur; return true; } } @@ -1219,13 +1272,33 @@ static bool ParseSubstitution(State *state) { return false; } +// Parse <mangled-name>, optionally followed by either a function-clone suffix +// or version suffix. Returns true only if all of "mangled_cur" was consumed. +static bool ParseTopLevelMangledName(State *state) { + if (ParseMangledName(state)) { + if (state->mangled_cur[0] != '\0') { + // Drop trailing function clone suffix, if any. + if (IsFunctionCloneSuffix(state->mangled_cur)) { + return true; + } + // Append trailing version suffix if any. + // ex. _Z3foo@@GLIBCXX_3.4 + if (state->mangled_cur[0] == '@') { + MaybeAppend(state, state->mangled_cur); + return true; + } + return false; // Unconsumed suffix. + } + return true; + } + return false; +} + // The demangler entry point. bool Demangle(const char *mangled, char *out, int out_size) { State state; InitState(&state, mangled, out, out_size); - return (ParseMangledName(&state) && - state.overflowed == false && - RemainingLength(&state) == 0); + return ParseTopLevelMangledName(&state) && !state.overflowed; } _END_GOOGLE_NAMESPACE_ diff --git a/chromium/base/third_party/symbolize/symbolize.cc b/chromium/base/third_party/symbolize/symbolize.cc index 85976bbc7b9..b25f7479d0d 100644 --- a/chromium/base/third_party/symbolize/symbolize.cc +++ b/chromium/base/third_party/symbolize/symbolize.cc @@ -45,8 +45,13 @@ // some functions which are not guaranteed to be so, such as memchr() // and memmove(). We assume they are async-signal-safe. // +// Additional header can be specified by the GLOG_BUILD_CONFIG_INCLUDE +// macro to add platform specific defines (e.g. OS_OPENBSD). + +#ifdef GLOG_BUILD_CONFIG_INCLUDE +#include GLOG_BUILD_CONFIG_INCLUDE +#endif // GLOG_BUILD_CONFIG_INCLUDE -#include "build/build_config.h" #include "utilities.h" #if defined(HAVE_SYMBOLIZE) @@ -75,6 +80,13 @@ void InstallSymbolizeCallback(SymbolizeCallback callback) { g_symbolize_callback = callback; } +static SymbolizeOpenObjectFileCallback g_symbolize_open_object_file_callback = + NULL; +void InstallSymbolizeOpenObjectFileCallback( + SymbolizeOpenObjectFileCallback callback) { + g_symbolize_open_object_file_callback = callback; +} + // This function wraps the Demangle function to provide an interface // where the input symbol is demangled in-place. // To keep stack consumption low, we would like this function to not @@ -83,8 +95,8 @@ static ATTRIBUTE_NOINLINE void DemangleInplace(char *out, int out_size) { char demangled[256]; // Big enough for sane demangled symbols. if (Demangle(out, demangled, sizeof(demangled))) { // Demangling succeeded. Copy to out if the space allows. - int len = strlen(demangled); - if (len + 1 <= out_size) { // +1 for '\0'. + size_t len = strlen(demangled); + if (len + 1 <= (size_t)out_size) { // +1 for '\0'. SAFE_ASSERT(len < sizeof(demangled)); memmove(out, demangled, len + 1); } @@ -483,13 +495,20 @@ static char *GetHex(const char *start, const char *end, uint64_t *hex) { return const_cast<char *>(p); } -// Search for the object file (from /proc/self/maps) that contains -// the specified pc. If found, open this file and return the file handle, -// and also set start_address to the start address of where this object -// file is mapped to in memory. Otherwise, return -1. +// Searches for the object file (from /proc/self/maps) that contains +// the specified pc. If found, sets |start_address| to the start address +// of where this object file is mapped in memory, sets the module base +// address into |base_address|, copies the object file name into +// |out_file_name|, and attempts to open the object file. If the object +// file is opened successfully, returns the file descriptor. Otherwise, +// returns -1. |out_file_name_size| is the size of the file name buffer +// (including the null-terminator). static ATTRIBUTE_NOINLINE int OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc, - uint64_t &start_address) { + uint64_t &start_address, + uint64_t &base_address, + char *out_file_name, + int out_file_name_size) { int object_fd; // Open /proc/self/maps. @@ -503,8 +522,10 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc, // Iterate over maps and look for the map containing the pc. Then // look into the symbol tables inside. char buf[1024]; // Big enough for line of sane /proc/self/maps + int num_maps = 0; LineReader reader(wrapped_maps_fd.get(), buf, sizeof(buf)); while (true) { + num_maps++; const char *cursor; const char *eol; if (!reader.ReadLine(&cursor, &eol)) { // EOF or malformed line. @@ -554,14 +575,35 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc, } ++cursor; // Skip ' '. - // Skip to file name. "cursor" now points to file offset. We need to - // skip at least three spaces for file offset, dev, and inode. + // Read file offset. + uint64_t file_offset; + cursor = GetHex(cursor, eol, &file_offset); + if (cursor == eol || *cursor != ' ') { + return -1; // Malformed line. + } + ++cursor; // Skip ' '. + + // Don't subtract 'start_address' from the first entry: + // * If a binary is compiled w/o -pie, then the first entry in + // process maps is likely the binary itself (all dynamic libs + // are mapped higher in address space). For such a binary, + // instruction offset in binary coincides with the actual + // instruction address in virtual memory (as code section + // is mapped to a fixed memory range). + // * If a binary is compiled with -pie, all the modules are + // mapped high at address space (in particular, higher than + // shadow memory of the tool), so the module can't be the + // first entry. + base_address = ((num_maps == 1) ? 0U : start_address) - file_offset; + + // Skip to file name. "cursor" now points to dev. We need to + // skip at least two spaces for dev and inode. int num_spaces = 0; while (cursor < eol) { if (*cursor == ' ') { ++num_spaces; - } else if (num_spaces >= 3) { - // The first non-space character after skipping three spaces + } else if (num_spaces >= 2) { + // The first non-space character after skipping two spaces // is the beginning of the file name. break; } @@ -574,12 +616,105 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc, // Finally, "cursor" now points to file name of our interest. NO_INTR(object_fd = open(cursor, O_RDONLY)); if (object_fd < 0) { + // Failed to open object file. Copy the object file name to + // |out_file_name|. + strncpy(out_file_name, cursor, out_file_name_size); + // Making sure |out_file_name| is always null-terminated. + out_file_name[out_file_name_size - 1] = '\0'; return -1; } return object_fd; } } +// POSIX doesn't define any async-signal safe function for converting +// an integer to ASCII. We'll have to define our own version. +// itoa_r() converts a (signed) integer to ASCII. It returns "buf", if the +// conversion was successful or NULL otherwise. It never writes more than "sz" +// bytes. Output will be truncated as needed, and a NUL character is always +// appended. +// NOTE: code from sandbox/linux/seccomp-bpf/demo.cc. +char *itoa_r(intptr_t i, char *buf, size_t sz, int base, size_t padding) { + // Make sure we can write at least one NUL byte. + size_t n = 1; + if (n > sz) + return NULL; + + if (base < 2 || base > 16) { + buf[0] = '\000'; + return NULL; + } + + char *start = buf; + + uintptr_t j = i; + + // Handle negative numbers (only for base 10). + if (i < 0 && base == 10) { + j = -i; + + // Make sure we can write the '-' character. + if (++n > sz) { + buf[0] = '\000'; + return NULL; + } + *start++ = '-'; + } + + // Loop until we have converted the entire number. Output at least one + // character (i.e. '0'). + char *ptr = start; + do { + // Make sure there is still enough space left in our output buffer. + if (++n > sz) { + buf[0] = '\000'; + return NULL; + } + + // Output the next digit. + *ptr++ = "0123456789abcdef"[j % base]; + j /= base; + + if (padding > 0) + padding--; + } while (j > 0 || padding > 0); + + // Terminate the output with a NUL character. + *ptr = '\000'; + + // Conversion to ASCII actually resulted in the digits being in reverse + // order. We can't easily generate them in forward order, as we can't tell + // the number of characters needed until we are done converting. + // So, now, we reverse the string (except for the possible "-" sign). + while (--ptr > start) { + char ch = *ptr; + *ptr = *start; + *start++ = ch; + } + return buf; +} + +// Safely appends string |source| to string |dest|. Never writes past the +// buffer size |dest_size| and guarantees that |dest| is null-terminated. +void SafeAppendString(const char* source, char* dest, int dest_size) { + int dest_string_length = strlen(dest); + SAFE_ASSERT(dest_string_length < dest_size); + dest += dest_string_length; + dest_size -= dest_string_length; + strncpy(dest, source, dest_size); + // Making sure |dest| is always null-terminated. + dest[dest_size - 1] = '\0'; +} + +// Converts a 64-bit value into a hex string, and safely appends it to |dest|. +// Never writes past the buffer size |dest_size| and guarantees that |dest| is +// null-terminated. +void SafeAppendHexNumber(uint64_t value, char* dest, int dest_size) { + // 64-bit numbers in hex can have up to 16 digits. + char buf[17] = {'\0'}; + SafeAppendString(itoa_r(value, buf, sizeof(buf), 16, 0), dest, dest_size); +} + // The implementation of our symbolization routine. If it // successfully finds the symbol containing "pc" and obtains the // symbol name, returns true and write the symbol name to "out". @@ -592,10 +727,40 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out, int out_size) { uint64_t pc0 = reinterpret_cast<uintptr_t>(pc); uint64_t start_address = 0; + uint64_t base_address = 0; + int object_fd = -1; - int object_fd = OpenObjectFileContainingPcAndGetStartAddress(pc0, - start_address); - if (object_fd == -1) { + if (out_size < 1) { + return false; + } + out[0] = '\0'; + SafeAppendString("(", out, out_size); + + if (g_symbolize_open_object_file_callback) { + object_fd = g_symbolize_open_object_file_callback(pc0, start_address, + base_address, out + 1, + out_size - 1); + } else { + object_fd = OpenObjectFileContainingPcAndGetStartAddress(pc0, start_address, + base_address, + out + 1, + out_size - 1); + } + + // Check whether a file name was returned. + if (object_fd < 0) { + if (out[1]) { + // The object file containing PC was determined successfully however the + // object file was not opened successfully. This is still considered + // success because the object file name and offset are known and tools + // like asan_symbolize.py can be used for the symbolization. + out[out_size - 1] = '\0'; // Making sure |out| is always null-terminated. + SafeAppendString("+0x", out, out_size); + SafeAppendHexNumber(pc0 - base_address, out, out_size); + SafeAppendString(")", out, out_size); + return true; + } + // Failed to determine the object file containing PC. Bail out. return false; } FileDescriptor wrapped_object_fd(object_fd); @@ -639,7 +804,7 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out, int out_size) { Dl_info info; if (dladdr(pc, &info)) { - if (strlen(info.dli_sname) < out_size) { + if ((int)strlen(info.dli_sname) < out_size) { strcpy(out, info.dli_sname); // Symbolization succeeded. Now we try to demangle the symbol. DemangleInplace(out, out_size); diff --git a/chromium/base/third_party/symbolize/symbolize.h b/chromium/base/third_party/symbolize/symbolize.h index 441e5436962..f617184249c 100644 --- a/chromium/base/third_party/symbolize/symbolize.h +++ b/chromium/base/third_party/symbolize/symbolize.h @@ -105,6 +105,10 @@ _END_GOOGLE_NAMESPACE_ _START_GOOGLE_NAMESPACE_ +// Restrictions on the callbacks that follow: +// - The callbacks must not use heaps but only use stacks. +// - The callbacks must be async-signal-safe. + // Installs a callback function, which will be called right before a symbol name // is printed. The callback is intended to be used for showing a file name and a // line number preceding a symbol name. @@ -116,6 +120,24 @@ typedef int (*SymbolizeCallback)(int fd, void *pc, char *out, size_t out_size, uint64 relocation); void InstallSymbolizeCallback(SymbolizeCallback callback); +// Installs a callback function, which will be called instead of +// OpenObjectFileContainingPcAndGetStartAddress. The callback is expected +// to searches for the object file (from /proc/self/maps) that contains +// the specified pc. If found, sets |start_address| to the start address +// of where this object file is mapped in memory, sets the module base +// address into |base_address|, copies the object file name into +// |out_file_name|, and attempts to open the object file. If the object +// file is opened successfully, returns the file descriptor. Otherwise, +// returns -1. |out_file_name_size| is the size of the file name buffer +// (including the null-terminator). +typedef int (*SymbolizeOpenObjectFileCallback)(uint64_t pc, + uint64_t &start_address, + uint64_t &base_address, + char *out_file_name, + int out_file_name_size); +void InstallSymbolizeOpenObjectFileCallback( + SymbolizeOpenObjectFileCallback callback); + _END_GOOGLE_NAMESPACE_ #endif diff --git a/chromium/base/third_party/xdg_mime/BUILD.gn b/chromium/base/third_party/xdg_mime/BUILD.gn new file mode 100644 index 00000000000..3e51e33a1d2 --- /dev/null +++ b/chromium/base/third_party/xdg_mime/BUILD.gn @@ -0,0 +1,28 @@ +# 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("xdg_mime") { + visibility = "//base/*" + sources = [ + "xdgmime.c", + "xdgmime.h", + "xdgmimealias.c", + "xdgmimealias.h", + "xdgmimecache.c", + "xdgmimecache.h", + "xdgmimeglob.c", + "xdgmimeglob.h", + "xdgmimeicon.c", + "xdgmimeicon.h", + "xdgmimeint.c", + "xdgmimeint.h", + "xdgmimemagic.c", + "xdgmimemagic.h", + "xdgmimeparent.c", + "xdgmimeparent.h", + ] + + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] +} diff --git a/chromium/base/third_party/xdg_user_dirs/BUILD.gn b/chromium/base/third_party/xdg_user_dirs/BUILD.gn new file mode 100644 index 00000000000..d866960bf1d --- /dev/null +++ b/chromium/base/third_party/xdg_user_dirs/BUILD.gn @@ -0,0 +1,11 @@ +# 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("xdg_user_dirs") { + visibility = "//base/*" + sources = [ + "xdg_user_dir_lookup.cc", + "xdg_user_dir_lookup.h", + ] +} diff --git a/chromium/base/threading/non_thread_safe.h b/chromium/base/threading/non_thread_safe.h index a27bb0ea0c5..d41c08608c9 100644 --- a/chromium/base/threading/non_thread_safe.h +++ b/chromium/base/threading/non_thread_safe.h @@ -18,9 +18,7 @@ #define ENABLE_NON_THREAD_SAFE 0 #endif -#if ENABLE_NON_THREAD_SAFE #include "base/threading/non_thread_safe_impl.h" -#endif namespace base { @@ -70,4 +68,4 @@ typedef NonThreadSafeDoNothing NonThreadSafe; } // namespace base -#endif // BASE_NON_THREAD_SAFE_H_ +#endif // BASE_THREADING_NON_THREAD_SAFE_H_ diff --git a/chromium/base/threading/platform_thread.h b/chromium/base/threading/platform_thread.h index 9742d570c6f..28743145610 100644 --- a/chromium/base/threading/platform_thread.h +++ b/chromium/base/threading/platform_thread.h @@ -23,12 +23,48 @@ namespace base { +// Used for logging. Always an integer value. #if defined(OS_WIN) typedef DWORD PlatformThreadId; #elif defined(OS_POSIX) typedef pid_t PlatformThreadId; #endif +// Used for thread checking and debugging. +// Meant to be as fast as possible. +// These are produced by PlatformThread::CurrentRef(), and used to later +// check if we are on the same thread or not by using ==. These are safe +// to copy between threads, but can't be copied to another process as they +// have no meaning there. Also, the internal identifier can be re-used +// after a thread dies, so a PlatformThreadRef cannot be reliably used +// to distinguish a new thread from an old, dead thread. +class PlatformThreadRef { + public: +#if defined(OS_WIN) + typedef DWORD RefType; +#elif defined(OS_POSIX) + typedef pthread_t RefType; +#endif + PlatformThreadRef() + : id_(0) { + } + + explicit PlatformThreadRef(RefType id) + : id_(id) { + } + + bool operator==(PlatformThreadRef other) const { + return id_ == other.id_; + } + + bool is_null() const { + return id_ == 0; + } + private: + RefType id_; +}; + +// Used to operate on threads. class PlatformThreadHandle { public: #if defined(OS_WIN) @@ -53,15 +89,15 @@ class PlatformThreadHandle { id_(id) { } - bool is_equal(const PlatformThreadHandle& other) { + bool is_equal(const PlatformThreadHandle& other) const { return handle_ == other.handle_; } - bool is_null() { + bool is_null() const { return !handle_; } - Handle platform_handle() { + Handle platform_handle() const { return handle_; } @@ -101,6 +137,10 @@ class BASE_EXPORT PlatformThread { // Gets the current thread id, which may be useful for logging purposes. static PlatformThreadId CurrentId(); + // Gets the current thread reference, which can be used to check if + // we're on the right thread quickly. + static PlatformThreadRef CurrentRef(); + // Get the current handle. static PlatformThreadHandle CurrentHandle(); diff --git a/chromium/base/threading/platform_thread_android.cc b/chromium/base/threading/platform_thread_android.cc index 28026350b66..bd0b4b33db7 100644 --- a/chromium/base/threading/platform_thread_android.cc +++ b/chromium/base/threading/platform_thread_android.cc @@ -5,6 +5,7 @@ #include "base/threading/platform_thread.h" #include <errno.h> +#include <sys/prctl.h> #include <sys/resource.h> #include "base/android/jni_android.h" @@ -77,6 +78,18 @@ void PlatformThread::SetThreadPriority(PlatformThreadHandle handle, void PlatformThread::SetName(const char* name) { ThreadIdNameManager::GetInstance()->SetName(CurrentId(), name); tracked_objects::ThreadData::InitializeThreadContext(name); + + // Like linux, on android we can get the thread names to show up in the + // debugger by setting the process name for the LWP. + // We don't want to do this for the main thread because that would rename + // the process, causing tools like killall to stop working. + if (PlatformThread::CurrentId() == getpid()) + return; + + // Set the name for the LWP (which gets truncated to 15 characters). + int err = prctl(PR_SET_NAME, name); + if (err < 0 && errno != EPERM) + DPLOG(ERROR) << "prctl(PR_SET_NAME)"; } @@ -95,7 +108,13 @@ void TerminateOnThread() { } size_t GetDefaultThreadStackSize(const pthread_attr_t& attributes) { +#if !defined(ADDRESS_SANITIZER) return 0; +#else + // AddressSanitizer bloats the stack approximately 2x. Default stack size of + // 1Mb is not enough for some tests (see http://crbug.com/263749 for example). + return 2 * (1 << 20); // 2Mb +#endif } bool RegisterThreadUtils(JNIEnv* env) { diff --git a/chromium/base/threading/platform_thread_freebsd.cc b/chromium/base/threading/platform_thread_freebsd.cc new file mode 100644 index 00000000000..7a24f4e055e --- /dev/null +++ b/chromium/base/threading/platform_thread_freebsd.cc @@ -0,0 +1,103 @@ +// 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/threading/platform_thread.h" + +#include <errno.h> +#include <sched.h> + +#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 <sys/types.h> +#include <unistd.h> +#endif + +namespace base { + +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; + } +} +} // namespace + +// static +void PlatformThread::SetName(const char* name) { + ThreadIdNameManager::GetInstance()->SetName(CurrentId(), name); + tracked_objects::ThreadData::InitializeThreadContext(name); + +#if !defined(OS_NACL) + // On FreeBSD we can get the thread names to show up in the debugger by + // setting the process name for the LWP. We don't want to do this for the + // main thread because that would rename the process, causing tools like + // 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; + } +#endif // !defined(OS_NACL) +} + +void InitThreading() {} + +void InitOnThread() {} + +void TerminateOnThread() {} + +size_t GetDefaultThreadStackSize(const pthread_attr_t& attributes) { +#if !defined(THREAD_SANITIZER) + return 0; +#else + // ThreadSanitizer bloats the stack heavily. Evidence has been that the + // default stack size isn't enough for some browser tests. + return 2 * (1 << 23); // 2 times 8192K (the default stack size on Linux). +#endif +} + +} // namespace base diff --git a/chromium/base/threading/platform_thread_linux.cc b/chromium/base/threading/platform_thread_linux.cc index 6d46de4cf26..636e13331c4 100644 --- a/chromium/base/threading/platform_thread_linux.cc +++ b/chromium/base/threading/platform_thread_linux.cc @@ -26,6 +26,7 @@ namespace base { namespace { + int ThreadNiceValue(ThreadPriority priority) { switch (priority) { case kThreadPriority_RealtimeAudio: @@ -41,7 +42,8 @@ int ThreadNiceValue(ThreadPriority priority) { return 0; } } -} // namespace + +} // namespace // static void PlatformThread::SetName(const char* name) { @@ -100,7 +102,14 @@ void InitOnThread() {} void TerminateOnThread() {} size_t GetDefaultThreadStackSize(const pthread_attr_t& attributes) { +#if !defined(THREAD_SANITIZER) && !defined(MEMORY_SANITIZER) return 0; +#else + // ThreadSanitizer bloats the stack heavily. Evidence has been that the + // default stack size isn't enough for some browser tests. + // MemorySanitizer needs this as a temporary fix for http://crbug.com/353687 + return 2 * (1 << 23); // 2 times 8192K (the default stack size on Linux). +#endif } } // namespace base diff --git a/chromium/base/threading/platform_thread_mac.mm b/chromium/base/threading/platform_thread_mac.mm index d81a286cb2c..147e625dbc8 100644 --- a/chromium/base/threading/platform_thread_mac.mm +++ b/chromium/base/threading/platform_thread_mac.mm @@ -5,15 +5,16 @@ #include "base/threading/platform_thread.h" #import <Foundation/Foundation.h> -#include <algorithm> -#include <dlfcn.h> #include <mach/mach.h> #include <mach/mach_time.h> #include <mach/thread_policy.h> #include <sys/resource.h> +#include <algorithm> + #include "base/lazy_instance.h" #include "base/logging.h" +#include "base/mac/mach_logging.h" #include "base/threading/thread_id_name_manager.h" #include "base/tracked_objects.h" @@ -45,21 +46,13 @@ void PlatformThread::SetName(const char* name) { ThreadIdNameManager::GetInstance()->SetName(CurrentId(), name); tracked_objects::ThreadData::InitializeThreadContext(name); - // pthread_setname_np is only available in 10.6 or later, so test - // for it at runtime. - int (*dynamic_pthread_setname_np)(const char*); - *reinterpret_cast<void**>(&dynamic_pthread_setname_np) = - dlsym(RTLD_DEFAULT, "pthread_setname_np"); - if (!dynamic_pthread_setname_np) - return; - // 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); // pthread_setname() fails (harmlessly) in the sandbox, ignore when it does. // See http://crbug.com/47058 - dynamic_pthread_setname_np(shortened_name.c_str()); + pthread_setname_np(shortened_name.c_str()); } namespace { @@ -69,20 +62,19 @@ void SetPriorityNormal(mach_port_t mach_thread_id) { // Please note that this call could fail in rare cases depending // on runtime conditions. thread_standard_policy policy; - kern_return_t result = thread_policy_set(mach_thread_id, - THREAD_STANDARD_POLICY, - (thread_policy_t)&policy, - THREAD_STANDARD_POLICY_COUNT); + kern_return_t result = + thread_policy_set(mach_thread_id, + THREAD_STANDARD_POLICY, + reinterpret_cast<thread_policy_t>(&policy), + THREAD_STANDARD_POLICY_COUNT); if (result != KERN_SUCCESS) - DVLOG(1) << "thread_policy_set() failure: " << result; + MACH_DVLOG(1, result) << "thread_policy_set"; } // Enables time-contraint policy and priority suitable for low-latency, // glitch-resistant audio. void SetPriorityRealtimeAudio(mach_port_t mach_thread_id) { - kern_return_t result; - // Increase thread priority to real-time. // Please note that the thread_policy_set() calls may fail in @@ -93,12 +85,13 @@ void SetPriorityRealtimeAudio(mach_port_t mach_thread_id) { // Make thread fixed priority. thread_extended_policy_data_t policy; policy.timeshare = 0; // Set to 1 for a non-fixed thread. - result = thread_policy_set(mach_thread_id, - THREAD_EXTENDED_POLICY, - (thread_policy_t)&policy, - THREAD_EXTENDED_POLICY_COUNT); + kern_return_t result = + thread_policy_set(mach_thread_id, + THREAD_EXTENDED_POLICY, + reinterpret_cast<thread_policy_t>(&policy), + THREAD_EXTENDED_POLICY_COUNT); if (result != KERN_SUCCESS) { - DVLOG(1) << "thread_policy_set() failure: " << result; + MACH_DVLOG(1, result) << "thread_policy_set"; return; } @@ -107,10 +100,10 @@ void SetPriorityRealtimeAudio(mach_port_t mach_thread_id) { precedence.importance = 63; result = thread_policy_set(mach_thread_id, THREAD_PRECEDENCE_POLICY, - (thread_policy_t)&precedence, + reinterpret_cast<thread_policy_t>(&precedence), THREAD_PRECEDENCE_POLICY_COUNT); if (result != KERN_SUCCESS) { - DVLOG(1) << "thread_policy_set() failure: " << result; + MACH_DVLOG(1, result) << "thread_policy_set"; return; } @@ -141,7 +134,7 @@ void SetPriorityRealtimeAudio(mach_port_t mach_thread_id) { mach_timebase_info_data_t tb_info; mach_timebase_info(&tb_info); double ms_to_abs_time = - ((double)tb_info.denom / (double)tb_info.numer) * 1000000; + (static_cast<double>(tb_info.denom) / tb_info.numer) * 1000000; thread_time_constraint_policy_data_t time_constraints; time_constraints.period = kTimeQuantum * ms_to_abs_time; @@ -149,12 +142,12 @@ void SetPriorityRealtimeAudio(mach_port_t mach_thread_id) { time_constraints.constraint = kMaxTimeAllowed * ms_to_abs_time; time_constraints.preemptible = 0; - result = thread_policy_set(mach_thread_id, - THREAD_TIME_CONSTRAINT_POLICY, - (thread_policy_t)&time_constraints, - THREAD_TIME_CONSTRAINT_POLICY_COUNT); - if (result != KERN_SUCCESS) - DVLOG(1) << "thread_policy_set() failure: " << result; + result = + thread_policy_set(mach_thread_id, + THREAD_TIME_CONSTRAINT_POLICY, + reinterpret_cast<thread_policy_t>(&time_constraints), + THREAD_TIME_CONSTRAINT_POLICY_COUNT); + MACH_DVLOG_IF(1, result != KERN_SUCCESS, result) << "thread_policy_set"; return; } diff --git a/chromium/base/threading/platform_thread_posix.cc b/chromium/base/threading/platform_thread_posix.cc index 392d4613369..42b416d4a38 100644 --- a/chromium/base/threading/platform_thread_posix.cc +++ b/chromium/base/threading/platform_thread_posix.cc @@ -116,13 +116,15 @@ bool CreateThread(size_t stack_size, bool joinable, params.priority = priority; params.handle = thread_handle; - pthread_t handle = 0; + pthread_t handle; int err = pthread_create(&handle, &attributes, ThreadFunc, ¶ms); success = !err; if (!success) { + // Value of |handle| is undefined if pthread_create fails. + handle = 0; errno = err; PLOG(ERROR) << "pthread_create"; } @@ -150,7 +152,7 @@ PlatformThreadId PlatformThread::CurrentId() { return syscall(__NR_gettid); #elif defined(OS_ANDROID) return gettid(); -#elif defined(OS_SOLARIS) +#elif defined(OS_SOLARIS) || defined(OS_QNX) return pthread_self(); #elif defined(OS_NACL) && defined(__GLIBC__) return pthread_self(); @@ -162,7 +164,12 @@ PlatformThreadId PlatformThread::CurrentId() { #endif } -//static +// static +PlatformThreadRef PlatformThread::CurrentRef() { + return PlatformThreadRef(pthread_self()); +} + +// static PlatformThreadHandle PlatformThread::CurrentHandle() { return PlatformThreadHandle(pthread_self(), CurrentId()); } diff --git a/chromium/base/threading/platform_thread_win.cc b/chromium/base/threading/platform_thread_win.cc index e9752ba2139..80f353a8b46 100644 --- a/chromium/base/threading/platform_thread_win.cc +++ b/chromium/base/threading/platform_thread_win.cc @@ -10,7 +10,7 @@ #include "base/threading/thread_id_name_manager.h" #include "base/threading/thread_restrictions.h" #include "base/tracked_objects.h" - +#include "base/win/scoped_handle.h" #include "base/win/windows_version.h" namespace base { @@ -54,28 +54,35 @@ DWORD __stdcall ThreadFunc(void* params) { if (!thread_params->joinable) base::ThreadRestrictions::SetSingletonAllowed(false); - /* Retrieve a copy of the thread handle to use as the key in the - * thread name mapping. */ + // Retrieve a copy of the thread handle to use as the key in the + // thread name mapping. PlatformThreadHandle::Handle platform_handle; - DuplicateHandle( - GetCurrentProcess(), - GetCurrentThread(), - GetCurrentProcess(), - &platform_handle, - 0, - FALSE, - DUPLICATE_SAME_ACCESS); - - ThreadIdNameManager::GetInstance()->RegisterThread( - platform_handle, - PlatformThread::CurrentId()); + BOOL did_dup = DuplicateHandle(GetCurrentProcess(), + GetCurrentThread(), + GetCurrentProcess(), + &platform_handle, + 0, + FALSE, + DUPLICATE_SAME_ACCESS); + + win::ScopedHandle scoped_platform_handle; + + if (did_dup) { + scoped_platform_handle.Set(platform_handle); + ThreadIdNameManager::GetInstance()->RegisterThread( + scoped_platform_handle.Get(), + PlatformThread::CurrentId()); + } delete thread_params; delegate->ThreadMain(); - ThreadIdNameManager::GetInstance()->RemoveName( - platform_handle, - PlatformThread::CurrentId()); + if (did_dup) { + ThreadIdNameManager::GetInstance()->RemoveName( + scoped_platform_handle.Get(), + PlatformThread::CurrentId()); + } + return NULL; } @@ -123,6 +130,11 @@ PlatformThreadId PlatformThread::CurrentId() { } // static +PlatformThreadRef PlatformThread::CurrentRef() { + return PlatformThreadRef(GetCurrentThreadId()); +} + +// static PlatformThreadHandle PlatformThread::CurrentHandle() { NOTIMPLEMENTED(); // See OpenThread() return PlatformThreadHandle(); diff --git a/chromium/base/threading/sequenced_worker_pool.cc b/chromium/base/threading/sequenced_worker_pool.cc index 4fc090d1186..532f26ebe5c 100644 --- a/chromium/base/threading/sequenced_worker_pool.cc +++ b/chromium/base/threading/sequenced_worker_pool.cc @@ -32,6 +32,8 @@ #if defined(OS_MACOSX) #include "base/mac/scoped_nsautorelease_pool.h" +#elif defined(OS_WIN) +#include "base/win/scoped_com_initializer.h" #endif #if !defined(OS_NACL) @@ -480,8 +482,7 @@ SequencedWorkerPool::Worker::Worker( const scoped_refptr<SequencedWorkerPool>& worker_pool, int thread_number, const std::string& prefix) - : SimpleThread( - prefix + StringPrintf("Worker%d", thread_number).c_str()), + : SimpleThread(prefix + StringPrintf("Worker%d", thread_number)), worker_pool_(worker_pool), running_shutdown_behavior_(CONTINUE_ON_SHUTDOWN) { Start(); @@ -491,6 +492,10 @@ SequencedWorkerPool::Worker::~Worker() { } void SequencedWorkerPool::Worker::Run() { +#if defined(OS_WIN) + win::ScopedCOMInitializer com_initializer; +#endif + // Store a pointer to the running sequence in thread local storage for // static function access. g_lazy_tls_ptr.Get().Set(&running_sequence_); @@ -593,7 +598,8 @@ bool SequencedWorkerPool::Inner::PostTask( // The trace_id is used for identifying the task in about:tracing. sequenced.trace_id = trace_id_++; - TRACE_EVENT_FLOW_BEGIN0("task", "SequencedWorkerPool::PostTask", + TRACE_EVENT_FLOW_BEGIN0(TRACE_DISABLED_BY_DEFAULT("toplevel.flow"), + "SequencedWorkerPool::PostTask", TRACE_ID_MANGLE(GetTaskTraceID(sequenced, static_cast<void*>(this)))); sequenced.sequence_task_number = LockedGetNextSequenceTaskNumber(); @@ -726,9 +732,10 @@ void SequencedWorkerPool::Inner::ThreadLoop(Worker* this_worker) { GetWorkStatus status = GetWork(&task, &wait_time, &delete_these_outside_lock); if (status == GET_WORK_FOUND) { - TRACE_EVENT_FLOW_END0("task", "SequencedWorkerPool::PostTask", + TRACE_EVENT_FLOW_END0(TRACE_DISABLED_BY_DEFAULT("toplevel.flow"), + "SequencedWorkerPool::PostTask", TRACE_ID_MANGLE(GetTaskTraceID(task, static_cast<void*>(this)))); - TRACE_EVENT2("task", "SequencedWorkerPool::ThreadLoop", + TRACE_EVENT2("toplevel", "SequencedWorkerPool::ThreadLoop", "src_file", task.posted_from.file_name(), "src_func", task.posted_from.function_name()); int new_thread_id = WillRunWorkerTask(task); diff --git a/chromium/base/threading/sequenced_worker_pool_unittest.cc b/chromium/base/threading/sequenced_worker_pool_unittest.cc index a07fd479095..10cf28ba30d 100644 --- a/chromium/base/threading/sequenced_worker_pool_unittest.cc +++ b/chromium/base/threading/sequenced_worker_pool_unittest.cc @@ -832,10 +832,6 @@ class SequencedWorkerPoolTaskRunnerTestDelegate { // reference to the pool. } - bool TaskRunnerHandlesNonZeroDelays() const { - return true; - } - private: MessageLoop message_loop_; scoped_ptr<SequencedWorkerPoolOwner> pool_owner_; @@ -872,10 +868,6 @@ class SequencedWorkerPoolTaskRunnerWithShutdownBehaviorTestDelegate { // reference to the pool. } - bool TaskRunnerHandlesNonZeroDelays() const { - return true; - } - private: MessageLoop message_loop_; scoped_ptr<SequencedWorkerPoolOwner> pool_owner_; @@ -913,10 +905,6 @@ class SequencedWorkerPoolSequencedTaskRunnerTestDelegate { // reference to the pool. } - bool TaskRunnerHandlesNonZeroDelays() const { - return true; - } - private: MessageLoop message_loop_; scoped_ptr<SequencedWorkerPoolOwner> pool_owner_; diff --git a/chromium/base/threading/thread.cc b/chromium/base/threading/thread.cc index ae4d3731947..7877b193548 100644 --- a/chromium/base/threading/thread.cc +++ b/chromium/base/threading/thread.cc @@ -63,7 +63,7 @@ Thread::Options::Options(MessageLoop::Type type, Thread::Options::~Options() { } -Thread::Thread(const char* name) +Thread::Thread(const std::string& name) : #if defined(OS_WIN) com_status_(NONE), diff --git a/chromium/base/threading/thread.h b/chromium/base/threading/thread.h index 99f9dd4a597..09a6fa5c505 100644 --- a/chromium/base/threading/thread.h +++ b/chromium/base/threading/thread.h @@ -58,7 +58,7 @@ class BASE_EXPORT Thread : PlatformThread::Delegate { // Constructor. // name is a display string to identify the thread. - explicit Thread(const char* name); + explicit Thread(const std::string& name); // Destroys the thread, stopping it if necessary. // diff --git a/chromium/base/threading/thread_checker.h b/chromium/base/threading/thread_checker.h index a2ab3fe4fab..4d71f798d4d 100644 --- a/chromium/base/threading/thread_checker.h +++ b/chromium/base/threading/thread_checker.h @@ -20,9 +20,7 @@ #define ENABLE_THREAD_CHECKER 0 #endif -#if ENABLE_THREAD_CHECKER #include "base/threading/thread_checker_impl.h" -#endif namespace base { diff --git a/chromium/base/threading/thread_checker_impl.cc b/chromium/base/threading/thread_checker_impl.cc index 985433e5f11..eb87bae772c 100644 --- a/chromium/base/threading/thread_checker_impl.cc +++ b/chromium/base/threading/thread_checker_impl.cc @@ -7,7 +7,7 @@ namespace base { ThreadCheckerImpl::ThreadCheckerImpl() - : valid_thread_id_(kInvalidThreadId) { + : valid_thread_id_() { EnsureThreadIdAssigned(); } @@ -16,19 +16,19 @@ ThreadCheckerImpl::~ThreadCheckerImpl() {} bool ThreadCheckerImpl::CalledOnValidThread() const { EnsureThreadIdAssigned(); AutoLock auto_lock(lock_); - return valid_thread_id_ == PlatformThread::CurrentId(); + return valid_thread_id_ == PlatformThread::CurrentRef(); } void ThreadCheckerImpl::DetachFromThread() { AutoLock auto_lock(lock_); - valid_thread_id_ = kInvalidThreadId; + valid_thread_id_ = PlatformThreadRef(); } void ThreadCheckerImpl::EnsureThreadIdAssigned() const { AutoLock auto_lock(lock_); - if (valid_thread_id_ != kInvalidThreadId) - return; - valid_thread_id_ = PlatformThread::CurrentId(); + if (valid_thread_id_.is_null()) { + valid_thread_id_ = PlatformThread::CurrentRef(); + } } } // namespace base diff --git a/chromium/base/threading/thread_checker_impl.h b/chromium/base/threading/thread_checker_impl.h index 24361c83221..879ac3ab354 100644 --- a/chromium/base/threading/thread_checker_impl.h +++ b/chromium/base/threading/thread_checker_impl.h @@ -35,7 +35,7 @@ class BASE_EXPORT ThreadCheckerImpl { mutable base::Lock lock_; // This is mutable so that CalledOnValidThread can set it. // It's guarded by |lock_|. - mutable PlatformThreadId valid_thread_id_; + mutable PlatformThreadRef valid_thread_id_; }; } // namespace base diff --git a/chromium/base/threading/thread_collision_warner.h b/chromium/base/threading/thread_collision_warner.h index d509a58e8de..5172b2e74e6 100644 --- a/chromium/base/threading/thread_collision_warner.h +++ b/chromium/base/threading/thread_collision_warner.h @@ -9,6 +9,7 @@ #include "base/atomicops.h" #include "base/base_export.h" +#include "base/basictypes.h" #include "base/compiler_specific.h" // A helper class alongside macros to be used to verify assumptions about thread diff --git a/chromium/base/threading/thread_local.h b/chromium/base/threading/thread_local.h index b13be1ab467..df9c4b72573 100644 --- a/chromium/base/threading/thread_local.h +++ b/chromium/base/threading/thread_local.h @@ -26,6 +26,9 @@ // you must of course properly deal with safety and race conditions. This // means a function-level static initializer is generally inappropiate. // +// In Android, the system TLS is limited, the implementation is backed with +// ThreadLocalStorage. +// // Example usage: // // My class is logically attached to a single thread. We cache a pointer // // on the thread it was created on, so we can implement current(). @@ -50,6 +53,7 @@ #include "base/base_export.h" #include "base/basictypes.h" +#include "base/threading/thread_local_storage.h" #if defined(OS_POSIX) #include <pthread.h> @@ -62,6 +66,8 @@ namespace internal { struct BASE_EXPORT ThreadLocalPlatform { #if defined(OS_WIN) typedef unsigned long SlotType; +#elif defined(OS_ANDROID) + typedef ThreadLocalStorage::StaticSlot SlotType; #elif defined(OS_POSIX) typedef pthread_key_t SlotType; #endif diff --git a/chromium/base/threading/thread_local_android.cc b/chromium/base/threading/thread_local_android.cc new file mode 100644 index 00000000000..c890237f67d --- /dev/null +++ b/chromium/base/threading/thread_local_android.cc @@ -0,0 +1,34 @@ +// 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/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); +} + +// static +void ThreadLocalPlatform::FreeSlot(SlotType slot) { + slot.Free(); +} + +// static +void* ThreadLocalPlatform::GetValueFromSlot(SlotType slot) { + return slot.Get(); +} + +// static +void ThreadLocalPlatform::SetValueInSlot(SlotType slot, void* value) { + slot.Set(value); +} + +} // namespace internal +} // namespace base diff --git a/chromium/base/threading/thread_local_posix.cc b/chromium/base/threading/thread_local_posix.cc index 526a66d0629..75ea4795d45 100644 --- a/chromium/base/threading/thread_local_posix.cc +++ b/chromium/base/threading/thread_local_posix.cc @@ -8,6 +8,8 @@ #include "base/logging.h" +#if !defined(OS_ANDROID) + namespace base { namespace internal { @@ -36,3 +38,5 @@ void ThreadLocalPlatform::SetValueInSlot(SlotType slot, void* value) { } // namespace internal } // namespace base + +#endif // !defined(OS_ANDROID) diff --git a/chromium/base/threading/thread_local_storage.cc b/chromium/base/threading/thread_local_storage.cc new file mode 100644 index 00000000000..e1749c467a8 --- /dev/null +++ b/chromium/base/threading/thread_local_storage.cc @@ -0,0 +1,250 @@ +// 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/threading/thread_local_storage.h" + +#include "base/atomicops.h" +#include "base/logging.h" + +using base::internal::PlatformThreadLocalStorage; + +namespace { +// In order to make TLS destructors work, we need to keep around a function +// pointer to the destructor for each slot. We keep this array of pointers in a +// global (static) array. +// We use the single OS-level TLS slot (giving us one pointer per thread) to +// hold a pointer to a per-thread array (table) of slots that we allocate to +// Chromium consumers. + +// g_native_tls_key is the one native TLS that we use. It stores our table. +base::subtle::AtomicWord g_native_tls_key = + PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES; + +// g_last_used_tls_key is the high-water-mark of allocated thread local storage. +// Each allocation is an index into our g_tls_destructors[]. Each such index is +// assigned to the instance variable slot_ in a ThreadLocalStorage::Slot +// instance. We reserve the value slot_ == 0 to indicate that the corresponding +// instance of ThreadLocalStorage::Slot has been freed (i.e., destructor called, +// etc.). This reserved use of 0 is then stated as the initial value of +// g_last_used_tls_key, so that the first issued index will be 1. +base::subtle::Atomic32 g_last_used_tls_key = 0; + +// The maximum number of 'slots' in our thread local storage stack. +const int kThreadLocalStorageSize = 256; + +// The maximum number of times to try to clear slots by calling destructors. +// Use pthread naming convention for clarity. +const int kMaxDestructorIterations = kThreadLocalStorageSize; + +// An array of destructor function pointers for the slots. If a slot has a +// destructor, it will be stored in its corresponding entry in this array. +// The elements are volatile to ensure that when the compiler reads the value +// to potentially call the destructor, it does so once, and that value is tested +// for null-ness and then used. Yes, that would be a weird de-optimization, +// but I can imagine some register machines where it was just as easy to +// re-fetch an array element, and I want to be sure a call to free the key +// (i.e., null out the destructor entry) that happens on a separate thread can't +// hurt the racy calls to the destructors on another thread. +volatile base::ThreadLocalStorage::TLSDestructorFunc + g_tls_destructors[kThreadLocalStorageSize]; + +// This function is called to initialize our entire Chromium TLS system. +// It may be called very early, and we need to complete most all of the setup +// (initialization) before calling *any* memory allocator functions, which may +// recursively depend on this initialization. +// As a result, we use Atomics, and avoid anything (like a singleton) that might +// require memory allocations. +void** ConstructTlsVector() { + PlatformThreadLocalStorage::TLSKey key = + base::subtle::NoBarrier_Load(&g_native_tls_key); + if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES) { + CHECK(PlatformThreadLocalStorage::AllocTLS(&key)); + + // The TLS_KEY_OUT_OF_INDEXES is used to find out whether the key is set or + // not in NoBarrier_CompareAndSwap, but Posix doesn't have invalid key, we + // define an almost impossible value be it. + // If we really get TLS_KEY_OUT_OF_INDEXES as value of key, just alloc + // another TLS slot. + if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES) { + PlatformThreadLocalStorage::TLSKey tmp = key; + CHECK(PlatformThreadLocalStorage::AllocTLS(&key) && + key != PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES); + PlatformThreadLocalStorage::FreeTLS(tmp); + } + // Atomically test-and-set the tls_key. If the key is + // TLS_KEY_OUT_OF_INDEXES, go ahead and set it. Otherwise, do nothing, as + // another thread already did our dirty work. + if (PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES != + base::subtle::NoBarrier_CompareAndSwap(&g_native_tls_key, + PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES, key)) { + // We've been shortcut. Another thread replaced g_native_tls_key first so + // we need to destroy our index and use the one the other thread got + // first. + PlatformThreadLocalStorage::FreeTLS(key); + key = base::subtle::NoBarrier_Load(&g_native_tls_key); + } + } + CHECK(!PlatformThreadLocalStorage::GetTLSValue(key)); + + // Some allocators, such as TCMalloc, make use of thread local storage. + // As a result, any attempt to call new (or malloc) will lazily cause such a + // system to initialize, which will include registering for a TLS key. If we + // are not careful here, then that request to create a key will call new back, + // and we'll have an infinite loop. We avoid that as follows: + // Use a stack allocated vector, so that we don't have dependence on our + // allocator until our service is in place. (i.e., don't even call new until + // after we're setup) + void* stack_allocated_tls_data[kThreadLocalStorageSize]; + memset(stack_allocated_tls_data, 0, sizeof(stack_allocated_tls_data)); + // Ensure that any rentrant calls change the temp version. + PlatformThreadLocalStorage::SetTLSValue(key, stack_allocated_tls_data); + + // Allocate an array to store our data. + void** tls_data = new void*[kThreadLocalStorageSize]; + memcpy(tls_data, stack_allocated_tls_data, sizeof(stack_allocated_tls_data)); + PlatformThreadLocalStorage::SetTLSValue(key, tls_data); + return tls_data; +} + +void OnThreadExitInternal(void* value) { + DCHECK(value); + void** tls_data = static_cast<void**>(value); + // Some allocators, such as TCMalloc, use TLS. As a result, when a thread + // terminates, one of the destructor calls we make may be to shut down an + // allocator. We have to be careful that after we've shutdown all of the + // known destructors (perchance including an allocator), that we don't call + // the allocator and cause it to resurrect itself (with no possibly destructor + // call to follow). We handle this problem as follows: + // Switch to using a stack allocated vector, so that we don't have dependence + // on our allocator after we have called all g_tls_destructors. (i.e., don't + // even call delete[] after we're done with destructors.) + void* stack_allocated_tls_data[kThreadLocalStorageSize]; + memcpy(stack_allocated_tls_data, tls_data, sizeof(stack_allocated_tls_data)); + // Ensure that any re-entrant calls change the temp version. + PlatformThreadLocalStorage::TLSKey key = + base::subtle::NoBarrier_Load(&g_native_tls_key); + PlatformThreadLocalStorage::SetTLSValue(key, stack_allocated_tls_data); + delete[] tls_data; // Our last dependence on an allocator. + + int remaining_attempts = kMaxDestructorIterations; + bool need_to_scan_destructors = true; + while (need_to_scan_destructors) { + need_to_scan_destructors = false; + // Try to destroy the first-created-slot (which is slot 1) in our last + // destructor call. That user was able to function, and define a slot with + // no other services running, so perhaps it is a basic service (like an + // allocator) and should also be destroyed last. If we get the order wrong, + // then we'll itterate several more times, so it is really not that + // critical (but it might help). + 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) + continue; + + base::ThreadLocalStorage::TLSDestructorFunc destructor = + g_tls_destructors[slot]; + if (destructor == NULL) + continue; + stack_allocated_tls_data[slot] = NULL; // pre-clear the slot. + destructor(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. + need_to_scan_destructors = true; + } + if (--remaining_attempts <= 0) { + NOTREACHED(); // Destructors might not have been called. + break; + } + } + + // Remove our stack allocated vector. + PlatformThreadLocalStorage::SetTLSValue(key, NULL); +} + +} // namespace + +namespace base { + +namespace internal { + +#if defined(OS_WIN) +void PlatformThreadLocalStorage::OnThreadExit() { + PlatformThreadLocalStorage::TLSKey key = + base::subtle::NoBarrier_Load(&g_native_tls_key); + if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES) + return; + void *tls_data = GetTLSValue(key); + // Maybe we have never initialized TLS for this thread. + if (!tls_data) + return; + OnThreadExitInternal(tls_data); +} +#elif defined(OS_POSIX) +void PlatformThreadLocalStorage::OnThreadExit(void* value) { + OnThreadExitInternal(value); +} +#endif // defined(OS_WIN) + +} // namespace internal + +ThreadLocalStorage::Slot::Slot(TLSDestructorFunc destructor) { + initialized_ = false; + slot_ = 0; + Initialize(destructor); +} + +bool ThreadLocalStorage::StaticSlot::Initialize(TLSDestructorFunc destructor) { + PlatformThreadLocalStorage::TLSKey key = + base::subtle::NoBarrier_Load(&g_native_tls_key); + if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES || + !PlatformThreadLocalStorage::GetTLSValue(key)) + ConstructTlsVector(); + + // Grab a new slot. + slot_ = base::subtle::NoBarrier_AtomicIncrement(&g_last_used_tls_key, 1); + DCHECK_GT(slot_, 0); + CHECK_LT(slot_, kThreadLocalStorageSize); + + // Setup our destructor. + g_tls_destructors[slot_] = destructor; + initialized_ = true; + return true; +} + +void ThreadLocalStorage::StaticSlot::Free() { + // At this time, we don't reclaim old indices for TLS slots. + // So all we need to do is wipe the destructor. + DCHECK_GT(slot_, 0); + DCHECK_LT(slot_, kThreadLocalStorageSize); + g_tls_destructors[slot_] = NULL; + slot_ = 0; + initialized_ = false; +} + +void* ThreadLocalStorage::StaticSlot::Get() const { + void** tls_data = static_cast<void**>( + PlatformThreadLocalStorage::GetTLSValue( + base::subtle::NoBarrier_Load(&g_native_tls_key))); + if (!tls_data) + tls_data = ConstructTlsVector(); + DCHECK_GT(slot_, 0); + DCHECK_LT(slot_, kThreadLocalStorageSize); + return tls_data[slot_]; +} + +void ThreadLocalStorage::StaticSlot::Set(void* value) { + void** tls_data = static_cast<void**>( + PlatformThreadLocalStorage::GetTLSValue( + base::subtle::NoBarrier_Load(&g_native_tls_key))); + if (!tls_data) + tls_data = ConstructTlsVector(); + DCHECK_GT(slot_, 0); + DCHECK_LT(slot_, kThreadLocalStorageSize); + tls_data[slot_] = value; +} + +} // namespace base diff --git a/chromium/base/threading/thread_local_storage.h b/chromium/base/threading/thread_local_storage.h index eb5648f76c9..53ebe55e51c 100644 --- a/chromium/base/threading/thread_local_storage.h +++ b/chromium/base/threading/thread_local_storage.h @@ -8,12 +8,71 @@ #include "base/base_export.h" #include "base/basictypes.h" -#if defined(OS_POSIX) +#if defined(OS_WIN) +#include <windows.h> +#elif defined(OS_POSIX) #include <pthread.h> #endif namespace base { +namespace internal { + +// WARNING: You should *NOT* be using this class directly. +// PlatformThreadLocalStorage is low-level abstraction to the OS's TLS +// interface, you should instead be using ThreadLocalStorage::StaticSlot/Slot. +class BASE_EXPORT PlatformThreadLocalStorage { + public: + +#if defined(OS_WIN) + typedef unsigned long TLSKey; + enum { TLS_KEY_OUT_OF_INDEXES = TLS_OUT_OF_INDEXES }; +#elif defined(OS_POSIX) + typedef pthread_key_t TLSKey; + // The following is a "reserved key" which is used in our generic Chromium + // ThreadLocalStorage implementation. We expect that an OS will not return + // such a key, but if it is returned (i.e., the OS tries to allocate it) we + // will just request another key. + enum { TLS_KEY_OUT_OF_INDEXES = 0x7FFFFFFF }; +#endif + + // The following methods need to be supported on each OS platform, so that + // the Chromium ThreadLocalStore functionality can be constructed. + // Chromium will use these methods to acquire a single OS slot, and then use + // that to support a much larger number of Chromium slots (independent of the + // OS restrictions). + // The following returns true if it successfully is able to return an OS + // key in |key|. + static bool AllocTLS(TLSKey* key); + // Note: FreeTLS() doesn't have to be called, it is fine with this leak, OS + // might not reuse released slot, you might just reset the TLS value with + // SetTLSValue(). + static void FreeTLS(TLSKey key); + static void SetTLSValue(TLSKey key, void* value); + static void* GetTLSValue(TLSKey key); + + // Each platform (OS implementation) is required to call this method on each + // terminating thread when the thread is about to terminate. This method + // will then call all registered destructors for slots in Chromium + // ThreadLocalStorage, until there are no slot values remaining as having + // been set on this thread. + // Destructors may end up being called multiple times on a terminating + // thread, as other destructors may re-set slots that were previously + // destroyed. +#if defined(OS_WIN) + // Since Windows which doesn't support TLS destructor, the implementation + // should use GetTLSValue() to retrieve the value of TLS slot. + static void OnThreadExit(); +#elif defined(OS_POSIX) + // |Value| is the data stored in TLS slot, The implementation can't use + // GetTLSValue() to retrieve the value of slot as it has already been reset + // in Posix. + static void OnThreadExit(void* value); +#endif +}; + +} // namespace internal + // Wrapper for thread local storage. This class doesn't do much except provide // an API for portability. class BASE_EXPORT ThreadLocalStorage { @@ -60,12 +119,7 @@ class BASE_EXPORT ThreadLocalStorage { // The internals of this struct should be considered private. bool initialized_; -#if defined(OS_WIN) int slot_; -#elif defined(OS_POSIX) - pthread_key_t key_; -#endif - }; // A convenience wrapper around StaticSlot with a constructor. Can be used @@ -77,11 +131,8 @@ class BASE_EXPORT ThreadLocalStorage { private: using StaticSlot::initialized_; -#if defined(OS_WIN) using StaticSlot::slot_; -#elif defined(OS_POSIX) - using StaticSlot::key_; -#endif + DISALLOW_COPY_AND_ASSIGN(Slot); }; diff --git a/chromium/base/threading/thread_local_storage_posix.cc b/chromium/base/threading/thread_local_storage_posix.cc index 75da5a7d8f0..ebaf4005d33 100644 --- a/chromium/base/threading/thread_local_storage_posix.cc +++ b/chromium/base/threading/thread_local_storage_posix.cc @@ -8,42 +8,27 @@ namespace base { -ThreadLocalStorage::Slot::Slot(TLSDestructorFunc destructor) { - initialized_ = false; - key_ = 0; - Initialize(destructor); -} - -bool ThreadLocalStorage::StaticSlot::Initialize(TLSDestructorFunc destructor) { - DCHECK(!initialized_); - int error = pthread_key_create(&key_, destructor); - if (error) { - NOTREACHED(); - return false; - } +namespace internal { - initialized_ = true; - return true; +bool PlatformThreadLocalStorage::AllocTLS(TLSKey* key) { + return !pthread_key_create(key, + base::internal::PlatformThreadLocalStorage::OnThreadExit); } -void ThreadLocalStorage::StaticSlot::Free() { - DCHECK(initialized_); - int error = pthread_key_delete(key_); - if (error) - NOTREACHED(); - initialized_ = false; +void PlatformThreadLocalStorage::FreeTLS(TLSKey key) { + int ret = pthread_key_delete(key); + DCHECK_EQ(ret, 0); } -void* ThreadLocalStorage::StaticSlot::Get() const { - DCHECK(initialized_); - return pthread_getspecific(key_); +void* PlatformThreadLocalStorage::GetTLSValue(TLSKey key) { + return pthread_getspecific(key); } -void ThreadLocalStorage::StaticSlot::Set(void* value) { - DCHECK(initialized_); - int error = pthread_setspecific(key_, value); - if (error) - NOTREACHED(); +void PlatformThreadLocalStorage::SetTLSValue(TLSKey key, void* value) { + int ret = pthread_setspecific(key, value); + DCHECK_EQ(ret, 0); } +} // namespace internal + } // namespace base diff --git a/chromium/base/threading/thread_local_storage_win.cc b/chromium/base/threading/thread_local_storage_win.cc index 0ae3cb4c8cd..42a7d016fdd 100644 --- a/chromium/base/threading/thread_local_storage_win.cc +++ b/chromium/base/threading/thread_local_storage_win.cc @@ -8,201 +8,35 @@ #include "base/logging.h" - -namespace { -// In order to make TLS destructors work, we need to keep function -// pointers to the destructor for each TLS that we allocate. -// We make this work by allocating a single OS-level TLS, which -// contains an array of slots for the application to use. In -// parallel, we also allocate an array of destructors, which we -// keep track of and call when threads terminate. - -// g_native_tls_key is the one native TLS that we use. It stores our table. -long g_native_tls_key = TLS_OUT_OF_INDEXES; - -// g_last_used_tls_key is the high-water-mark of allocated thread local storage. -// Each allocation is an index into our g_tls_destructors[]. Each such index is -// assigned to the instance variable slot_ in a ThreadLocalStorage::Slot -// instance. We reserve the value slot_ == 0 to indicate that the corresponding -// instance of ThreadLocalStorage::Slot has been freed (i.e., destructor called, -// etc.). This reserved use of 0 is then stated as the initial value of -// g_last_used_tls_key, so that the first issued index will be 1. -long g_last_used_tls_key = 0; - -// The maximum number of 'slots' in our thread local storage stack. -const int kThreadLocalStorageSize = 64; - -// The maximum number of times to try to clear slots by calling destructors. -// Use pthread naming convention for clarity. -const int kMaxDestructorIterations = kThreadLocalStorageSize; - -// An array of destructor function pointers for the slots. If a slot has a -// destructor, it will be stored in its corresponding entry in this array. -// The elements are volatile to ensure that when the compiler reads the value -// to potentially call the destructor, it does so once, and that value is tested -// for null-ness and then used. Yes, that would be a weird de-optimization, -// but I can imagine some register machines where it was just as easy to -// re-fetch an array element, and I want to be sure a call to free the key -// (i.e., null out the destructor entry) that happens on a separate thread can't -// hurt the racy calls to the destructors on another thread. -volatile base::ThreadLocalStorage::TLSDestructorFunc - g_tls_destructors[kThreadLocalStorageSize]; - -void** ConstructTlsVector() { - if (g_native_tls_key == TLS_OUT_OF_INDEXES) { - long value = TlsAlloc(); - DCHECK(value != TLS_OUT_OF_INDEXES); - - // Atomically test-and-set the tls_key. If the key is TLS_OUT_OF_INDEXES, - // go ahead and set it. Otherwise, do nothing, as another - // thread already did our dirty work. - if (TLS_OUT_OF_INDEXES != InterlockedCompareExchange( - &g_native_tls_key, value, TLS_OUT_OF_INDEXES)) { - // We've been shortcut. Another thread replaced g_native_tls_key first so - // we need to destroy our index and use the one the other thread got - // first. - TlsFree(value); - } - } - DCHECK(!TlsGetValue(g_native_tls_key)); - - // Some allocators, such as TCMalloc, make use of thread local storage. - // As a result, any attempt to call new (or malloc) will lazily cause such a - // system to initialize, which will include registering for a TLS key. If we - // are not careful here, then that request to create a key will call new back, - // and we'll have an infinite loop. We avoid that as follows: - // Use a stack allocated vector, so that we don't have dependence on our - // allocator until our service is in place. (i.e., don't even call new until - // after we're setup) - void* stack_allocated_tls_data[kThreadLocalStorageSize]; - memset(stack_allocated_tls_data, 0, sizeof(stack_allocated_tls_data)); - // Ensure that any rentrant calls change the temp version. - TlsSetValue(g_native_tls_key, stack_allocated_tls_data); - - // Allocate an array to store our data. - void** tls_data = new void*[kThreadLocalStorageSize]; - memcpy(tls_data, stack_allocated_tls_data, sizeof(stack_allocated_tls_data)); - TlsSetValue(g_native_tls_key, tls_data); - return tls_data; -} - -// Called when we terminate a thread, this function calls any TLS destructors -// that are pending for this thread. -void WinThreadExit() { - if (g_native_tls_key == TLS_OUT_OF_INDEXES) - return; - - void** tls_data = static_cast<void**>(TlsGetValue(g_native_tls_key)); - // Maybe we have never initialized TLS for this thread. - if (!tls_data) - return; - - // Some allocators, such as TCMalloc, use TLS. As a result, when a thread - // terminates, one of the destructor calls we make may be to shut down an - // allocator. We have to be careful that after we've shutdown all of the - // known destructors (perchance including an allocator), that we don't call - // the allocator and cause it to resurrect itself (with no possibly destructor - // call to follow). We handle this problem as follows: - // Switch to using a stack allocated vector, so that we don't have dependence - // on our allocator after we have called all g_tls_destructors. (i.e., don't - // even call delete[] after we're done with destructors.) - void* stack_allocated_tls_data[kThreadLocalStorageSize]; - memcpy(stack_allocated_tls_data, tls_data, sizeof(stack_allocated_tls_data)); - // Ensure that any re-entrant calls change the temp version. - TlsSetValue(g_native_tls_key, stack_allocated_tls_data); - delete[] tls_data; // Our last dependence on an allocator. - - int remaining_attempts = kMaxDestructorIterations; - bool need_to_scan_destructors = true; - while (need_to_scan_destructors) { - need_to_scan_destructors = false; - // Try to destroy the first-created-slot (which is slot 1) in our last - // destructor call. That user was able to function, and define a slot with - // no other services running, so perhaps it is a basic service (like an - // allocator) and should also be destroyed last. If we get the order wrong, - // then we'll itterate several more times, so it is really not that - // critical (but it might help). - for (int slot = g_last_used_tls_key; slot > 0; --slot) { - void* value = stack_allocated_tls_data[slot]; - if (value == NULL) - continue; - base::ThreadLocalStorage::TLSDestructorFunc destructor = - g_tls_destructors[slot]; - if (destructor == NULL) - continue; - stack_allocated_tls_data[slot] = NULL; // pre-clear the slot. - destructor(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. - need_to_scan_destructors = true; - } - if (--remaining_attempts <= 0) { - NOTREACHED(); // Destructors might not have been called. - break; - } - } - - // Remove our stack allocated vector. - TlsSetValue(g_native_tls_key, NULL); -} - -} // namespace - namespace base { -ThreadLocalStorage::Slot::Slot(TLSDestructorFunc destructor) { - initialized_ = false; - slot_ = 0; - Initialize(destructor); -} - -bool ThreadLocalStorage::StaticSlot::Initialize(TLSDestructorFunc destructor) { - if (g_native_tls_key == TLS_OUT_OF_INDEXES || !TlsGetValue(g_native_tls_key)) - ConstructTlsVector(); +namespace internal { - // Grab a new slot. - slot_ = InterlockedIncrement(&g_last_used_tls_key); - DCHECK_GT(slot_, 0); - if (slot_ >= kThreadLocalStorageSize) { - NOTREACHED(); - return false; +bool PlatformThreadLocalStorage::AllocTLS(TLSKey* key) { + TLSKey value = TlsAlloc(); + if (value != TLS_OUT_OF_INDEXES) { + *key = value; + return true; } - - // Setup our destructor. - g_tls_destructors[slot_] = destructor; - initialized_ = true; - return true; + return false; } -void ThreadLocalStorage::StaticSlot::Free() { - // At this time, we don't reclaim old indices for TLS slots. - // So all we need to do is wipe the destructor. - DCHECK_GT(slot_, 0); - DCHECK_LT(slot_, kThreadLocalStorageSize); - g_tls_destructors[slot_] = NULL; - slot_ = 0; - initialized_ = false; +void PlatformThreadLocalStorage::FreeTLS(TLSKey key) { + BOOL ret = TlsFree(key); + DCHECK(ret); } -void* ThreadLocalStorage::StaticSlot::Get() const { - void** tls_data = static_cast<void**>(TlsGetValue(g_native_tls_key)); - if (!tls_data) - tls_data = ConstructTlsVector(); - DCHECK_GT(slot_, 0); - DCHECK_LT(slot_, kThreadLocalStorageSize); - return tls_data[slot_]; +void* PlatformThreadLocalStorage::GetTLSValue(TLSKey key) { + return TlsGetValue(key); } -void ThreadLocalStorage::StaticSlot::Set(void* value) { - void** tls_data = static_cast<void**>(TlsGetValue(g_native_tls_key)); - if (!tls_data) - tls_data = ConstructTlsVector(); - DCHECK_GT(slot_, 0); - DCHECK_LT(slot_, kThreadLocalStorageSize); - tls_data[slot_] = value; +void PlatformThreadLocalStorage::SetTLSValue(TLSKey key, void* value) { + BOOL ret = TlsSetValue(key, value); + DCHECK(ret); } +} // namespace internal + } // namespace base // Thread Termination Callbacks. @@ -233,7 +67,7 @@ void NTAPI OnThreadExit(PVOID module, DWORD reason, PVOID reserved) { // On XP SP0 & SP1, the DLL_PROCESS_ATTACH is never seen. It is sent on SP2+ // and on W2K and W2K3. So don't assume it is sent. if (DLL_THREAD_DETACH == reason || DLL_PROCESS_DETACH == reason) - WinThreadExit(); + base::internal::PlatformThreadLocalStorage::OnThreadExit(); } // .CRT$XLA to .CRT$XLZ is an array of PIMAGE_TLS_CALLBACK pointers that are diff --git a/chromium/base/threading/thread_perftest.cc b/chromium/base/threading/thread_perftest.cc new file mode 100644 index 00000000000..eaeddf94435 --- /dev/null +++ b/chromium/base/threading/thread_perftest.cc @@ -0,0 +1,314 @@ +// 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/base_switches.h" +#include "base/bind.h" +#include "base/command_line.h" +#include "base/memory/scoped_vector.h" +#include "base/synchronization/condition_variable.h" +#include "base/synchronization/lock.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/thread.h" +#include "base/time/time.h" +#include "build/build_config.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/perf/perf_test.h" + +#if defined(OS_POSIX) +#include <pthread.h> +#endif + +namespace base { + +namespace { + +const int kNumRuns = 100000; + +// Base class for a threading perf-test. This sets up some threads for the +// test and measures the clock-time in addition to time spent on each thread. +class ThreadPerfTest : public testing::Test { + public: + ThreadPerfTest() + : done_(false, false) { + // Disable the task profiler as it adds significant cost! + CommandLine::Init(0, NULL); + CommandLine::ForCurrentProcess()->AppendSwitchASCII( + switches::kProfilerTiming, + switches::kProfilerTimingDisabledValue); + } + + // To be implemented by each test. Subclass must uses threads_ such that + // their cpu-time can be measured. Test must return from PingPong() _and_ + // call FinishMeasurement from any thread to complete the test. + virtual void Init() {} + virtual void PingPong(int hops) = 0; + virtual void Reset() {} + + void TimeOnThread(base::TimeTicks* ticks, base::WaitableEvent* done) { + *ticks = base::TimeTicks::ThreadNow(); + done->Signal(); + } + + 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)); + done.Wait(); + return ticks; + } + + void RunPingPongTest(const std::string& name, unsigned num_threads) { + // Create threads and collect starting cpu-time for each thread. + std::vector<base::TimeTicks> thread_starts; + while (threads_.size() < num_threads) { + threads_.push_back(new base::Thread("PingPonger")); + threads_.back()->Start(); + if (base::TimeTicks::IsThreadNowSupported()) + thread_starts.push_back(ThreadNow(threads_.back())); + } + + Init(); + + base::TimeTicks start = base::TimeTicks::HighResNow(); + PingPong(kNumRuns); + done_.Wait(); + base::TimeTicks end = base::TimeTicks::HighResNow(); + + // Gather the cpu-time spent on each thread. This does one extra tasks, + // but that should be in the noise given enough runs. + base::TimeDelta thread_time; + while (threads_.size()) { + if (base::TimeTicks::IsThreadNowSupported()) { + thread_time += ThreadNow(threads_.back()) - thread_starts.back(); + thread_starts.pop_back(); + } + threads_.pop_back(); + } + + Reset(); + + double num_runs = static_cast<double>(kNumRuns); + double us_per_task_clock = (end - start).InMicroseconds() / num_runs; + double us_per_task_cpu = thread_time.InMicroseconds() / num_runs; + + // Clock time per task. + perf_test::PrintResult( + "task", "", name + "_time ", us_per_task_clock, "us/hop", true); + + // Total utilization across threads if available (likely higher). + if (base::TimeTicks::IsThreadNowSupported()) { + perf_test::PrintResult( + "task", "", name + "_cpu ", us_per_task_cpu, "us/hop", true); + } + } + + protected: + void FinishMeasurement() { done_.Signal(); } + ScopedVector<base::Thread> threads_; + + private: + base::WaitableEvent done_; +}; + +// Class to test task performance by posting empty tasks back and forth. +class TaskPerfTest : public ThreadPerfTest { + base::Thread* NextThread(int count) { + return threads_[count % threads_.size()]; + } + + virtual void PingPong(int hops) OVERRIDE { + if (!hops) { + FinishMeasurement(); + return; + } + NextThread(hops)->message_loop_proxy()->PostTask( + FROM_HERE, + base::Bind( + &ThreadPerfTest::PingPong, base::Unretained(this), hops - 1)); + } +}; + +// This tries to test the 'best-case' as well as the 'worst-case' task posting +// performance. The best-case keeps one thread alive such that it never yeilds, +// while the worse-case forces a context switch for every task. Four threads are +// used to ensure the threads do yeild (with just two it might be possible for +// both threads to stay awake if they can signal each other fast enough). +TEST_F(TaskPerfTest, TaskPingPong) { + RunPingPongTest("1_Task_Threads", 1); + RunPingPongTest("4_Task_Threads", 4); +} + + +// Same as above, but add observers to test their perf impact. +class MessageLoopObserver : public base::MessageLoop::TaskObserver { + public: + virtual void WillProcessTask(const base::PendingTask& pending_task) OVERRIDE { + } + virtual void DidProcessTask(const base::PendingTask& pending_task) OVERRIDE { + } +}; +MessageLoopObserver message_loop_observer; + +class TaskObserverPerfTest : public TaskPerfTest { + public: + virtual void Init() OVERRIDE { + TaskPerfTest::Init(); + for (size_t i = 0; i < threads_.size(); i++) { + threads_[i]->message_loop()->AddTaskObserver(&message_loop_observer); + } + } +}; + +TEST_F(TaskObserverPerfTest, TaskPingPong) { + RunPingPongTest("1_Task_Threads_With_Observer", 1); + RunPingPongTest("4_Task_Threads_With_Observer", 4); +} + +// Class to test our WaitableEvent performance by signaling back and fort. +// WaitableEvent is templated so we can also compare with other versions. +template <typename WaitableEventType> +class EventPerfTest : public ThreadPerfTest { + public: + virtual 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 WaitAndSignalOnThread(size_t event) { + size_t next_event = (event + 1) % events_.size(); + int my_hops = 0; + do { + events_[event]->Wait(); + my_hops = --remaining_hops_; // We own 'hops' between Wait and Signal. + events_[next_event]->Signal(); + } while (my_hops > 0); + // Once we are done, all threads will signal as hops passes zero. + // We only signal completion once, on the thread that reaches zero. + if (!my_hops) + FinishMeasurement(); + } + + virtual 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)); + } + + // Kick off the Signal ping-ponging. + events_.front()->Signal(); + } + + int remaining_hops_; + ScopedVector<WaitableEventType> events_; +}; + +// Similar to the task posting test, this just tests similar functionality +// using WaitableEvents. We only test four threads (worst-case), but we +// might want to craft a way to test the best-case (where the thread doesn't +// end up blocking because the event is already signalled). +typedef EventPerfTest<base::WaitableEvent> WaitableEventPerfTest; +TEST_F(WaitableEventPerfTest, EventPingPong) { + RunPingPongTest("4_WaitableEvent_Threads", 4); +} + +// Build a minimal event using ConditionVariable. +class ConditionVariableEvent { + public: + ConditionVariableEvent(bool manual_reset, bool initially_signaled) + : cond_(&lock_), signaled_(false) { + DCHECK(!manual_reset); + DCHECK(!initially_signaled); + } + + void Signal() { + { + base::AutoLock scoped_lock(lock_); + signaled_ = true; + } + cond_.Signal(); + } + + void Wait() { + base::AutoLock scoped_lock(lock_); + while (!signaled_) + cond_.Wait(); + signaled_ = false; + } + + private: + base::Lock lock_; + base::ConditionVariable cond_; + bool signaled_; +}; + +// This is meant to test the absolute minimal context switching time +// using our own base synchronization code. +typedef EventPerfTest<ConditionVariableEvent> ConditionVariablePerfTest; +TEST_F(ConditionVariablePerfTest, EventPingPong) { + RunPingPongTest("4_ConditionVariable_Threads", 4); +} + +#if defined(OS_POSIX) + +// Absolutely 100% minimal posix waitable event. If there is a better/faster +// way to force a context switch, we should use that instead. +class PthreadEvent { + public: + PthreadEvent(bool manual_reset, bool initially_signaled) { + DCHECK(!manual_reset); + DCHECK(!initially_signaled); + pthread_mutex_init(&mutex_, 0); + pthread_cond_init(&cond_, 0); + signaled_ = false; + } + + ~PthreadEvent() { + pthread_cond_destroy(&cond_); + pthread_mutex_destroy(&mutex_); + } + + void Signal() { + pthread_mutex_lock(&mutex_); + signaled_ = true; + pthread_mutex_unlock(&mutex_); + pthread_cond_signal(&cond_); + } + + void Wait() { + pthread_mutex_lock(&mutex_); + while (!signaled_) + pthread_cond_wait(&cond_, &mutex_); + signaled_ = false; + pthread_mutex_unlock(&mutex_); + } + + private: + bool signaled_; + pthread_mutex_t mutex_; + pthread_cond_t cond_; +}; + +// This is meant to test the absolute minimal context switching time. +// If there is any faster way to do this we should substitute it in. +typedef EventPerfTest<PthreadEvent> PthreadEventPerfTest; +TEST_F(PthreadEventPerfTest, EventPingPong) { + RunPingPongTest("4_PthreadCondVar_Threads", 4); +} + +#endif + +} // namespace + +} // namespace base diff --git a/chromium/base/threading/worker_pool_posix.cc b/chromium/base/threading/worker_pool_posix.cc index c4c523f270d..f00d7994d01 100644 --- a/chromium/base/threading/worker_pool_posix.cc +++ b/chromium/base/threading/worker_pool_posix.cc @@ -91,7 +91,7 @@ void WorkerThread::ThreadMain() { PendingTask pending_task = pool_->WaitForTask(); if (pending_task.task.is_null()) break; - TRACE_EVENT2("task", "WorkerThread::ThreadMain::Run", + TRACE_EVENT2("toplevel", "WorkerThread::ThreadMain::Run", "src_file", pending_task.posted_from.file_name(), "src_func", pending_task.posted_from.function_name()); diff --git a/chromium/base/threading/worker_pool_win.cc b/chromium/base/threading/worker_pool_win.cc index c27010fb12f..fa11dc5a763 100644 --- a/chromium/base/threading/worker_pool_win.cc +++ b/chromium/base/threading/worker_pool_win.cc @@ -21,7 +21,7 @@ base::LazyInstance<ThreadLocalBoolean>::Leaky DWORD CALLBACK WorkItemCallback(void* param) { PendingTask* pending_task = static_cast<PendingTask*>(param); - TRACE_EVENT2("task", "WorkItemCallback::Run", + TRACE_EVENT2("toplevel", "WorkItemCallback::Run", "src_file", pending_task->posted_from.file_name(), "src_func", pending_task->posted_from.function_name()); @@ -48,7 +48,7 @@ bool PostTaskInternal(PendingTask* pending_task, bool task_is_slow) { flags |= WT_EXECUTELONGFUNCTION; if (!QueueUserWorkItem(WorkItemCallback, pending_task, flags)) { - DLOG_GETLASTERROR(ERROR) << "QueueUserWorkItem failed"; + DPLOG(ERROR) << "QueueUserWorkItem failed"; delete pending_task; return false; } diff --git a/chromium/base/time/pr_time_unittest.cc b/chromium/base/time/pr_time_unittest.cc index 9e0c4ed04f0..105b8e45318 100644 --- a/chromium/base/time/pr_time_unittest.cc +++ b/chromium/base/time/pr_time_unittest.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <stdint.h> #include <time.h> #include "base/compiler_specific.h" @@ -16,6 +17,10 @@ namespace { // time_t representation of 15th Oct 2007 12:45:00 PDT PRTime comparison_time_pdt = 1192477500 * Time::kMicrosecondsPerSecond; +// Time with positive tz offset and fractional seconds: +// 2013-07-08T11:28:12.441381+02:00 +PRTime comparison_time_2 = INT64_C(1373275692441381); // represented as GMT + // Specialized test fixture allowing time strings without timezones to be // tested by comparing them to a known time in the local zone. class PRTimeTest : public testing::Test { @@ -36,12 +41,30 @@ class PRTimeTest : public testing::Test { 0, // day of year (ignored, output only) -1 // DST in effect, -1 tells mktime to figure it out }; - comparison_time_local_ = mktime(&local_comparison_tm) * - Time::kMicrosecondsPerSecond; + comparison_time_local_ = + mktime(&local_comparison_tm) * Time::kMicrosecondsPerSecond; ASSERT_GT(comparison_time_local_, 0); + + const int microseconds = 441381; + struct tm local_comparison_tm_2 = { + 12, // second + 28, // minute + 11, // hour + 8, // day of month + 7 - 1, // month + 2013 - 1900, // year + 0, // day of week (ignored, output only) + 0, // day of year (ignored, output only) + -1 // DST in effect, -1 tells mktime to figure it out + }; + comparison_time_local_2_ = + mktime(&local_comparison_tm_2) * Time::kMicrosecondsPerSecond; + ASSERT_GT(comparison_time_local_2_, 0); + comparison_time_local_2_ += microseconds; } PRTime comparison_time_local_; + PRTime comparison_time_local_2_; }; // Tests the PR_ParseTimeString nspr helper function for @@ -74,7 +97,7 @@ TEST_F(PRTimeTest, ParseTimeTest2) { PRStatus result = PR_ParseTimeString("Mon, 15 Oct 2007 19:45:00 GMT", PR_FALSE, &parsed_time); EXPECT_EQ(PR_SUCCESS, result); - EXPECT_EQ(parsed_time, comparison_time_pdt); + EXPECT_EQ(comparison_time_pdt, parsed_time); } TEST_F(PRTimeTest, ParseTimeTest3) { @@ -82,7 +105,7 @@ TEST_F(PRTimeTest, ParseTimeTest3) { PRStatus result = PR_ParseTimeString("15 Oct 07 12:45:00", PR_FALSE, &parsed_time); EXPECT_EQ(PR_SUCCESS, result); - EXPECT_EQ(parsed_time, comparison_time_local_); + EXPECT_EQ(comparison_time_local_, parsed_time); } TEST_F(PRTimeTest, ParseTimeTest4) { @@ -90,7 +113,7 @@ TEST_F(PRTimeTest, ParseTimeTest4) { PRStatus result = PR_ParseTimeString("15 Oct 07 19:45 GMT", PR_FALSE, &parsed_time); EXPECT_EQ(PR_SUCCESS, result); - EXPECT_EQ(parsed_time, comparison_time_pdt); + EXPECT_EQ(comparison_time_pdt, parsed_time); } TEST_F(PRTimeTest, ParseTimeTest5) { @@ -98,7 +121,7 @@ TEST_F(PRTimeTest, ParseTimeTest5) { PRStatus result = PR_ParseTimeString("Mon Oct 15 12:45 PDT 2007", PR_FALSE, &parsed_time); EXPECT_EQ(PR_SUCCESS, result); - EXPECT_EQ(parsed_time, comparison_time_pdt); + EXPECT_EQ(comparison_time_pdt, parsed_time); } TEST_F(PRTimeTest, ParseTimeTest6) { @@ -106,7 +129,7 @@ TEST_F(PRTimeTest, ParseTimeTest6) { PRStatus result = PR_ParseTimeString("Monday, Oct 15, 2007 12:45 PM", PR_FALSE, &parsed_time); EXPECT_EQ(PR_SUCCESS, result); - EXPECT_EQ(parsed_time, comparison_time_local_); + EXPECT_EQ(comparison_time_local_, parsed_time); } TEST_F(PRTimeTest, ParseTimeTest7) { @@ -114,23 +137,123 @@ TEST_F(PRTimeTest, ParseTimeTest7) { PRStatus result = PR_ParseTimeString("10/15/07 12:45:00 PM", PR_FALSE, &parsed_time); EXPECT_EQ(PR_SUCCESS, result); - EXPECT_EQ(parsed_time, comparison_time_local_); + EXPECT_EQ(comparison_time_local_, parsed_time); } TEST_F(PRTimeTest, ParseTimeTest8) { PRTime parsed_time = 0; - PRStatus result = PR_ParseTimeString("15-OCT-2007 12:45pm", PR_FALSE, + PRStatus result = PR_ParseTimeString("10/15/07 12:45:00. PM", PR_FALSE, &parsed_time); EXPECT_EQ(PR_SUCCESS, result); - EXPECT_EQ(parsed_time, comparison_time_local_); + EXPECT_EQ(comparison_time_local_, parsed_time); } TEST_F(PRTimeTest, ParseTimeTest9) { PRTime parsed_time = 0; + PRStatus result = PR_ParseTimeString("10/15/07 12:45:00.0 PM", PR_FALSE, + &parsed_time); + EXPECT_EQ(PR_SUCCESS, result); + EXPECT_EQ(comparison_time_local_, parsed_time); +} + +TEST_F(PRTimeTest, ParseTimeTest10) { + PRTime parsed_time = 0; + PRStatus result = PR_ParseTimeString("15-OCT-2007 12:45pm", PR_FALSE, + &parsed_time); + EXPECT_EQ(PR_SUCCESS, result); + EXPECT_EQ(comparison_time_local_, parsed_time); +} + +TEST_F(PRTimeTest, ParseTimeTest11) { + PRTime parsed_time = 0; PRStatus result = PR_ParseTimeString("16 Oct 2007 4:45-JST (Tuesday)", PR_FALSE, &parsed_time); EXPECT_EQ(PR_SUCCESS, result); - EXPECT_EQ(parsed_time, comparison_time_pdt); + EXPECT_EQ(comparison_time_pdt, parsed_time); +} + +// hh:mm timezone offset. +TEST_F(PRTimeTest, ParseTimeTest12) { + PRTime parsed_time = 0; + PRStatus result = PR_ParseTimeString("2013-07-08T11:28:12.441381+02:00", + PR_FALSE, &parsed_time); + EXPECT_EQ(PR_SUCCESS, result); + EXPECT_EQ(comparison_time_2, parsed_time); +} + +// hhmm timezone offset. +TEST_F(PRTimeTest, ParseTimeTest13) { + PRTime parsed_time = 0; + PRStatus result = PR_ParseTimeString("2013-07-08T11:28:12.441381+0200", + PR_FALSE, &parsed_time); + EXPECT_EQ(PR_SUCCESS, result); + EXPECT_EQ(comparison_time_2, parsed_time); +} + +// hh timezone offset. +TEST_F(PRTimeTest, ParseTimeTest14) { + PRTime parsed_time = 0; + PRStatus result = PR_ParseTimeString("2013-07-08T11:28:12.4413819+02", + PR_FALSE, &parsed_time); + EXPECT_EQ(PR_SUCCESS, result); + EXPECT_EQ(comparison_time_2, parsed_time); +} + +// 5 digits fractional second. +TEST_F(PRTimeTest, ParseTimeTest15) { + PRTime parsed_time = 0; + PRStatus result = PR_ParseTimeString("2013-07-08T09:28:12.44138Z", + PR_FALSE, &parsed_time); + EXPECT_EQ(PR_SUCCESS, result); + EXPECT_EQ(comparison_time_2-1, parsed_time); +} + +// Fractional seconds, local timezone. +TEST_F(PRTimeTest, ParseTimeTest16) { + PRTime parsed_time = 0; + PRStatus result = PR_ParseTimeString("2013-07-08T11:28:12.441381", + PR_FALSE, &parsed_time); + EXPECT_EQ(PR_SUCCESS, result); + EXPECT_EQ(comparison_time_local_2_, parsed_time); +} + +// "Z" (=GMT) timezone. +TEST_F(PRTimeTest, ParseTimeTest17) { + PRTime parsed_time = 0; + PRStatus result = PR_ParseTimeString("2013-07-08T09:28:12.441381Z", + PR_FALSE, &parsed_time); + EXPECT_EQ(PR_SUCCESS, result); + EXPECT_EQ(comparison_time_2, parsed_time); +} + +// "T" delimiter replaced by space. +TEST_F(PRTimeTest, ParseTimeTest18) { + PRTime parsed_time = 0; + PRStatus result = PR_ParseTimeString("2013-07-08 09:28:12.441381Z", + PR_FALSE, &parsed_time); + EXPECT_EQ(PR_SUCCESS, result); + EXPECT_EQ(comparison_time_2, parsed_time); +} + +TEST_F(PRTimeTest, ParseTimeTestInvalid1) { + PRTime parsed_time = 0; + PRStatus result = PR_ParseTimeString("201-07-08T09:28:12.441381Z", + PR_FALSE, &parsed_time); + EXPECT_EQ(PR_FAILURE, result); +} + +TEST_F(PRTimeTest, ParseTimeTestInvalid2) { + PRTime parsed_time = 0; + PRStatus result = PR_ParseTimeString("2013-007-08T09:28:12.441381Z", + PR_FALSE, &parsed_time); + EXPECT_EQ(PR_FAILURE, result); +} + +TEST_F(PRTimeTest, ParseTimeTestInvalid3) { + PRTime parsed_time = 0; + PRStatus result = PR_ParseTimeString("2013-07-008T09:28:12.441381Z", + PR_FALSE, &parsed_time); + EXPECT_EQ(PR_FAILURE, result); } // This test should not crash when compiled with Visual C++ 2005 (see diff --git a/chromium/base/time/time.cc b/chromium/base/time/time.cc index 9f3c53d6695..f7313565a3e 100644 --- a/chromium/base/time/time.cc +++ b/chromium/base/time/time.cc @@ -11,46 +11,86 @@ #include "base/lazy_instance.h" #include "base/logging.h" #include "base/third_party/nspr/prtime.h" -#include "base/third_party/nspr/prtypes.h" namespace base { // TimeDelta ------------------------------------------------------------------ +// static +TimeDelta TimeDelta::Max() { + return TimeDelta(std::numeric_limits<int64>::max()); +} + int TimeDelta::InDays() const { + if (is_max()) { + // Preserve max to prevent overflow. + return std::numeric_limits<int>::max(); + } return static_cast<int>(delta_ / Time::kMicrosecondsPerDay); } int TimeDelta::InHours() const { + if (is_max()) { + // Preserve max to prevent overflow. + return std::numeric_limits<int>::max(); + } return static_cast<int>(delta_ / Time::kMicrosecondsPerHour); } int TimeDelta::InMinutes() const { + if (is_max()) { + // Preserve max to prevent overflow. + return std::numeric_limits<int>::max(); + } return static_cast<int>(delta_ / Time::kMicrosecondsPerMinute); } double TimeDelta::InSecondsF() const { + if (is_max()) { + // Preserve max to prevent overflow. + return std::numeric_limits<double>::infinity(); + } return static_cast<double>(delta_) / Time::kMicrosecondsPerSecond; } int64 TimeDelta::InSeconds() const { + if (is_max()) { + // Preserve max to prevent overflow. + return std::numeric_limits<int64>::max(); + } return delta_ / Time::kMicrosecondsPerSecond; } double TimeDelta::InMillisecondsF() const { + if (is_max()) { + // Preserve max to prevent overflow. + return std::numeric_limits<double>::infinity(); + } return static_cast<double>(delta_) / Time::kMicrosecondsPerMillisecond; } int64 TimeDelta::InMilliseconds() const { + if (is_max()) { + // Preserve max to prevent overflow. + return std::numeric_limits<int64>::max(); + } return delta_ / Time::kMicrosecondsPerMillisecond; } int64 TimeDelta::InMillisecondsRoundedUp() const { + if (is_max()) { + // Preserve max to prevent overflow. + return std::numeric_limits<int64>::max(); + } return (delta_ + Time::kMicrosecondsPerMillisecond - 1) / Time::kMicrosecondsPerMillisecond; } int64 TimeDelta::InMicroseconds() const { + if (is_max()) { + // Preserve max to prevent overflow. + return std::numeric_limits<int64>::max(); + } return delta_; } @@ -89,7 +129,7 @@ time_t Time::ToTimeT() const { Time Time::FromDoubleT(double dt) { if (dt == 0 || IsNaN(dt)) return Time(); // Preserve 0 so we can tell it doesn't exist. - if (dt == std::numeric_limits<double>::max()) + if (dt == std::numeric_limits<double>::infinity()) return Max(); return Time(static_cast<int64>((dt * static_cast<double>(kMicrosecondsPerSecond)) + @@ -101,7 +141,7 @@ double Time::ToDoubleT() const { return 0; // Preserve 0 so we can tell it doesn't exist. if (is_max()) { // Preserve max without offset to prevent overflow. - return std::numeric_limits<double>::max(); + return std::numeric_limits<double>::infinity(); } return (static_cast<double>(us_ - kTimeTToMicrosecondsOffset) / static_cast<double>(kMicrosecondsPerSecond)); @@ -120,7 +160,7 @@ Time Time::FromTimeSpec(const timespec& ts) { Time Time::FromJsTime(double ms_since_epoch) { // The epoch is a valid time, so this constructor doesn't interpret // 0 as the null time. - if (ms_since_epoch == std::numeric_limits<double>::max()) + if (ms_since_epoch == std::numeric_limits<double>::infinity()) return Max(); return Time(static_cast<int64>(ms_since_epoch * kMicrosecondsPerMillisecond) + kTimeTToMicrosecondsOffset); @@ -133,7 +173,7 @@ double Time::ToJsTime() const { } if (is_max()) { // Preserve max without offset to prevent overflow. - return std::numeric_limits<double>::max(); + return std::numeric_limits<double>::infinity(); } return (static_cast<double>(us_ - kTimeTToMicrosecondsOffset) / kMicrosecondsPerMillisecond); diff --git a/chromium/base/time/time.h b/chromium/base/time/time.h index 40566b92317..41d662afa6a 100644 --- a/chromium/base/time/time.h +++ b/chromium/base/time/time.h @@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Time represents an absolute point in time, internally represented as -// microseconds (s/1,000,000) since the Windows epoch (1601-01-01 00:00:00 UTC) -// (See http://crbug.com/14734). System-dependent clock interface routines are -// defined in time_PLATFORM.cc. +// Time represents an absolute point in coordinated universal time (UTC), +// internally represented as microseconds (s/1,000,000) since the Windows epoch +// (1601-01-01 00:00:00 UTC) (See http://crbug.com/14734). System-dependent +// clock interface routines are defined in time_PLATFORM.cc. // // TimeDelta represents a duration of time, internally represented in // microseconds. @@ -61,11 +61,13 @@ class BASE_EXPORT TimeDelta { } // Converts units of time to TimeDeltas. - static TimeDelta FromDays(int64 days); - static TimeDelta FromHours(int64 hours); - static TimeDelta FromMinutes(int64 minutes); + static TimeDelta FromDays(int days); + static TimeDelta FromHours(int hours); + static TimeDelta FromMinutes(int minutes); static TimeDelta FromSeconds(int64 secs); static TimeDelta FromMilliseconds(int64 ms); + static TimeDelta FromSecondsD(double secs); + static TimeDelta FromMillisecondsD(double ms); static TimeDelta FromMicroseconds(int64 us); #if defined(OS_WIN) static TimeDelta FromQPCValue(LONGLONG qpc_value); @@ -79,6 +81,11 @@ class BASE_EXPORT TimeDelta { return TimeDelta(delta); } + // Returns the maximum time delta, which should be greater than any reasonable + // time delta we might compare it to. Adding or subtracting the maximum time + // delta to a time or another time delta has an undefined result. + static TimeDelta Max(); + // Returns the internal numeric value of the TimeDelta object. Please don't // use this and do arithmetic on it, as it is more error prone than using the // provided operators. @@ -87,6 +94,11 @@ class BASE_EXPORT TimeDelta { return delta_; } + // Returns true if the time delta is the maximum time delta. + bool is_max() const { + return delta_ == std::numeric_limits<int64>::max(); + } + #if defined(OS_POSIX) struct timespec ToTimeSpec() const; #endif @@ -196,7 +208,7 @@ inline TimeDelta operator*(int64 a, TimeDelta td) { // Time ----------------------------------------------------------------------- -// Represents a wall clock time. +// Represents a wall clock time in UTC. class BASE_EXPORT Time { public: static const int64 kMillisecondsPerSecond = 1000; @@ -493,32 +505,66 @@ class BASE_EXPORT Time { // Inline the TimeDelta factory methods, for fast TimeDelta construction. // static -inline TimeDelta TimeDelta::FromDays(int64 days) { +inline TimeDelta TimeDelta::FromDays(int days) { + // Preserve max to prevent overflow. + if (days == std::numeric_limits<int>::max()) + return Max(); return TimeDelta(days * Time::kMicrosecondsPerDay); } // static -inline TimeDelta TimeDelta::FromHours(int64 hours) { +inline TimeDelta TimeDelta::FromHours(int hours) { + // Preserve max to prevent overflow. + if (hours == std::numeric_limits<int>::max()) + return Max(); return TimeDelta(hours * Time::kMicrosecondsPerHour); } // static -inline TimeDelta TimeDelta::FromMinutes(int64 minutes) { +inline TimeDelta TimeDelta::FromMinutes(int minutes) { + // Preserve max to prevent overflow. + if (minutes == std::numeric_limits<int>::max()) + return Max(); return TimeDelta(minutes * Time::kMicrosecondsPerMinute); } // static inline TimeDelta TimeDelta::FromSeconds(int64 secs) { + // Preserve max to prevent overflow. + if (secs == std::numeric_limits<int64>::max()) + return Max(); return TimeDelta(secs * Time::kMicrosecondsPerSecond); } // static inline TimeDelta TimeDelta::FromMilliseconds(int64 ms) { + // Preserve max to prevent overflow. + if (ms == std::numeric_limits<int64>::max()) + return Max(); + return TimeDelta(ms * Time::kMicrosecondsPerMillisecond); +} + +// static +inline TimeDelta TimeDelta::FromSecondsD(double secs) { + // Preserve max to prevent overflow. + if (secs == std::numeric_limits<double>::infinity()) + return Max(); + return TimeDelta(secs * Time::kMicrosecondsPerSecond); +} + +// static +inline TimeDelta TimeDelta::FromMillisecondsD(double ms) { + // Preserve max to prevent overflow. + if (ms == std::numeric_limits<double>::infinity()) + return Max(); return TimeDelta(ms * Time::kMicrosecondsPerMillisecond); } // static inline TimeDelta TimeDelta::FromMicroseconds(int64 us) { + // Preserve max to prevent overflow. + if (us == std::numeric_limits<int64>::max()) + return Max(); return TimeDelta(us); } @@ -530,6 +576,14 @@ inline Time TimeDelta::operator+(Time t) const { class BASE_EXPORT TimeTicks { public: + // We define this even without OS_CHROMEOS for seccomp sandbox testing. +#if defined(OS_LINUX) + // Force definition of the system trace clock; it is a chromeos-only api + // at the moment and surfacing it in the right place requires mucking + // with glibc et al. + static const clockid_t kClockSystemTrace = 11; +#endif + TimeTicks() : ticks_(0) { } diff --git a/chromium/base/time/time_mac.cc b/chromium/base/time/time_mac.cc index 2388ddc6e69..26c5d7a715b 100644 --- a/chromium/base/time/time_mac.cc +++ b/chromium/base/time/time_mac.cc @@ -15,6 +15,7 @@ #include "base/basictypes.h" #include "base/logging.h" +#include "base/mac/mach_logging.h" #include "base/mac/scoped_cftyperef.h" #include "base/mac/scoped_mach_port.h" @@ -46,7 +47,7 @@ uint64_t ComputeCurrentTicks() { // whether mach_timebase_info has already been called. This is // recommended by Apple's QA1398. kern_return_t kr = mach_timebase_info(&timebase_info); - DCHECK_EQ(KERN_SUCCESS, kr); + MACH_DCHECK(kr == KERN_SUCCESS, kr) << "mach_timebase_info"; } // mach_absolute_time is it when it comes to ticks on the Mac. Other calls @@ -71,11 +72,11 @@ uint64_t ComputeThreadTicks() { NOTREACHED(); return 0; #else - base::mac::ScopedMachPort thread(mach_thread_self()); + base::mac::ScopedMachSendRight thread(mach_thread_self()); mach_msg_type_number_t thread_info_count = THREAD_BASIC_INFO_COUNT; thread_basic_info_data_t thread_info_data; - if (thread == MACH_PORT_NULL) { + if (thread.get() == MACH_PORT_NULL) { DLOG(ERROR) << "Failed to get mach_thread_self()"; return 0; } @@ -85,7 +86,7 @@ uint64_t ComputeThreadTicks() { THREAD_BASIC_INFO, reinterpret_cast<thread_info_t>(&thread_info_data), &thread_info_count); - DCHECK_EQ(KERN_SUCCESS, kr); + MACH_DCHECK(kr == KERN_SUCCESS, kr) << "thread_info"; return (thread_info_data.user_time.seconds * base::Time::kMicrosecondsPerSecond) + @@ -131,9 +132,11 @@ Time Time::Now() { // static Time Time::FromCFAbsoluteTime(CFAbsoluteTime t) { + COMPILE_ASSERT(std::numeric_limits<CFAbsoluteTime>::has_infinity, + numeric_limits_infinity_is_undefined_when_not_has_infinity); if (t == 0) return Time(); // Consider 0 as a null Time. - if (t == std::numeric_limits<CFAbsoluteTime>::max()) + if (t == std::numeric_limits<CFAbsoluteTime>::infinity()) return Max(); return Time(static_cast<int64>( (t + kCFAbsoluteTimeIntervalSince1970) * kMicrosecondsPerSecond) + @@ -141,10 +144,12 @@ Time Time::FromCFAbsoluteTime(CFAbsoluteTime t) { } CFAbsoluteTime Time::ToCFAbsoluteTime() const { + COMPILE_ASSERT(std::numeric_limits<CFAbsoluteTime>::has_infinity, + numeric_limits_infinity_is_undefined_when_not_has_infinity); if (is_null()) return 0; // Consider 0 as a null Time. if (is_max()) - return std::numeric_limits<CFAbsoluteTime>::max(); + return std::numeric_limits<CFAbsoluteTime>::infinity(); return (static_cast<CFAbsoluteTime>(us_ - kWindowsEpochDeltaMicroseconds) / kMicrosecondsPerSecond) - kCFAbsoluteTimeIntervalSince1970; } diff --git a/chromium/base/time/time_posix.cc b/chromium/base/time/time_posix.cc index 3378038fe44..b5b8213846d 100644 --- a/chromium/base/time/time_posix.cc +++ b/chromium/base/time/time_posix.cc @@ -7,7 +7,7 @@ #include <stdint.h> #include <sys/time.h> #include <time.h> -#if defined(OS_ANDROID) +#if defined(OS_ANDROID) && !defined(__LP64__) #include <time64.h> #endif #include <unistd.h> @@ -32,7 +32,7 @@ namespace { // Define a system-specific SysTime that wraps either to a time_t or // a time64_t depending on the host system, and associated convertion. // See crbug.com/162007 -#if defined(OS_ANDROID) +#if defined(OS_ANDROID) && !defined(__LP64__) typedef time64_t SysTime; SysTime SysTimeFromTimeStruct(struct tm* timestruct, bool is_local) { @@ -49,7 +49,7 @@ void SysTimeToTimeStruct(SysTime t, struct tm* timestruct, bool is_local) { gmtime64_r(&t, timestruct); } -#else // OS_ANDROID +#else // OS_ANDROID && !__LP64__ typedef time_t SysTime; SysTime SysTimeFromTimeStruct(struct tm* timestruct, bool is_local) { @@ -140,7 +140,7 @@ Time Time::Now() { struct timezone tz = { 0, 0 }; // UTC if (gettimeofday(&tv, &tz) != 0) { DCHECK(0) << "Could not determine time of day"; - LOG_ERRNO(ERROR) << "Call to gettimeofday failed."; + PLOG(ERROR) << "Call to gettimeofday failed."; // Return null instead of uninitialized |tv| value, which contains random // garbage data. This may result in the crash seen in crbug.com/147570. return Time(); @@ -320,18 +320,14 @@ TimeTicks TimeTicks::ThreadNow() { #endif } +// Use the Chrome OS specific system-wide clock. #if defined(OS_CHROMEOS) -// Force definition of the system trace clock; it is a chromeos-only api -// at the moment and surfacing it in the right place requires mucking -// with glibc et al. -#define CLOCK_SYSTEM_TRACE 11 - // static TimeTicks TimeTicks::NowFromSystemTraceTime() { uint64_t absolute_micro; struct timespec ts; - if (clock_gettime(CLOCK_SYSTEM_TRACE, &ts) != 0) { + if (clock_gettime(kClockSystemTrace, &ts) != 0) { // NB: fall-back for a chrome os build running on linux return HighResNow(); } @@ -343,14 +339,14 @@ TimeTicks TimeTicks::NowFromSystemTraceTime() { return TimeTicks(absolute_micro); } -#else // !defined(OS_CHROMEOS) +#else // !defined(OS_CHROMEOS) // static TimeTicks TimeTicks::NowFromSystemTraceTime() { return HighResNow(); } -#endif // defined(OS_CHROMEOS) +#endif // defined(OS_CHROMEOS) #endif // !OS_MACOSX diff --git a/chromium/base/time/time_unittest.cc b/chromium/base/time/time_unittest.cc index e78a61cf4bd..63c3a1ac771 100644 --- a/chromium/base/time/time_unittest.cc +++ b/chromium/base/time/time_unittest.cc @@ -481,6 +481,52 @@ TEST_F(TimeTest, ExplodeBeforeUnixEpoch) { EXPECT_EQ(1, exploded.millisecond); } +TEST_F(TimeTest, TimeDeltaMax) { + TimeDelta max = TimeDelta::Max(); + EXPECT_TRUE(max.is_max()); + EXPECT_EQ(max, TimeDelta::Max()); + EXPECT_GT(max, TimeDelta::FromDays(100 * 365)); + EXPECT_GT(max, TimeDelta()); +} + +TEST_F(TimeTest, TimeDeltaMaxConversions) { + TimeDelta t = TimeDelta::Max(); + EXPECT_EQ(std::numeric_limits<int64>::max(), t.ToInternalValue()); + + EXPECT_EQ(std::numeric_limits<int>::max(), t.InDays()); + EXPECT_EQ(std::numeric_limits<int>::max(), t.InHours()); + EXPECT_EQ(std::numeric_limits<int>::max(), t.InMinutes()); + EXPECT_EQ(std::numeric_limits<double>::infinity(), t.InSecondsF()); + EXPECT_EQ(std::numeric_limits<int64>::max(), t.InSeconds()); + EXPECT_EQ(std::numeric_limits<double>::infinity(), t.InMillisecondsF()); + EXPECT_EQ(std::numeric_limits<int64>::max(), t.InMilliseconds()); + EXPECT_EQ(std::numeric_limits<int64>::max(), t.InMillisecondsRoundedUp()); + + t = TimeDelta::FromDays(std::numeric_limits<int>::max()); + EXPECT_TRUE(t.is_max()); + + t = TimeDelta::FromHours(std::numeric_limits<int>::max()); + EXPECT_TRUE(t.is_max()); + + t = TimeDelta::FromMinutes(std::numeric_limits<int>::max()); + EXPECT_TRUE(t.is_max()); + + t = TimeDelta::FromSeconds(std::numeric_limits<int64>::max()); + EXPECT_TRUE(t.is_max()); + + t = TimeDelta::FromMilliseconds(std::numeric_limits<int64>::max()); + EXPECT_TRUE(t.is_max()); + + t = TimeDelta::FromSecondsD(std::numeric_limits<double>::infinity()); + EXPECT_TRUE(t.is_max()); + + t = TimeDelta::FromMillisecondsD(std::numeric_limits<double>::infinity()); + EXPECT_TRUE(t.is_max()); + + t = TimeDelta::FromMicroseconds(std::numeric_limits<int64>::max()); + EXPECT_TRUE(t.is_max()); +} + TEST_F(TimeTest, Max) { Time max = Time::Max(); EXPECT_TRUE(max.is_max()); @@ -493,13 +539,13 @@ TEST_F(TimeTest, MaxConversions) { Time t = Time::Max(); EXPECT_EQ(std::numeric_limits<int64>::max(), t.ToInternalValue()); - t = Time::FromDoubleT(std::numeric_limits<double>::max()); + t = Time::FromDoubleT(std::numeric_limits<double>::infinity()); EXPECT_TRUE(t.is_max()); - EXPECT_EQ(std::numeric_limits<double>::max(), t.ToDoubleT()); + EXPECT_EQ(std::numeric_limits<double>::infinity(), t.ToDoubleT()); - t = Time::FromJsTime(std::numeric_limits<double>::max()); + t = Time::FromJsTime(std::numeric_limits<double>::infinity()); EXPECT_TRUE(t.is_max()); - EXPECT_EQ(std::numeric_limits<double>::max(), t.ToJsTime()); + EXPECT_EQ(std::numeric_limits<double>::infinity(), t.ToJsTime()); t = Time::FromTimeT(std::numeric_limits<time_t>::max()); EXPECT_TRUE(t.is_max()); @@ -518,9 +564,10 @@ TEST_F(TimeTest, MaxConversions) { #endif #if defined(OS_MACOSX) - t = Time::FromCFAbsoluteTime(std::numeric_limits<CFAbsoluteTime>::max()); + t = Time::FromCFAbsoluteTime(std::numeric_limits<CFAbsoluteTime>::infinity()); EXPECT_TRUE(t.is_max()); - EXPECT_EQ(std::numeric_limits<CFAbsoluteTime>::max(), t.ToCFAbsoluteTime()); + EXPECT_EQ(std::numeric_limits<CFAbsoluteTime>::infinity(), + t.ToCFAbsoluteTime()); #endif #if defined(OS_WIN) @@ -635,7 +682,14 @@ TEST(TimeTicks, HighResNow) { HighResClockTest(&TimeTicks::HighResNow); } -TEST(TimeTicks, ThreadNow) { +// Fails frequently on Android http://crbug.com/352633 with: +// Expected: (delta_thread.InMicroseconds()) > (0), actual: 0 vs 0 +#if defined(OS_ANDROID) +#define MAYBE_ThreadNow DISABLED_ThreadNow +#else +#define MAYBE_ThreadNow ThreadNow +#endif +TEST(TimeTicks, MAYBE_ThreadNow) { if (TimeTicks::IsThreadNowSupported()) { TimeTicks begin = TimeTicks::Now(); TimeTicks begin_thread = TimeTicks::ThreadNow(); @@ -667,6 +721,10 @@ TEST(TimeDelta, FromAndIn) { EXPECT_TRUE(TimeDelta::FromSeconds(2) == TimeDelta::FromMilliseconds(2000)); EXPECT_TRUE(TimeDelta::FromMilliseconds(2) == TimeDelta::FromMicroseconds(2000)); + EXPECT_TRUE(TimeDelta::FromSecondsD(2.3) == + TimeDelta::FromMilliseconds(2300)); + EXPECT_TRUE(TimeDelta::FromMillisecondsD(2.5) == + TimeDelta::FromMicroseconds(2500)); EXPECT_EQ(13, TimeDelta::FromDays(13).InDays()); EXPECT_EQ(13, TimeDelta::FromHours(13).InHours()); EXPECT_EQ(13, TimeDelta::FromMinutes(13).InMinutes()); @@ -674,6 +732,10 @@ TEST(TimeDelta, FromAndIn) { EXPECT_EQ(13.0, TimeDelta::FromSeconds(13).InSecondsF()); EXPECT_EQ(13, TimeDelta::FromMilliseconds(13).InMilliseconds()); EXPECT_EQ(13.0, TimeDelta::FromMilliseconds(13).InMillisecondsF()); + EXPECT_EQ(13, TimeDelta::FromSecondsD(13.1).InSeconds()); + EXPECT_EQ(13.1, TimeDelta::FromSecondsD(13.1).InSecondsF()); + EXPECT_EQ(13, TimeDelta::FromMillisecondsD(13.3).InMilliseconds()); + EXPECT_EQ(13.3, TimeDelta::FromMillisecondsD(13.3).InMillisecondsF()); EXPECT_EQ(13, TimeDelta::FromMicroseconds(13).InMicroseconds()); } diff --git a/chromium/base/time/time_win.cc b/chromium/base/time/time_win.cc index ef8d29a92cb..93f0c25e2c4 100644 --- a/chromium/base/time/time_win.cc +++ b/chromium/base/time/time_win.cc @@ -42,8 +42,8 @@ #include "base/basictypes.h" #include "base/cpu.h" +#include "base/lazy_instance.h" #include "base/logging.h" -#include "base/memory/singleton.h" #include "base/synchronization/lock.h" using base::Time; @@ -358,8 +358,14 @@ bool IsBuggyAthlon(const base::CPU& cpu) { // retrieve and more reliable. class HighResNowSingleton { public: - static HighResNowSingleton* GetInstance() { - return Singleton<HighResNowSingleton>::get(); + HighResNowSingleton() + : ticks_per_second_(0), + skew_(0) { + InitializeClock(); + + base::CPU cpu; + if (IsBuggyAthlon(cpu)) + DisableHighResClock(); } bool IsUsingHighResClock() { @@ -399,16 +405,6 @@ class HighResNowSingleton { } private: - HighResNowSingleton() - : ticks_per_second_(0), - skew_(0) { - InitializeClock(); - - base::CPU cpu; - if (IsBuggyAthlon(cpu)) - DisableHighResClock(); - } - // Synchronize the QPC clock with GetSystemTimeAsFileTime. void InitializeClock() { LARGE_INTEGER ticks_per_sec = {0}; @@ -433,12 +429,17 @@ class HighResNowSingleton { int64 ticks_per_second_; // 0 indicates QPF failed and we're broken. int64 skew_; // Skew between lo-res and hi-res clocks (for debugging). - - friend struct DefaultSingletonTraits<HighResNowSingleton>; }; +static base::LazyInstance<HighResNowSingleton>::Leaky + leaky_high_res_now_singleton = LAZY_INSTANCE_INITIALIZER; + +HighResNowSingleton* GetHighResNowSingleton() { + return leaky_high_res_now_singleton.Pointer(); +} + TimeDelta HighResNowWrapper() { - return HighResNowSingleton::GetInstance()->Now(); + return GetHighResNowSingleton()->Now(); } typedef TimeDelta (*NowFunction)(void); @@ -485,7 +486,7 @@ TimeTicks TimeTicks::Now() { // static TimeTicks TimeTicks::HighResNow() { - return TimeTicks() + HighResNowSingleton::GetInstance()->Now(); + return TimeTicks() + HighResNowWrapper(); } // static @@ -506,18 +507,17 @@ TimeTicks TimeTicks::NowFromSystemTraceTime() { // static int64 TimeTicks::GetQPCDriftMicroseconds() { - return HighResNowSingleton::GetInstance()->GetQPCDriftMicroseconds(); + return GetHighResNowSingleton()->GetQPCDriftMicroseconds(); } // static TimeTicks TimeTicks::FromQPCValue(LONGLONG qpc_value) { - return TimeTicks( - HighResNowSingleton::GetInstance()->QPCValueToMicroseconds(qpc_value)); + return TimeTicks(GetHighResNowSingleton()->QPCValueToMicroseconds(qpc_value)); } // static bool TimeTicks::IsHighResClockWorking() { - return HighResNowSingleton::GetInstance()->IsUsingHighResClock(); + return GetHighResNowSingleton()->IsUsingHighResClock(); } TimeTicks TimeTicks::UnprotectedNow() { @@ -532,6 +532,5 @@ TimeTicks TimeTicks::UnprotectedNow() { // static TimeDelta TimeDelta::FromQPCValue(LONGLONG qpc_value) { - return TimeDelta( - HighResNowSingleton::GetInstance()->QPCValueToMicroseconds(qpc_value)); + return TimeDelta(GetHighResNowSingleton()->QPCValueToMicroseconds(qpc_value)); } diff --git a/chromium/base/time/time_win_unittest.cc b/chromium/base/time/time_win_unittest.cc index 803222febcb..9c7654fde45 100644 --- a/chromium/base/time/time_win_unittest.cc +++ b/chromium/base/time/time_win_unittest.cc @@ -99,6 +99,9 @@ TEST(TimeTicks, WinRollover) { for (int index = 0; index < kThreads; index++) { DWORD rv = WaitForSingleObject(threads[index], INFINITE); EXPECT_EQ(rv, WAIT_OBJECT_0); + // Since using _beginthreadex() (as opposed to _beginthread), + // an explicit CloseHandle() is supposed to be called. + CloseHandle(threads[index]); } CloseHandle(g_rollover_test_start); diff --git a/chromium/base/timer/elapsed_timer.h b/chromium/base/timer/elapsed_timer.h index 3ea2bfadf38..1cb9f1193d8 100644 --- a/chromium/base/timer/elapsed_timer.h +++ b/chromium/base/timer/elapsed_timer.h @@ -15,9 +15,10 @@ namespace base { class BASE_EXPORT ElapsedTimer { public: ElapsedTimer(); + virtual ~ElapsedTimer() {} // Returns the time elapsed since object construction. - TimeDelta Elapsed() const; + virtual TimeDelta Elapsed() const; private: TimeTicks begin_; diff --git a/chromium/base/timer/mock_timer.cc b/chromium/base/timer/mock_timer.cc new file mode 100644 index 00000000000..296071e8e37 --- /dev/null +++ b/chromium/base/timer/mock_timer.cc @@ -0,0 +1,63 @@ +// 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/timer/mock_timer.h" + +namespace base { + +MockTimer::MockTimer(bool retain_user_task, bool is_repeating) + : Timer(retain_user_task, is_repeating), + is_running_(false) { +} + +MockTimer::MockTimer(const tracked_objects::Location& posted_from, + TimeDelta delay, + const base::Closure& user_task, + bool is_repeating) + : Timer(true, is_repeating), + delay_(delay), + is_running_(false) { +} + +MockTimer::~MockTimer() { +} + +bool MockTimer::IsRunning() const { + return is_running_; +} + +base::TimeDelta MockTimer::GetCurrentDelay() const { + return delay_; +} + +void MockTimer::Start(const tracked_objects::Location& posted_from, + TimeDelta delay, + const base::Closure& user_task) { + delay_ = delay; + user_task_ = user_task; + Reset(); +} + +void MockTimer::Stop() { + is_running_ = false; + if (!retain_user_task()) + user_task_.Reset(); +} + +void MockTimer::Reset() { + DCHECK(!user_task_.is_null()); + is_running_ = true; +} + +void MockTimer::Fire() { + DCHECK(is_running_); + base::Closure old_task = user_task_; + if (is_repeating()) + Reset(); + else + Stop(); + old_task.Run(); +} + +} // namespace base diff --git a/chromium/base/timer/mock_timer.h b/chromium/base/timer/mock_timer.h new file mode 100644 index 00000000000..456cb5be90b --- /dev/null +++ b/chromium/base/timer/mock_timer.h @@ -0,0 +1,41 @@ +// 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_TIMER_MOCK_TIMER_H_ +#define BASE_TIMER_MOCK_TIMER_H_ + +#include "base/timer/timer.h" + +namespace base { + +class BASE_EXPORT MockTimer : public Timer { + public: + MockTimer(bool retain_user_task, bool is_repeating); + MockTimer(const tracked_objects::Location& posted_from, + TimeDelta delay, + const base::Closure& user_task, + bool is_repeating); + virtual ~MockTimer(); + + // base::Timer implementation. + virtual bool IsRunning() const OVERRIDE; + virtual base::TimeDelta GetCurrentDelay() const OVERRIDE; + virtual void Start(const tracked_objects::Location& posted_from, + base::TimeDelta delay, + const base::Closure& user_task) OVERRIDE; + virtual void Stop() OVERRIDE; + virtual void Reset() OVERRIDE; + + // Testing methods. + void Fire(); + + private: + base::Closure user_task_; + TimeDelta delay_; + bool is_running_; +}; + +} // namespace base + +#endif // !BASE_TIMER_MOCK_TIMER_H_ diff --git a/chromium/base/timer/mock_timer_unittest.cc b/chromium/base/timer/mock_timer_unittest.cc new file mode 100644 index 00000000000..f6b6953648a --- /dev/null +++ b/chromium/base/timer/mock_timer_unittest.cc @@ -0,0 +1,82 @@ +// 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/timer/mock_timer.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +void CallMeMaybe(int *number) { + (*number)++; +} + +TEST(MockTimerTest, FiresOnce) { + int calls = 0; + base::MockTimer timer(false, false); + base::TimeDelta delay = base::TimeDelta::FromSeconds(2); + timer.Start(FROM_HERE, delay, + base::Bind(&CallMeMaybe, + base::Unretained(&calls))); + EXPECT_EQ(delay, timer.GetCurrentDelay()); + EXPECT_TRUE(timer.IsRunning()); + timer.Fire(); + EXPECT_FALSE(timer.IsRunning()); + EXPECT_EQ(1, calls); +} + +TEST(MockTimerTest, FiresRepeatedly) { + int calls = 0; + base::MockTimer timer(true, true); + base::TimeDelta delay = base::TimeDelta::FromSeconds(2); + timer.Start(FROM_HERE, delay, + base::Bind(&CallMeMaybe, + base::Unretained(&calls))); + timer.Fire(); + EXPECT_TRUE(timer.IsRunning()); + timer.Fire(); + timer.Fire(); + EXPECT_TRUE(timer.IsRunning()); + EXPECT_EQ(3, calls); +} + +TEST(MockTimerTest, Stops) { + int calls = 0; + base::MockTimer timer(true, true); + base::TimeDelta delay = base::TimeDelta::FromSeconds(2); + timer.Start(FROM_HERE, delay, + base::Bind(&CallMeMaybe, + base::Unretained(&calls))); + EXPECT_TRUE(timer.IsRunning()); + timer.Stop(); + EXPECT_FALSE(timer.IsRunning()); +} + +class HasWeakPtr : public base::SupportsWeakPtr<HasWeakPtr> { + public: + HasWeakPtr() {} + virtual ~HasWeakPtr() {} + + private: + DISALLOW_COPY_AND_ASSIGN(HasWeakPtr); +}; + +void DoNothingWithWeakPtr(HasWeakPtr* has_weak_ptr) { +} + +TEST(MockTimerTest, DoesNotRetainClosure) { + HasWeakPtr *has_weak_ptr = new HasWeakPtr(); + base::WeakPtr<HasWeakPtr> weak_ptr(has_weak_ptr->AsWeakPtr()); + base::MockTimer timer(false, false); + base::TimeDelta delay = base::TimeDelta::FromSeconds(2); + ASSERT_TRUE(weak_ptr.get()); + timer.Start(FROM_HERE, delay, + base::Bind(&DoNothingWithWeakPtr, + base::Owned(has_weak_ptr))); + ASSERT_TRUE(weak_ptr.get()); + timer.Fire(); + ASSERT_FALSE(weak_ptr.get()); +} + +} // namespace diff --git a/chromium/base/timer/timer.cc b/chromium/base/timer/timer.cc index 1c4b10c6de5..1916ccc34eb 100644 --- a/chromium/base/timer/timer.cc +++ b/chromium/base/timer/timer.cc @@ -85,6 +85,14 @@ Timer::~Timer() { StopAndAbandon(); } +bool Timer::IsRunning() const { + return is_running_; +} + +TimeDelta Timer::GetCurrentDelay() const { + return delay_; +} + void Timer::Start(const tracked_objects::Location& posted_from, TimeDelta delay, const base::Closure& user_task) { diff --git a/chromium/base/timer/timer.h b/chromium/base/timer/timer.h index 71a7ae8888a..6d282ee8e7a 100644 --- a/chromium/base/timer/timer.h +++ b/chromium/base/timer/timer.h @@ -82,28 +82,24 @@ class BASE_EXPORT Timer { virtual ~Timer(); // Returns true if the timer is running (i.e., not stopped). - bool IsRunning() const { - return is_running_; - } + virtual bool IsRunning() const; // Returns the current delay for this timer. - TimeDelta GetCurrentDelay() const { - return delay_; - } + virtual TimeDelta GetCurrentDelay() const; // Start the timer to run at the given |delay| from now. If the timer is // already running, it will be replaced to call the given |user_task|. - void Start(const tracked_objects::Location& posted_from, - TimeDelta delay, - const base::Closure& user_task); + virtual void Start(const tracked_objects::Location& posted_from, + TimeDelta delay, + const base::Closure& user_task); // Call this method to stop and cancel the timer. It is a no-op if the timer // is not running. - void Stop(); + virtual void Stop(); // Call this method to reset the timer delay. The user_task_ must be set. If // the timer is not running, this will start it by posting a task. - void Reset(); + virtual void Reset(); const base::Closure& user_task() const { return user_task_; } const TimeTicks& desired_run_time() const { return desired_run_time_; } @@ -115,6 +111,9 @@ class BASE_EXPORT Timer { TimeDelta delay, const base::Closure& user_task); + bool retain_user_task() const { return retain_user_task_; } + bool is_repeating() const { return is_repeating_; } + private: friend class BaseTimerTaskInternal; @@ -197,10 +196,10 @@ class BaseTimerMethodPointer : public Timer { // Start the timer to run at the given |delay| from now. If the timer is // already running, it will be replaced to call a task formed from // |reviewer->*method|. - void Start(const tracked_objects::Location& posted_from, - TimeDelta delay, - Receiver* receiver, - ReceiverMethod method) { + virtual void Start(const tracked_objects::Location& posted_from, + TimeDelta delay, + Receiver* receiver, + ReceiverMethod method) { Timer::Start(posted_from, delay, base::Bind(method, base::Unretained(receiver))); } diff --git a/chromium/base/timer/timer_unittest.cc b/chromium/base/timer/timer_unittest.cc index 728e861b537..0fb2b454ad7 100644 --- a/chromium/base/timer/timer_unittest.cc +++ b/chromium/base/timer/timer_unittest.cc @@ -375,7 +375,7 @@ TEST(TimerTest, MessageLoopShutdown) { OneShotTimerTester c(&did_run); OneShotTimerTester d(&did_run); { - base::MessageLoop loop(base::MessageLoop::TYPE_DEFAULT); + base::MessageLoop loop; a.Start(); b.Start(); } // MessageLoop destructs by falling out of scope. @@ -389,7 +389,7 @@ void TimerTestCallback() { TEST(TimerTest, NonRepeatIsRunning) { { - base::MessageLoop loop(base::MessageLoop::TYPE_DEFAULT); + base::MessageLoop loop; base::Timer timer(false, false); EXPECT_FALSE(timer.IsRunning()); timer.Start(FROM_HERE, TimeDelta::FromDays(1), @@ -402,7 +402,7 @@ TEST(TimerTest, NonRepeatIsRunning) { { base::Timer timer(true, false); - base::MessageLoop loop(base::MessageLoop::TYPE_DEFAULT); + base::MessageLoop loop; EXPECT_FALSE(timer.IsRunning()); timer.Start(FROM_HERE, TimeDelta::FromDays(1), base::Bind(&TimerTestCallback)); @@ -418,7 +418,7 @@ TEST(TimerTest, NonRepeatIsRunning) { TEST(TimerTest, NonRepeatMessageLoopDeath) { base::Timer timer(false, false); { - base::MessageLoop loop(base::MessageLoop::TYPE_DEFAULT); + base::MessageLoop loop; EXPECT_FALSE(timer.IsRunning()); timer.Start(FROM_HERE, TimeDelta::FromDays(1), base::Bind(&TimerTestCallback)); @@ -429,7 +429,7 @@ TEST(TimerTest, NonRepeatMessageLoopDeath) { } TEST(TimerTest, RetainRepeatIsRunning) { - base::MessageLoop loop(base::MessageLoop::TYPE_DEFAULT); + base::MessageLoop loop; base::Timer timer(FROM_HERE, TimeDelta::FromDays(1), base::Bind(&TimerTestCallback), true); EXPECT_FALSE(timer.IsRunning()); @@ -442,7 +442,7 @@ TEST(TimerTest, RetainRepeatIsRunning) { } TEST(TimerTest, RetainNonRepeatIsRunning) { - base::MessageLoop loop(base::MessageLoop::TYPE_DEFAULT); + base::MessageLoop loop; base::Timer timer(FROM_HERE, TimeDelta::FromDays(1), base::Bind(&TimerTestCallback), false); EXPECT_FALSE(timer.IsRunning()); @@ -477,7 +477,7 @@ void SetCallbackHappened2() { TEST(TimerTest, ContinuationStopStart) { { ClearAllCallbackHappened(); - base::MessageLoop loop(base::MessageLoop::TYPE_DEFAULT); + base::MessageLoop loop; base::Timer timer(false, false); timer.Start(FROM_HERE, TimeDelta::FromMilliseconds(10), base::Bind(&SetCallbackHappened1)); @@ -493,7 +493,7 @@ TEST(TimerTest, ContinuationStopStart) { TEST(TimerTest, ContinuationReset) { { ClearAllCallbackHappened(); - base::MessageLoop loop(base::MessageLoop::TYPE_DEFAULT); + base::MessageLoop loop; base::Timer timer(false, false); timer.Start(FROM_HERE, TimeDelta::FromMilliseconds(10), base::Bind(&SetCallbackHappened1)); diff --git a/chromium/base/tools_sanity_unittest.cc b/chromium/base/tools_sanity_unittest.cc index ff789380122..7d4c96eb5f7 100644 --- a/chromium/base/tools_sanity_unittest.cc +++ b/chromium/base/tools_sanity_unittest.cc @@ -7,6 +7,8 @@ // errors to verify the sanity of the tools. #include "base/atomicops.h" +#include "base/debug/asan_invalid_access.h" +#include "base/debug/profiler.h" #include "base/message_loop/message_loop.h" #include "base/third_party/dynamic_annotations/dynamic_annotations.h" #include "base/threading/thread.h" @@ -20,29 +22,45 @@ const base::subtle::Atomic32 kMagicValue = 42; // Helper for memory accesses that can potentially corrupt memory or cause a // crash during a native run. -#if defined(ADDRESS_SANITIZER) +#if defined(ADDRESS_SANITIZER) || defined(SYZYASAN) #if defined(OS_IOS) // EXPECT_DEATH is not supported on IOS. #define HARMFUL_ACCESS(action,error_regexp) do { action; } while (0) +#elif defined(SYZYASAN) +// We won't get a meaningful error message because we're not running under the +// SyzyASan logger, but we can at least make sure that the error has been +// generated in the SyzyASan runtime. +#define HARMFUL_ACCESS(action,unused) \ +if (debug::IsBinaryInstrumented()) { EXPECT_DEATH(action, \ + "AsanRuntime::OnError"); } #else #define HARMFUL_ACCESS(action,error_regexp) EXPECT_DEATH(action,error_regexp) -#endif // !OS_IOS +#endif // !OS_IOS && !SYZYASAN #else #define HARMFUL_ACCESS(action,error_regexp) \ do { if (RunningOnValgrind()) { action; } } while (0) #endif -void ReadUninitializedValue(char *ptr) { +void DoReadUninitializedValue(char *ptr) { // Comparison with 64 is to prevent clang from optimizing away the // jump -- valgrind only catches jumps and conditional moves, but clang uses // the borrow flag if the condition is just `*ptr == '\0'`. if (*ptr == 64) { - (*ptr)++; + VLOG(1) << "Uninit condition is true"; } else { - (*ptr)--; + VLOG(1) << "Uninit condition is false"; } } +void ReadUninitializedValue(char *ptr) { +#if defined(MEMORY_SANITIZER) + EXPECT_DEATH(DoReadUninitializedValue(ptr), + "use-of-uninitialized-value"); +#else + DoReadUninitializedValue(ptr); +#endif +} + void ReadValueOutOfArrayBoundsLeft(char *ptr) { char c = ptr[-2]; VLOG(1) << "Reading a byte out of bounds: " << c; @@ -65,14 +83,15 @@ void WriteValueOutOfArrayBoundsRight(char *ptr, size_t size) { void MakeSomeErrors(char *ptr, size_t size) { ReadUninitializedValue(ptr); + HARMFUL_ACCESS(ReadValueOutOfArrayBoundsLeft(ptr), - "heap-buffer-overflow.*2 bytes to the left"); + "2 bytes to the left"); HARMFUL_ACCESS(ReadValueOutOfArrayBoundsRight(ptr, size), - "heap-buffer-overflow.*1 bytes to the right"); + "1 bytes to the right"); HARMFUL_ACCESS(WriteValueOutOfArrayBoundsLeft(ptr), - "heap-buffer-overflow.*1 bytes to the left"); + "1 bytes to the left"); HARMFUL_ACCESS(WriteValueOutOfArrayBoundsRight(ptr, size), - "heap-buffer-overflow.*0 bytes to the right"); + "0 bytes to the right"); } } // namespace @@ -84,10 +103,10 @@ TEST(ToolsSanityTest, MemoryLeak) { leak[4] = 1; // Make sure the allocated memory is used. } -#if defined(ADDRESS_SANITIZER) && (defined(OS_IOS) || defined(OS_WIN)) +#if (defined(ADDRESS_SANITIZER) && defined(OS_IOS)) || defined(SYZYASAN) // Because iOS doesn't support death tests, each of the following tests will -// crash the whole program under Asan. On Windows Asan is based on SyzyAsan, the -// error report mecanism is different than with Asan so those test will fail. +// crash the whole program under Asan. On Windows Asan is based on SyzyAsan; the +// error report mechanism is different than with Asan so these tests will fail. #define MAYBE_AccessesToNewMemory DISABLED_AccessesToNewMemory #define MAYBE_AccessesToMallocMemory DISABLED_AccessesToMallocMemory #else @@ -103,7 +122,7 @@ TEST(ToolsSanityTest, MemoryLeak) { // tests should be put back under the (defined(OS_IOS) || defined(OS_WIN)) // clause above. // See also http://crbug.com/172614. -#if defined(ADDRESS_SANITIZER) +#if defined(ADDRESS_SANITIZER) || defined(SYZYASAN) #define MAYBE_SingleElementDeletedWithBraces \ DISABLED_SingleElementDeletedWithBraces #define MAYBE_ArrayDeletedWithoutBraces DISABLED_ArrayDeletedWithoutBraces @@ -125,7 +144,7 @@ TEST(ToolsSanityTest, MAYBE_AccessesToMallocMemory) { } TEST(ToolsSanityTest, MAYBE_ArrayDeletedWithoutBraces) { -#if !defined(ADDRESS_SANITIZER) +#if !defined(ADDRESS_SANITIZER) && !defined(SYZYASAN) // This test may corrupt memory if not run under Valgrind or compiled with // AddressSanitizer. if (!RunningOnValgrind()) @@ -151,7 +170,8 @@ TEST(ToolsSanityTest, MAYBE_SingleElementDeletedWithBraces) { delete [] foo; } -#if defined(ADDRESS_SANITIZER) +#if defined(ADDRESS_SANITIZER) || defined(SYZYASAN) + TEST(ToolsSanityTest, DISABLED_AddressSanitizerNullDerefCrashTest) { // Intentionally crash to make sure AddressSanitizer is running. // This test should not be ran on bots. @@ -183,7 +203,31 @@ TEST(ToolsSanityTest, DISABLED_AddressSanitizerGlobalOOBCrashTest) { *access = 43; } -#endif +TEST(ToolsSanityTest, AsanHeapOverflow) { + HARMFUL_ACCESS(debug::AsanHeapOverflow() ,"to the right"); +} + +TEST(ToolsSanityTest, AsanHeapUnderflow) { + HARMFUL_ACCESS(debug::AsanHeapUnderflow(), "to the left"); +} + +TEST(ToolsSanityTest, AsanHeapUseAfterFree) { + HARMFUL_ACCESS(debug::AsanHeapUseAfterFree(), "heap-use-after-free"); +} + +#if defined(SYZYASAN) +TEST(ToolsSanityTest, AsanCorruptHeapBlock) { + HARMFUL_ACCESS(debug::AsanCorruptHeapBlock(), ""); +} + +TEST(ToolsSanityTest, AsanCorruptHeap) { + // This test will kill the process by raising an exception, there's no + // particular string to look for in the stack trace. + EXPECT_DEATH(debug::AsanCorruptHeap(), ""); +} +#endif // SYZYASAN + +#endif // ADDRESS_SANITIZER || SYZYASAN namespace { diff --git a/chromium/base/tracked_objects.cc b/chromium/base/tracked_objects.cc index 0db7094c498..56b44c10b2c 100644 --- a/chromium/base/tracked_objects.cc +++ b/chromium/base/tracked_objects.cc @@ -793,11 +793,14 @@ void ThreadData::EnsureCleanupWasCalled(int major_threads_shutdown_count) { base::AutoLock lock(*list_lock_.Pointer()); if (worker_thread_data_creation_count_ == 0) return; // We haven't really run much, and couldn't have leaked. + + // TODO(jar): until this is working on XP, don't run the real test. +#if 0 // Verify that we've at least shutdown/cleanup the major namesd threads. The // caller should tell us how many thread shutdowns should have taken place by // now. - return; // TODO(jar): until this is working on XP, don't run the real test. CHECK_GT(cleanup_count_, major_threads_shutdown_count); +#endif } // static diff --git a/chromium/base/tracked_objects.h b/chromium/base/tracked_objects.h index 31ce4f5fb93..511a2890661 100644 --- a/chromium/base/tracked_objects.h +++ b/chromium/base/tracked_objects.h @@ -362,6 +362,7 @@ class BASE_EXPORT ThreadData { 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 }; typedef std::map<Location, Births*> BirthMap; diff --git a/chromium/base/values.cc b/chromium/base/values.cc index 6068556bb3d..f27c09567ee 100644 --- a/chromium/base/values.cc +++ b/chromium/base/values.cc @@ -134,6 +134,10 @@ bool Value::GetAsString(string16* out_value) const { return false; } +bool Value::GetAsString(const StringValue** out_value) const { + return false; +} + bool Value::GetAsList(ListValue** out_value) { return false; } @@ -225,13 +229,13 @@ bool FundamentalValue::GetAsDouble(double* out_value) const { FundamentalValue* FundamentalValue::DeepCopy() const { switch (GetType()) { case TYPE_BOOLEAN: - return CreateBooleanValue(boolean_value_); + return new FundamentalValue(boolean_value_); case TYPE_INTEGER: - return CreateIntegerValue(integer_value_); + return new FundamentalValue(integer_value_); case TYPE_DOUBLE: - return CreateDoubleValue(double_value_); + return new FundamentalValue(double_value_); default: NOTREACHED(); @@ -278,6 +282,14 @@ StringValue::StringValue(const string16& in_value) StringValue::~StringValue() { } +std::string* StringValue::GetString() { + return &value_; +} + +const std::string& StringValue::GetString() const { + return value_; +} + bool StringValue::GetAsString(std::string* out_value) const { if (out_value) *out_value = value_; @@ -290,8 +302,14 @@ bool StringValue::GetAsString(string16* out_value) const { return true; } +bool StringValue::GetAsString(const StringValue** out_value) const { + if (out_value) + *out_value = this; + return true; +} + StringValue* StringValue::DeepCopy() const { - return CreateStringValue(value_); + return new StringValue(value_); } bool StringValue::Equals(const Value* other) const { @@ -403,25 +421,25 @@ void DictionaryValue::Set(const std::string& path, Value* in_value) { } void DictionaryValue::SetBoolean(const std::string& path, bool in_value) { - Set(path, CreateBooleanValue(in_value)); + Set(path, new FundamentalValue(in_value)); } void DictionaryValue::SetInteger(const std::string& path, int in_value) { - Set(path, CreateIntegerValue(in_value)); + Set(path, new FundamentalValue(in_value)); } void DictionaryValue::SetDouble(const std::string& path, double in_value) { - Set(path, CreateDoubleValue(in_value)); + Set(path, new FundamentalValue(in_value)); } void DictionaryValue::SetString(const std::string& path, const std::string& in_value) { - Set(path, CreateStringValue(in_value)); + Set(path, new StringValue(in_value)); } void DictionaryValue::SetString(const std::string& path, const string16& in_value) { - Set(path, CreateStringValue(in_value)); + Set(path, new StringValue(in_value)); } void DictionaryValue::SetWithoutPathExpansion(const std::string& key, @@ -439,27 +457,27 @@ void DictionaryValue::SetWithoutPathExpansion(const std::string& key, void DictionaryValue::SetBooleanWithoutPathExpansion( const std::string& path, bool in_value) { - SetWithoutPathExpansion(path, CreateBooleanValue(in_value)); + SetWithoutPathExpansion(path, new FundamentalValue(in_value)); } void DictionaryValue::SetIntegerWithoutPathExpansion( const std::string& path, int in_value) { - SetWithoutPathExpansion(path, CreateIntegerValue(in_value)); + SetWithoutPathExpansion(path, new FundamentalValue(in_value)); } void DictionaryValue::SetDoubleWithoutPathExpansion( const std::string& path, double in_value) { - SetWithoutPathExpansion(path, CreateDoubleValue(in_value)); + SetWithoutPathExpansion(path, new FundamentalValue(in_value)); } void DictionaryValue::SetStringWithoutPathExpansion( const std::string& path, const std::string& in_value) { - SetWithoutPathExpansion(path, CreateStringValue(in_value)); + SetWithoutPathExpansion(path, new StringValue(in_value)); } void DictionaryValue::SetStringWithoutPathExpansion( const std::string& path, const string16& in_value) { - SetWithoutPathExpansion(path, CreateStringValue(in_value)); + SetWithoutPathExpansion(path, new StringValue(in_value)); } bool DictionaryValue::Get(const std::string& path, @@ -1025,23 +1043,23 @@ void ListValue::Append(Value* in_value) { } void ListValue::AppendBoolean(bool in_value) { - Append(CreateBooleanValue(in_value)); + Append(new FundamentalValue(in_value)); } void ListValue::AppendInteger(int in_value) { - Append(CreateIntegerValue(in_value)); + Append(new FundamentalValue(in_value)); } void ListValue::AppendDouble(double in_value) { - Append(CreateDoubleValue(in_value)); + Append(new FundamentalValue(in_value)); } void ListValue::AppendString(const std::string& in_value) { - Append(CreateStringValue(in_value)); + Append(new StringValue(in_value)); } void ListValue::AppendString(const string16& in_value) { - Append(CreateStringValue(in_value)); + Append(new StringValue(in_value)); } void ListValue::AppendStrings(const std::vector<std::string>& in_values) { diff --git a/chromium/base/values.h b/chromium/base/values.h index 65121808e64..c478e3ac79e 100644 --- a/chromium/base/values.h +++ b/chromium/base/values.h @@ -31,11 +31,6 @@ #include "base/memory/scoped_ptr.h" #include "base/strings/string16.h" -// This file declares "using base::Value", etc. at the bottom, so that -// current code can use these classes without the base namespace. In -// new code, please always use base::Value, etc. or add your own -// "using" declaration. -// http://crbug.com/88666 namespace base { class DictionaryValue; @@ -96,6 +91,7 @@ class BASE_EXPORT Value { virtual bool GetAsDouble(double* out_value) const; 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 GetAsList(ListValue** out_value); virtual bool GetAsList(const ListValue** out_value) const; virtual bool GetAsDictionary(DictionaryValue** out_value); @@ -137,6 +133,8 @@ class BASE_EXPORT FundamentalValue : public Value { // Overridden from Value: virtual bool GetAsBoolean(bool* out_value) const OVERRIDE; virtual bool GetAsInteger(int* out_value) const OVERRIDE; + // Values of both type TYPE_INTEGER and TYPE_DOUBLE can be obtained as + // doubles. virtual bool GetAsDouble(double* out_value) const OVERRIDE; virtual FundamentalValue* DeepCopy() const OVERRIDE; virtual bool Equals(const Value* other) const OVERRIDE; @@ -159,9 +157,14 @@ class BASE_EXPORT StringValue : public Value { virtual ~StringValue(); + // Returns |value_| as a pointer or reference. + std::string* GetString(); + const std::string& GetString() const; + // Overridden from Value: virtual bool GetAsString(std::string* out_value) const OVERRIDE; virtual bool GetAsString(string16* out_value) const OVERRIDE; + virtual bool GetAsString(const StringValue** out_value) const OVERRIDE; virtual StringValue* DeepCopy() const OVERRIDE; virtual bool Equals(const Value* other) const OVERRIDE; @@ -266,14 +269,18 @@ class BASE_EXPORT DictionaryValue : public Value { // through the |out_value| parameter, and the function will return true. // Otherwise, it will return false and |out_value| will be untouched. // Note that the dictionary always owns the value that's returned. + // |out_value| is optional and will only be set if non-NULL. bool Get(const std::string& path, const Value** out_value) const; bool Get(const std::string& path, Value** out_value); // These are convenience forms of Get(). The value will be retrieved // and the return value will be true if the path is valid and the value at // the end of the path can be returned in the form specified. + // |out_value| is optional and will only be set if non-NULL. bool GetBoolean(const std::string& path, bool* out_value) const; bool GetInteger(const std::string& path, int* out_value) const; + // Values of both type TYPE_INTEGER and TYPE_DOUBLE can be obtained as + // doubles. bool GetDouble(const std::string& path, double* out_value) const; bool GetString(const std::string& path, std::string* out_value) const; bool GetString(const std::string& path, string16* out_value) const; @@ -399,14 +406,18 @@ class BASE_EXPORT ListValue : public Value { // Gets the Value at the given index. Modifies |out_value| (and returns true) // only if the index falls within the current list range. // Note that the list always owns the Value passed out via |out_value|. + // |out_value| is optional and will only be set if non-NULL. bool Get(size_t index, const Value** out_value) const; bool Get(size_t index, Value** out_value); // Convenience forms of Get(). Modifies |out_value| (and returns true) // only if the index is valid and the Value at that index can be returned // in the specified form. + // |out_value| is optional and will only be set if non-NULL. bool GetBoolean(size_t index, bool* out_value) const; bool GetInteger(size_t index, int* out_value) const; + // Values of both type TYPE_INTEGER and TYPE_DOUBLE can be obtained as + // doubles. bool GetDouble(size_t index, double* out_value) const; bool GetString(size_t index, std::string* out_value) const; bool GetString(size_t index, string16* out_value) const; @@ -528,10 +539,4 @@ BASE_EXPORT inline std::ostream& operator<<(std::ostream& out, } // namespace base -// http://crbug.com/88666 -using base::DictionaryValue; -using base::ListValue; -using base::StringValue; -using base::Value; - #endif // BASE_VALUES_H_ diff --git a/chromium/base/values_unittest.cc b/chromium/base/values_unittest.cc index 70acdfd6966..c8a6cbf9a77 100644 --- a/chromium/base/values_unittest.cc +++ b/chromium/base/values_unittest.cc @@ -130,7 +130,7 @@ TEST(ValuesTest, BinaryValue) { } TEST(ValuesTest, StringValue) { - // Test overloaded CreateStringValue. + // Test overloaded StringValue constructor. scoped_ptr<Value> narrow_value(new StringValue("narrow")); ASSERT_TRUE(narrow_value.get()); ASSERT_TRUE(narrow_value->IsType(Value::TYPE_STRING)); @@ -138,18 +138,29 @@ TEST(ValuesTest, StringValue) { ASSERT_TRUE(utf16_value.get()); ASSERT_TRUE(utf16_value->IsType(Value::TYPE_STRING)); - // Test overloaded GetString. + // Test overloaded GetAsString. std::string narrow = "http://google.com"; string16 utf16 = ASCIIToUTF16("http://google.com"); + const StringValue* string_value = NULL; ASSERT_TRUE(narrow_value->GetAsString(&narrow)); ASSERT_TRUE(narrow_value->GetAsString(&utf16)); + ASSERT_TRUE(narrow_value->GetAsString(&string_value)); ASSERT_EQ(std::string("narrow"), narrow); ASSERT_EQ(ASCIIToUTF16("narrow"), utf16); + ASSERT_EQ(string_value->GetString(), narrow); ASSERT_TRUE(utf16_value->GetAsString(&narrow)); ASSERT_TRUE(utf16_value->GetAsString(&utf16)); + ASSERT_TRUE(utf16_value->GetAsString(&string_value)); ASSERT_EQ(std::string("utf16"), narrow); ASSERT_EQ(ASCIIToUTF16("utf16"), utf16); + ASSERT_EQ(string_value->GetString(), narrow); + + // Don't choke on NULL values. + ASSERT_TRUE(narrow_value->GetAsString(static_cast<string16*>(NULL))); + ASSERT_TRUE(narrow_value->GetAsString(static_cast<std::string*>(NULL))); + ASSERT_TRUE(narrow_value->GetAsString( + static_cast<const StringValue**>(NULL))); } // This is a Value object that allows us to tell if it's been @@ -325,8 +336,8 @@ TEST(ValuesTest, DictionaryWithoutPathExpansion) { TEST(ValuesTest, DictionaryRemovePath) { DictionaryValue dict; - dict.Set("a.long.way.down", Value::CreateIntegerValue(1)); - dict.Set("a.long.key.path", Value::CreateBooleanValue(true)); + dict.Set("a.long.way.down", new FundamentalValue(1)); + dict.Set("a.long.key.path", new FundamentalValue(true)); scoped_ptr<Value> removed_item; EXPECT_TRUE(dict.RemovePath("a.long.way.down", &removed_item)); @@ -798,4 +809,289 @@ TEST(ValuesTest, DictionaryIterator) { EXPECT_TRUE(seen2); } +// DictionaryValue/ListValue's Get*() methods should accept NULL as an out-value +// and still return true/false based on success. +TEST(ValuesTest, GetWithNullOutValue) { + DictionaryValue main_dict; + ListValue main_list; + + FundamentalValue bool_value(false); + FundamentalValue int_value(1234); + FundamentalValue double_value(12.34567); + StringValue string_value("foo"); + BinaryValue binary_value; + 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()); + + EXPECT_TRUE(main_dict.Get("bool", NULL)); + EXPECT_TRUE(main_dict.Get("int", NULL)); + EXPECT_TRUE(main_dict.Get("double", NULL)); + EXPECT_TRUE(main_dict.Get("string", NULL)); + EXPECT_TRUE(main_dict.Get("binary", NULL)); + EXPECT_TRUE(main_dict.Get("dict", NULL)); + EXPECT_TRUE(main_dict.Get("list", NULL)); + EXPECT_FALSE(main_dict.Get("DNE", NULL)); + + EXPECT_TRUE(main_dict.GetBoolean("bool", NULL)); + EXPECT_FALSE(main_dict.GetBoolean("int", NULL)); + EXPECT_FALSE(main_dict.GetBoolean("double", NULL)); + EXPECT_FALSE(main_dict.GetBoolean("string", NULL)); + EXPECT_FALSE(main_dict.GetBoolean("binary", NULL)); + EXPECT_FALSE(main_dict.GetBoolean("dict", NULL)); + EXPECT_FALSE(main_dict.GetBoolean("list", NULL)); + EXPECT_FALSE(main_dict.GetBoolean("DNE", NULL)); + + EXPECT_FALSE(main_dict.GetInteger("bool", NULL)); + EXPECT_TRUE(main_dict.GetInteger("int", NULL)); + EXPECT_FALSE(main_dict.GetInteger("double", NULL)); + EXPECT_FALSE(main_dict.GetInteger("string", NULL)); + EXPECT_FALSE(main_dict.GetInteger("binary", NULL)); + EXPECT_FALSE(main_dict.GetInteger("dict", NULL)); + EXPECT_FALSE(main_dict.GetInteger("list", NULL)); + EXPECT_FALSE(main_dict.GetInteger("DNE", NULL)); + + // Both int and double values can be obtained from GetDouble. + EXPECT_FALSE(main_dict.GetDouble("bool", NULL)); + EXPECT_TRUE(main_dict.GetDouble("int", NULL)); + EXPECT_TRUE(main_dict.GetDouble("double", NULL)); + EXPECT_FALSE(main_dict.GetDouble("string", NULL)); + EXPECT_FALSE(main_dict.GetDouble("binary", NULL)); + EXPECT_FALSE(main_dict.GetDouble("dict", NULL)); + EXPECT_FALSE(main_dict.GetDouble("list", NULL)); + EXPECT_FALSE(main_dict.GetDouble("DNE", NULL)); + + EXPECT_FALSE(main_dict.GetString("bool", static_cast<std::string*>(NULL))); + EXPECT_FALSE(main_dict.GetString("int", static_cast<std::string*>(NULL))); + EXPECT_FALSE(main_dict.GetString("double", static_cast<std::string*>(NULL))); + EXPECT_TRUE(main_dict.GetString("string", static_cast<std::string*>(NULL))); + EXPECT_FALSE(main_dict.GetString("binary", static_cast<std::string*>(NULL))); + EXPECT_FALSE(main_dict.GetString("dict", static_cast<std::string*>(NULL))); + EXPECT_FALSE(main_dict.GetString("list", static_cast<std::string*>(NULL))); + EXPECT_FALSE(main_dict.GetString("DNE", static_cast<std::string*>(NULL))); + + EXPECT_FALSE(main_dict.GetString("bool", static_cast<string16*>(NULL))); + EXPECT_FALSE(main_dict.GetString("int", static_cast<string16*>(NULL))); + EXPECT_FALSE(main_dict.GetString("double", static_cast<string16*>(NULL))); + EXPECT_TRUE(main_dict.GetString("string", static_cast<string16*>(NULL))); + EXPECT_FALSE(main_dict.GetString("binary", static_cast<string16*>(NULL))); + EXPECT_FALSE(main_dict.GetString("dict", static_cast<string16*>(NULL))); + EXPECT_FALSE(main_dict.GetString("list", static_cast<string16*>(NULL))); + EXPECT_FALSE(main_dict.GetString("DNE", static_cast<string16*>(NULL))); + + EXPECT_FALSE(main_dict.GetBinary("bool", NULL)); + EXPECT_FALSE(main_dict.GetBinary("int", NULL)); + EXPECT_FALSE(main_dict.GetBinary("double", NULL)); + EXPECT_FALSE(main_dict.GetBinary("string", NULL)); + EXPECT_TRUE(main_dict.GetBinary("binary", NULL)); + EXPECT_FALSE(main_dict.GetBinary("dict", NULL)); + EXPECT_FALSE(main_dict.GetBinary("list", NULL)); + EXPECT_FALSE(main_dict.GetBinary("DNE", NULL)); + + EXPECT_FALSE(main_dict.GetDictionary("bool", NULL)); + EXPECT_FALSE(main_dict.GetDictionary("int", NULL)); + EXPECT_FALSE(main_dict.GetDictionary("double", NULL)); + EXPECT_FALSE(main_dict.GetDictionary("string", NULL)); + EXPECT_FALSE(main_dict.GetDictionary("binary", NULL)); + EXPECT_TRUE(main_dict.GetDictionary("dict", NULL)); + EXPECT_FALSE(main_dict.GetDictionary("list", NULL)); + EXPECT_FALSE(main_dict.GetDictionary("DNE", NULL)); + + EXPECT_FALSE(main_dict.GetList("bool", NULL)); + EXPECT_FALSE(main_dict.GetList("int", NULL)); + EXPECT_FALSE(main_dict.GetList("double", NULL)); + EXPECT_FALSE(main_dict.GetList("string", NULL)); + EXPECT_FALSE(main_dict.GetList("binary", NULL)); + EXPECT_FALSE(main_dict.GetList("dict", NULL)); + EXPECT_TRUE(main_dict.GetList("list", NULL)); + EXPECT_FALSE(main_dict.GetList("DNE", NULL)); + + EXPECT_TRUE(main_dict.GetWithoutPathExpansion("bool", NULL)); + EXPECT_TRUE(main_dict.GetWithoutPathExpansion("int", NULL)); + EXPECT_TRUE(main_dict.GetWithoutPathExpansion("double", NULL)); + EXPECT_TRUE(main_dict.GetWithoutPathExpansion("string", NULL)); + EXPECT_TRUE(main_dict.GetWithoutPathExpansion("binary", NULL)); + EXPECT_TRUE(main_dict.GetWithoutPathExpansion("dict", NULL)); + EXPECT_TRUE(main_dict.GetWithoutPathExpansion("list", NULL)); + EXPECT_FALSE(main_dict.GetWithoutPathExpansion("DNE", NULL)); + + EXPECT_TRUE(main_dict.GetBooleanWithoutPathExpansion("bool", NULL)); + EXPECT_FALSE(main_dict.GetBooleanWithoutPathExpansion("int", NULL)); + EXPECT_FALSE(main_dict.GetBooleanWithoutPathExpansion("double", NULL)); + EXPECT_FALSE(main_dict.GetBooleanWithoutPathExpansion("string", NULL)); + EXPECT_FALSE(main_dict.GetBooleanWithoutPathExpansion("binary", NULL)); + EXPECT_FALSE(main_dict.GetBooleanWithoutPathExpansion("dict", NULL)); + EXPECT_FALSE(main_dict.GetBooleanWithoutPathExpansion("list", NULL)); + EXPECT_FALSE(main_dict.GetBooleanWithoutPathExpansion("DNE", NULL)); + + EXPECT_FALSE(main_dict.GetIntegerWithoutPathExpansion("bool", NULL)); + EXPECT_TRUE(main_dict.GetIntegerWithoutPathExpansion("int", NULL)); + EXPECT_FALSE(main_dict.GetIntegerWithoutPathExpansion("double", NULL)); + EXPECT_FALSE(main_dict.GetIntegerWithoutPathExpansion("string", NULL)); + EXPECT_FALSE(main_dict.GetIntegerWithoutPathExpansion("binary", NULL)); + EXPECT_FALSE(main_dict.GetIntegerWithoutPathExpansion("dict", NULL)); + EXPECT_FALSE(main_dict.GetIntegerWithoutPathExpansion("list", NULL)); + EXPECT_FALSE(main_dict.GetIntegerWithoutPathExpansion("DNE", NULL)); + + EXPECT_FALSE(main_dict.GetDoubleWithoutPathExpansion("bool", NULL)); + EXPECT_TRUE(main_dict.GetDoubleWithoutPathExpansion("int", NULL)); + EXPECT_TRUE(main_dict.GetDoubleWithoutPathExpansion("double", NULL)); + EXPECT_FALSE(main_dict.GetDoubleWithoutPathExpansion("string", NULL)); + EXPECT_FALSE(main_dict.GetDoubleWithoutPathExpansion("binary", NULL)); + EXPECT_FALSE(main_dict.GetDoubleWithoutPathExpansion("dict", NULL)); + EXPECT_FALSE(main_dict.GetDoubleWithoutPathExpansion("list", NULL)); + EXPECT_FALSE(main_dict.GetDoubleWithoutPathExpansion("DNE", NULL)); + + EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion( + "bool", static_cast<std::string*>(NULL))); + EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion( + "int", static_cast<std::string*>(NULL))); + EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion( + "double", static_cast<std::string*>(NULL))); + EXPECT_TRUE(main_dict.GetStringWithoutPathExpansion( + "string", static_cast<std::string*>(NULL))); + EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion( + "binary", static_cast<std::string*>(NULL))); + EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion( + "dict", static_cast<std::string*>(NULL))); + EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion( + "list", static_cast<std::string*>(NULL))); + EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion( + "DNE", static_cast<std::string*>(NULL))); + + EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion( + "bool", static_cast<string16*>(NULL))); + EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion( + "int", static_cast<string16*>(NULL))); + EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion( + "double", static_cast<string16*>(NULL))); + EXPECT_TRUE(main_dict.GetStringWithoutPathExpansion( + "string", static_cast<string16*>(NULL))); + EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion( + "binary", static_cast<string16*>(NULL))); + EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion( + "dict", static_cast<string16*>(NULL))); + EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion( + "list", static_cast<string16*>(NULL))); + EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion( + "DNE", static_cast<string16*>(NULL))); + + // There is no GetBinaryWithoutPathExpansion for some reason, but if there + // were it should be tested here... + + EXPECT_FALSE(main_dict.GetDictionaryWithoutPathExpansion("bool", NULL)); + EXPECT_FALSE(main_dict.GetDictionaryWithoutPathExpansion("int", NULL)); + EXPECT_FALSE(main_dict.GetDictionaryWithoutPathExpansion("double", NULL)); + EXPECT_FALSE(main_dict.GetDictionaryWithoutPathExpansion("string", NULL)); + EXPECT_FALSE(main_dict.GetDictionaryWithoutPathExpansion("binary", NULL)); + EXPECT_TRUE(main_dict.GetDictionaryWithoutPathExpansion("dict", NULL)); + EXPECT_FALSE(main_dict.GetDictionaryWithoutPathExpansion("list", NULL)); + EXPECT_FALSE(main_dict.GetDictionaryWithoutPathExpansion("DNE", NULL)); + + EXPECT_FALSE(main_dict.GetListWithoutPathExpansion("bool", NULL)); + EXPECT_FALSE(main_dict.GetListWithoutPathExpansion("int", NULL)); + EXPECT_FALSE(main_dict.GetListWithoutPathExpansion("double", NULL)); + EXPECT_FALSE(main_dict.GetListWithoutPathExpansion("string", NULL)); + EXPECT_FALSE(main_dict.GetListWithoutPathExpansion("binary", NULL)); + EXPECT_FALSE(main_dict.GetListWithoutPathExpansion("dict", NULL)); + EXPECT_TRUE(main_dict.GetListWithoutPathExpansion("list", NULL)); + EXPECT_FALSE(main_dict.GetListWithoutPathExpansion("DNE", NULL)); + + EXPECT_TRUE(main_list.Get(0, NULL)); + EXPECT_TRUE(main_list.Get(1, NULL)); + EXPECT_TRUE(main_list.Get(2, NULL)); + EXPECT_TRUE(main_list.Get(3, NULL)); + EXPECT_TRUE(main_list.Get(4, NULL)); + EXPECT_TRUE(main_list.Get(5, NULL)); + EXPECT_TRUE(main_list.Get(6, NULL)); + EXPECT_FALSE(main_list.Get(7, NULL)); + + EXPECT_TRUE(main_list.GetBoolean(0, NULL)); + EXPECT_FALSE(main_list.GetBoolean(1, NULL)); + EXPECT_FALSE(main_list.GetBoolean(2, NULL)); + EXPECT_FALSE(main_list.GetBoolean(3, NULL)); + EXPECT_FALSE(main_list.GetBoolean(4, NULL)); + EXPECT_FALSE(main_list.GetBoolean(5, NULL)); + EXPECT_FALSE(main_list.GetBoolean(6, NULL)); + EXPECT_FALSE(main_list.GetBoolean(7, NULL)); + + EXPECT_FALSE(main_list.GetInteger(0, NULL)); + EXPECT_TRUE(main_list.GetInteger(1, NULL)); + EXPECT_FALSE(main_list.GetInteger(2, NULL)); + EXPECT_FALSE(main_list.GetInteger(3, NULL)); + EXPECT_FALSE(main_list.GetInteger(4, NULL)); + EXPECT_FALSE(main_list.GetInteger(5, NULL)); + EXPECT_FALSE(main_list.GetInteger(6, NULL)); + EXPECT_FALSE(main_list.GetInteger(7, NULL)); + + EXPECT_FALSE(main_list.GetDouble(0, NULL)); + EXPECT_TRUE(main_list.GetDouble(1, NULL)); + EXPECT_TRUE(main_list.GetDouble(2, NULL)); + EXPECT_FALSE(main_list.GetDouble(3, NULL)); + EXPECT_FALSE(main_list.GetDouble(4, NULL)); + EXPECT_FALSE(main_list.GetDouble(5, NULL)); + EXPECT_FALSE(main_list.GetDouble(6, NULL)); + EXPECT_FALSE(main_list.GetDouble(7, NULL)); + + EXPECT_FALSE(main_list.GetString(0, static_cast<std::string*>(NULL))); + EXPECT_FALSE(main_list.GetString(1, static_cast<std::string*>(NULL))); + EXPECT_FALSE(main_list.GetString(2, static_cast<std::string*>(NULL))); + EXPECT_TRUE(main_list.GetString(3, static_cast<std::string*>(NULL))); + EXPECT_FALSE(main_list.GetString(4, static_cast<std::string*>(NULL))); + EXPECT_FALSE(main_list.GetString(5, static_cast<std::string*>(NULL))); + EXPECT_FALSE(main_list.GetString(6, static_cast<std::string*>(NULL))); + EXPECT_FALSE(main_list.GetString(7, static_cast<std::string*>(NULL))); + + EXPECT_FALSE(main_list.GetString(0, static_cast<string16*>(NULL))); + EXPECT_FALSE(main_list.GetString(1, static_cast<string16*>(NULL))); + EXPECT_FALSE(main_list.GetString(2, static_cast<string16*>(NULL))); + EXPECT_TRUE(main_list.GetString(3, static_cast<string16*>(NULL))); + EXPECT_FALSE(main_list.GetString(4, static_cast<string16*>(NULL))); + EXPECT_FALSE(main_list.GetString(5, static_cast<string16*>(NULL))); + EXPECT_FALSE(main_list.GetString(6, static_cast<string16*>(NULL))); + EXPECT_FALSE(main_list.GetString(7, static_cast<string16*>(NULL))); + + EXPECT_FALSE(main_list.GetBinary(0, NULL)); + EXPECT_FALSE(main_list.GetBinary(1, NULL)); + EXPECT_FALSE(main_list.GetBinary(2, NULL)); + EXPECT_FALSE(main_list.GetBinary(3, NULL)); + EXPECT_TRUE(main_list.GetBinary(4, NULL)); + EXPECT_FALSE(main_list.GetBinary(5, NULL)); + EXPECT_FALSE(main_list.GetBinary(6, NULL)); + EXPECT_FALSE(main_list.GetBinary(7, NULL)); + + EXPECT_FALSE(main_list.GetDictionary(0, NULL)); + EXPECT_FALSE(main_list.GetDictionary(1, NULL)); + EXPECT_FALSE(main_list.GetDictionary(2, NULL)); + EXPECT_FALSE(main_list.GetDictionary(3, NULL)); + EXPECT_FALSE(main_list.GetDictionary(4, NULL)); + EXPECT_TRUE(main_list.GetDictionary(5, NULL)); + EXPECT_FALSE(main_list.GetDictionary(6, NULL)); + EXPECT_FALSE(main_list.GetDictionary(7, NULL)); + + EXPECT_FALSE(main_list.GetList(0, NULL)); + EXPECT_FALSE(main_list.GetList(1, NULL)); + EXPECT_FALSE(main_list.GetList(2, NULL)); + EXPECT_FALSE(main_list.GetList(3, NULL)); + EXPECT_FALSE(main_list.GetList(4, NULL)); + EXPECT_FALSE(main_list.GetList(5, NULL)); + EXPECT_TRUE(main_list.GetList(6, NULL)); + EXPECT_FALSE(main_list.GetList(7, NULL)); +} + } // namespace base diff --git a/chromium/base/win/OWNERS b/chromium/base/win/OWNERS index 3aae3d6bec8..65ed72168fd 100644 --- a/chromium/base/win/OWNERS +++ b/chromium/base/win/OWNERS @@ -1 +1,2 @@ cpu@chromium.org +rvargas@chromium.org diff --git a/chromium/base/win/dllmain.cc b/chromium/base/win/dllmain.cc index 9d2a6dc76ce..907c7f4034e 100644 --- a/chromium/base/win/dllmain.cc +++ b/chromium/base/win/dllmain.cc @@ -54,9 +54,9 @@ static void NTAPI on_callback(PVOID h, DWORD reason, PVOID reserved); #endif // _WIN64 -// Explicitly depend on tlssup.cc variable to bracket the list of TLS callbacks. -extern "C" PIMAGE_TLS_CALLBACK __xl_a; -extern "C" PIMAGE_TLS_CALLBACK __xl_z; +// Explicitly depend on VC\crt\src\tlssup.c variables +// to bracket the list of TLS callbacks. +extern "C" PIMAGE_TLS_CALLBACK __xl_a, __xl_z; // extern "C" suppresses C++ name mangling so we know the symbol names for the // linker /INCLUDE:symbol pragmas above. diff --git a/chromium/base/win/event_trace_consumer.h b/chromium/base/win/event_trace_consumer.h index c1b42b4fd93..9322e1e9b4f 100644 --- a/chromium/base/win/event_trace_consumer.h +++ b/chromium/base/win/event_trace_consumer.h @@ -16,7 +16,7 @@ namespace base { namespace win { // This class is a base class that makes it easier to consume events -// from realtime or file sessions. Concrete consumers need to sublass +// from realtime or file sessions. Concrete consumers need to subclass // a specialization of this class and override the ProcessEvent and/or // the ProcessBuffer methods to implement the event consumption logic. // Usage might look like: diff --git a/chromium/base/win/event_trace_consumer_unittest.cc b/chromium/base/win/event_trace_consumer_unittest.cc index d238192c4f8..9066a7c8ff0 100644 --- a/chromium/base/win/event_trace_consumer_unittest.cc +++ b/chromium/base/win/event_trace_consumer_unittest.cc @@ -43,10 +43,9 @@ class TestConsumer: public EtwTraceConsumerBase<TestConsumer> { } void ClearQueue() { - EventQueue::const_iterator it(events_.begin()), end(events_.end()); - - for (; it != end; ++it) { - delete [] it->MofData; + for (EventQueue::const_iterator it(events_.begin()), end(events_.end()); + it != end; ++it) { + delete[] it->MofData; } events_.clear(); @@ -56,7 +55,7 @@ class TestConsumer: public EtwTraceConsumerBase<TestConsumer> { events_.push_back(*event); EVENT_TRACE& back = events_.back(); - if (NULL != event->MofData && 0 != event->MofLength) { + if (event->MofData != NULL && event->MofLength != 0) { back.MofData = new char[event->MofLength]; memcpy(back.MofData, event->MofData, event->MofLength); } @@ -94,7 +93,7 @@ class EtwTraceConsumerBaseTest: public testing::Test { } virtual void TearDown() { - // Cleanup any potentially danging sessions. + // Cleanup any potentially dangling sessions. EtwTraceProperties ignore; EtwTraceController::Stop(session_name_.c_str(), &ignore); } @@ -112,14 +111,12 @@ TEST_F(EtwTraceConsumerBaseTest, Initialize) { TEST_F(EtwTraceConsumerBaseTest, OpenRealtimeSucceedsWhenNoSession) { TestConsumer consumer_; - ASSERT_HRESULT_SUCCEEDED( consumer_.OpenRealtimeSession(session_name_.c_str())); } TEST_F(EtwTraceConsumerBaseTest, ConsumerImmediateFailureWhenNoSession) { TestConsumer consumer_; - ASSERT_HRESULT_SUCCEEDED( consumer_.OpenRealtimeSession(session_name_.c_str())); ASSERT_HRESULT_FAILED(consumer_.Consume()); @@ -131,22 +128,18 @@ class EtwTraceConsumerRealtimeTest: public EtwTraceConsumerBaseTest { public: virtual void SetUp() { EtwTraceConsumerBaseTest::SetUp(); - ASSERT_HRESULT_SUCCEEDED( consumer_.OpenRealtimeSession(session_name_.c_str())); } virtual void TearDown() { consumer_.Close(); - EtwTraceConsumerBaseTest::TearDown(); } DWORD ConsumerThread() { ::SetEvent(consumer_ready_.Get()); - - HRESULT hr = consumer_.Consume(); - return hr; + return consumer_.Consume(); } static DWORD WINAPI ConsumerThreadMainProc(void* arg) { @@ -157,12 +150,11 @@ class EtwTraceConsumerRealtimeTest: public EtwTraceConsumerBaseTest { HRESULT StartConsumerThread() { consumer_ready_.Set(::CreateEvent(NULL, TRUE, FALSE, NULL)); EXPECT_TRUE(consumer_ready_ != NULL); - consumer_thread_.Set(::CreateThread(NULL, 0, ConsumerThreadMainProc, - this, 0, NULL)); - if (NULL == consumer_thread_.Get()) + consumer_thread_.Set(::CreateThread(NULL, 0, ConsumerThreadMainProc, this, + 0, NULL)); + if (consumer_thread_.Get() == NULL) return HRESULT_FROM_WIN32(::GetLastError()); - HRESULT hr = S_OK; HANDLE events[] = { consumer_ready_, consumer_thread_ }; DWORD result = ::WaitForMultipleObjects(arraysize(events), events, FALSE, INFINITE); @@ -173,26 +165,21 @@ class EtwTraceConsumerRealtimeTest: public EtwTraceConsumerBaseTest { case WAIT_OBJECT_0 + 1: { // The thread finished. This may race with the event, so check // explicitly for the event here, before concluding there's trouble. - if (WAIT_OBJECT_0 == ::WaitForSingleObject(consumer_ready_, 0)) + if (::WaitForSingleObject(consumer_ready_, 0) == WAIT_OBJECT_0) return S_OK; DWORD exit_code = 0; if (::GetExitCodeThread(consumer_thread_, &exit_code)) return exit_code; - else - return HRESULT_FROM_WIN32(::GetLastError()); - break; + return HRESULT_FROM_WIN32(::GetLastError()); } default: return E_UNEXPECTED; - break; } - - return hr; } // Waits for consumer_ thread to exit, and returns its exit code. HRESULT JoinConsumerThread() { - if (WAIT_OBJECT_0 != ::WaitForSingleObject(consumer_thread_, INFINITE)) + if (::WaitForSingleObject(consumer_thread_, INFINITE) != WAIT_OBJECT_0) return HRESULT_FROM_WIN32(::GetLastError()); DWORD exit_code = 0; @@ -211,10 +198,8 @@ class EtwTraceConsumerRealtimeTest: public EtwTraceConsumerBaseTest { TEST_F(EtwTraceConsumerRealtimeTest, ConsumerReturnsWhenSessionClosed) { EtwTraceController controller; - - HRESULT hr = controller.StartRealtimeSession(session_name_.c_str(), - 100 * 1024); - if (hr == E_ACCESSDENIED) { + if (controller.StartRealtimeSession(session_name_.c_str(), 100 * 1024) == + E_ACCESSDENIED) { VLOG(1) << "You must be an administrator to run this test on Vista"; return; } @@ -224,7 +209,6 @@ TEST_F(EtwTraceConsumerRealtimeTest, ConsumerReturnsWhenSessionClosed) { // Wait around for the consumer_ thread a bit. ASSERT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(consumer_thread_, 50)); - ASSERT_HRESULT_SUCCEEDED(controller.Stop(NULL)); // The consumer_ returns success on session stop. @@ -234,34 +218,32 @@ TEST_F(EtwTraceConsumerRealtimeTest, ConsumerReturnsWhenSessionClosed) { namespace { // {57E47923-A549-476f-86CA-503D57F59E62} -DEFINE_GUID(kTestEventType, - 0x57e47923, 0xa549, 0x476f, 0x86, 0xca, 0x50, 0x3d, 0x57, 0xf5, 0x9e, 0x62); +DEFINE_GUID( + kTestEventType, + 0x57e47923, 0xa549, 0x476f, 0x86, 0xca, 0x50, 0x3d, 0x57, 0xf5, 0x9e, 0x62); } // namespace TEST_F(EtwTraceConsumerRealtimeTest, ConsumeEvent) { EtwTraceController controller; - HRESULT hr = controller.StartRealtimeSession(session_name_.c_str(), - 100 * 1024); - if (hr == E_ACCESSDENIED) { + if (controller.StartRealtimeSession(session_name_.c_str(), 100 * 1024) == + E_ACCESSDENIED) { VLOG(1) << "You must be an administrator to run this test on Vista"; return; } - ASSERT_HRESULT_SUCCEEDED(controller.EnableProvider(test_provider_, - TRACE_LEVEL_VERBOSE, 0xFFFFFFFF)); + ASSERT_HRESULT_SUCCEEDED(controller.EnableProvider( + test_provider_, TRACE_LEVEL_VERBOSE, 0xFFFFFFFF)); EtwTraceProvider provider(test_provider_); ASSERT_EQ(ERROR_SUCCESS, provider.Register()); // Start the consumer_. ASSERT_HRESULT_SUCCEEDED(StartConsumerThread()); - ASSERT_EQ(0, TestConsumer::events_.size()); EtwMofEvent<1> event(kTestEventType, 1, TRACE_LEVEL_ERROR); EXPECT_EQ(ERROR_SUCCESS, provider.Log(&event.header)); - EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(TestConsumer::sank_event_, INFINITE)); ASSERT_HRESULT_SUCCEEDED(controller.Stop(NULL)); @@ -306,8 +288,8 @@ class EtwTraceConsumerDataTest: public EtwTraceConsumerBaseTest { return hr; // Enable our provider. - EXPECT_HRESULT_SUCCEEDED(controller.EnableProvider(test_provider_, - TRACE_LEVEL_VERBOSE, 0xFFFFFFFF)); + EXPECT_HRESULT_SUCCEEDED(controller.EnableProvider( + test_provider_, TRACE_LEVEL_VERBOSE, 0xFFFFFFFF)); EtwTraceProvider provider(test_provider_); // Then register our provider, means we get a session handle immediately. @@ -374,7 +356,7 @@ TEST_F(EtwTraceConsumerDataTest, RoundTrip) { return; } ASSERT_HRESULT_SUCCEEDED(hr) << "RoundTripEvent failed"; - ASSERT_TRUE(NULL != trace); + ASSERT_TRUE(trace != NULL); ASSERT_EQ(sizeof(kData), trace->MofLength); ASSERT_STREQ(kData, reinterpret_cast<const char*>(trace->MofData)); } diff --git a/chromium/base/win/event_trace_controller.cc b/chromium/base/win/event_trace_controller.cc index 0391fbc3016..9a35a6bd57a 100644 --- a/chromium/base/win/event_trace_controller.cc +++ b/chromium/base/win/event_trace_controller.cc @@ -64,7 +64,7 @@ HRESULT EtwTraceController::Start(const wchar_t* session_name, } HRESULT EtwTraceController::StartFileSession(const wchar_t* session_name, - const wchar_t* logfile_path, bool realtime) { + const wchar_t* logfile_path, bool realtime) { DCHECK(NULL == session_ && session_name_.empty()); EtwTraceProperties prop; diff --git a/chromium/base/win/iat_patch_function.cc b/chromium/base/win/iat_patch_function.cc index a4a89028b87..21c39950cc3 100644 --- a/chromium/base/win/iat_patch_function.cc +++ b/chromium/base/win/iat_patch_function.cc @@ -56,11 +56,23 @@ DWORD ModifyCode(void* old_code, void* new_code, int length) { } // Change the page protection so that we can write. + MEMORY_BASIC_INFORMATION memory_info; DWORD error = NO_ERROR; DWORD old_page_protection = 0; + + if (!VirtualQuery(old_code, &memory_info, sizeof(memory_info))) { + error = GetLastError(); + return error; + } + + DWORD is_executable = (PAGE_EXECUTE | PAGE_EXECUTE_READ | + PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) & + memory_info.Protect; + if (VirtualProtect(old_code, length, - PAGE_READWRITE, + is_executable ? PAGE_EXECUTE_READWRITE : + PAGE_READWRITE, &old_page_protection)) { // Write the data. @@ -74,7 +86,6 @@ DWORD ModifyCode(void* old_code, void* new_code, int length) { &old_page_protection); } else { error = GetLastError(); - NOTREACHED(); } return error; @@ -274,5 +285,10 @@ DWORD IATPatchFunction::Unpatch() { return error; } +void* IATPatchFunction::original_function() const { + DCHECK(is_patched()); + return original_function_; +} + } // namespace win } // namespace base diff --git a/chromium/base/win/iat_patch_function.h b/chromium/base/win/iat_patch_function.h index 3ae1f3c460a..5026e0eb954 100644 --- a/chromium/base/win/iat_patch_function.h +++ b/chromium/base/win/iat_patch_function.h @@ -57,6 +57,8 @@ class BASE_EXPORT IATPatchFunction { return (NULL != intercept_function_); } + void* original_function() const; + private: HMODULE module_handle_; void* intercept_function_; diff --git a/chromium/base/win/message_window.cc b/chromium/base/win/message_window.cc index 56660740fc1..57fe64c7981 100644 --- a/chromium/base/win/message_window.cc +++ b/chromium/base/win/message_window.cc @@ -52,7 +52,7 @@ MessageWindow::WindowClass::WindowClass() window_class.hIconSm = NULL; atom_ = RegisterClassEx(&window_class); if (atom_ == 0) { - LOG_GETLASTERROR(ERROR) + PLOG(ERROR) << "Failed to register the window class for a message-only window"; } } @@ -108,7 +108,7 @@ bool MessageWindow::DoCreate(const MessageCallback& message_callback, window_ = CreateWindow(MAKEINTATOM(window_class.atom()), window_name, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, window_class.instance(), this); if (!window_) { - LOG_GETLASTERROR(ERROR) << "Failed to create a message-only window"; + PLOG(ERROR) << "Failed to create a message-only window"; return false; } diff --git a/chromium/base/win/metro.cc b/chromium/base/win/metro.cc index c78cc0946ee..62743c799b5 100644 --- a/chromium/base/win/metro.cc +++ b/chromium/base/win/metro.cc @@ -12,10 +12,6 @@ namespace base { namespace win { -namespace { -bool g_should_tsf_aware_required = false; -} - HMODULE GetMetroModule() { const HMODULE kUninitialized = reinterpret_cast<HMODULE>(1); static HMODULE metro_module = kUninitialized; @@ -70,40 +66,6 @@ bool IsProcessImmersive(HANDLE process) { return false; } -bool IsTSFAwareRequired() { - // Although this function is equal to IsMetroProcess at this moment, - // Chrome for Win7 and Vista may support TSF in the future. - return g_should_tsf_aware_required || IsMetroProcess(); -} - -void SetForceToUseTSF() { - g_should_tsf_aware_required = true; - - // Since Windows 8 Metro mode disables CUAS (Cicero Unaware Application - // Support) via ImmDisableLegacyIME API, Chrome must be fully TSF-aware on - // Metro mode. For debugging purposes, explicitly call ImmDisableLegacyIME so - // that one can test TSF functionality even on Windows 8 desktop mode. Note - // that CUAS cannot be disabled on Windows Vista/7 where ImmDisableLegacyIME - // is not available. - typedef BOOL (* ImmDisableLegacyIMEFunc)(); - HMODULE imm32 = ::GetModuleHandleA("imm32.dll"); - if (imm32 == NULL) - return; - - ImmDisableLegacyIMEFunc imm_disable_legacy_ime = - reinterpret_cast<ImmDisableLegacyIMEFunc>( - ::GetProcAddress(imm32, "ImmDisableLegacyIME")); - - if (imm_disable_legacy_ime == NULL) { - // Unsupported API, just do nothing. - return; - } - - if (!imm_disable_legacy_ime()) { - DVLOG(1) << "Failed to disable legacy IME."; - } -} - wchar_t* LocalAllocAndCopyString(const string16& src) { size_t dest_size = (src.length() + 1) * sizeof(wchar_t); wchar_t* dest = reinterpret_cast<wchar_t*>(LocalAlloc(LPTR, dest_size)); diff --git a/chromium/base/win/metro.h b/chromium/base/win/metro.h index b2208fcb49c..5894ef06dae 100644 --- a/chromium/base/win/metro.h +++ b/chromium/base/win/metro.h @@ -76,17 +76,6 @@ BASE_EXPORT bool IsMetroProcess(); // immersive (Metro) process. BASE_EXPORT bool IsProcessImmersive(HANDLE process); -// Returns true if this process is running under Text Services Framework (TSF) -// and browser must be TSF-aware. -BASE_EXPORT bool IsTSFAwareRequired(); - -// Sets browser to use Text Services Framework (TSF) regardless of process -// status. On Windows 8, this function also disables CUAS (Cicero Unaware -// Application Support) to emulate Windows Metro mode in terms of IME -// functionality. This should be beneficial in QA process because on can test -// IME functionality in Windows 8 desktop mode. -BASE_EXPORT void SetForceToUseTSF(); - // Allocates and returns the destination string via the LocalAlloc API after // copying the src to it. BASE_EXPORT wchar_t* LocalAllocAndCopyString(const string16& src); diff --git a/chromium/base/win/object_watcher.cc b/chromium/base/win/object_watcher.cc index 078f5b9fa1c..3bb1cd32c52 100644 --- a/chromium/base/win/object_watcher.cc +++ b/chromium/base/win/object_watcher.cc @@ -43,7 +43,7 @@ bool ObjectWatcher::StartWatching(HANDLE object, Delegate* delegate) { if (!RegisterWaitForSingleObject(&wait_object_, object, DoneWaiting, this, INFINITE, wait_flags)) { - DLOG_GETLASTERROR(FATAL) << "RegisterWaitForSingleObject failed"; + DPLOG(FATAL) << "RegisterWaitForSingleObject failed"; object_ = NULL; wait_object_ = NULL; return false; @@ -65,7 +65,7 @@ bool ObjectWatcher::StopWatching() { // Blocking call to cancel the wait. Any callbacks already in progress will // finish before we return from this call. if (!UnregisterWaitEx(wait_object_, INVALID_HANDLE_VALUE)) { - DLOG_GETLASTERROR(FATAL) << "UnregisterWaitEx failed"; + DPLOG(FATAL) << "UnregisterWaitEx failed"; return false; } diff --git a/chromium/base/win/registry.cc b/chromium/base/win/registry.cc index 8bfe4329711..a6cb9ae89f7 100644 --- a/chromium/base/win/registry.cc +++ b/chromium/base/win/registry.cc @@ -10,8 +10,7 @@ #include "base/logging.h" #include "base/strings/string_util.h" #include "base/threading/thread_restrictions.h" - -#pragma comment(lib, "shlwapi.lib") // for SHDeleteKey +#include "base/win/windows_version.h" namespace base { namespace win { @@ -30,23 +29,29 @@ inline DWORD to_wchar_size(DWORD byte_size) { return (byte_size + sizeof(wchar_t) - 1) / sizeof(wchar_t); } +// Mask to pull WOW64 access flags out of REGSAM access. +const REGSAM kWow64AccessMask = KEY_WOW64_32KEY | KEY_WOW64_64KEY; + } // namespace // RegKey ---------------------------------------------------------------------- RegKey::RegKey() : key_(NULL), - watch_event_(0) { + watch_event_(0), + wow64access_(0) { } RegKey::RegKey(HKEY key) : key_(key), - watch_event_(0) { + watch_event_(0), + wow64access_(0) { } RegKey::RegKey(HKEY rootkey, const wchar_t* subkey, REGSAM access) : key_(NULL), - watch_event_(0) { + watch_event_(0), + wow64access_(0) { if (rootkey) { if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK)) Create(rootkey, subkey, access); @@ -54,6 +59,7 @@ RegKey::RegKey(HKEY rootkey, const wchar_t* subkey, REGSAM access) Open(rootkey, subkey, access); } else { DCHECK(!subkey); + wow64access_ = access & kWow64AccessMask; } } @@ -69,43 +75,77 @@ LONG RegKey::Create(HKEY rootkey, const wchar_t* subkey, REGSAM access) { LONG RegKey::CreateWithDisposition(HKEY rootkey, const wchar_t* subkey, DWORD* disposition, REGSAM access) { DCHECK(rootkey && subkey && access && disposition); - Close(); - + HKEY subhkey = NULL; LONG result = RegCreateKeyEx(rootkey, subkey, 0, NULL, - REG_OPTION_NON_VOLATILE, access, NULL, &key_, + REG_OPTION_NON_VOLATILE, access, NULL, &subhkey, disposition); + if (result == ERROR_SUCCESS) { + Close(); + key_ = subhkey; + wow64access_ = access & kWow64AccessMask; + } + return result; } LONG RegKey::CreateKey(const wchar_t* name, REGSAM access) { DCHECK(name && access); + // After the application has accessed an alternate registry view using one of + // the [KEY_WOW64_32KEY / KEY_WOW64_64KEY] flags, all subsequent operations + // (create, delete, or open) on child registry keys must explicitly use the + // same flag. Otherwise, there can be unexpected behavior. + // http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129.aspx. + if ((access & kWow64AccessMask) != wow64access_) { + NOTREACHED(); + return ERROR_INVALID_PARAMETER; + } HKEY subkey = NULL; LONG result = RegCreateKeyEx(key_, name, 0, NULL, REG_OPTION_NON_VOLATILE, access, NULL, &subkey, NULL); - Close(); + if (result == ERROR_SUCCESS) { + Close(); + key_ = subkey; + wow64access_ = access & kWow64AccessMask; + } - key_ = subkey; return result; } LONG RegKey::Open(HKEY rootkey, const wchar_t* subkey, REGSAM access) { DCHECK(rootkey && subkey && access); - Close(); + HKEY subhkey = NULL; + + LONG result = RegOpenKeyEx(rootkey, subkey, 0, access, &subhkey); + if (result == ERROR_SUCCESS) { + Close(); + key_ = subhkey; + wow64access_ = access & kWow64AccessMask; + } - LONG result = RegOpenKeyEx(rootkey, subkey, 0, access, &key_); return result; } LONG RegKey::OpenKey(const wchar_t* relative_key_name, REGSAM access) { DCHECK(relative_key_name && access); + // After the application has accessed an alternate registry view using one of + // the [KEY_WOW64_32KEY / KEY_WOW64_64KEY] flags, all subsequent operations + // (create, delete, or open) on child registry keys must explicitly use the + // same flag. Otherwise, there can be unexpected behavior. + // http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129.aspx. + if ((access & kWow64AccessMask) != wow64access_) { + NOTREACHED(); + return ERROR_INVALID_PARAMETER; + } HKEY subkey = NULL; LONG result = RegOpenKeyEx(key_, relative_key_name, 0, access, &subkey); // We have to close the current opened key before replacing it with the new // one. - Close(); - - key_ = subkey; + if (result == ERROR_SUCCESS) { + Close(); + key_ = subkey; + wow64access_ = access & kWow64AccessMask; + } return result; } @@ -114,9 +154,11 @@ void RegKey::Close() { if (key_) { ::RegCloseKey(key_); key_ = NULL; + wow64access_ = 0; } } +// TODO(wfh): Remove this and other unsafe methods. See http://crbug.com/375400 void RegKey::Set(HKEY key) { if (key_ != key) { Close(); @@ -125,6 +167,7 @@ void RegKey::Set(HKEY key) { } HKEY RegKey::Take() { + DCHECK(wow64access_ == 0); StopWatching(); HKEY key = key_; key_ = NULL; @@ -155,8 +198,43 @@ LONG RegKey::GetValueNameAt(int index, std::wstring* name) const { LONG RegKey::DeleteKey(const wchar_t* name) { DCHECK(key_); DCHECK(name); - LONG result = SHDeleteKey(key_, name); - return result; + HKEY subkey = NULL; + + // Verify the key exists before attempting delete to replicate previous + // behavior. + LONG result = + RegOpenKeyEx(key_, name, 0, READ_CONTROL | wow64access_, &subkey); + if (result != ERROR_SUCCESS) + return result; + RegCloseKey(subkey); + + return RegDelRecurse(key_, std::wstring(name), wow64access_); +} + +LONG RegKey::DeleteEmptyKey(const wchar_t* name) { + DCHECK(key_); + DCHECK(name); + + HKEY target_key = NULL; + LONG result = RegOpenKeyEx(key_, name, 0, KEY_READ | wow64access_, + &target_key); + + if (result != ERROR_SUCCESS) + return result; + + DWORD count = 0; + result = RegQueryInfoKey(target_key, NULL, 0, NULL, NULL, NULL, NULL, &count, + NULL, NULL, NULL, NULL); + + RegCloseKey(target_key); + + if (result != ERROR_SUCCESS) + return result; + + if (count == 0) + return RegDeleteKeyExWrapper(key_, name, wow64access_, 0); + + return ERROR_DIR_NOT_EMPTY; } LONG RegKey::DeleteValue(const wchar_t* value_name) { @@ -329,6 +407,83 @@ LONG RegKey::StopWatching() { return result; } +// static +LONG RegKey::RegDeleteKeyExWrapper(HKEY hKey, + const wchar_t* lpSubKey, + REGSAM samDesired, + DWORD Reserved) { + typedef LSTATUS(WINAPI* RegDeleteKeyExPtr)(HKEY, LPCWSTR, REGSAM, DWORD); + + RegDeleteKeyExPtr reg_delete_key_ex_func = + reinterpret_cast<RegDeleteKeyExPtr>( + GetProcAddress(GetModuleHandleA("advapi32.dll"), "RegDeleteKeyExW")); + + if (reg_delete_key_ex_func) + return reg_delete_key_ex_func(hKey, lpSubKey, samDesired, Reserved); + + // Windows XP does not support RegDeleteKeyEx, so fallback to RegDeleteKey. + return RegDeleteKey(hKey, lpSubKey); +} + +// static +LONG RegKey::RegDelRecurse(HKEY root_key, + const std::wstring& name, + REGSAM access) { + // First, see if the key can be deleted without having to recurse. + LONG result = RegDeleteKeyExWrapper(root_key, name.c_str(), access, 0); + if (result == ERROR_SUCCESS) + return result; + + HKEY target_key = NULL; + result = RegOpenKeyEx( + root_key, name.c_str(), 0, KEY_ENUMERATE_SUB_KEYS | access, &target_key); + + if (result == ERROR_FILE_NOT_FOUND) + return ERROR_SUCCESS; + if (result != ERROR_SUCCESS) + return result; + + std::wstring subkey_name(name); + + // Check for an ending slash and add one if it is missing. + if (!name.empty() && subkey_name[name.length() - 1] != L'\\') + subkey_name += L"\\"; + + // Enumerate the keys + result = ERROR_SUCCESS; + const DWORD kMaxKeyNameLength = MAX_PATH; + const size_t base_key_length = subkey_name.length(); + std::wstring key_name; + while (result == ERROR_SUCCESS) { + DWORD key_size = kMaxKeyNameLength; + result = RegEnumKeyEx(target_key, + 0, + WriteInto(&key_name, kMaxKeyNameLength), + &key_size, + NULL, + NULL, + NULL, + NULL); + + if (result != ERROR_SUCCESS) + break; + + key_name.resize(key_size); + subkey_name.resize(base_key_length); + subkey_name += key_name; + + if (RegDelRecurse(root_key, subkey_name, access) != ERROR_SUCCESS) + break; + } + + RegCloseKey(target_key); + + // Try again to delete the key. + result = RegDeleteKeyExWrapper(root_key, name.c_str(), access, 0); + + return result; +} + // RegistryValueIterator ------------------------------------------------------ RegistryValueIterator::RegistryValueIterator(HKEY root_key, diff --git a/chromium/base/win/registry.h b/chromium/base/win/registry.h index f97f4f5a379..af1aee7dce9 100644 --- a/chromium/base/win/registry.h +++ b/chromium/base/win/registry.h @@ -53,11 +53,11 @@ class BASE_EXPORT RegKey { // Transfers ownership away from this object. HKEY Take(); - // Returns false if this key does not have the specified value, of if an error + // Returns false if this key does not have the specified value, or if an error // occurrs while attempting to access it. bool HasValue(const wchar_t* value_name) const; - // Returns the number of values for this key, of 0 if the number cannot be + // Returns the number of values for this key, or 0 if the number cannot be // determined. DWORD GetValueCount() const; @@ -71,6 +71,10 @@ class BASE_EXPORT RegKey { // it. LONG DeleteKey(const wchar_t* name); + // Deletes an empty subkey. If the subkey has subkeys or values then this + // will fail. + LONG DeleteEmptyKey(const wchar_t* name); + // Deletes a single value within the key. LONG DeleteValue(const wchar_t* name); @@ -132,8 +136,20 @@ class BASE_EXPORT RegKey { HKEY Handle() const { return key_; } private: + // Calls RegDeleteKeyEx on supported platforms, alternatively falls back to + // RegDeleteKey. + static LONG RegDeleteKeyExWrapper(HKEY hKey, + const wchar_t* lpSubKey, + REGSAM samDesired, + DWORD Reserved); + + // Recursively deletes a key and all of its subkeys. + static LONG RegDelRecurse(HKEY root_key, + const std::wstring& name, + REGSAM access); HKEY key_; // The registry key being iterated. HANDLE watch_event_; + REGSAM wow64access_; DISALLOW_COPY_AND_ASSIGN(RegKey); }; diff --git a/chromium/base/win/registry_unittest.cc b/chromium/base/win/registry_unittest.cc index 155402a351f..84074b38586 100644 --- a/chromium/base/win/registry_unittest.cc +++ b/chromium/base/win/registry_unittest.cc @@ -9,6 +9,7 @@ #include "base/compiler_specific.h" #include "base/stl_util.h" +#include "base/win/windows_version.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { @@ -16,31 +17,54 @@ namespace win { namespace { -const wchar_t kRootKey[] = L"Base_Registry_Unittest"; - class RegistryTest : public testing::Test { - public: - RegistryTest() {} - protected: +#if defined(_WIN64) + static const REGSAM kNativeViewMask = KEY_WOW64_64KEY; + static const REGSAM kRedirectedViewMask = KEY_WOW64_32KEY; +#else + static const REGSAM kNativeViewMask = KEY_WOW64_32KEY; + static const REGSAM kRedirectedViewMask = KEY_WOW64_64KEY; +#endif // _WIN64 + + RegistryTest() {} virtual void SetUp() OVERRIDE { // Create a temporary key. RegKey key(HKEY_CURRENT_USER, L"", KEY_ALL_ACCESS); key.DeleteKey(kRootKey); ASSERT_NE(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_READ)); ASSERT_EQ(ERROR_SUCCESS, key.Create(HKEY_CURRENT_USER, kRootKey, KEY_READ)); + foo_software_key_ = L"Software\\"; + foo_software_key_ += kRootKey; + foo_software_key_ += L"\\Foo"; } virtual void TearDown() OVERRIDE { // Clean up the temporary key. RegKey key(HKEY_CURRENT_USER, L"", KEY_SET_VALUE); ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(kRootKey)); + ASSERT_NE(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_READ)); } + static bool IsRedirectorPresent() { +#if defined(_WIN64) + return true; +#else + return OSInfo::GetInstance()->wow64_status() == OSInfo::WOW64_ENABLED; +#endif + } + + const wchar_t* const kRootKey = L"Base_Registry_Unittest"; + std::wstring foo_software_key_; + private: DISALLOW_COPY_AND_ASSIGN(RegistryTest); }; +// static +const REGSAM RegistryTest::kNativeViewMask; +const REGSAM RegistryTest::kRedirectedViewMask; + TEST_F(RegistryTest, ValueTest) { RegKey key; @@ -158,6 +182,173 @@ TEST_F(RegistryTest, TruncatedCharTest) { EXPECT_FALSE(iterator.Valid()); } +TEST_F(RegistryTest, RecursiveDelete) { + RegKey key; + // Create kRootKey->Foo + // \->Bar (TestValue) + // \->Foo (TestValue) + // \->Bar + // \->Foo + // \->Moo + // \->Foo + // and delete kRootKey->Foo + std::wstring foo_key(kRootKey); + foo_key += L"\\Foo"; + ASSERT_EQ(ERROR_SUCCESS, + key.Create(HKEY_CURRENT_USER, foo_key.c_str(), KEY_WRITE)); + ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Bar", KEY_WRITE)); + ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(L"TestValue", L"TestData")); + ASSERT_EQ(ERROR_SUCCESS, + key.Create(HKEY_CURRENT_USER, foo_key.c_str(), KEY_WRITE)); + ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Moo", KEY_WRITE)); + ASSERT_EQ(ERROR_SUCCESS, + key.Create(HKEY_CURRENT_USER, foo_key.c_str(), KEY_WRITE)); + ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Foo", KEY_WRITE)); + foo_key += L"\\Bar"; + ASSERT_EQ(ERROR_SUCCESS, + key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_WRITE)); + foo_key += L"\\Foo"; + ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Foo", KEY_WRITE)); + ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(L"TestValue", L"TestData")); + ASSERT_EQ(ERROR_SUCCESS, + key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_READ)); + + ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_WRITE)); + ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(L"Bar")); + ASSERT_NE(ERROR_SUCCESS, key.DeleteEmptyKey(L"Foo")); + ASSERT_NE(ERROR_SUCCESS, key.DeleteEmptyKey(L"Foo\\Bar\\Foo")); + ASSERT_NE(ERROR_SUCCESS, key.DeleteEmptyKey(L"Foo\\Bar")); + ASSERT_EQ(ERROR_SUCCESS, key.DeleteEmptyKey(L"Foo\\Foo")); + + ASSERT_EQ(ERROR_SUCCESS, + key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_WRITE)); + ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Bar", KEY_WRITE)); + ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Foo", KEY_WRITE)); + ASSERT_EQ(ERROR_SUCCESS, + key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_WRITE)); + ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(L"")); + ASSERT_NE(ERROR_SUCCESS, + key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_READ)); + + ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_WRITE)); + ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(L"Foo")); + ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(L"Foo")); + ASSERT_NE(ERROR_SUCCESS, + key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_READ)); +} + +// This test requires running as an Administrator as it tests redirected +// registry writes to HKLM\Software +// http://msdn.microsoft.com/en-us/library/windows/desktop/aa384253.aspx +// TODO(wfh): flaky test on Vista. See http://crbug.com/377917 +TEST_F(RegistryTest, DISABLED_Wow64RedirectedFromNative) { + if (!IsRedirectorPresent()) + return; + + RegKey key; + + // Test redirected key access from non-redirected. + ASSERT_EQ(ERROR_SUCCESS, + key.Create(HKEY_LOCAL_MACHINE, + foo_software_key_.c_str(), + KEY_WRITE | kRedirectedViewMask)); + ASSERT_NE(ERROR_SUCCESS, + key.Open(HKEY_LOCAL_MACHINE, foo_software_key_.c_str(), KEY_READ)); + ASSERT_NE(ERROR_SUCCESS, + key.Open(HKEY_LOCAL_MACHINE, + foo_software_key_.c_str(), + KEY_READ | kNativeViewMask)); + + // Open the non-redirected view of the parent and try to delete the test key. + ASSERT_EQ(ERROR_SUCCESS, + key.Open(HKEY_LOCAL_MACHINE, L"Software", KEY_SET_VALUE)); + ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(kRootKey)); + ASSERT_EQ(ERROR_SUCCESS, + key.Open(HKEY_LOCAL_MACHINE, + L"Software", + KEY_SET_VALUE | kNativeViewMask)); + ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(kRootKey)); + + // Open the redirected view and delete the key created above. + ASSERT_EQ(ERROR_SUCCESS, + key.Open(HKEY_LOCAL_MACHINE, + L"Software", + KEY_SET_VALUE | kRedirectedViewMask)); + ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(kRootKey)); +} + +// Test for the issue found in http://crbug.com/384587 where OpenKey would call +// Close() and reset wow64_access_ flag to 0 and cause a NOTREACHED to hit on a +// subsequent OpenKey call. +TEST_F(RegistryTest, SameWowFlags) { + RegKey key; + + ASSERT_EQ(ERROR_SUCCESS, + key.Open(HKEY_LOCAL_MACHINE, + L"Software", + KEY_READ | KEY_WOW64_64KEY)); + ASSERT_EQ(ERROR_SUCCESS, + key.OpenKey(L"Microsoft", + KEY_READ | KEY_WOW64_64KEY)); + ASSERT_EQ(ERROR_SUCCESS, + key.OpenKey(L"Windows", + KEY_READ | KEY_WOW64_64KEY)); +} + +// TODO(wfh): flaky test on Vista. See http://crbug.com/377917 +TEST_F(RegistryTest, DISABLED_Wow64NativeFromRedirected) { + if (!IsRedirectorPresent()) + return; + RegKey key; + + // Test non-redirected key access from redirected. + ASSERT_EQ(ERROR_SUCCESS, + key.Create(HKEY_LOCAL_MACHINE, + foo_software_key_.c_str(), + KEY_WRITE | kNativeViewMask)); + ASSERT_EQ(ERROR_SUCCESS, + key.Open(HKEY_LOCAL_MACHINE, foo_software_key_.c_str(), KEY_READ)); + ASSERT_NE(ERROR_SUCCESS, + key.Open(HKEY_LOCAL_MACHINE, + foo_software_key_.c_str(), + KEY_READ | kRedirectedViewMask)); + + // Open the redirected view of the parent and try to delete the test key + // from the non-redirected view. + ASSERT_EQ(ERROR_SUCCESS, + key.Open(HKEY_LOCAL_MACHINE, + L"Software", + KEY_SET_VALUE | kRedirectedViewMask)); + ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(kRootKey)); + + ASSERT_EQ(ERROR_SUCCESS, + key.Open(HKEY_LOCAL_MACHINE, + L"Software", + KEY_SET_VALUE | kNativeViewMask)); + ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(kRootKey)); +} + +TEST_F(RegistryTest, OpenSubKey) { + RegKey key; + ASSERT_EQ(ERROR_SUCCESS, + key.Open(HKEY_CURRENT_USER, + kRootKey, + KEY_READ | KEY_CREATE_SUB_KEY)); + + ASSERT_NE(ERROR_SUCCESS, key.OpenKey(L"foo", KEY_READ)); + ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"foo", KEY_READ)); + ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_READ)); + ASSERT_EQ(ERROR_SUCCESS, key.OpenKey(L"foo", KEY_READ)); + + std::wstring foo_key(kRootKey); + foo_key += L"\\Foo"; + ASSERT_EQ(ERROR_SUCCESS, + key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_READ)); + + ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_WRITE)); + ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(L"foo")); +} + } // namespace } // namespace win diff --git a/chromium/base/win/scoped_com_initializer.h b/chromium/base/win/scoped_com_initializer.h index 392c351cc7d..92228baa507 100644 --- a/chromium/base/win/scoped_com_initializer.h +++ b/chromium/base/win/scoped_com_initializer.h @@ -16,6 +16,11 @@ namespace win { // Initializes COM in the constructor (STA or MTA), and uninitializes COM in the // destructor. +// +// WARNING: This should only be used once per thread, ideally scoped to a +// similar lifetime as the thread itself. You should not be using this in +// random utility functions that make COM calls -- instead ensure these +// functions are running on a COM-supporting thread! class ScopedCOMInitializer { public: // Enum value provided to initialize the thread as an MTA instead of STA. diff --git a/chromium/base/win/scoped_gdi_object.h b/chromium/base/win/scoped_gdi_object.h index d44310a1598..57b013e2fba 100644 --- a/chromium/base/win/scoped_gdi_object.h +++ b/chromium/base/win/scoped_gdi_object.h @@ -60,7 +60,7 @@ class ScopedGDIObject { // An explicit specialization for HICON because we have to call DestroyIcon() // instead of DeleteObject() for HICON. template<> -void ScopedGDIObject<HICON>::Close() { +void inline ScopedGDIObject<HICON>::Close() { if (object_) DestroyIcon(object_); } diff --git a/chromium/base/win/scoped_handle.h b/chromium/base/win/scoped_handle.h index 0d038e010bd..a85e08d26e5 100644 --- a/chromium/base/win/scoped_handle.h +++ b/chromium/base/win/scoped_handle.h @@ -27,11 +27,9 @@ namespace win { // Generic wrapper for raw handles that takes care of closing handles // automatically. The class interface follows the style of -// the ScopedStdioHandle class with a few additions: +// the ScopedFILE class with one addition: // - IsValid() method can tolerate multiple invalid handle values such as NULL // and INVALID_HANDLE_VALUE (-1) for Win32 handles. -// - Receive() method allows to receive a handle value from a function that -// takes a raw handle pointer only. template <class Traits, class Verifier> class GenericScopedHandle { MOVE_ONLY_TYPE_FOR_CPP_03(GenericScopedHandle, RValue) @@ -168,7 +166,7 @@ class BASE_EXPORT VerifierTraits { DISALLOW_IMPLICIT_CONSTRUCTORS(VerifierTraits); }; -typedef GenericScopedHandle<HandleTraits, VerifierTraits> ScopedHandle; +typedef GenericScopedHandle<HandleTraits, DummyVerifierTraits> ScopedHandle; } // namespace win } // namespace base diff --git a/chromium/base/win/scoped_process_information_unittest.cc b/chromium/base/win/scoped_process_information_unittest.cc index 550076ef9c6..0b720cb1a39 100644 --- a/chromium/base/win/scoped_process_information_unittest.cc +++ b/chromium/base/win/scoped_process_information_unittest.cc @@ -46,8 +46,7 @@ MULTIPROCESS_TEST_MAIN(ReturnNine) { void ScopedProcessInformationTest::DoCreateProcess( const std::string& main_id, PROCESS_INFORMATION* process_handle) { - std::wstring cmd_line = - this->MakeCmdLine(main_id, false).GetCommandLineString(); + std::wstring cmd_line = MakeCmdLine(main_id).GetCommandLineString(); STARTUPINFO startup_info = {}; startup_info.cb = sizeof(startup_info); diff --git a/chromium/base/win/shortcut.cc b/chromium/base/win/shortcut.cc index 57a93dc6524..7dace5988d0 100644 --- a/chromium/base/win/shortcut.cc +++ b/chromium/base/win/shortcut.cc @@ -11,6 +11,7 @@ #include "base/file_util.h" #include "base/threading/thread_restrictions.h" #include "base/win/scoped_comptr.h" +#include "base/win/scoped_propvariant.h" #include "base/win/win_util.h" #include "base/win/windows_version.h" @@ -172,52 +173,138 @@ bool CreateOrUpdateShortcutLink(const FilePath& shortcut_path, return succeeded; } -bool ResolveShortcut(const FilePath& shortcut_path, - FilePath* target_path, - string16* args) { +bool ResolveShortcutProperties(const FilePath& shortcut_path, + uint32 options, + ShortcutProperties* properties) { + DCHECK(options && properties); base::ThreadRestrictions::AssertIOAllowed(); - HRESULT result; + if (options & ~ShortcutProperties::PROPERTIES_ALL) + NOTREACHED() << "Unhandled property is used."; + ScopedComPtr<IShellLink> i_shell_link; // Get pointer to the IShellLink interface. - result = i_shell_link.CreateInstance(CLSID_ShellLink, NULL, - CLSCTX_INPROC_SERVER); - if (FAILED(result)) + if (FAILED(i_shell_link.CreateInstance(CLSID_ShellLink, NULL, + CLSCTX_INPROC_SERVER))) { return false; + } ScopedComPtr<IPersistFile> persist; // Query IShellLink for the IPersistFile interface. - result = persist.QueryFrom(i_shell_link); - if (FAILED(result)) + if (FAILED(persist.QueryFrom(i_shell_link))) return false; // Load the shell link. - result = persist->Load(shortcut_path.value().c_str(), STGM_READ); - if (FAILED(result)) + if (FAILED(persist->Load(shortcut_path.value().c_str(), STGM_READ))) return false; - WCHAR temp[MAX_PATH]; - if (target_path) { + // Reset |properties|. + properties->options = 0; + + wchar_t temp[MAX_PATH]; + if (options & ShortcutProperties::PROPERTIES_TARGET) { // Try to find the target of a shortcut. - result = i_shell_link->Resolve(0, SLR_NO_UI | SLR_NOSEARCH); - if (FAILED(result)) + if (FAILED(i_shell_link->Resolve(0, SLR_NO_UI | SLR_NOSEARCH))) return false; + if (FAILED(i_shell_link->GetPath(temp, MAX_PATH, NULL, SLGP_UNCPRIORITY))) + return false; + properties->set_target(FilePath(temp)); + } - result = i_shell_link->GetPath(temp, MAX_PATH, NULL, SLGP_UNCPRIORITY); - if (FAILED(result)) + if (options & ShortcutProperties::PROPERTIES_WORKING_DIR) { + if (FAILED(i_shell_link->GetWorkingDirectory(temp, MAX_PATH))) return false; + properties->set_working_dir(FilePath(temp)); + } - *target_path = FilePath(temp); + if (options & ShortcutProperties::PROPERTIES_ARGUMENTS) { + if (FAILED(i_shell_link->GetArguments(temp, MAX_PATH))) + return false; + properties->set_arguments(temp); } - if (args) { - result = i_shell_link->GetArguments(temp, MAX_PATH); - if (FAILED(result)) + if (options & ShortcutProperties::PROPERTIES_DESCRIPTION) { + // Note: description length constrained by MAX_PATH. + if (FAILED(i_shell_link->GetDescription(temp, MAX_PATH))) return false; + properties->set_description(temp); + } - *args = string16(temp); + if (options & ShortcutProperties::PROPERTIES_ICON) { + int temp_index; + if (FAILED(i_shell_link->GetIconLocation(temp, MAX_PATH, &temp_index))) + return false; + properties->set_icon(FilePath(temp), temp_index); } + + // Windows 7+ options, avoiding unnecessary work. + if ((options & ShortcutProperties::PROPERTIES_WIN7) && + GetVersion() >= VERSION_WIN7) { + ScopedComPtr<IPropertyStore> property_store; + if (FAILED(property_store.QueryFrom(i_shell_link))) + return false; + + if (options & ShortcutProperties::PROPERTIES_APP_ID) { + ScopedPropVariant pv_app_id; + if (property_store->GetValue(PKEY_AppUserModel_ID, + pv_app_id.Receive()) != S_OK) { + return false; + } + switch (pv_app_id.get().vt) { + case VT_EMPTY: + properties->set_app_id(L""); + break; + case VT_LPWSTR: + properties->set_app_id(pv_app_id.get().pwszVal); + break; + default: + NOTREACHED() << "Unexpected variant type: " << pv_app_id.get().vt; + return false; + } + } + + if (options & ShortcutProperties::PROPERTIES_DUAL_MODE) { + ScopedPropVariant pv_dual_mode; + if (property_store->GetValue(PKEY_AppUserModel_IsDualMode, + pv_dual_mode.Receive()) != S_OK) { + return false; + } + switch (pv_dual_mode.get().vt) { + case VT_EMPTY: + properties->set_dual_mode(false); + break; + case VT_BOOL: + properties->set_dual_mode(pv_dual_mode.get().boolVal == VARIANT_TRUE); + break; + default: + NOTREACHED() << "Unexpected variant type: " << pv_dual_mode.get().vt; + return false; + } + } + } + + return true; +} + +bool ResolveShortcut(const FilePath& shortcut_path, + FilePath* target_path, + string16* args) { + uint32 options = 0; + if (target_path) + options |= ShortcutProperties::PROPERTIES_TARGET; + if (args) + options |= ShortcutProperties::PROPERTIES_ARGUMENTS; + DCHECK(options); + + ShortcutProperties properties; + if (!ResolveShortcutProperties(shortcut_path, options, &properties)) + return false; + + if (target_path) + *target_path = properties.target; + if (args) + *args = properties.arguments; return true; } @@ -237,7 +324,7 @@ bool TaskbarUnpinShortcutLink(const wchar_t* shortcut) { base::ThreadRestrictions::AssertIOAllowed(); // "Unpin from taskbar" is only supported after Win7. - if (base::win::GetVersion() < base::win::VERSION_WIN7) + if (GetVersion() < VERSION_WIN7) return false; int result = reinterpret_cast<int>(ShellExecute(NULL, L"taskbarunpin", diff --git a/chromium/base/win/shortcut.h b/chromium/base/win/shortcut.h index 0f5fb0f686b..13940ed52c4 100644 --- a/chromium/base/win/shortcut.h +++ b/chromium/base/win/shortcut.h @@ -31,13 +31,21 @@ enum ShortcutOperation { // setting |options| as desired. struct ShortcutProperties { enum IndividualProperties { - PROPERTIES_TARGET = 1 << 0, - PROPERTIES_WORKING_DIR = 1 << 1, - PROPERTIES_ARGUMENTS = 1 << 2, - PROPERTIES_DESCRIPTION = 1 << 3, - PROPERTIES_ICON = 1 << 4, - PROPERTIES_APP_ID = 1 << 5, - PROPERTIES_DUAL_MODE = 1 << 6, + PROPERTIES_TARGET = 1U << 0, + PROPERTIES_WORKING_DIR = 1U << 1, + PROPERTIES_ARGUMENTS = 1U << 2, + PROPERTIES_DESCRIPTION = 1U << 3, + PROPERTIES_ICON = 1U << 4, + PROPERTIES_APP_ID = 1U << 5, + PROPERTIES_DUAL_MODE = 1U << 6, + // Be sure to update the values below when adding a new property. + PROPERTIES_BASIC = PROPERTIES_TARGET | + PROPERTIES_WORKING_DIR | + PROPERTIES_ARGUMENTS | + PROPERTIES_DESCRIPTION | + PROPERTIES_ICON, + PROPERTIES_WIN7 = PROPERTIES_APP_ID | PROPERTIES_DUAL_MODE, + PROPERTIES_ALL = PROPERTIES_BASIC | PROPERTIES_WIN7 }; ShortcutProperties() : icon_index(-1), dual_mode(false), options(0U) {} @@ -117,14 +125,25 @@ BASE_EXPORT bool CreateOrUpdateShortcutLink( const ShortcutProperties& properties, ShortcutOperation operation); -// Resolve Windows shortcut (.LNK file) -// This methods tries to resolve a shortcut .LNK file. The path of the shortcut -// to resolve is in |shortcut_path|. If |target_path| is not NULL, the target -// will be resolved and placed in |target_path|. If |args| is not NULL, the -// arguments will be retrieved and placed in |args|. The function returns true -// if all requested fields are found successfully. -// Callers can safely use the same variable for both |shortcut_path| and -// |target_path|. +// Resolves Windows shortcut (.LNK file) +// This methods tries to resolve selected properties of a shortcut .LNK file. +// The path of the shortcut to resolve is in |shortcut_path|. |options| is a bit +// field composed of ShortcutProperties::IndividualProperties, to specify which +// properties to read. It should be non-0. The resulting data are read into +// |properties|, which must not be NULL. The function returns true if all +// requested properties are successfully read. Otherwise some reads have failed +// and intermediate values written to |properties| should be ignored. +BASE_EXPORT bool ResolveShortcutProperties(const FilePath& shortcut_path, + uint32 options, + ShortcutProperties* properties); + +// Resolves Windows shortcut (.LNK file). +// This is a wrapper to ResolveShortcutProperties() to handle the common use +// case of resolving target and arguments. |target_path| and |args| are +// optional output variables that are ignored if NULL (but at least one must be +// non-NULL). The function returns true if all requested fields are found +// successfully. Callers can safely use the same variable for both +// |shortcut_path| and |target_path|. BASE_EXPORT bool ResolveShortcut(const FilePath& shortcut_path, FilePath* target_path, string16* args); diff --git a/chromium/base/win/shortcut_unittest.cc b/chromium/base/win/shortcut_unittest.cc index eaf152eb9dc..7a6f41dae92 100644 --- a/chromium/base/win/shortcut_unittest.cc +++ b/chromium/base/win/shortcut_unittest.cc @@ -12,6 +12,7 @@ #include "base/test/test_file_util.h" #include "base/test/test_shortcut_win.h" #include "base/win/scoped_com_initializer.h" +#include "base/win/windows_version.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { @@ -33,8 +34,7 @@ class ShortcutTest : public testing::Test { // Shortcut 1's properties { const FilePath target_file(temp_dir_.path().Append(L"Target 1.txt")); - file_util::WriteFile(target_file, kFileContents, - arraysize(kFileContents)); + WriteFile(target_file, kFileContents, arraysize(kFileContents)); link_properties_.set_target(target_file); link_properties_.set_working_dir(temp_dir_.path()); @@ -48,11 +48,10 @@ class ShortcutTest : public testing::Test { // Shortcut 2's properties (all different from properties of shortcut 1). { const FilePath target_file_2(temp_dir_.path().Append(L"Target 2.txt")); - file_util::WriteFile(target_file_2, kFileContents2, - arraysize(kFileContents2)); + WriteFile(target_file_2, kFileContents2, arraysize(kFileContents2)); FilePath icon_path_2; - base::CreateTemporaryFileInDir(temp_dir_.path(), &icon_path_2); + CreateTemporaryFileInDir(temp_dir_.path(), &icon_path_2); link_properties_2_.set_target(target_file_2); link_properties_2_.set_working_dir(temp_dir_2_.path()); @@ -80,6 +79,56 @@ class ShortcutTest : public testing::Test { } // namespace +TEST_F(ShortcutTest, CreateAndResolveShortcutProperties) { + uint32 valid_properties = ShortcutProperties::PROPERTIES_BASIC; + if (GetVersion() >= VERSION_WIN7) + valid_properties |= ShortcutProperties::PROPERTIES_WIN7; + + // Test all properties. + FilePath file_1(temp_dir_.path().Append(L"Link1.lnk")); + ASSERT_TRUE(CreateOrUpdateShortcutLink( + file_1, link_properties_, SHORTCUT_CREATE_ALWAYS)); + + ShortcutProperties properties_read_1; + ASSERT_TRUE(ResolveShortcutProperties( + file_1, ShortcutProperties::PROPERTIES_ALL, &properties_read_1)); + EXPECT_EQ(valid_properties, properties_read_1.options); + ValidatePathsAreEqual(link_properties_.target, properties_read_1.target); + ValidatePathsAreEqual(link_properties_.working_dir, + properties_read_1.working_dir); + EXPECT_EQ(link_properties_.arguments, properties_read_1.arguments); + EXPECT_EQ(link_properties_.description, properties_read_1.description); + ValidatePathsAreEqual(link_properties_.icon, properties_read_1.icon); + EXPECT_EQ(link_properties_.icon_index, properties_read_1.icon_index); + if (GetVersion() >= VERSION_WIN7) { + EXPECT_EQ(link_properties_.app_id, properties_read_1.app_id); + EXPECT_EQ(link_properties_.dual_mode, properties_read_1.dual_mode); + } + + // Test simple shortcut with no special properties set. + FilePath file_2(temp_dir_.path().Append(L"Link2.lnk")); + ShortcutProperties only_target_properties; + only_target_properties.set_target(link_properties_.target); + ASSERT_TRUE(CreateOrUpdateShortcutLink( + file_2, only_target_properties, SHORTCUT_CREATE_ALWAYS)); + + ShortcutProperties properties_read_2; + ASSERT_TRUE(ResolveShortcutProperties( + file_2, ShortcutProperties::PROPERTIES_ALL, &properties_read_2)); + EXPECT_EQ(valid_properties, properties_read_2.options); + ValidatePathsAreEqual(only_target_properties.target, + properties_read_2.target); + ValidatePathsAreEqual(FilePath(), properties_read_2.working_dir); + EXPECT_EQ(L"", properties_read_2.arguments); + EXPECT_EQ(L"", properties_read_2.description); + ValidatePathsAreEqual(FilePath(), properties_read_2.icon); + EXPECT_EQ(0, properties_read_2.icon_index); + if (GetVersion() >= VERSION_WIN7) { + EXPECT_EQ(L"", properties_read_2.app_id); + EXPECT_FALSE(properties_read_2.dual_mode); + } +} + TEST_F(ShortcutTest, CreateAndResolveShortcut) { ShortcutProperties only_target_properties; only_target_properties.set_target(link_properties_.target); diff --git a/chromium/base/win/startup_information_unittest.cc b/chromium/base/win/startup_information_unittest.cc index d637ebd68a1..43276c5ee34 100644 --- a/chromium/base/win/startup_information_unittest.cc +++ b/chromium/base/win/startup_information_unittest.cc @@ -62,7 +62,7 @@ TEST_F(StartupInformationTest, InheritStdOut) { sizeof(events[0]))); std::wstring cmd_line = - this->MakeCmdLine("FireInheritedEvents", false).GetCommandLineString(); + MakeCmdLine("FireInheritedEvents").GetCommandLineString(); PROCESS_INFORMATION temp_process_info = {}; ASSERT_TRUE(::CreateProcess(NULL, const_cast<wchar_t*>(cmd_line.c_str()), diff --git a/chromium/base/win/text_services_message_filter.cc b/chromium/base/win/text_services_message_filter.cc deleted file mode 100644 index 7ce233d9fd4..00000000000 --- a/chromium/base/win/text_services_message_filter.cc +++ /dev/null @@ -1,82 +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/win/text_services_message_filter.h" - -namespace base { -namespace win { - -TextServicesMessageFilter::TextServicesMessageFilter() - : client_id_(TF_CLIENTID_NULL), - is_initialized_(false) { -} - -TextServicesMessageFilter::~TextServicesMessageFilter() { - if (is_initialized_) - thread_mgr_->Deactivate(); -} - -bool TextServicesMessageFilter::Init() { - if (FAILED(thread_mgr_.CreateInstance(CLSID_TF_ThreadMgr))) - return false; - - if (FAILED(message_pump_.QueryFrom(thread_mgr_))) - return false; - - if (FAILED(keystroke_mgr_.QueryFrom(thread_mgr_))) - return false; - - if (FAILED(thread_mgr_->Activate(&client_id_))) - return false; - - is_initialized_ = true; - return is_initialized_; -} - -// Wraps for ITfMessagePump::PeekMessage with win32 PeekMessage signature. -// Obtains messages from application message queue. -BOOL TextServicesMessageFilter::DoPeekMessage(MSG* msg, - HWND window_handle, - UINT msg_filter_min, - UINT msg_filter_max, - UINT remove_msg) { - BOOL result = FALSE; - if (FAILED(message_pump_->PeekMessage(msg, window_handle, - msg_filter_min, msg_filter_max, - remove_msg, &result))) { - result = FALSE; - } - - return result; -} - -// Sends message to Text Service Manager. -// The message will be used to input composition text. -// Returns true if |message| was consumed by text service manager. -bool TextServicesMessageFilter::ProcessMessage(const MSG& msg) { - if (msg.message == WM_KEYDOWN) { - BOOL eaten = FALSE; - HRESULT hr = keystroke_mgr_->TestKeyDown(msg.wParam, msg.lParam, &eaten); - if (FAILED(hr) && !eaten) - return false; - eaten = FALSE; - hr = keystroke_mgr_->KeyDown(msg.wParam, msg.lParam, &eaten); - return (SUCCEEDED(hr) && !!eaten); - } - - if (msg.message == WM_KEYUP) { - BOOL eaten = FALSE; - HRESULT hr = keystroke_mgr_->TestKeyUp(msg.wParam, msg.lParam, &eaten); - if (FAILED(hr) && !eaten) - return false; - eaten = FALSE; - hr = keystroke_mgr_->KeyUp(msg.wParam, msg.lParam, &eaten); - return (SUCCEEDED(hr) && !!eaten); - } - - return false; -} - -} // namespace win -} // namespace base diff --git a/chromium/base/win/text_services_message_filter.h b/chromium/base/win/text_services_message_filter.h deleted file mode 100644 index 704c1da640b..00000000000 --- a/chromium/base/win/text_services_message_filter.h +++ /dev/null @@ -1,48 +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_WIN_TEXT_SERVICES_MESSAGE_FILTER_H_ -#define BASE_WIN_TEXT_SERVICES_MESSAGE_FILTER_H_ - -#include <msctf.h> -#include <Windows.h> - -#include "base/memory/scoped_ptr.h" -#include "base/message_loop/message_pump_win.h" -#include "base/win/metro.h" -#include "base/win/scoped_comptr.h" - -namespace base { -namespace win { - -// TextServicesMessageFilter extends MessageFilter with methods that are using -// Text Services Framework COM component. -class BASE_EXPORT TextServicesMessageFilter - : public base::MessagePumpForUI::MessageFilter { - public: - TextServicesMessageFilter(); - virtual ~TextServicesMessageFilter(); - virtual BOOL DoPeekMessage(MSG* msg, - HWND window_handle, - UINT msg_filter_min, - UINT msg_filter_max, - UINT remove_msg) OVERRIDE; - virtual bool ProcessMessage(const MSG& msg) OVERRIDE; - - bool Init(); - - private: - TfClientId client_id_; - bool is_initialized_; - base::win::ScopedComPtr<ITfThreadMgr> thread_mgr_; - base::win::ScopedComPtr<ITfMessagePump> message_pump_; - base::win::ScopedComPtr<ITfKeystrokeMgr> keystroke_mgr_; - - DISALLOW_COPY_AND_ASSIGN(TextServicesMessageFilter); -}; - -} // namespace win -} // namespace base - -#endif // BASE_WIN_TEXT_SERVICES_MESSAGE_FILTER_H_ diff --git a/chromium/base/win/win_util.cc b/chromium/base/win/win_util.cc index 7b85418a1ea..ee923522fc9 100644 --- a/chromium/base/win/win_util.cc +++ b/chromium/base/win/win_util.cc @@ -6,6 +6,7 @@ #include <aclapi.h> #include <lm.h> +#include <powrprof.h> #include <shellapi.h> #include <shlobj.h> #include <shobjidl.h> // Must be before propkey. @@ -221,14 +222,29 @@ void SetAbortBehaviorForCrashReporting() { signal(SIGABRT, ForceCrashOnSigAbort); } -bool IsTouchEnabledDevice() { - if (base::win::GetVersion() < base::win::VERSION_WIN7) +bool IsTabletDevice() { + if (GetSystemMetrics(SM_MAXIMUMTOUCHES) == 0) return false; - const int kMultiTouch = NID_INTEGRATED_TOUCH | NID_MULTI_INPUT | NID_READY; - int sm = GetSystemMetrics(SM_DIGITIZER); - if ((sm & kMultiTouch) == kMultiTouch) { - return true; - } + + base::win::Version version = base::win::GetVersion(); + if (version == base::win::VERSION_XP) + return (GetSystemMetrics(SM_TABLETPC) != 0); + + // If the device is docked, the user is treating the device as a PC. + if (GetSystemMetrics(SM_SYSTEMDOCKED) != 0) + return false; + + // PlatformRoleSlate was only added in Windows 8, but prior to Win8 it is + // still possible to check for a mobile power profile. + POWER_PLATFORM_ROLE role = PowerDeterminePlatformRole(); + bool mobile_power_profile = (role == PlatformRoleMobile); + bool slate_power_profile = false; + if (version >= base::win::VERSION_WIN8) + slate_power_profile = (role == PlatformRoleSlate); + + if (mobile_power_profile || slate_power_profile) + return (GetSystemMetrics(SM_CONVERTIBLESLATEMODE) == 0); + return false; } @@ -329,41 +345,29 @@ bool DismissVirtualKeyboard() { typedef HWND (*MetroRootWindow) (); -// As of this writing, GetMonitorInfo function seem to return wrong values -// for rcWork.left and rcWork.top in case of split screen situation inside -// metro mode. In order to get required values we query for core window screen -// coordinates. -// TODO(shrikant): Remove detour code once GetMonitorInfo is fixed for 8.1. -BOOL GetMonitorInfoWrapper(HMONITOR monitor, MONITORINFO* mi) { - BOOL ret = ::GetMonitorInfo(monitor, mi); -#if !defined(USE_ASH) - if (base::win::IsMetroProcess() && - base::win::GetVersion() >= base::win::VERSION_WIN8_1) { - static MetroRootWindow root_window = NULL; - if (!root_window) { - HMODULE metro = base::win::GetMetroModule(); - // There are apparently instances when current process is inside metro - // environment but metro driver dll is not loaded. - if (!metro) { - return ret; - } - root_window = reinterpret_cast<MetroRootWindow>( - ::GetProcAddress(metro, "GetRootWindow")); - } - ret = ::GetWindowRect(root_window(), &(mi->rcWork)); - } -#endif - return ret; -} +enum DomainEnrollementState {UNKNOWN = -1, NOT_ENROLLED, ENROLLED}; +static volatile long int g_domain_state = UNKNOWN; bool IsEnrolledToDomain() { - LPWSTR domain; - NETSETUP_JOIN_STATUS join_status; - if(::NetGetJoinInformation(NULL, &domain, &join_status) != NERR_Success) - return false; - ::NetApiBufferFree(domain); + // Doesn't make any sense to retry inside a user session because joining a + // domain will only kick in on a restart. + if (g_domain_state == UNKNOWN) { + LPWSTR domain; + NETSETUP_JOIN_STATUS join_status; + if(::NetGetJoinInformation(NULL, &domain, &join_status) != NERR_Success) + return false; + ::NetApiBufferFree(domain); + ::InterlockedCompareExchange(&g_domain_state, + join_status == ::NetSetupDomainName ? + ENROLLED : NOT_ENROLLED, + UNKNOWN); + } + + return g_domain_state == ENROLLED; +} - return join_status == ::NetSetupDomainName; +void SetDomainStateForTesting(bool state) { + g_domain_state = state ? ENROLLED : NOT_ENROLLED; } } // namespace win diff --git a/chromium/base/win/win_util.h b/chromium/base/win/win_util.h index eebb64ac8ef..573c4c72506 100644 --- a/chromium/base/win/win_util.h +++ b/chromium/base/win/win_util.h @@ -107,9 +107,10 @@ BASE_EXPORT bool ShouldCrashOnProcessDetach(); // process is aborted. BASE_EXPORT void SetAbortBehaviorForCrashReporting(); -// A touch enabled device by this definition is something that has -// integrated multi-touch ready to use and has Windows version > Windows7. -BASE_EXPORT bool IsTouchEnabledDevice(); +// A tablet is a device that is touch enabled and also is being used +// "like a tablet". This is used primarily for metrics in order to gain some +// insight into how users use Chrome. +BASE_EXPORT bool IsTabletDevice(); // Get the size of a struct up to and including the specified member. // This is necessary to set compatible struct sizes for different versions @@ -126,13 +127,13 @@ BASE_EXPORT bool DisplayVirtualKeyboard(); // above. Returns true on success. BASE_EXPORT bool DismissVirtualKeyboard(); -// Returns monitor info after correcting rcWorkArea based on metro version. -// see bug #247430 for more details. -BASE_EXPORT BOOL GetMonitorInfoWrapper(HMONITOR monitor, MONITORINFO* mi); - // Returns true if the machine is enrolled to a domain. BASE_EXPORT bool IsEnrolledToDomain(); +// Used by tests to mock any wanted state. Call with |state| set to true to +// simulate being in a domain and false otherwise. +BASE_EXPORT void SetDomainStateForTesting(bool state); + } // namespace win } // namespace base diff --git a/chromium/base/x11/edid_parser_x11.cc b/chromium/base/x11/edid_parser_x11.cc deleted file mode 100644 index 74b14b65c43..00000000000 --- a/chromium/base/x11/edid_parser_x11.cc +++ /dev/null @@ -1,196 +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/x11/edid_parser_x11.h" - -#include <X11/extensions/Xrandr.h> -#include <X11/Xatom.h> -#include <X11/Xlib.h> - -#include "base/hash.h" -#include "base/message_loop/message_loop.h" -#include "base/strings/string_util.h" -#include "base/sys_byteorder.h" - -namespace { - -// Returns 64-bit persistent ID for the specified manufacturer's ID and -// product_code_hash, and the index of the output it is connected to. -// |output_index| is used to distinguish the displays of the same type. For -// example, swapping two identical display between two outputs will not be -// treated as swap. The 'serial number' field in EDID isn't used here because -// it is not guaranteed to have unique number and it may have the same fixed -// value (like 0). -int64 GetID(uint16 manufacturer_id, - uint32 product_code_hash, - uint8 output_index) { - return ((static_cast<int64>(manufacturer_id) << 40) | - (static_cast<int64>(product_code_hash) << 8) | output_index); -} - -bool IsRandRAvailable() { - int randr_version_major = 0; - int randr_version_minor = 0; - static bool is_randr_available = XRRQueryVersion( - base::MessagePumpX11::GetDefaultXDisplay(), - &randr_version_major, &randr_version_minor); - return is_randr_available; -} - -} // namespace - -namespace base { - -bool GetEDIDProperty(XID output, unsigned long* nitems, unsigned char** prop) { - if (!IsRandRAvailable()) - return false; - - Display* display = base::MessagePumpX11::GetDefaultXDisplay(); - - static Atom edid_property = XInternAtom( - base::MessagePumpX11::GetDefaultXDisplay(), - RR_PROPERTY_RANDR_EDID, false); - - bool has_edid_property = false; - int num_properties = 0; - Atom* properties = XRRListOutputProperties(display, output, &num_properties); - for (int i = 0; i < num_properties; ++i) { - if (properties[i] == edid_property) { - has_edid_property = true; - break; - } - } - XFree(properties); - if (!has_edid_property) - return false; - - Atom actual_type; - int actual_format; - unsigned long bytes_after; - XRRGetOutputProperty(display, - output, - edid_property, - 0, // offset - 128, // length - false, // _delete - false, // pending - AnyPropertyType, // req_type - &actual_type, - &actual_format, - nitems, - &bytes_after, - prop); - DCHECK_EQ(XA_INTEGER, actual_type); - DCHECK_EQ(8, actual_format); - return true; -} - -bool GetDisplayId(XID output_id, size_t output_index, int64* display_id_out) { - unsigned long nitems = 0; - unsigned char* prop = NULL; - if (!GetEDIDProperty(output_id, &nitems, &prop)) - return false; - - bool result = - GetDisplayIdFromEDID(prop, nitems, output_index, display_id_out); - XFree(prop); - return result; -} - -bool GetDisplayIdFromEDID(const unsigned char* prop, - unsigned long nitems, - size_t output_index, - int64* display_id_out) { - uint16 manufacturer_id = 0; - std::string product_name; - - // ParseOutputDeviceData fails if it doesn't have product_name. - ParseOutputDeviceData(prop, nitems, &manufacturer_id, &product_name); - - // Generates product specific value from product_name instead of product code. - // See crbug.com/240341 - uint32 product_code_hash = product_name.empty() ? - 0 : base::Hash(product_name); - if (manufacturer_id != 0) { - // An ID based on display's index will be assigned later if this call - // fails. - *display_id_out = GetID( - manufacturer_id, product_code_hash, output_index); - return true; - } - return false; -} - -bool ParseOutputDeviceData(const unsigned char* prop, - unsigned long nitems, - uint16* manufacturer_id, - std::string* human_readable_name) { - // See http://en.wikipedia.org/wiki/Extended_display_identification_data - // for the details of EDID data format. We use the following data: - // bytes 8-9: manufacturer EISA ID, in big-endian - // bytes 54-125: four descriptors (18-bytes each) which may contain - // the display name. - const unsigned int kManufacturerOffset = 8; - const unsigned int kManufacturerLength = 2; - const unsigned int kDescriptorOffset = 54; - const unsigned int kNumDescriptors = 4; - const unsigned int kDescriptorLength = 18; - // The specifier types. - const unsigned char kMonitorNameDescriptor = 0xfc; - - if (manufacturer_id) { - if (nitems < kManufacturerOffset + kManufacturerLength) { - LOG(ERROR) << "too short EDID data: manifacturer id"; - return false; - } - - *manufacturer_id = - *reinterpret_cast<const uint16*>(prop + kManufacturerOffset); -#if defined(ARCH_CPU_LITTLE_ENDIAN) - *manufacturer_id = base::ByteSwap(*manufacturer_id); -#endif - } - - if (!human_readable_name) - return true; - - human_readable_name->clear(); - for (unsigned int i = 0; i < kNumDescriptors; ++i) { - if (nitems < kDescriptorOffset + (i + 1) * kDescriptorLength) - break; - - const unsigned char* desc_buf = - prop + kDescriptorOffset + i * kDescriptorLength; - // If the descriptor contains the display name, it has the following - // structure: - // bytes 0-2, 4: \0 - // byte 3: descriptor type, defined above. - // bytes 5-17: text data, ending with \r, padding with spaces - // we should check bytes 0-2 and 4, since it may have other values in - // case that the descriptor contains other type of data. - if (desc_buf[0] == 0 && desc_buf[1] == 0 && desc_buf[2] == 0 && - desc_buf[4] == 0) { - if (desc_buf[3] == kMonitorNameDescriptor) { - std::string found_name( - reinterpret_cast<const char*>(desc_buf + 5), kDescriptorLength - 5); - TrimWhitespaceASCII(found_name, TRIM_TRAILING, human_readable_name); - break; - } - } - } - - // Verify if the |human_readable_name| consists of printable characters only. - for (size_t i = 0; i < human_readable_name->size(); ++i) { - char c = (*human_readable_name)[i]; - if (!isascii(c) || !isprint(c)) { - human_readable_name->clear(); - LOG(ERROR) << "invalid EDID: human unreadable char in name"; - return false; - } - } - - return true; -} - -} // namespace base diff --git a/chromium/base/x11/edid_parser_x11.h b/chromium/base/x11/edid_parser_x11.h deleted file mode 100644 index 0fba0b54d7b..00000000000 --- a/chromium/base/x11/edid_parser_x11.h +++ /dev/null @@ -1,54 +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. - -#ifndef BASE_X11_EDID_PARSER_X11_H_ -#define BASE_X11_EDID_PARSER_X11_H_ - -#include <string> - -#include "base/base_export.h" -#include "base/basictypes.h" - -typedef unsigned long XID; - -// EDID (Extended Display Identification Data) is a format for monitor -// metadata. This provides a parser for the data and an interface to get it -// from XRandR. - -namespace base { - -// Get the EDID data from the |output| and stores to |prop|. |nitem| will store -// the number of characters |prop| will have. It doesn't take the ownership of -// |prop|, so caller must release it by XFree(). -// Returns true if EDID property is successfully obtained. Otherwise returns -// false and does not touch |prop| and |nitems|. -BASE_EXPORT bool GetEDIDProperty(XID output, - unsigned long* nitems, - unsigned char** prop); - -// Gets the EDID data from |output| and generates the display id through -// |GetDisplayIdFromEDID|. -BASE_EXPORT bool GetDisplayId(XID output, size_t index, - int64* display_id_out); - -// Generates the display id for the pair of |prop| with |nitems| length and -// |index|, and store in |display_id_out|. Returns true if the display id is -// successfully generated, or false otherwise. -BASE_EXPORT bool GetDisplayIdFromEDID(const unsigned char* prop, - unsigned long nitems, - size_t index, - int64* display_id_out); - -// Parses |prop| as EDID data and stores extracted data into |manufacturer_id| -// and |human_readable_name| and returns true. NULL can be passed for unwanted -// output parameters. Some devices (especially internal displays) may not have -// the field for |human_readable_name|, and it will return true in that case. -BASE_EXPORT bool ParseOutputDeviceData(const unsigned char* prop, - unsigned long nitems, - uint16* manufacturer_id, - std::string* human_readable_name); - -} // namespace base - -#endif // BASE_X11_EDID_PARSER_X11_H_ diff --git a/chromium/base/x11/edid_parser_x11_unittest.cc b/chromium/base/x11/edid_parser_x11_unittest.cc deleted file mode 100644 index 97e3ce10340..00000000000 --- a/chromium/base/x11/edid_parser_x11_unittest.cc +++ /dev/null @@ -1,167 +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/x11/edid_parser_x11.h" - -#include "base/memory/scoped_ptr.h" -#include "testing/gtest/include/gtest/gtest.h" - -#include <X11/extensions/Xrandr.h> - -namespace base { - -namespace { - -// Returns the number of characters in the string literal but doesn't count its -// terminator NULL byte. -#define charsize(str) (arraysize(str) - 1) - -// Sample EDID data extracted from real devices. -const unsigned char kNormalDisplay[] = - "\x00\xff\xff\xff\xff\xff\xff\x00\x22\xf0\x6c\x28\x01\x01\x01\x01" - "\x02\x16\x01\x04\xb5\x40\x28\x78\xe2\x8d\x85\xad\x4f\x35\xb1\x25" - "\x0e\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01" - "\x01\x01\x01\x01\x01\x01\xe2\x68\x00\xa0\xa0\x40\x2e\x60\x30\x20" - "\x36\x00\x81\x90\x21\x00\x00\x1a\xbc\x1b\x00\xa0\x50\x20\x17\x30" - "\x30\x20\x36\x00\x81\x90\x21\x00\x00\x1a\x00\x00\x00\xfc\x00\x48" - "\x50\x20\x5a\x52\x33\x30\x77\x0a\x20\x20\x20\x20\x00\x00\x00\xff" - "\x00\x43\x4e\x34\x32\x30\x32\x31\x33\x37\x51\x0a\x20\x20\x00\x71"; - -const unsigned char kInternalDisplay[] = - "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\xa3\x42\x31\x00\x00\x00\x00" - "\x00\x15\x01\x03\x80\x1a\x10\x78\x0a\xd3\xe5\x95\x5c\x60\x90\x27" - "\x19\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01" - "\x01\x01\x01\x01\x01\x01\x9e\x1b\x00\xa0\x50\x20\x12\x30\x10\x30" - "\x13\x00\x05\xa3\x10\x00\x00\x19\x00\x00\x00\x0f\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x23\x87\x02\x64\x00\x00\x00\x00\xfe\x00\x53" - "\x41\x4d\x53\x55\x4e\x47\x0a\x20\x20\x20\x20\x20\x00\x00\x00\xfe" - "\x00\x31\x32\x31\x41\x54\x31\x31\x2d\x38\x30\x31\x0a\x20\x00\x45"; - -const unsigned char kOverscanDisplay[] = - "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\x2d\xfe\x08\x00\x00\x00\x00" - "\x29\x15\x01\x03\x80\x10\x09\x78\x0a\xee\x91\xa3\x54\x4c\x99\x26" - "\x0f\x50\x54\xbd\xef\x80\x71\x4f\x81\xc0\x81\x00\x81\x80\x95\x00" - "\xa9\xc0\xb3\x00\x01\x01\x02\x3a\x80\x18\x71\x38\x2d\x40\x58\x2c" - "\x45\x00\xa0\x5a\x00\x00\x00\x1e\x66\x21\x56\xaa\x51\x00\x1e\x30" - "\x46\x8f\x33\x00\xa0\x5a\x00\x00\x00\x1e\x00\x00\x00\xfd\x00\x18" - "\x4b\x0f\x51\x17\x00\x0a\x20\x20\x20\x20\x20\x20\x00\x00\x00\xfc" - "\x00\x53\x41\x4d\x53\x55\x4e\x47\x0a\x20\x20\x20\x20\x20\x01\x1d" - "\x02\x03\x1f\xf1\x47\x90\x04\x05\x03\x20\x22\x07\x23\x09\x07\x07" - "\x83\x01\x00\x00\xe2\x00\x0f\x67\x03\x0c\x00\x20\x00\xb8\x2d\x01" - "\x1d\x80\x18\x71\x1c\x16\x20\x58\x2c\x25\x00\xa0\x5a\x00\x00\x00" - "\x9e\x01\x1d\x00\x72\x51\xd0\x1e\x20\x6e\x28\x55\x00\xa0\x5a\x00" - "\x00\x00\x1e\x8c\x0a\xd0\x8a\x20\xe0\x2d\x10\x10\x3e\x96\x00\xa0" - "\x5a\x00\x00\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc6"; - -const unsigned char kLP2565A[] = - "\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x22\xF0\x76\x26\x01\x01\x01\x01" - "\x02\x12\x01\x03\x80\x34\x21\x78\xEE\xEF\x95\xA3\x54\x4C\x9B\x26" - "\x0F\x50\x54\xA5\x6B\x80\x81\x40\x81\x80\x81\x99\x71\x00\xA9\x00" - "\xA9\x40\xB3\x00\xD1\x00\x28\x3C\x80\xA0\x70\xB0\x23\x40\x30\x20" - "\x36\x00\x07\x44\x21\x00\x00\x1A\x00\x00\x00\xFD\x00\x30\x55\x1E" - "\x5E\x11\x00\x0A\x20\x20\x20\x20\x20\x20\x00\x00\x00\xFC\x00\x48" - "\x50\x20\x4C\x50\x32\x34\x36\x35\x0A\x20\x20\x20\x00\x00\x00\xFF" - "\x00\x43\x4E\x4B\x38\x30\x32\x30\x34\x48\x4D\x0A\x20\x20\x00\xA4"; - -const unsigned char kLP2565B[] = - "\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x22\xF0\x75\x26\x01\x01\x01\x01" - "\x02\x12\x01\x03\x6E\x34\x21\x78\xEE\xEF\x95\xA3\x54\x4C\x9B\x26" - "\x0F\x50\x54\xA5\x6B\x80\x81\x40\x71\x00\xA9\x00\xA9\x40\xA9\x4F" - "\xB3\x00\xD1\xC0\xD1\x00\x28\x3C\x80\xA0\x70\xB0\x23\x40\x30\x20" - "\x36\x00\x07\x44\x21\x00\x00\x1A\x00\x00\x00\xFD\x00\x30\x55\x1E" - "\x5E\x15\x00\x0A\x20\x20\x20\x20\x20\x20\x00\x00\x00\xFC\x00\x48" - "\x50\x20\x4C\x50\x32\x34\x36\x35\x0A\x20\x20\x20\x00\x00\x00\xFF" - "\x00\x43\x4E\x4B\x38\x30\x32\x30\x34\x48\x4D\x0A\x20\x20\x00\x45"; - -} // namespace - -TEST(EdidParserX11Test, ParseEDID) { - uint16 manufacturer_id = 0; - std::string human_readable_name; - EXPECT_TRUE(ParseOutputDeviceData( - kNormalDisplay, charsize(kNormalDisplay), - &manufacturer_id, &human_readable_name)); - EXPECT_EQ(0x22f0u, manufacturer_id); - EXPECT_EQ("HP ZR30w", human_readable_name); - - manufacturer_id = 0; - human_readable_name.clear(); - EXPECT_TRUE(ParseOutputDeviceData( - kInternalDisplay, charsize(kInternalDisplay), - &manufacturer_id, NULL)); - EXPECT_EQ(0x4ca3u, manufacturer_id); - EXPECT_EQ("", human_readable_name); - - // Internal display doesn't have name. - EXPECT_TRUE(ParseOutputDeviceData( - kInternalDisplay, charsize(kInternalDisplay), - NULL, &human_readable_name)); - EXPECT_TRUE(human_readable_name.empty()); - - manufacturer_id = 0; - human_readable_name.clear(); - EXPECT_TRUE(ParseOutputDeviceData( - kOverscanDisplay, charsize(kOverscanDisplay), - &manufacturer_id, &human_readable_name)); - EXPECT_EQ(0x4c2du, manufacturer_id); - EXPECT_EQ("SAMSUNG", human_readable_name); -} - -TEST(EdidParserX11Test, ParseBrokenEDID) { - uint16 manufacturer_id = 0; - std::string human_readable_name; - - // length == 0 - EXPECT_FALSE(ParseOutputDeviceData( - kNormalDisplay, 0, - &manufacturer_id, &human_readable_name)); - - // name is broken. Copying kNormalDisplay and substitute its name data by - // some control code. - std::string display_data( - reinterpret_cast<const char*>(kNormalDisplay), charsize(kNormalDisplay)); - - // display's name data is embedded in byte 95-107 in this specific example. - // Fix here too when the contents of kNormalDisplay is altered. - display_data[97] = '\x1b'; - EXPECT_FALSE(ParseOutputDeviceData( - reinterpret_cast<const unsigned char*>(display_data.data()), - display_data.size(), - &manufacturer_id, &human_readable_name)); - - // If |human_readable_name| isn't specified, it skips parsing the name. - manufacturer_id = 0; - EXPECT_TRUE(ParseOutputDeviceData( - reinterpret_cast<const unsigned char*>(display_data.data()), - display_data.size(), - &manufacturer_id, NULL)); - EXPECT_EQ(0x22f0u, manufacturer_id); -} - -TEST(EdidParserX11Test, GetDisplayId) { - // EDID of kLP2565A and B are slightly different but actually the same device. - int64 id1 = -1; - int64 id2 = -1; - EXPECT_TRUE(GetDisplayIdFromEDID(kLP2565A, charsize(kLP2565A), 0, &id1)); - EXPECT_TRUE(GetDisplayIdFromEDID(kLP2565B, charsize(kLP2565B), 0, &id2)); - EXPECT_EQ(id1, id2); - EXPECT_NE(-1, id1); -} - -TEST(EdidParserX11Test, GetDisplayIdFromInternal) { - int64 id = -1; - EXPECT_TRUE(GetDisplayIdFromEDID( - kInternalDisplay, charsize(kInternalDisplay), 0, &id)); - EXPECT_NE(-1, id); -} - -TEST(EdidParserX11Test, GetDisplayIdFailure) { - int64 id = -1; - EXPECT_FALSE(GetDisplayIdFromEDID(NULL, 0, 0, &id)); - EXPECT_EQ(-1, id); -} - -} // namespace base diff --git a/chromium/base/x11/x11_error_tracker.cc b/chromium/base/x11/x11_error_tracker.cc deleted file mode 100644 index 446408c5c3d..00000000000 --- a/chromium/base/x11/x11_error_tracker.cc +++ /dev/null @@ -1,37 +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/x11/x11_error_tracker.h" - -#include "base/message_loop/message_pump_x11.h" - -namespace { - -unsigned char g_x11_error_code = 0; - -int X11ErrorHandler(Display* display, XErrorEvent* error) { - g_x11_error_code = error->error_code; - return 0; -} - -} - -namespace base { - -X11ErrorTracker::X11ErrorTracker() { - old_handler_ = XSetErrorHandler(X11ErrorHandler); -} - -X11ErrorTracker::~X11ErrorTracker() { - XSetErrorHandler(old_handler_); -} - -bool X11ErrorTracker::FoundNewError() { - XSync(MessagePumpForUI::GetDefaultXDisplay(), False); - unsigned char error = g_x11_error_code; - g_x11_error_code = 0; - return error != 0; -} - -} // namespace ui diff --git a/chromium/base/x11/x11_error_tracker.h b/chromium/base/x11/x11_error_tracker.h deleted file mode 100644 index 5542f52a056..00000000000 --- a/chromium/base/x11/x11_error_tracker.h +++ /dev/null @@ -1,38 +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_X11_X11_ERROR_TRACKER_H_ -#define BASE_X11_X11_ERROR_TRACKER_H_ - -#include <X11/Xlib.h> - -#include "base/base_export.h" -#include "base/basictypes.h" - -namespace base { - -// X11ErrorTracker catches X11 errors in a non-fatal way. It does so by -// temporarily changing the X11 error handler. The old error handler is -// restored when the tracker is destroyed. -class BASE_EXPORT X11ErrorTracker { - public: - X11ErrorTracker(); - ~X11ErrorTracker(); - - // Returns whether an X11 error happened since this function was last called - // (or since the creation of the tracker). This is potentially expensive, - // since this causes a sync with the X server. - bool FoundNewError(); - - private: -#if !defined(TOOLKIT_GTK) - XErrorHandler old_handler_; -#endif - - DISALLOW_COPY_AND_ASSIGN(X11ErrorTracker); -}; - -} // namespace base - -#endif // BASE_X11_X11_ERROR_TRACKER_H_ diff --git a/chromium/base/x11/x11_error_tracker_gtk.cc b/chromium/base/x11/x11_error_tracker_gtk.cc deleted file mode 100644 index 7a78a7c2750..00000000000 --- a/chromium/base/x11/x11_error_tracker_gtk.cc +++ /dev/null @@ -1,29 +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/x11/x11_error_tracker.h" - -#include <gdk/gdkx.h> - -#include "base/logging.h" - -namespace base { - -X11ErrorTracker::X11ErrorTracker() { - gdk_error_trap_push(); -} - -X11ErrorTracker::~X11ErrorTracker() { - gdk_error_trap_pop(); -} - -bool X11ErrorTracker::FoundNewError() { - gdk_flush(); - bool found_error = gdk_error_trap_pop() != 0; - - gdk_error_trap_push(); - return found_error; -} - -} // namespace base |